4 * Copyright (C) 1997-2012 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "findobjwindow.h"
29 #include "overlayframe.h"
35 REGISTER_PLUGIN(FindObjMain)
37 FindObjConfig::FindObjConfig()
39 algorithm = NO_ALGORITHM;
44 draw_object_border = 0;
45 object_x = 50; object_y = 50;
46 object_w = 100; object_h = 100;
47 scene_x = 50; scene_y = 50;
48 scene_w = 100; scene_h = 100;
55 void FindObjConfig::boundaries()
57 bclamp(object_x, 0, 100); bclamp(object_y, 0, 100);
58 bclamp(object_w, 0, 100); bclamp(object_h, 0, 100);
59 bclamp(scene_x, 0, 100); bclamp(scene_y, 0, 100);
60 bclamp(scene_w, 0, 100); bclamp(scene_h, 0, 100);
61 bclamp(object_layer, MIN_LAYER, MAX_LAYER);
62 bclamp(replace_layer, MIN_LAYER, MAX_LAYER);
63 bclamp(scene_layer, MIN_LAYER, MAX_LAYER);
64 bclamp(blend, MIN_BLEND, MAX_BLEND);
67 int FindObjConfig::equivalent(FindObjConfig &that)
70 algorithm == that.algorithm &&
71 use_flann == that.use_flann &&
72 draw_keypoints == that.draw_keypoints &&
73 draw_border == that.draw_border &&
74 replace_object == that.replace_object &&
75 draw_object_border == that.draw_object_border &&
76 object_x == that.object_x && object_y == that.object_y &&
77 object_w == that.object_w && object_h == that.object_h &&
78 scene_x == that.scene_x && scene_y == that.scene_y &&
79 scene_w == that.scene_w && scene_h == that.scene_h &&
80 object_layer == that.object_layer &&
81 replace_layer == that.replace_layer &&
82 scene_layer == that.scene_layer &&
87 void FindObjConfig::copy_from(FindObjConfig &that)
89 algorithm = that.algorithm;
90 use_flann = that.use_flann;
91 draw_keypoints = that.draw_keypoints;
92 draw_border = that.draw_border;
93 replace_object = that.replace_object;
94 draw_object_border = that.draw_object_border;
95 object_x = that.object_x; object_y = that.object_y;
96 object_w = that.object_w; object_h = that.object_h;
97 scene_x = that.scene_x; scene_y = that.scene_y;
98 scene_w = that.scene_w; scene_h = that.scene_h;
99 object_layer = that.object_layer;
100 replace_layer = that.replace_layer;
101 scene_layer = that.scene_layer;
105 void FindObjConfig::interpolate(FindObjConfig &prev, FindObjConfig &next,
106 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
112 FindObjMain::FindObjMain(PluginServer *server)
113 : PluginVClient(server)
120 object = scene = replace = 0;
121 object_x = object_y = 0;
122 object_w = object_h = 0;
123 scene_x = scene_y = 0;
124 scene_w = scene_h = 0;
129 border_x1 = 0; border_y1 = 0;
130 border_x2 = 0; border_y2 = 0;
131 border_x3 = 0; border_y3 = 0;
132 border_x4 = 0; border_y4 = 0;
134 obj_x1 = 0; obj_y1 = 0;
135 obj_x2 = 0; obj_y2 = 0;
136 obj_x3 = 0; obj_y3 = 0;
137 obj_x4 = 0; obj_y4 = 0;
142 FindObjMain::~FindObjMain()
148 const char* FindObjMain::plugin_title() { return _("FindObj"); }
149 int FindObjMain::is_realtime() { return 1; }
150 int FindObjMain::is_multichannel() { return 1; }
152 NEW_WINDOW_MACRO(FindObjMain, FindObjWindow)
153 LOAD_CONFIGURATION_MACRO(FindObjMain, FindObjConfig)
155 void FindObjMain::update_gui()
157 if( !thread ) return;
158 if( !load_configuration() ) return;
159 FindObjWindow *window = (FindObjWindow*)thread->window;
160 window->lock_window("FindObjMain::update_gui");
161 window->algorithm->set_text(FindObjAlgorithm::to_text(config.algorithm));
162 window->use_flann->update(config.use_flann);
163 window->object_x->update(config.object_x);
164 window->object_x_text->update((float)config.object_x);
165 window->object_y->update(config.object_y);
166 window->object_y_text->update((float)config.object_y);
167 window->object_w->update(config.object_w);
168 window->object_w_text->update((float)config.object_w);
169 window->object_h->update(config.object_h);
170 window->object_h_text->update((float)config.object_h);
171 window->scene_x->update(config.scene_x);
172 window->scene_x_text->update((float)config.scene_x);
173 window->scene_y->update(config.scene_y);
174 window->scene_y_text->update((float)config.scene_y);
175 window->scene_w->update(config.scene_w);
176 window->scene_w_text->update((float)config.scene_w);
177 window->scene_h->update(config.scene_h);
178 window->scene_h_text->update((float)config.scene_h);
179 window->draw_keypoints->update(config.draw_keypoints);
180 window->draw_border->update(config.draw_border);
181 window->replace_object->update(config.replace_object);
182 window->draw_object_border->update(config.draw_object_border);
183 window->object_layer->update( (int64_t)config.object_layer);
184 window->replace_layer->update( (int64_t)config.replace_layer);
185 window->scene_layer->update( (int64_t)config.scene_layer);
186 window->blend->update( (int64_t)config.blend);
188 window->unlock_window();
191 void FindObjMain::save_data(KeyFrame *keyframe)
195 // cause data to be stored directly in text
196 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
197 output.tag.set_title("FINDOBJ");
198 output.tag.set_property("ALGORITHM", config.algorithm);
199 output.tag.set_property("USE_FLANN", config.use_flann);
200 output.tag.set_property("OBJECT_X", config.object_x);
201 output.tag.set_property("OBJECT_Y", config.object_y);
202 output.tag.set_property("OBJECT_W", config.object_w);
203 output.tag.set_property("OBJECT_H", config.object_h);
204 output.tag.set_property("SCENE_X", config.scene_x);
205 output.tag.set_property("SCENE_Y", config.scene_y);
206 output.tag.set_property("SCENE_W", config.scene_w);
207 output.tag.set_property("SCENE_H", config.scene_h);
208 output.tag.set_property("DRAW_KEYPOINTS", config.draw_keypoints);
209 output.tag.set_property("DRAW_BORDER", config.draw_border);
210 output.tag.set_property("REPLACE_OBJECT", config.replace_object);
211 output.tag.set_property("DRAW_OBJECT_BORDER", config.draw_object_border);
212 output.tag.set_property("OBJECT_LAYER", config.object_layer);
213 output.tag.set_property("REPLACE_LAYER", config.replace_layer);
214 output.tag.set_property("SCENE_LAYER", config.scene_layer);
215 output.tag.set_property("BLEND", config.blend);
217 output.tag.set_title("/FINDOBJ");
219 output.append_newline();
220 output.terminate_string();
223 void FindObjMain::read_data(KeyFrame *keyframe)
227 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
231 while( !(result = input.read_tag()) ) {
232 if( input.tag.title_is("FINDOBJ") ) {
233 config.algorithm = input.tag.get_property("ALGORITHM", config.algorithm);
234 config.use_flann = input.tag.get_property("USE_FLANN", config.use_flann);
235 config.object_x = input.tag.get_property("OBJECT_X", config.object_x);
236 config.object_y = input.tag.get_property("OBJECT_Y", config.object_y);
237 config.object_w = input.tag.get_property("OBJECT_W", config.object_w);
238 config.object_h = input.tag.get_property("OBJECT_H", config.object_h);
239 config.scene_x = input.tag.get_property("SCENE_X", config.scene_x);
240 config.scene_y = input.tag.get_property("SCENE_Y", config.scene_y);
241 config.scene_w = input.tag.get_property("SCENE_W", config.scene_w);
242 config.scene_h = input.tag.get_property("SCENE_H", config.scene_h);
243 config.draw_keypoints = input.tag.get_property("DRAW_KEYPOINTS", config.draw_keypoints);
244 config.draw_border = input.tag.get_property("DRAW_BORDER", config.draw_border);
245 config.replace_object = input.tag.get_property("REPLACE_OBJECT", config.replace_object);
246 config.draw_object_border = input.tag.get_property("DRAW_OBJECT_BORDER", config.draw_object_border);
247 config.object_layer = input.tag.get_property("OBJECT_LAYER", config.object_layer);
248 config.replace_layer = input.tag.get_property("REPLACE_LAYER", config.replace_layer);
249 config.scene_layer = input.tag.get_property("SCENE_LAYER", config.scene_layer);
250 config.blend = input.tag.get_property("BLEND", config.blend);
257 void FindObjMain::draw_line(VFrame *vframe, int x1, int y1, int x2, int y2)
259 vframe->draw_line(x1, y1, x2, y2);
262 void FindObjMain::draw_rect(VFrame *vframe, int x1, int y1, int x2, int y2)
264 draw_line(vframe, x1, y1, x2, y1);
265 draw_line(vframe, x2, y1 + 1, x2, y2);
266 draw_line(vframe, x2 - 1, y2, x1, y2);
267 draw_line(vframe, x1, y2 - 1, x1, y1 + 1);
270 void FindObjMain::draw_circle(VFrame *vframe, int x, int y, int r)
272 int x1 = x-r, x2 = x+r;
273 int y1 = y-r, y2 = y+r;
274 vframe->draw_smooth(x1,y, x1,y1, x,y1);
275 vframe->draw_smooth(x,y1, x2,y1, x2,y);
276 vframe->draw_smooth(x2,y, x2,y2, x,y2);
277 vframe->draw_smooth(x,y2, x1,y2, x1,y);
280 void FindObjMain::filter_matches(ptV &p1, ptV &p2, double ratio)
282 DMatches::iterator it;
283 for( it=pairs.begin(); it!=pairs.end(); ++it ) {
285 if( m.size() == 2 && m[0].distance < m[1].distance*ratio ) {
286 p1.push_back(obj_keypts[m[0].queryIdx].pt);
287 p2.push_back(scn_keypts[m[0].trainIdx].pt);
292 void FindObjMain::to_mat(Mat &mat, int mcols, int mrows,
293 VFrame *inp, int ix,int iy, BC_CModel mcolor_model)
295 int mcomp = BC_CModels::components(mcolor_model);
296 int mbpp = BC_CModels::calculate_pixelsize(mcolor_model);
297 int psz = mbpp / mcomp;
298 int mdepth = psz < 2 ? CV_8U : CV_16U;
299 if( mat.dims != 2 || mat.depth() != mdepth || mat.channels() != mcomp ||
300 mat.cols != mcols || mat.rows != mrows ) {
304 int type = CV_MAKETYPE(mdepth, mcomp);
305 mat.create(mrows, mcols, type);
307 uint8_t *mat_rows[mrows];
308 for( int y=0; y<mrows; ++y ) mat_rows[y] = mat.ptr(y);
309 uint8_t **inp_rows = inp->get_rows();
310 int ibpl = inp->get_bytes_per_line(), obpl = mcols * mbpp;
311 int icolor_model = inp->get_color_model();
312 BC_CModels::transfer(mat_rows, mcolor_model, 0,0, mcols,mrows, obpl,
313 inp_rows, icolor_model, ix,iy, mcols,mrows, ibpl, 0);
314 // VFrame vfrm(mat_rows[0], -1, mcols,mrows, mcolor_model, mat_rows[1]-mat_rows[0]);
315 // static int vfrm_no = 0; char vfn[64]; sprintf(vfn,"/tmp/dat/%06d.png", vfrm_no++);
316 // vfrm.write_png(vfn);
319 void FindObjMain::detect(Mat &mat, KeyPointV &keypts,Mat &descrs)
324 detector->detectAndCompute(mat, noArray(), keypts, descrs);
325 } catch(std::exception e) { printf(_("detector exception: %s\n"), e.what()); }
328 void FindObjMain::match()
332 matcher->knnMatch(obj_descrs, scn_descrs, pairs, 2);
333 } catch(std::exception e) { printf(_("match execption: %s\n"), e.what()); }
336 Ptr<DescriptorMatcher> FindObjMain::flann_kdtree_matcher()
338 const Ptr<flann::IndexParams>& indexParams =
339 makePtr<flann::KDTreeIndexParams>(5);
340 const Ptr<flann::SearchParams>& searchParams =
341 makePtr<flann::SearchParams>();
342 return makePtr<FlannBasedMatcher>(indexParams, searchParams);
344 Ptr<DescriptorMatcher> FindObjMain::flann_lshidx_matcher()
345 { // table_number = 6#12, key_size = 12#20, multi_probe_level = 1#2
346 const Ptr<flann::IndexParams>& indexParams =
347 makePtr<flann::LshIndexParams>(6, 12, 1);
348 const Ptr<flann::SearchParams>& searchParams =
349 makePtr<flann::SearchParams>();
350 return makePtr<FlannBasedMatcher>(indexParams, searchParams);
352 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_l2()
354 return BFMatcher::create(NORM_L2);
356 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_hamming()
358 return BFMatcher::create(NORM_HAMMING);
362 void FindObjMain::set_sift()
365 detector = SIFT::create();
366 matcher = config.use_flann ?
367 flann_kdtree_matcher() : bf_matcher_norm_l2();
371 void FindObjMain::set_surf()
374 detector = SURF::create(800);
375 matcher = config.use_flann ?
376 flann_kdtree_matcher() : bf_matcher_norm_l2();
380 void FindObjMain::set_orb()
383 detector = ORB::create();
384 matcher = config.use_flann ?
385 flann_lshidx_matcher() : bf_matcher_norm_hamming();
389 void FindObjMain::set_akaze()
392 detector = AKAZE::create();
393 matcher = config.use_flann ?
394 flann_lshidx_matcher() : bf_matcher_norm_hamming();
398 void FindObjMain::set_brisk()
401 detector = BRISK::create();
402 matcher = config.use_flann ?
403 flann_lshidx_matcher() : bf_matcher_norm_hamming();
407 void FindObjMain::process_match()
409 if( config.algorithm == NO_ALGORITHM ) return;
410 if( !config.replace_object &&
411 !config.draw_border &&
412 !config.draw_keypoints ) return;
414 if( detector.empty() ) {
415 switch( config.algorithm ) {
417 case ALGORITHM_SIFT: set_sift(); break;
420 case ALGORITHM_SURF: set_surf(); break;
423 case ALGORITHM_ORB: set_orb(); break;
426 case ALGORITHM_AKAZE: set_akaze(); break;
429 case ALGORITHM_BRISK: set_brisk(); break;
432 obj_keypts.clear(); obj_descrs.release();
433 to_mat(object_mat, object_w,object_h, object, object_x,object_y, cvmodel);
434 detect(object_mat, obj_keypts, obj_descrs);
435 //printf("detect obj %d features\n", (int)obj_keypts.size());
438 to_mat(scene_mat, scene_w,scene_h, scene, scene_x,scene_y, cvmodel);
439 detect(scene_mat, scn_keypts, scn_descrs);
440 //printf("detect scn %d features\n", (int)scn_keypts.size());
443 filter_matches(p1, p2);
444 if( p1.size() < 4 ) return;
445 Mat H = findHomography(p1, p2, RANSAC, 5.0);
446 if( !H.dims || !H.rows || !H.cols ) {
447 //printf("fail, size p1=%d,p2=%d\n",(int)p1.size(),(int)p2.size());
452 float obj_x1 = 0, obj_x2 = object_w;
453 float obj_y1 = 0, obj_y2 = object_h;
454 src.push_back(Point2f(obj_x1,obj_y1));
455 src.push_back(Point2f(obj_x2,obj_y1));
456 src.push_back(Point2f(obj_x2,obj_y2));
457 src.push_back(Point2f(obj_x1,obj_y2));
458 perspectiveTransform(src, dst, H);
460 border_x1 = dst[0].x + scene_x; border_y1 = dst[0].y + scene_y;
461 border_x2 = dst[1].x + scene_x; border_y2 = dst[1].y + scene_y;
462 border_x3 = dst[2].x + scene_x; border_y3 = dst[2].y + scene_y;
463 border_x4 = dst[3].x + scene_x; border_y4 = dst[3].y + scene_y;
464 //printf("src %f,%f %f,%f %f,%f %f,%f\n",
465 // src[0].x,src[0].y, src[1].x,src[1].y, src[2].x,src[2].y, src[3].x,src[3].y);
466 //printf("dst %f,%f %f,%f %f,%f %f,%f\n",
467 // dst[0].x,dst[0].y, dst[1].x,dst[1].y, dst[2].x,dst[2].y, dst[3].x,dst[3].y);
470 int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
472 int prev_algorithm = config.algorithm;
473 int prev_use_flann = config.use_flann;
475 if( load_configuration() )
478 if( prev_algorithm != config.algorithm ||
479 prev_use_flann != config.use_flann ) {
484 w = frame[0]->get_w();
485 h = frame[0]->get_h();
487 object_layer = config.object_layer;
488 scene_layer = config.scene_layer;
489 replace_layer = config.replace_layer;
491 int max_layer = PluginClient::get_total_buffers() - 1;
492 object_layer = bclip(config.object_layer, 0, max_layer);
493 scene_layer = bclip(config.scene_layer, 0, max_layer);
494 replace_layer = bclip(config.replace_layer, 0, max_layer);
496 int cfg_w, cfg_h, cfg_x1, cfg_y1, cfg_x2, cfg_y2;
497 cfg_w = (int)(config.object_w * w / 100);
498 cfg_h = (int)(config.object_h * h / 100);
499 cfg_x1 = (int)(config.object_x * w / 100 - cfg_w / 2);
500 cfg_y1 = (int)(config.object_y * h / 100 - cfg_h / 2);
501 cfg_x2 = cfg_x1 + cfg_w;
502 cfg_y2 = cfg_y1 + cfg_h;
503 bclamp(cfg_x1, 0, w); object_x = cfg_x1;
504 bclamp(cfg_y1, 0, h); object_y = cfg_y1;
505 bclamp(cfg_x2, 0, w); object_w = cfg_x2 - cfg_x1;
506 bclamp(cfg_y2, 0, h); object_h = cfg_y2 - cfg_y1;
508 cfg_w = (int)(config.scene_w * w / 100);
509 cfg_h = (int)(config.scene_h * h / 100);
510 cfg_x1 = (int)(config.scene_x * w / 100 - cfg_w / 2);
511 cfg_y1 = (int)(config.scene_y * h / 100 - cfg_h / 2);
512 cfg_x2 = cfg_x1 + cfg_w;
513 cfg_y2 = cfg_y1 + cfg_h;
514 bclamp(cfg_x1, 0, w); scene_x = cfg_x1;
515 bclamp(cfg_y1, 0, h); scene_y = cfg_y1;
516 bclamp(cfg_x2, 0, w); scene_w = cfg_x2 - cfg_x1;
517 bclamp(cfg_y2, 0, h); scene_h = cfg_y2 - cfg_y1;
519 // Read in the input frames
520 for( int i = 0; i < PluginClient::get_total_buffers(); i++ ) {
521 read_frame(frame[i], i, start_position, frame_rate, 0);
524 object = frame[object_layer];
525 scene = frame[scene_layer];
526 replace = frame[replace_layer];
528 border_x1 = obj_x1; border_y1 = obj_y1;
529 border_x2 = obj_x2; border_y2 = obj_y2;
530 border_x3 = obj_x3; border_y3 = obj_y3;
531 border_x4 = obj_x4; border_y4 = obj_y4;
533 if( scene_w > 0 && scene_h > 0 && object_w > 0 && object_h > 0 ) {
537 double w0 = init_border ? 1. : config.blend/100., w1 = 1. - w0;
538 obj_x1 = border_x1*w0 + obj_x1*w1; obj_y1 = border_y1*w0 + obj_y1*w1;
539 obj_x2 = border_x2*w0 + obj_x2*w1; obj_y2 = border_y2*w0 + obj_y2*w1;
540 obj_x3 = border_x3*w0 + obj_x3*w1; obj_y3 = border_y3*w0 + obj_y3*w1;
541 obj_x4 = border_x4*w0 + obj_x4*w1; obj_y4 = border_y4*w0 + obj_y4*w1;
543 // Replace object in the scene layer
544 if( config.replace_object ) {
545 int cpus1 = get_project_smp() + 1;
547 affine = new AffineEngine(cpus1, cpus1);
549 overlayer = new OverlayFrame(cpus1);
551 VFrame *temp = new_temp(scene->get_w(), scene->get_h(), scene->get_color_model());
553 float sx = 100./w, sy = 100./h;
554 float x1 = sx * obj_x1, y1 = sy * obj_y1;
555 float x2 = sx * obj_x2, y2 = sy * obj_y2;
556 float x3 = sx * obj_x3, y3 = sy * obj_y3;
557 float x4 = sx * obj_x4, y4 = sy * obj_y4;
558 affine->process(temp, replace, 0, AffineEngine::PERSPECTIVE,
559 x1,y1, x2,y2, x3,y3, x4,y4, 1);
560 overlayer->overlay(scene, temp, 0,0, w,h, 0,0, w,h,
561 1, TRANSFER_NORMAL, NEAREST_NEIGHBOR);
565 if( config.draw_border ) {
566 int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
567 scene->set_pixel_color(WHITE); scene->set_stiple(ss*2);
568 draw_line(scene, obj_x1, obj_y1, obj_x2, obj_y2);
569 draw_line(scene, obj_x2, obj_y2, obj_x3, obj_y3);
570 draw_line(scene, obj_x3, obj_y3, obj_x4, obj_y4);
571 draw_line(scene, obj_x4, obj_y4, obj_x1, obj_y1);
574 if( config.draw_keypoints ) {
575 scene->set_pixel_color(RED); scene->set_stiple(0);
576 for( int i=0,n=scn_keypts.size(); i<n; ++i ) {
577 Point2f &pt = scn_keypts[i].pt;
578 int r = scn_keypts[i].size * 1.2/9 * 2;
579 int x = pt.x + scene_x, y = pt.y + scene_y;
580 draw_circle(scene, x, y, r);
584 // Draw object outline in the object layer
585 if( config.draw_object_border ) {
586 int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
587 scene->set_pixel_color(YELLOW); scene->set_stiple(ss*3);
588 int x1 = object_x, x2 = x1 + object_w - 1;
589 int y1 = object_y, y2 = y1 + object_h - 1;
590 draw_rect(scene, x1, y1, x2, y2);
591 scene->set_pixel_color(GREEN); scene->set_stiple(0);
592 x1 = scene_x, x2 = x1 + scene_w - 1;
593 y1 = scene_y, y2 = y1 + scene_h - 1;
594 draw_rect(scene, x1, y1, x2, y2);
597 scene->set_pixel_color(BLACK);
598 scene->set_stiple(0);