dynamic keyframes, textbox rework, andrea ffmpeg.opts, perpetual chkpt undo, lv2...
[goodguy/history.git] / cinelerra-5.1 / plugins / findobj / findobj.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2012 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "affine.h"
22 #include "bccolors.h"
23 #include "clip.h"
24 #include "filexml.h"
25 #include "language.h"
26 #include "findobj.h"
27 #include "findobjwindow.h"
28 #include "mutex.h"
29 #include "overlayframe.h"
30 #include "plugin.h"
31 #include "pluginserver.h"
32 #include "track.h"
33
34 #include <errno.h>
35 #include <exception>
36 #include <unistd.h>
37
38 REGISTER_PLUGIN(FindObjMain)
39
40 FindObjConfig::FindObjConfig()
41 {
42         reset();
43 }
44
45 void FindObjConfig::reset()
46 {
47         algorithm = NO_ALGORITHM;
48         use_flann = 1;
49         mode = MODE_QUADRILATERAL;
50         draw_match = 0;
51         aspect = 1;
52         scale = 1;
53         translate = 1;
54         rotate = 1;
55         draw_keypoints = 0;
56         draw_scene_border = 0;
57         replace_object = 0;
58         draw_object_border = 0;
59         draw_replace_border = 0;
60         object_x = 50;  object_y = 50;
61         object_w = 100; object_h = 100;
62         drag_object = 0;
63         scene_x = 50;   scene_y = 50;
64         scene_w = 100;  scene_h = 100;
65         drag_replace = 0;
66         replace_x = 50;   replace_y = 50;
67         replace_w = 100;  replace_h = 100;
68         replace_dx = 0;   replace_dy = 0;
69         drag_scene = 0;
70         scene_layer = 0;
71         object_layer = 1;
72         replace_layer = 2;
73         blend = 100;
74 }
75
76 void FindObjConfig::boundaries()
77 {
78         bclamp(object_x, 0, 100);  bclamp(object_y, 0, 100);
79         bclamp(object_w, 0, 100);  bclamp(object_h, 0, 100);
80         bclamp(scene_x, 0, 100);   bclamp(scene_y, 0, 100);
81         bclamp(scene_w, 0, 100);   bclamp(scene_h, 0, 100);
82         bclamp(object_layer, MIN_LAYER, MAX_LAYER);
83         bclamp(replace_layer, MIN_LAYER, MAX_LAYER);
84         bclamp(scene_layer, MIN_LAYER, MAX_LAYER);
85         bclamp(blend, MIN_BLEND, MAX_BLEND);
86 }
87
88 int FindObjConfig::equivalent(FindObjConfig &that)
89 {
90         int result =
91                 algorithm == that.algorithm &&
92                 use_flann == that.use_flann &&
93                 mode == that.mode &&
94                 aspect == that.aspect &&
95                 scale == that.scale &&
96                 translate == that.translate &&
97                 rotate == that.rotate &&
98                 draw_keypoints == that.draw_keypoints &&
99                 draw_match == that.draw_match &&
100                 draw_scene_border == that.draw_scene_border &&
101                 replace_object == that.replace_object &&
102                 draw_object_border == that.draw_object_border &&
103                 draw_replace_border == that.draw_replace_border &&
104                 object_x == that.object_x && object_y == that.object_y &&
105                 object_w == that.object_w && object_h == that.object_h &&
106                 drag_object == that.drag_object &&
107                 scene_x == that.scene_x && scene_y == that.scene_y &&
108                 scene_w == that.scene_w && scene_h == that.scene_h &&
109                 drag_scene == that.drag_scene &&
110                 replace_x == that.replace_x && replace_y == that.replace_y &&
111                 replace_w == that.replace_w && replace_h == that.replace_h &&
112                 replace_dx == that.replace_dx && replace_dy == that.replace_dy &&
113                 drag_replace == that.drag_replace &&
114                 object_layer == that.object_layer &&
115                 replace_layer == that.replace_layer &&
116                 scene_layer == that.scene_layer &&
117                 blend == that.blend;
118         return result;
119 }
120
121 void FindObjConfig::copy_from(FindObjConfig &that)
122 {
123         algorithm = that.algorithm;
124         use_flann = that.use_flann;
125         mode = that.mode;
126         aspect = that.aspect;
127         scale = that.scale;
128         translate = that.translate;
129         rotate = that.rotate;
130         draw_keypoints = that.draw_keypoints;
131         draw_match = that.draw_match;
132         draw_scene_border = that.draw_scene_border;
133         replace_object = that.replace_object;
134         draw_object_border = that.draw_object_border;
135         draw_replace_border = that.draw_replace_border;
136         object_x = that.object_x;  object_y = that.object_y;
137         object_w = that.object_w;  object_h = that.object_h;
138         drag_object = that.drag_object;
139         scene_x = that.scene_x;    scene_y = that.scene_y;
140         scene_w = that.scene_w;    scene_h = that.scene_h;
141         drag_scene = that.drag_scene;
142         replace_x = that.replace_x;   replace_y = that.replace_y;
143         replace_w = that.replace_w;   replace_h = that.replace_h;
144         replace_dx = that.replace_dx; replace_dy = that.replace_dy;
145         drag_replace = that.drag_replace;
146         object_layer = that.object_layer;
147         replace_layer = that.replace_layer;
148         scene_layer = that.scene_layer;
149         blend = that.blend;
150 }
151
152 void FindObjConfig::interpolate(FindObjConfig &prev, FindObjConfig &next,
153         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
154 {
155         copy_from(prev);
156 }
157
158
159 FindObjMain::FindObjMain(PluginServer *server)
160  : PluginVClient(server)
161 {
162         affine = 0;
163         overlayer = 0;
164
165         cvmodel = BC_RGB888;
166         w = h = 0;
167         object = scene = replace = 0;
168         object_x = object_y = 0;
169         object_w = object_h = 0;
170         scene_x = scene_y = 0;
171         scene_w = scene_h = 0;
172         object_layer = 0;
173         scene_layer = 1;
174         replace_layer = 2;
175
176         match_x1 = 0;  match_y1 = 0;
177         match_x2 = 0;  match_y2 = 0;
178         match_x3 = 0;  match_y3 = 0;
179         match_x4 = 0;  match_y4 = 0;
180         shape_x1 = 0;  shape_y1 = 0;
181         shape_x2 = 0;  shape_y2 = 0;
182         shape_x3 = 0;  shape_y3 = 0;
183         shape_x4 = 0;  shape_y4 = 0;
184         out_x1 = 0;    out_y1 = 0;
185         out_x2 = 0;    out_y2 = 0;
186         out_x3 = 0;    out_y3 = 0;
187         out_x4 = 0;    out_y4 = 0;
188
189         init_border = 1;
190 }
191
192 FindObjMain::~FindObjMain()
193 {
194         delete affine;
195         delete overlayer;
196 }
197
198 const char* FindObjMain::plugin_title() { return N_("FindObj"); }
199 int FindObjMain::is_realtime() { return 1; }
200 int FindObjMain::is_multichannel() { return 1; }
201
202 NEW_WINDOW_MACRO(FindObjMain, FindObjWindow)
203 LOAD_CONFIGURATION_MACRO(FindObjMain, FindObjConfig)
204
205 void FindObjMain::update_gui()
206 {
207         if( !thread ) return;
208         if( !load_configuration() ) return;
209         FindObjWindow *window = (FindObjWindow*)thread->window;
210         window->lock_window("FindObjMain::update_gui");
211         window->update_gui();
212         window->flush();
213         window->unlock_window();
214 }
215
216 void FindObjMain::save_data(KeyFrame *keyframe)
217 {
218         FileXML output;
219
220 // cause data to be stored directly in text
221         output.set_shared_output(keyframe->xbuf);
222         output.tag.set_title("FINDOBJ");
223         output.tag.set_property("ALGORITHM", config.algorithm);
224         output.tag.set_property("USE_FLANN", config.use_flann);
225         output.tag.set_property("MODE", config.mode);
226         output.tag.set_property("ASPECT", config.aspect);
227         output.tag.set_property("SCALE", config.scale);
228         output.tag.set_property("TRANSLATE", config.translate);
229         output.tag.set_property("ROTATE", config.rotate);
230         output.tag.set_property("DRAG_OBJECT", config.drag_object);
231         output.tag.set_property("OBJECT_X", config.object_x);
232         output.tag.set_property("OBJECT_Y", config.object_y);
233         output.tag.set_property("OBJECT_W", config.object_w);
234         output.tag.set_property("OBJECT_H", config.object_h);
235         output.tag.set_property("DRAG_SCENE", config.drag_scene);
236         output.tag.set_property("SCENE_X", config.scene_x);
237         output.tag.set_property("SCENE_Y", config.scene_y);
238         output.tag.set_property("SCENE_W", config.scene_w);
239         output.tag.set_property("SCENE_H", config.scene_h);
240         output.tag.set_property("DRAG_REPLACE", config.drag_replace);
241         output.tag.set_property("REPLACE_X", config.replace_x);
242         output.tag.set_property("REPLACE_Y", config.replace_y);
243         output.tag.set_property("REPLACE_W", config.replace_w);
244         output.tag.set_property("REPLACE_H", config.replace_h);
245         output.tag.set_property("REPLACE_DX", config.replace_dx);
246         output.tag.set_property("REPLACE_DY", config.replace_dy);
247         output.tag.set_property("DRAW_KEYPOINTS", config.draw_keypoints);
248         output.tag.set_property("DRAW_MATCH", config.draw_match);
249         output.tag.set_property("DRAW_SCENE_BORDER", config.draw_scene_border);
250         output.tag.set_property("REPLACE_OBJECT", config.replace_object);
251         output.tag.set_property("DRAW_OBJECT_BORDER", config.draw_object_border);
252         output.tag.set_property("DRAW_REPLACE_BORDER", config.draw_replace_border);
253         output.tag.set_property("OBJECT_LAYER", config.object_layer);
254         output.tag.set_property("REPLACE_LAYER", config.replace_layer);
255         output.tag.set_property("SCENE_LAYER", config.scene_layer);
256         output.tag.set_property("BLEND", config.blend);
257         output.append_tag();
258         output.tag.set_title("/FINDOBJ");
259         output.append_tag();
260         output.append_newline();
261         output.terminate_string();
262 }
263
264 void FindObjMain::read_data(KeyFrame *keyframe)
265 {
266         FileXML input;
267
268         input.set_shared_input(keyframe->xbuf);
269
270         int result = 0;
271
272         while( !(result = input.read_tag()) ) {
273                 if( input.tag.title_is("FINDOBJ") ) {
274                         config.algorithm = input.tag.get_property("ALGORITHM", config.algorithm);
275                         config.use_flann = input.tag.get_property("USE_FLANN", config.use_flann);
276                         config.mode = input.tag.get_property("MODE", config.mode);
277                         config.aspect = input.tag.get_property("ASPECT", config.aspect);
278                         config.scale = input.tag.get_property("SCALE", config.scale);
279                         config.translate = input.tag.get_property("TRANSLATE", config.translate);
280                         config.rotate = input.tag.get_property("ROTATE", config.rotate);
281                         config.object_x = input.tag.get_property("OBJECT_X", config.object_x);
282                         config.object_y = input.tag.get_property("OBJECT_Y", config.object_y);
283                         config.object_w = input.tag.get_property("OBJECT_W", config.object_w);
284                         config.object_h = input.tag.get_property("OBJECT_H", config.object_h);
285                         config.drag_object = input.tag.get_property("DRAG_OBJECT", config.drag_object);
286                         config.scene_x = input.tag.get_property("SCENE_X", config.scene_x);
287                         config.scene_y = input.tag.get_property("SCENE_Y", config.scene_y);
288                         config.scene_w = input.tag.get_property("SCENE_W", config.scene_w);
289                         config.scene_h = input.tag.get_property("SCENE_H", config.scene_h);
290                         config.drag_scene = input.tag.get_property("DRAG_SCENE", config.drag_scene);
291                         config.replace_x = input.tag.get_property("REPLACE_X", config.replace_x);
292                         config.replace_y = input.tag.get_property("REPLACE_Y", config.replace_y);
293                         config.replace_w = input.tag.get_property("REPLACE_W", config.replace_w);
294                         config.replace_h = input.tag.get_property("REPLACE_H", config.replace_h);
295                         config.replace_dx = input.tag.get_property("REPLACE_DX", config.replace_dx);
296                         config.replace_dy = input.tag.get_property("REPLACE_DY", config.replace_dy);
297                         config.drag_replace = input.tag.get_property("DRAG_REPLACE", config.drag_replace);
298                         config.draw_keypoints = input.tag.get_property("DRAW_KEYPOINTS", config.draw_keypoints);
299                         config.draw_match = input.tag.get_property("DRAW_MATCH", config.draw_match);
300                         config.draw_scene_border = input.tag.get_property("DRAW_SCENE_BORDER", config.draw_scene_border);
301                         config.replace_object = input.tag.get_property("REPLACE_OBJECT", config.replace_object);
302                         config.draw_object_border = input.tag.get_property("DRAW_OBJECT_BORDER", config.draw_object_border);
303                         config.draw_replace_border = input.tag.get_property("DRAW_REPLACE_BORDER", config.draw_replace_border);
304                         config.object_layer = input.tag.get_property("OBJECT_LAYER", config.object_layer);
305                         config.replace_layer = input.tag.get_property("REPLACE_LAYER", config.replace_layer);
306                         config.scene_layer = input.tag.get_property("SCENE_LAYER", config.scene_layer);
307                         config.blend = input.tag.get_property("BLEND", config.blend);
308                 }
309         }
310
311         config.boundaries();
312 }
313
314 void FindObjMain::draw_line(VFrame *vframe, int x1, int y1, int x2, int y2)
315 {
316         vframe->draw_line(x1, y1, x2, y2);
317 }
318
319 void FindObjMain::draw_quad(VFrame *vframe,
320                 int x1, int y1, int x2, int y2,
321                 int x3, int y3, int x4, int y4)
322 {
323         int r = bmin(vframe->get_w(), vframe->get_h()) / 200 + 1;
324         for( int i=r; --i>0; ) {
325                 draw_line(vframe, x1+i, y1+i, x2, y2);
326                 draw_line(vframe, x1-i, y1-i, x2, y2);
327                 draw_line(vframe, x2+i, y2+i, x3, y3);
328                 draw_line(vframe, x2-i, y2-i, x3, y3);
329                 draw_line(vframe, x3+i, y3+i, x4, y4);
330                 draw_line(vframe, x3-i, y3-i, x4, y4);
331                 draw_line(vframe, x4+i, y4+i, x1, y1);
332                 draw_line(vframe, x4-i, y4-i, x1, y1);
333         }
334         draw_line(vframe, x1, y1, x2, y2);
335         draw_line(vframe, x2, y2, x3, y3);
336         draw_line(vframe, x3, y3, x4, y4);
337         draw_line(vframe, x4, y4, x1, y1);
338 }
339
340 void FindObjMain::draw_point(VFrame *vframe, int x1, int y1)
341 {
342         int r = bmin(vframe->get_w(), vframe->get_h()) / 200 + 1;
343         for( int i=r; --i>0; )
344                 draw_circle(vframe, x1, y1, i);
345 }
346
347 void FindObjMain::draw_rect(VFrame *vframe, int x1, int y1, int x2, int y2)
348 {
349         int r = bmin(vframe->get_w(), vframe->get_h()) / 200 + 1;
350         for( int i=r; --i>0; ) {
351                 --x2;  --y2;
352                 draw_line(vframe, x1, y1, x2, y1);
353                 draw_line(vframe, x2, y1, x2, y2);
354                 draw_line(vframe, x2, y2, x1, y2);
355                 draw_line(vframe, x1, y2, x1, y1);
356                 ++x1;  ++y1;
357         }
358 }
359
360 void FindObjMain::draw_circle(VFrame *vframe, int x, int y, int r)
361 {
362         int x1 = x-r, x2 = x+r;
363         int y1 = y-r, y2 = y+r;
364         vframe->draw_smooth(x1,y, x1,y1, x,y1);
365         vframe->draw_smooth(x,y1, x2,y1, x2,y);
366         vframe->draw_smooth(x2,y, x2,y2, x,y2);
367         vframe->draw_smooth(x,y2, x1,y2, x1,y);
368 }
369
370 void FindObjMain::filter_matches(ptV &p1, ptV &p2, double ratio)
371 {
372         DMatches::iterator it;
373         for( it=pairs.begin(); it!=pairs.end(); ++it ) { 
374                 DMatchV &m = *it;
375                 if( m.size() == 2 && m[0].distance < m[1].distance*ratio ) {
376                         p1.push_back(obj_keypts[m[0].queryIdx].pt);
377                         p2.push_back(scn_keypts[m[0].trainIdx].pt);
378                 }
379         }
380 }
381
382 void FindObjMain::to_mat(Mat &mat, int mcols, int mrows,
383         VFrame *inp, int ix,int iy, BC_CModel mcolor_model)
384 {
385         int mcomp = BC_CModels::components(mcolor_model);
386         int mbpp = BC_CModels::calculate_pixelsize(mcolor_model);
387         int psz = mbpp / mcomp;
388         int mdepth = psz < 2 ? CV_8U : CV_16U;
389         if( mat.dims != 2 || mat.depth() != mdepth || mat.channels() != mcomp ||
390             mat.cols != mcols || mat.rows != mrows ) {
391                 mat.release();
392         }
393         if( mat.empty() ) {
394                 int type = CV_MAKETYPE(mdepth, mcomp);
395                 mat.create(mrows, mcols, type);
396         }
397         uint8_t *mat_rows[mrows];
398         for( int y=0; y<mrows; ++y ) mat_rows[y] = mat.ptr(y);
399         uint8_t **inp_rows = inp->get_rows();
400         int ibpl = inp->get_bytes_per_line(), obpl = mcols * mbpp;
401         int icolor_model = inp->get_color_model();
402         BC_CModels::transfer(mat_rows, mcolor_model, 0,0, mcols,mrows, obpl,
403                 inp_rows, icolor_model, ix,iy, mcols,mrows, ibpl, 0);
404 //      VFrame vfrm(mat_rows[0], -1, mcols,mrows, mcolor_model, mat_rows[1]-mat_rows[0]);
405 //      static int vfrm_no = 0; char vfn[64]; sprintf(vfn,"/tmp/dat/%06d.png", vfrm_no++);
406 //      vfrm.write_png(vfn);
407 }
408
409 void FindObjMain::detect(Mat &mat, KeyPointV &keypts,Mat &descrs)
410 {
411         keypts.clear();
412         descrs.release();
413         try {
414                 detector->detectAndCompute(mat, noArray(), keypts, descrs);
415         } catch(std::exception e) { printf(_("detector exception: %s\n"), e.what()); }
416 }
417
418 void FindObjMain::match()
419 {
420         pairs.clear();
421         try {
422                 matcher->knnMatch(obj_descrs, scn_descrs, pairs, 2);
423         } catch(std::exception e) { printf(_("match execption: %s\n"), e.what()); }
424 }
425
426 Ptr<DescriptorMatcher> FindObjMain::flann_kdtree_matcher()
427 { // trees=5
428         const Ptr<flann::IndexParams>& indexParams =
429                 makePtr<flann::KDTreeIndexParams>(5);
430         const Ptr<flann::SearchParams>& searchParams =
431                 makePtr<flann::SearchParams>();
432         return makePtr<FlannBasedMatcher>(indexParams, searchParams);
433 }
434 Ptr<DescriptorMatcher> FindObjMain::flann_lshidx_matcher()
435 { // table_number = 6#12, key_size = 12#20, multi_probe_level = 1#2
436         const Ptr<flann::IndexParams>& indexParams =
437                 makePtr<flann::LshIndexParams>(6, 12, 1);
438         const Ptr<flann::SearchParams>& searchParams =
439                 makePtr<flann::SearchParams>();
440         return makePtr<FlannBasedMatcher>(indexParams, searchParams);
441 }
442 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_l2()
443 {
444         return BFMatcher::create(NORM_L2);
445 }
446 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_hamming()
447 {
448         return BFMatcher::create(NORM_HAMMING);
449 }
450
451 #ifdef _SIFT
452 void FindObjMain::set_sift()
453 {
454         cvmodel = BC_GREY8;
455         detector = SIFT::create();
456         matcher = config.use_flann ?
457                 flann_kdtree_matcher() : bf_matcher_norm_l2();
458 }
459 #endif
460 #ifdef _SURF
461 void FindObjMain::set_surf()
462 {
463         cvmodel = BC_GREY8;
464         detector = SURF::create(800);
465         matcher = config.use_flann ?
466                 flann_kdtree_matcher() : bf_matcher_norm_l2();
467 }
468 #endif
469 #ifdef _ORB
470 void FindObjMain::set_orb()
471 {
472         cvmodel = BC_GREY8;
473         detector = ORB::create();
474         matcher = config.use_flann ?
475                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
476 }
477 #endif
478 #ifdef _AKAZE
479 void FindObjMain::set_akaze()
480 {
481         cvmodel = BC_GREY8;
482         detector = AKAZE::create();
483         matcher = config.use_flann ?
484                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
485 }
486 #endif
487 #ifdef _BRISK
488 void FindObjMain::set_brisk()
489 {
490         cvmodel = BC_GREY8;
491         detector = BRISK::create();
492         matcher = config.use_flann ?
493                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
494 }
495 #endif
496
497
498 void FindObjMain::process_match()
499 {
500         if( config.algorithm == NO_ALGORITHM ) return;
501
502         if( detector.empty() ) {
503                 switch( config.algorithm ) {
504 #ifdef _SIFT
505                 case ALGORITHM_SIFT:   set_sift();   break;
506 #endif
507 #ifdef _SURF
508                 case ALGORITHM_SURF:   set_surf();   break;
509 #endif
510 #ifdef _ORB
511                 case ALGORITHM_ORB:    set_orb();    break;
512 #endif
513 #ifdef _AKAZE
514                 case ALGORITHM_AKAZE:  set_akaze();  break;
515 #endif
516 #ifdef _BRISK
517                 case ALGORITHM_BRISK:  set_brisk();  break;
518 #endif
519                 }
520                 obj_keypts.clear();  obj_descrs.release();
521                 to_mat(object_mat, object_w,object_h, object, object_x,object_y, cvmodel);
522                 detect(object_mat, obj_keypts, obj_descrs);
523 //printf("detect obj %d features\n", (int)obj_keypts.size());
524         }
525
526         to_mat(scene_mat, scene_w,scene_h, scene, scene_x,scene_y, cvmodel);
527         detect(scene_mat, scn_keypts, scn_descrs);
528 //printf("detect scn %d features\n", (int)scn_keypts.size());
529         match();
530         ptV p1, p2;
531         filter_matches(p1, p2);
532         if( p1.size() < 4 ) return;
533         Mat H = findHomography(p1, p2, RANSAC, 5.0);
534         if( !H.dims || !H.rows || !H.cols ) {
535 //printf("fail, size p1=%d,p2=%d\n",(int)p1.size(),(int)p2.size());
536                 return;
537         }
538
539         ptV src, dst;
540         float out_x1 = 0, out_x2 = object_w;
541         float out_y1 = 0, out_y2 = object_h;
542         src.push_back(Point2f(out_x1,out_y1));
543         src.push_back(Point2f(out_x2,out_y1));
544         src.push_back(Point2f(out_x2,out_y2));
545         src.push_back(Point2f(out_x1,out_y2));
546         perspectiveTransform(src, dst, H);
547
548         match_x1 = dst[0].x + scene_x;  match_y1 = dst[0].y + scene_y;
549         match_x2 = dst[1].x + scene_x;  match_y2 = dst[1].y + scene_y;
550         match_x3 = dst[2].x + scene_x;  match_y3 = dst[2].y + scene_y;
551         match_x4 = dst[3].x + scene_x;  match_y4 = dst[3].y + scene_y;
552 }
553
554
555 static double area(float x1, float y1, float x2, float y2,
556                 float x3, float y3, float x4, float y4)
557 { // quadrelateral area, sign is +ccw,-cw, use abs
558         double dx1 = x3-x1, dy1 = y3-y1;
559         double dx2 = x4-x2, dy2 = y4-y2;
560         return 0.5 * (dx1 * dy2 - dx2 * dy1);
561 }
562 static double dist(float x1,float y1, float x2, float y2)
563 {
564         double dx = x2-x1, dy = y2-y1;
565         return sqrt(dx*dx + dy*dy);
566 }
567 static int intersects(double x1, double y1, double x2, double y2,
568                 double x3, double y3, double x4, double y4)
569 {
570         double dx12 = x2 - x1, dy12 = y2 - y1;
571         double dx34 = x4 - x3, dy34 = y4 - y3;
572         double d = dx12*dy34 - dx34*dy12;
573         if( !d ) return 0; // parallel
574         double dx13 = x3 - x1, dy13 = y3 - y1;
575         double u = (dx13*dy34 - dx34*dy13) / d;
576         if( u < 0 || u > 1 ) return 0;
577         double v = (dx13*dy12 - dx12*dy13) / d;
578         if( v < 0 || v > 1 ) return 0;
579         return 1;
580 }
581
582 /*
583  * 4---------3    1---------2
584  * |0,h   w,h|    |0,0   w,0|
585  * |    +    |    |    +    |
586  * |0,0   w,0|    |0,h   w,h|
587  * 1---------2    1---------2
588  * pt locations    screen pts
589  */
590 void FindObjMain::reshape()
591 {
592         if( config.mode == MODE_NONE ) return;
593         const double pi = M_PI;
594         double x1 = match_x1, y1 = match_y1;
595         double x2 = match_x2, y2 = match_y2;
596         double x3 = match_x3, y3 = match_y3;
597         double x4 = match_x4, y4 = match_y4;
598         double ia = area(x1,y1, x2,y2, x3,y3, x4,y4);
599 // centroid
600         double cx = (x1 + x2 + x3 + x4) / 4;
601         double cy = (y1 + y2 + y3 + y4) / 4;
602 // centered
603         x1 -= cx;  x2 -= cx;  x3 -= cx;  x4 -= cx;
604         y1 -= cy;  y2 -= cy;  y3 -= cy;  y4 -= cy;
605 // bowtied
606         if( intersects(x1,y1, x2,y2, x3,y3, x4,y4) ) {
607                 double x = x2, y = y2;
608                 x2 = x3;  y2 = y3;
609                 x3 = x;   y3 = y;
610         }
611         else if( intersects(x1,y1, x4,y4, x3,y3, x2,y2) ) {
612                 double x = x4, y = y4;
613                 x4 = x3;  y4 = y3;
614                 x3 = x;   y3 = y;
615         }
616
617 // rotation, if mode is quad: reverse rotate
618         double r = 0;
619         if( (config.mode == MODE_QUADRILATERAL) ^ (config.rotate != 0) ) {
620 // edge centers
621                 double cx12 = (x1 + x2) / 2, cy12 = (y1 + y2) / 2;
622                 double cx23 = (x2 + x3) / 2, cy23 = (y2 + y3) / 2;
623                 double cx34 = (x3 + x4) / 2, cy34 = (y3 + y4) / 2;
624                 double cx41 = (x4 + x1) / 2, cy41 = (y4 + y1) / 2;
625                 double vx = cx34 - cx12, vy = cy34 - cy12;
626                 double hx = cx23 - cx41, hy = cy23 - cy41;
627                 double v = atan2(vy, vx);
628                 double h = atan2(hy, hx);
629                 r = (h + v - pi/2) / 2;
630                 if( config.mode != MODE_QUADRILATERAL ) r = -r;
631         }
632 // diagonal length
633         double a = dist(x1,y1, x3,y3) / 2;
634         double b = dist(x2,y2, x4,y4) / 2;
635         if( config.mode == MODE_SQUARE ||
636             config.mode == MODE_RECTANGLE )
637                 a = b = (a + b) / 2;
638 // central angles
639         double a1 = atan2(y1, x1);
640         double a2 = atan2(y2, x2);
641         double a3 = atan2(y3, x3);
642         double a4 = atan2(y4, x4);
643 // edge angles
644         double a12 = a2 - a1, a23 = a3 - a2;
645         double a34 = a4 - a3, a41 = a1 - a4;
646         double dt = (a12 - a23 + a34 - a41)/4;
647 // mirrored
648         if( ia < 0 ) { ia = -ia;  dt = -dt;  r = -r; }
649         switch( config.mode ) {
650         case MODE_SQUARE:
651         case MODE_RHOMBUS:
652                 dt = pi/2;
653         case MODE_RECTANGLE:
654         case MODE_PARALLELOGRAM: {
655                 double t = -(pi+dt)/2;
656                 x1 = a*cos(t);  y1 = a*sin(t);  t += dt;
657                 x2 = b*cos(t);  y2 = b*sin(t);  t += pi - dt;
658                 x3 = a*cos(t);  y3 = a*sin(t);  t += dt;
659                 x4 = b*cos(t);  y4 = b*sin(t); }
660         case MODE_QUADRILATERAL:
661                 break;
662         }
663 // aspect
664         if( !config.aspect ) {
665                 double cx12 = (x1 + x2) / 2, cy12 = (y1 + y2) / 2;
666                 double cx23 = (x2 + x3) / 2, cy23 = (y2 + y3) / 2;
667                 double cx34 = (x3 + x4) / 2, cy34 = (y3 + y4) / 2;
668                 double cx41 = (x4 + x1) / 2, cy41 = (y4 + y1) / 2;
669                 double iw = dist(cx41,cy41, cx23,cy23);
670                 double ih = dist(cx12,cy12, cx34,cy34);
671                 double ow = object_w, oh = object_h;
672                 double sx = iw && ih ? sqrt((ih*ow)/(iw*oh)) : 1;
673                 double sy = sx ? 1 / sx : 1;
674                 x1 *= sx;  x2 *= sx;  x3 *= sx;  x4 *= sx;
675                 y1 *= sy;  y2 *= sy;  y3 *= sy;  y4 *= sy;
676         }
677 // rotation
678         if( r ) {
679                 double ct = cos(r), st = sin(r), x, y;
680                 x = x1*ct + y1*st;  y = y1*ct - x1*st;  x1 = x;  y1 = y;
681                 x = x2*ct + y2*st;  y = y2*ct - x2*st;  x2 = x;  y2 = y;
682                 x = x3*ct + y3*st;  y = y3*ct - x3*st;  x3 = x;  y3 = y;
683                 x = x4*ct + y4*st;  y = y4*ct - x4*st;  x4 = x;  y4 = y;
684         }
685 // scaling
686         ia = !config.scale ? object_w * object_h : ia;
687         double oa = abs(area(x1,y1, x2,y2, x3,y3, x4,y4));
688         double sf =  oa ? sqrt(ia / oa) : 0;
689         x1 *= sf;  x2 *= sf;  x3 *= sf;  x4 *= sf;
690         y1 *= sf;  y2 *= sf;  y3 *= sf;  y4 *= sf;
691 // translation
692         double ox = !config.translate ? object_x + object_w/2. : cx;
693         double oy = !config.translate ? object_y + object_h/2. : cy;
694         x1 += ox;  x2 += ox;  x3 += ox;  x4 += ox;
695         y1 += oy;  y2 += oy;  y3 += oy;  y4 += oy;
696
697         shape_x1 = x1;  shape_y1 = y1;
698         shape_x2 = x2;  shape_y2 = y2;
699         shape_x3 = x3;  shape_y3 = y3;
700         shape_x4 = x4;  shape_y4 = y4;
701 }
702
703 int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
704 {
705         int prev_algorithm = config.algorithm;
706         int prev_use_flann = config.use_flann;
707
708         if( load_configuration() )
709                 init_border = 1;
710
711         if( prev_algorithm != config.algorithm ||
712             prev_use_flann != config.use_flann ) {
713                 detector.release();
714                 matcher.release();
715         }
716
717         object_layer = config.object_layer;
718         scene_layer = config.scene_layer;
719         replace_layer = config.replace_layer;
720         Track *track = server->plugin->track;
721         w = track->track_w;
722         h = track->track_h;
723
724         int max_layer = PluginClient::get_total_buffers() - 1;
725         object_layer = bclip(config.object_layer, 0, max_layer);
726         scene_layer = bclip(config.scene_layer, 0, max_layer);
727         replace_layer = bclip(config.replace_layer, 0, max_layer);
728
729         int cfg_w = (int)(w * config.object_w / 100.);
730         int cfg_h = (int)(h * config.object_h / 100.);
731         int cfg_x1 = (int)(w * config.object_x / 100. - cfg_w / 2);
732         int cfg_y1 = (int)(h * config.object_y / 100. - cfg_h / 2);
733         int cfg_x2 = cfg_x1 + cfg_w;
734         int cfg_y2 = cfg_y1 + cfg_h;
735         bclamp(cfg_x1, 0, w);  object_x = cfg_x1;
736         bclamp(cfg_y1, 0, h);  object_y = cfg_y1;
737         bclamp(cfg_x2, 0, w);  object_w = cfg_x2 - cfg_x1;
738         bclamp(cfg_y2, 0, h);  object_h = cfg_y2 - cfg_y1;
739
740         cfg_w = (int)(w * config.scene_w / 100.);
741         cfg_h = (int)(h * config.scene_h / 100.);
742         cfg_x1 = (int)(w * config.scene_x / 100. - cfg_w / 2);
743         cfg_y1 = (int)(h * config.scene_y / 100. - cfg_h / 2);
744         cfg_x2 = cfg_x1 + cfg_w;
745         cfg_y2 = cfg_y1 + cfg_h;
746         bclamp(cfg_x1, 0, w);  scene_x = cfg_x1;
747         bclamp(cfg_y1, 0, h);  scene_y = cfg_y1;
748         bclamp(cfg_x2, 0, w);  scene_w = cfg_x2 - cfg_x1;
749         bclamp(cfg_y2, 0, h);  scene_h = cfg_y2 - cfg_y1;
750
751         cfg_w = (int)(w * config.replace_w / 100.);
752         cfg_h = (int)(h * config.replace_h / 100.);
753         cfg_x1 = (int)(w * config.replace_x / 100. - cfg_w / 2);
754         cfg_y1 = (int)(h * config.replace_y / 100. - cfg_h / 2);
755         cfg_x2 = cfg_x1 + cfg_w;
756         cfg_y2 = cfg_y1 + cfg_h;
757         bclamp(cfg_x1, 0, w);  replace_x = cfg_x1;
758         bclamp(cfg_y1, 0, h);  replace_y = cfg_y1;
759         bclamp(cfg_x2, 0, w);  replace_w = cfg_x2 - cfg_x1;
760         bclamp(cfg_y2, 0, h);  replace_h = cfg_y2 - cfg_y1;
761
762         int cfg_dx = (int)(w * config.replace_dx / 100.);
763         int cfg_dy = (int)(h * config.replace_dy / 100.);
764         bclamp(cfg_dx, -h, h);  replace_dx = cfg_dx;
765         bclamp(cfg_dy, -w, w);  replace_dy = cfg_dy;
766
767 // Read in the input frames
768         for( int i = 0; i < PluginClient::get_total_buffers(); i++ ) {
769                 read_frame(frame[i], i, start_position, frame_rate, 0);
770         }
771
772         object = frame[object_layer];
773         scene = frame[scene_layer];
774         replace = frame[replace_layer];
775
776         shape_x1 = out_x1;  shape_y1 = out_y1;
777         shape_x2 = out_x2;  shape_y2 = out_y2;
778         shape_x3 = out_x3;  shape_y3 = out_y3;
779         shape_x4 = out_x4;  shape_y4 = out_y4;
780
781         if( scene_w > 0 && scene_h > 0 && object_w > 0 && object_h > 0 ) {
782                 process_match();
783                 reshape();
784         }
785
786         double w0 = init_border ? 1. : config.blend/100., w1 = 1. - w0;
787         init_border = 0;
788         out_x1 = shape_x1*w0 + out_x1*w1;  out_y1 = shape_y1*w0 + out_y1*w1;
789         out_x2 = shape_x2*w0 + out_x2*w1;  out_y2 = shape_y2*w0 + out_y2*w1;
790         out_x3 = shape_x3*w0 + out_x3*w1;  out_y3 = shape_y3*w0 + out_y3*w1;
791         out_x4 = shape_x4*w0 + out_x4*w1;  out_y4 = shape_y4*w0 + out_y4*w1;
792 // Replace object in the scene layer
793         if( config.replace_object ) {
794                 int cpus1 = get_project_smp() + 1;
795                 if( !affine )
796                         affine = new AffineEngine(cpus1, cpus1);
797                 if( !overlayer )
798                         overlayer = new OverlayFrame(cpus1);
799                 VFrame *temp = new_temp(w, h, scene->get_color_model());
800                 temp->clear_frame();
801                 affine->set_in_viewport(replace_x, replace_y, replace_w, replace_h);
802                 float ix1 = replace_x, ix2 = ix1 + replace_w;
803                 float iy1 = replace_y, iy2 = iy1 + replace_h;
804                 float dx = replace_dx, dy = replace_dy;
805                 float ox1 = out_x1+dx, ox2 = out_x2+dx, ox3 = out_x3+dx, ox4 = out_x4+dx;
806                 float oy1 = out_y1-dy, oy2 = out_y2-dy, oy3 = out_y3-dy, oy4 = out_y4-dy;
807                 affine->set_matrix(ix1,iy1, ix2,iy2, ox1,oy1, ox2,oy2, ox4,oy4, ox3,oy3);
808                 affine->process(temp, replace, 0,
809                         AffineEngine::TRANSFORM, 0,0, 100,0, 100,100, 0,100, 1);
810                 overlayer->overlay(scene, temp,  0,0, w,h,  0,0, w,h,
811                         1, TRANSFER_NORMAL, NEAREST_NEIGHBOR);
812
813         }
814
815         int wh = (w+h)>>8, ss = 1; while( wh>>=1 ) ss<<=1;
816         if( config.draw_scene_border ) {
817                 scene->set_stiple(ss*2);
818                 scene->set_pixel_color(WHITE);
819                 draw_rect(scene, scene_x, scene_y, scene_x+scene_w, scene_y+scene_h);
820         }
821         if( config.draw_object_border ) {
822                 scene->set_stiple(ss*3);
823                 scene->set_pixel_color(YELLOW); 
824                 draw_rect(scene, object_x, object_y, object_x+object_w, object_y+object_h);
825         }
826         if( config.draw_replace_border ) {
827                 scene->set_stiple(ss*3);
828                 scene->set_pixel_color(GREEN);
829                 draw_rect(scene, replace_x, replace_y, replace_x+replace_w, replace_y+replace_h);
830         }
831         scene->set_stiple(0);
832         if( config.draw_keypoints ) {
833                 scene->set_pixel_color(GREEN);
834                 for( int i=0,n=obj_keypts.size(); i<n; ++i ) {
835                         Point2f &pt = obj_keypts[i].pt;
836                         int r = obj_keypts[i].size * 1.2/9 * 2;
837                         int x = pt.x + object_x, y = pt.y + object_y;
838                         draw_circle(scene, x, y, r);
839                 }
840                 scene->set_pixel_color(RED);
841                 for( int i=0,n=scn_keypts.size(); i<n; ++i ) {
842                         Point2f &pt = scn_keypts[i].pt;
843                         int r = scn_keypts[i].size * 1.2/9 * 2;
844                         int x = pt.x + scene_x, y = pt.y + scene_y;
845                         draw_circle(scene, x, y, r);
846                 }
847         }
848         if( config.draw_match ) {
849                 scene->set_pixel_color(BLUE);
850                 draw_quad(scene, match_x1, match_y1, match_x2, match_y2,
851                                 match_x3, match_y3, match_x4, match_y4);
852                 scene->set_pixel_color(LTGREEN);
853                 draw_point(scene, match_x1, match_y1);
854         }
855
856         if( gui_open() ) {
857                 if( config.drag_scene ) {
858                         scene->set_pixel_color(WHITE);
859                         DragCheckBox::draw_boundary(scene, scene_x, scene_y, scene_w, scene_h);
860                 }
861                 if( config.drag_object ) {
862                         scene->set_pixel_color(YELLOW);
863                         DragCheckBox::draw_boundary(scene, object_x, object_y, object_w, object_h);
864                 }
865                 if( config.drag_replace ) {
866                         scene->set_pixel_color(GREEN);
867                         DragCheckBox::draw_boundary(scene, replace_x, replace_y, replace_w, replace_h);
868                 }
869         }
870
871         scene->set_pixel_color(BLACK);
872         scene->set_stiple(0);
873         return 0;
874 }
875