forked from errno-mmd/readfacevmd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsmoothvmd.cc
142 lines (131 loc) · 4.65 KB
/
smoothvmd.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// VMDモーションを平滑化する
#include <map>
#include <string>
#include <vector>
#include <Eigen/Core>
#include <unsupported/Eigen/FFT>
#include "VMD.h"
#include "MMDFileIOUtil.h"
#include "interpolate.h"
#include "reducevmd.h"
#include "smoothvmd.h"
using namespace Eigen;
using namespace MMDFileIOUtil;
using namespace std;
// ローパスフィルタ。cutoff_freqより高い周波数成分を除去する
void lowpass_filter(vector<float>& v, float cutoff_freq)
{
if (v.size() < 2) {
return;
}
FFT<float> fft;
vector<complex<float>> freqvec;
fft.fwd(freqvec, v);
// フィルタリング
const float sampling_freq = 30.0; // サンプリング周波数[Hz]。MMDは30FPSなので。
int data_size = v.size();
int cutoff_idx;
// 打ち切る位置(cutoff_idx)を求める: cutoff_idx / data_size = cutoff_freq / sampling_freq
cutoff_idx = cutoff_freq * data_size / sampling_freq;
vector<complex<float>> filtered(data_size);
for (int i = 0; i < data_size; i++) {
if (i < cutoff_idx) {
filtered[i] = freqvec[i];
} else {
filtered[i] = 0.0;
}
}
fft.inv(v, filtered);
}
// ボーンキーフレーム列fvの値を平滑化する
// 引数fvには同一ボーンのキーフレームがフレーム番号順に格納されているものとする
void smooth_bone_frame(vector<VMD_Frame>& fv, float cutoff_freq, bool bezier)
{
sort(fv.begin(), fv.end());
fv = fill_bone_frame(fv, bezier); // キーフレームの隙間をなくす
if (cutoff_freq < 0) {
return;
}
// ローパスフィルタにかける
vector<float> x;
for_each(fv.begin(), fv.end(), [&x](VMD_Frame f) { x.push_back(f.position.x()); });
lowpass_filter(x, cutoff_freq);
for (unsigned int i = 0; i < x.size(); i++) {
fv[i].position.x() = x[i];
}
vector<float> y;
for_each(fv.begin(), fv.end(), [&y](VMD_Frame f) { y.push_back(f.position.y()); });
lowpass_filter(y, cutoff_freq);
for (unsigned int i = 0; i < y.size(); i++) {
fv[i].position.y() = y[i];
}
vector<float> z;
for_each(fv.begin(), fv.end(), [&z](VMD_Frame f) { z.push_back(f.position.z()); });
lowpass_filter(z, cutoff_freq);
for (unsigned int i = 0; i < z.size(); i++) {
fv[i].position.z() = z[i];
}
// 回転のローパスフィルタ
// ※正しいやり方が分からないため、クォータニオンの各要素に対してローパスフィルタを掛けている。
// TODO: クォータニオンのフーリエ変換
// 同じ回転を表すクォータニオンが正負2通りあるので、wの符号が正のほうに統一する
for (unsigned int i = 0; i < fv.size(); i++) {
if (fv[i].rotation.w() < 0) {
fv[i].rotation.w() *= -1;
fv[i].rotation.x() *= -1;
fv[i].rotation.y() *= -1;
fv[i].rotation.z() *= -1;
}
}
vector<float> rx;
for_each(fv.begin(), fv.end(), [&rx](VMD_Frame f) { rx.push_back(f.rotation.x()); });
lowpass_filter(rx, cutoff_freq);
for (unsigned int i = 0; i < rx.size(); i++) {
fv[i].rotation.x() = rx[i];
}
vector<float> ry;
for_each(fv.begin(), fv.end(), [&ry](VMD_Frame f) { ry.push_back(f.rotation.y()); });
lowpass_filter(ry, cutoff_freq);
for (unsigned int i = 0; i < ry.size(); i++) {
fv[i].rotation.y() = ry[i];
}
vector<float> rz;
for_each(fv.begin(), fv.end(), [&rz](VMD_Frame f) { rz.push_back(f.rotation.z()); });
lowpass_filter(rz, cutoff_freq);
for (unsigned int i = 0; i < rz.size(); i++) {
fv[i].rotation.z() = rz[i];
}
vector<float> rw;
for_each(fv.begin(), fv.end(), [&rw](VMD_Frame f) { rw.push_back(f.rotation.w()); });
lowpass_filter(rw, cutoff_freq);
for (unsigned int i = 0; i < rw.size(); i++) {
fv[i].rotation.w() = rw[i];
}
// 各要素(w, x, y, z)に対し独立に変換をかけているので、正規化しておく
// (正規化しないと、回転した先の部分が歪む)
for (unsigned int i = 0; i < fv.size(); i++) {
fv[i].rotation.normalize();
}
}
// 表情キーフレーム列mvの値を平滑化する
// 引数mvには同一モーフのキーフレームがフレーム番号順に格納されているものとする
void smooth_morph_frame(vector<VMD_Morph>& mv, float cutoff_freq)
{
sort(mv.begin(), mv.end());
mv = fill_morph_frame(mv); // キーフレームの隙間をなくす
if (cutoff_freq < 0) {
return;
}
// ローパスフィルタにかける
vector<float> w;
for_each(mv.begin(), mv.end(), [&w](VMD_Morph s) { w.push_back(s.weight); });
lowpass_filter(w, cutoff_freq);
for (unsigned int i = 0; i < w.size(); i++) {
mv[i].weight = w[i];
if (w[i] > 1.0) {
mv[i].weight = 1.0;
} else if (w[i] < 0.0) {
mv[i].weight = 0.0;
}
}
}