86235f2d19bed0fe22013dc101850849374ab070
[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
31 #include <errno.h>
32 #include <exception>
33 #include <unistd.h>
34
35 REGISTER_PLUGIN(FindObjMain)
36
37 FindObjConfig::FindObjConfig()
38 {
39         algorithm = NO_ALGORITHM;
40         use_flann = 1;
41         draw_keypoints = 0;
42         draw_border = 0;
43         replace_object = 0;
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;
49         scene_layer = 0;
50         object_layer = 1;
51         replace_layer = 2;
52         blend = 100;
53 }
54
55 void FindObjConfig::boundaries()
56 {
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);
65 }
66
67 int FindObjConfig::equivalent(FindObjConfig &that)
68 {
69         int result =
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 &&
83                 blend == that.blend;
84         return result;
85 }
86
87 void FindObjConfig::copy_from(FindObjConfig &that)
88 {
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;
102         blend = that.blend;
103 }
104
105 void FindObjConfig::interpolate(FindObjConfig &prev, FindObjConfig &next,
106         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
107 {
108         copy_from(prev);
109 }
110
111
112 FindObjMain::FindObjMain(PluginServer *server)
113  : PluginVClient(server)
114 {
115         affine = 0;
116         overlayer = 0;
117
118         cvmodel = BC_RGB888;
119         w = h = 0;
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;
125         object_layer = 0;
126         scene_layer = 1;
127         replace_layer = 2;
128
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;
133
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;
138
139         init_border = 1;
140 }
141
142 FindObjMain::~FindObjMain()
143 {
144         delete affine;
145         delete overlayer;
146 }
147
148 const char* FindObjMain::plugin_title() { return N_("FindObj"); }
149 int FindObjMain::is_realtime() { return 1; }
150 int FindObjMain::is_multichannel() { return 1; }
151
152 NEW_WINDOW_MACRO(FindObjMain, FindObjWindow)
153 LOAD_CONFIGURATION_MACRO(FindObjMain, FindObjConfig)
154
155 void FindObjMain::update_gui()
156 {
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);
187         window->flush();
188         window->unlock_window();
189 }
190
191 void FindObjMain::save_data(KeyFrame *keyframe)
192 {
193         FileXML output;
194
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);
216         output.append_tag();
217         output.tag.set_title("/FINDOBJ");
218         output.append_tag();
219         output.append_newline();
220         output.terminate_string();
221 }
222
223 void FindObjMain::read_data(KeyFrame *keyframe)
224 {
225         FileXML input;
226
227         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
228
229         int result = 0;
230
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);
251                 }
252         }
253
254         config.boundaries();
255 }
256
257 void FindObjMain::draw_line(VFrame *vframe, int x1, int y1, int x2, int y2)
258 {
259         vframe->draw_line(x1, y1, x2, y2);
260 }
261
262 void FindObjMain::draw_rect(VFrame *vframe, int x1, int y1, int x2, int y2)
263 {
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);
268 }
269
270 void FindObjMain::draw_circle(VFrame *vframe, int x, int y, int r)
271 {
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);
278 }
279
280 void FindObjMain::filter_matches(ptV &p1, ptV &p2, double ratio)
281 {
282         DMatches::iterator it;
283         for( it=pairs.begin(); it!=pairs.end(); ++it ) { 
284                 DMatchV &m = *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);
288                 }
289         }
290 }
291
292 void FindObjMain::to_mat(Mat &mat, int mcols, int mrows,
293         VFrame *inp, int ix,int iy, BC_CModel mcolor_model)
294 {
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 ) {
301                 mat.release();
302         }
303         if( mat.empty() ) {
304                 int type = CV_MAKETYPE(mdepth, mcomp);
305                 mat.create(mrows, mcols, type);
306         }
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);
317 }
318
319 void FindObjMain::detect(Mat &mat, KeyPointV &keypts,Mat &descrs)
320 {
321         keypts.clear();
322         descrs.release();
323         try {
324                 detector->detectAndCompute(mat, noArray(), keypts, descrs);
325         } catch(std::exception e) { printf(_("detector exception: %s\n"), e.what()); }
326 }
327
328 void FindObjMain::match()
329 {
330         pairs.clear();
331         try {
332                 matcher->knnMatch(obj_descrs, scn_descrs, pairs, 2);
333         } catch(std::exception e) { printf(_("match execption: %s\n"), e.what()); }
334 }
335
336 Ptr<DescriptorMatcher> FindObjMain::flann_kdtree_matcher()
337 { // trees=5
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);
343 }
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);
351 }
352 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_l2()
353 {
354         return BFMatcher::create(NORM_L2);
355 }
356 Ptr<DescriptorMatcher> FindObjMain::bf_matcher_norm_hamming()
357 {
358         return BFMatcher::create(NORM_HAMMING);
359 }
360
361 #ifdef _SIFT
362 void FindObjMain::set_sift()
363 {
364         cvmodel = BC_GREY8;
365         detector = SIFT::create();
366         matcher = config.use_flann ?
367                 flann_kdtree_matcher() : bf_matcher_norm_l2();
368 }
369 #endif
370 #ifdef _SURF
371 void FindObjMain::set_surf()
372 {
373         cvmodel = BC_GREY8;
374         detector = SURF::create(800);
375         matcher = config.use_flann ?
376                 flann_kdtree_matcher() : bf_matcher_norm_l2();
377 }
378 #endif
379 #ifdef _ORB
380 void FindObjMain::set_orb()
381 {
382         cvmodel = BC_GREY8;
383         detector = ORB::create();
384         matcher = config.use_flann ?
385                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
386 }
387 #endif
388 #ifdef _AKAZE
389 void FindObjMain::set_akaze()
390 {
391         cvmodel = BC_GREY8;
392         detector = AKAZE::create();
393         matcher = config.use_flann ?
394                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
395 }
396 #endif
397 #ifdef _BRISK
398 void FindObjMain::set_brisk()
399 {
400         cvmodel = BC_GREY8;
401         detector = BRISK::create();
402         matcher = config.use_flann ?
403                 flann_lshidx_matcher() : bf_matcher_norm_hamming();
404 }
405 #endif
406
407 void FindObjMain::process_match()
408 {
409         if( config.algorithm == NO_ALGORITHM ) return;
410         if( !config.replace_object &&
411             !config.draw_border &&
412             !config.draw_keypoints ) return;
413
414         if( detector.empty() ) {
415                 switch( config.algorithm ) {
416 #ifdef _SIFT
417                 case ALGORITHM_SIFT:   set_sift();   break;
418 #endif
419 #ifdef _SURF
420                 case ALGORITHM_SURF:   set_surf();   break;
421 #endif
422 #ifdef _ORB
423                 case ALGORITHM_ORB:    set_orb();    break;
424 #endif
425 #ifdef _AKAZE
426                 case ALGORITHM_AKAZE:  set_akaze();  break;
427 #endif
428 #ifdef _BRISK
429                 case ALGORITHM_BRISK:  set_brisk();  break;
430 #endif
431                 }
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());
436         }
437
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());
441         match();
442         ptV p1, p2;
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());
448                 return;
449         }
450
451         ptV src, dst;
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);
459
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);
468 }
469
470 int FindObjMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate)
471 {
472         int prev_algorithm = config.algorithm;
473         int prev_use_flann = config.use_flann;
474
475         if( load_configuration() )
476                 init_border = 1;
477
478         if( prev_algorithm != config.algorithm ||
479             prev_use_flann != config.use_flann ) {
480                 detector.release();
481                 matcher.release();
482         }
483
484         w = frame[0]->get_w();
485         h = frame[0]->get_h();
486
487         object_layer = config.object_layer;
488         scene_layer = config.scene_layer;
489         replace_layer = config.replace_layer;
490
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);
495
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;
507
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;
518
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);
522         }
523
524         object = frame[object_layer];
525         scene = frame[scene_layer];
526         replace = frame[replace_layer];
527
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;
532
533         if( scene_w > 0 && scene_h > 0 && object_w > 0 && object_h > 0 ) {
534                 process_match();
535         }
536
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;
542
543 // Replace object in the scene layer
544         if( config.replace_object ) {
545                 int cpus1 = get_project_smp() + 1;
546                 if( !affine )
547                         affine = new AffineEngine(cpus1, cpus1);
548                 if( !overlayer )
549                         overlayer = new OverlayFrame(cpus1);
550
551                 VFrame *temp = new_temp(scene->get_w(), scene->get_h(), scene->get_color_model());
552                 temp->clear_frame();
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);
562
563         }
564
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);
572         }
573
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);
581                 }
582         }
583
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);
595         }
596
597         scene->set_pixel_color(BLACK);
598         scene->set_stiple(0);
599         return 0;
600 }
601