/
testApp.cpp
298 lines (240 loc) · 8.03 KB
/
testApp.cpp
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#include "testApp.h"
#include <deque>
#include "ofxOpenCv.h"
#define _USE_LIVE_VIDEO
#ifdef _USE_LIVE_VIDEO
ofVideoGrabber vidGrabber;
#else
ofVideoPlayer vidPlayer;
#endif
ofxCvColorImage src_image;
static const int VIDEO_WIDTH = 640, VIDEO_HEIGHT = 480;
static const CvSize VIDEO_SIZE = cvSize(VIDEO_WIDTH, VIDEO_HEIGHT);
// 位置と色の状態を持った構造体
struct FlowPoint
{
ofVec3f pos;
ofVec3f color;
};
// 軌跡を描画するためのクラス
class FlowLine
{
public:
ofVec3f pos; // 軌跡の先頭の位置
ofVec3f color; // 軌跡の先頭の色
deque<FlowPoint> points; // 軌跡を構成する点列
float alpha; // アルファ値
float rise_speed; // 上昇スピード
FlowLine()
{
// 軌跡が上っていくスピードをランダムでちらす
rise_speed = ofRandom(0.5, 5);
}
void update()
{
// 軌跡の先頭に現在のポイントを追加
FlowPoint p;
p.pos = (points.front().pos*0.2 + pos*0.8);
p.color = (points.front().color*0.2 + color*0.8);
points.push_front(p);
if (points.size() > 2)
{
for (int i = 1; i < points.size(); i++)
{
// 軌跡がだんだん上っていくように
points[i].pos.y -= rise_speed;
// 線をなめらかに
points[i-1].pos = points[i].pos*0.6 + points[i-1].pos*0.4;
}
}
// 軌跡の流さを制限する
if (points.size() > 100) points.pop_back();
// アルファ値をだんだん小さくする
alpha += -alpha * 0.05;
}
void draw()
{
// 軌跡の描画
glBegin(GL_LINE_STRIP);
for (int i = 0; i < points.size(); i++)
{
FlowPoint &p = points[i];
float a = 1 - (float)i / (float)points.size(); // だんだん透明に
glColor4f(p.color.x,p.color.y,p.color.z,a*alpha);
glVertex2fv(p.pos.getPtr());
}
glEnd();
}
// アルファ値が十分に小さいときに消去するためのフラグ用の関数
bool alive()
{
return alpha > 0.05;
}
};
// 軌跡オブジェクトの配列
vector<FlowLine*> flow_lines;
// 一番近い点を持ったオブジェクトを探すためにソートをかける関数オブジェクト
struct sort_by_distance
{
sort_by_distance(ofVec2f pos)
{
this->pos = pos;
}
bool operator()(const FlowLine* a, const FlowLine* b)
{
float len_a = (a->pos - pos).lengthSquared();
float len_b = (b->pos - pos).lengthSquared();
return len_a < len_b;
}
ofVec2f pos;
};
// 検出したフローに対する処理
void updateFlowPoint(ofVec2f to, ofVec2f from)
{
// 始点と終点のベクトル距離(=移動量)を求める
float len = (from - to).length();
if (len > 1 && len < 50) // 距離がいい感じだったら...
{
// 終点周辺のピクセルの色を取得
CvScalar c = cvGet2D(src_image.getCvImage(), (int)to.y, (int)to.x);
ofVec3f color = ofVec3f(c.val[0] / 255.0f, c.val[1] / 255.0f, c.val[2] / 255.0f);
// temp_linesの中のオブジェクトを始点に近い順にソート
sort(flow_lines.begin(), flow_lines.end(), sort_by_distance(from));
if (flow_lines.empty() || (flow_lines[0]->pos - from).length() > 30)
{
// temp_linesが空か、始点の近くにオブジェクトがない場合新しく追加
FlowLine *line = new FlowLine();
line->pos = to;
line->alpha = 0;
line->color = color;
FlowPoint point;
point.pos = to;
point.color = color;
line->points.push_back(point);
flow_lines.push_back(line);
}
else
{
// 近いオブジェクトがみつかったので色とか位置とかを更新
FlowLine *line = flow_lines[0];
line->color = color;
line->pos = to;
line->alpha += (1 - line->alpha) * 0.1;
}
}
}
//--------------------------------------------------------------
void testApp::setup(){
ofSetVerticalSync(true);
ofSetFrameRate(60);
ofEnableAlphaBlending();
ofEnableSmoothing();
ofBackground(0,0,0);
#ifdef _USE_LIVE_VIDEO
vidGrabber.setVerbose(true);
vidGrabber.initGrabber(VIDEO_WIDTH, VIDEO_HEIGHT);
#else
vidPlayer.loadMovie("test.mov");
vidPlayer.play();
#endif
src_image.allocate(VIDEO_WIDTH, VIDEO_HEIGHT);
}
//--------------------------------------------------------------
void testApp::update(){
bool bNewFrame = false;
#ifdef _USE_LIVE_VIDEO
vidGrabber.update();
bNewFrame = vidGrabber.isFrameNew();
#else
vidPlayer.idleMovie();
bNewFrame = vidPlayer.isFrameNew();
#endif
if (bNewFrame){
#ifdef _USE_LIVE_VIDEO
src_image.setFromPixels(vidGrabber.getPixels(), VIDEO_WIDTH, VIDEO_HEIGHT);
#else
src_image.setFromPixels(vidPlayer.getPixels(), VIDEO_WIDTH, VIDEO_HEIGHT);
#endif
// オプティカルフロー処理
// ほぼ http://opencv.jp/sample/optical_flow.html からコピペ
int count = 150; // 検出するポイントの最大数
static IplImage *eig = cvCreateImage(VIDEO_SIZE, IPL_DEPTH_32F, 1);
static IplImage *temp = cvCreateImage(VIDEO_SIZE, IPL_DEPTH_32F, 1);
static CvPoint2D32f *corners1 = (CvPoint2D32f*)cvAlloc(count * sizeof(CvPoint2D32f));
static CvPoint2D32f *corners2 = (CvPoint2D32f*)cvAlloc(count * sizeof(CvPoint2D32f));
static IplImage *prev_pyramid = cvCreateImage(cvSize(VIDEO_WIDTH+8, VIDEO_HEIGHT/3), IPL_DEPTH_8U, 1);
static IplImage *curr_pyramid = cvCreateImage(cvSize(VIDEO_WIDTH+8, VIDEO_HEIGHT/3), IPL_DEPTH_8U, 1);
static char *status = (char*)cvAlloc(count);
static IplImage *curr_image = cvCreateImage(VIDEO_SIZE, IPL_DEPTH_8U, 1);
static IplImage *prev_image = cvCreateImage(VIDEO_SIZE, IPL_DEPTH_8U, 1);
// src_imageの中身をグレイスケールに変換しつつ curr_image にコピー
cvCvtColor(src_image.getCvImage(), curr_image, CV_RGB2GRAY);
// 特徴点を抽出
float block_size = 10; // 検出するポイント間の最小距離
cvGoodFeaturesToTrack(curr_image, eig, temp, corners1, &count, 0.001, block_size, NULL);
// curr_image と prev_image についてオプティカルフローを計算
cvCalcOpticalFlowPyrLK(curr_image, prev_image, curr_pyramid, prev_pyramid, corners1, corners2, count, cvSize(10,10), 4, status, NULL, cvTermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER, 64, 0.01), 0);
// curr_image を prev_image にコピー
cvCopyImage(curr_image, prev_image);
// 検出できたフローに対するループ
for (int i = 0; i < count; i++)
{
if (status[i])
{
ofVec2f to = ofVec2f(corners1[i].x, corners1[i].y); // 始点
ofVec2f from = ofVec2f(corners2[i].x, corners2[i].y); // 終点
// 取得したフローに対して何かしらの処理をする
updateFlowPoint(to, from);
}
}
// FlowLineの更新、削除
vector<FlowLine*>::iterator it = flow_lines.begin();
while (it != flow_lines.end())
{
FlowLine *line = (*it);
// 更新
line->update();
// オブジェクトの生存をチェックして、falseだった場合は消去する
if (line->alive() == false)
{
it = flow_lines.erase(it);
delete line;
}
else
{
it++;
}
}
}
}
//--------------------------------------------------------------
void testApp::draw(){
// ウィンドウのサイズを変更しても動画が全画面で表示されるように
glScalef((float)ofGetWidth()/(float)VIDEO_WIDTH, (float)ofGetHeight()/(float)VIDEO_HEIGHT, 1);
ofSetColor(127,127,127);
src_image.draw(0, 0);
// 軌跡の描画
for (int i = 0; i < flow_lines.size(); i++)
{
FlowLine *line = flow_lines[i];
line->draw();
}
}
//--------------------------------------------------------------
void testApp::keyPressed (int key){
}
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
}
//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
}