clean up cv extra files, replace motion load data fallback scan
[goodguy/history.git] / cinelerra-5.1 / plugins / motion-cv / motion-cv.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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
22 #include "affine.h"
23 #include "bcdisplayinfo.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "bcsignals.h"
27 #include "filexml.h"
28 #include "keyframe.h"
29 #include "language.h"
30 #include "motion-cv.h"
31 #include "motionwindow-cv.h"
32 #include "mutex.h"
33 #include "overlayframe.h"
34 #include "rotateframe.h"
35 #include "transportque.h"
36
37
38 #include <errno.h>
39 #include <unistd.h>
40
41 REGISTER_PLUGIN(MotionCVMain)
42
43 //#undef DEBUG
44
45 #ifndef DEBUG
46 #define DEBUG
47 #endif
48
49
50 MotionCVConfig::MotionCVConfig()
51 {
52         global_range_w = 5;
53         global_range_h = 5;
54         rotation_range = 5;
55         block_count = 1;
56         global_block_w = MIN_BLOCK;
57         global_block_h = MIN_BLOCK;
58         rotation_block_w = MIN_BLOCK;
59         rotation_block_h = MIN_BLOCK;
60         block_x = 50;
61         block_y = 50;
62         global_positions = 256;
63         rotate_positions = 4;
64         magnitude = 100;
65         return_speed = 0;
66         mode1 = STABILIZE;
67         global = 1;
68         rotate = 1;
69         addtrackedframeoffset = 0;
70         strcpy(tracking_file, TRACKING_FILE);
71         mode2 = RECALCULATE;
72         draw_vectors = 1;
73         mode3 = MotionCVConfig::TRACK_SINGLE;
74         track_frame = 0;
75         bottom_is_master = 1;
76         horizontal_only = 0;
77         vertical_only = 0;
78 }
79
80 void MotionCVConfig::boundaries()
81 {
82         CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
83         CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
84         CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
85         CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
86         CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
87         CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
88         CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
89         CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
90 }
91
92 int MotionCVConfig::equivalent(MotionCVConfig &that)
93 {
94         return global_range_w == that.global_range_w &&
95                 global_range_h == that.global_range_h &&
96                 rotation_range == that.rotation_range &&
97                 mode1 == that.mode1 &&
98                 global == that.global &&
99                 rotate == that.rotate &&
100                 addtrackedframeoffset == that.addtrackedframeoffset &&
101                 !strcmp(tracking_file, that.tracking_file) &&
102                 draw_vectors == that.draw_vectors &&
103                 block_count == that.block_count &&
104                 global_block_w == that.global_block_w &&
105                 global_block_h == that.global_block_h &&
106                 rotation_block_w == that.rotation_block_w &&
107                 rotation_block_h == that.rotation_block_h &&
108                 EQUIV(block_x, that.block_x) &&
109                 EQUIV(block_y, that.block_y) &&
110                 global_positions == that.global_positions &&
111                 rotate_positions == that.rotate_positions &&
112                 magnitude == that.magnitude &&
113                 return_speed == that.return_speed &&
114                 mode3 == that.mode3 &&
115                 track_frame == that.track_frame &&
116                 bottom_is_master == that.bottom_is_master &&
117                 horizontal_only == that.horizontal_only &&
118                 vertical_only == that.vertical_only;
119 }
120
121 void MotionCVConfig::copy_from(MotionCVConfig &that)
122 {
123         global_range_w = that.global_range_w;
124         global_range_h = that.global_range_h;
125         rotation_range = that.rotation_range;
126         mode1 = that.mode1;
127         global = that.global;
128         rotate = that.rotate;
129         addtrackedframeoffset = that.addtrackedframeoffset;
130         strcpy(tracking_file, that.tracking_file);
131         mode2 = that.mode2;
132         draw_vectors = that.draw_vectors;
133         block_count = that.block_count;
134         block_x = that.block_x;
135         block_y = that.block_y;
136         global_positions = that.global_positions;
137         rotate_positions = that.rotate_positions;
138         global_block_w = that.global_block_w;
139         global_block_h = that.global_block_h;
140         rotation_block_w = that.rotation_block_w;
141         rotation_block_h = that.rotation_block_h;
142         magnitude = that.magnitude;
143         return_speed = that.return_speed;
144         mode3 = that.mode3;
145         track_frame = that.track_frame;
146         bottom_is_master = that.bottom_is_master;
147         horizontal_only = that.horizontal_only;
148         vertical_only = that.vertical_only;
149 }
150
151 void MotionCVConfig::interpolate(MotionCVConfig &prev, MotionCVConfig &next, 
152                 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
153 {
154         copy_from(prev);
155 }
156
157 MotionCVMain::MotionCVMain(PluginServer *server)
158  : PluginVClient(server)
159 {
160         engine = 0;
161         rotate_engine = 0;
162         motion_rotate = 0;
163         total_dx = 0;
164         total_dy = 0;
165         total_angle = 0;
166         overlayer = 0;
167         search_area = 0;
168         search_size = 0;
169         temp_frame = 0;
170         previous_frame_number = -1;
171
172         prev_global_ref = 0;
173         current_global_ref = 0;
174         global_target_src = 0;
175         global_target_dst = 0;
176
177         active_fp = 0;
178         active_file[0] = 0;
179         tracking_frame = -1;
180         dx_offset = dy_offset = 0;
181         load_ok = 0;
182         save_dx = load_dx = 0;
183         save_dy = load_dy = 0;
184         save_dt = load_dt = 0;
185
186         prev_rotate_ref = 0;
187         current_rotate_ref = 0;
188         rotate_target_src = 0;
189         rotate_target_dst = 0;
190 }
191
192 MotionCVMain::~MotionCVMain()
193 {
194
195         delete engine;
196         delete overlayer;
197         delete [] search_area;
198         delete temp_frame;
199         delete rotate_engine;
200         delete motion_rotate;
201
202         delete prev_global_ref;
203         delete current_global_ref;
204         delete global_target_src;
205         delete global_target_dst;
206
207         if( active_fp ) fclose(active_fp); 
208
209         delete prev_rotate_ref;
210         delete current_rotate_ref;
211         delete rotate_target_src;
212         delete rotate_target_dst;
213 }
214
215 const char* MotionCVMain::plugin_title() { return _("MotionCV"); }
216 int MotionCVMain::is_realtime() { return 1; }
217 int MotionCVMain::is_multichannel() { return 1; }
218
219
220 NEW_WINDOW_MACRO(MotionCVMain, MotionCVWindow)
221
222 LOAD_CONFIGURATION_MACRO(MotionCVMain, MotionCVConfig)
223
224
225
226 void MotionCVMain::update_gui()
227 {
228         if(thread)
229         {
230                 if(load_configuration())
231                 {
232                         thread->window->lock_window("MotionCVMain::update_gui");
233                         MotionCVWindow *window = (MotionCVWindow *)thread->window;
234                         
235                         char string[BCTEXTLEN];
236                         sprintf(string, "%d", config.global_positions);
237                         window->global_search_positions->set_text(string);
238                         sprintf(string, "%d", config.rotate_positions);
239                         window->rotation_search_positions->set_text(string);
240
241                         window->global_block_w->update(config.global_block_w);
242                         window->global_block_h->update(config.global_block_h);
243                         window->rotation_block_w->update(config.rotation_block_w);
244                         window->rotation_block_h->update(config.rotation_block_h);
245                         window->block_x->update(config.block_x);
246                         window->block_y->update(config.block_y);
247                         window->block_x_text->update((float)config.block_x);
248                         window->block_y_text->update((float)config.block_y);
249                         window->magnitude->update(config.magnitude);
250                         window->return_speed->update(config.return_speed);
251
252
253                         window->track_single->update(config.mode3 == MotionCVConfig::TRACK_SINGLE);
254                         window->track_frame_number->update(config.track_frame);
255                         window->track_previous->update(config.mode3 == MotionCVConfig::TRACK_PREVIOUS);
256                         window->previous_same->update(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK);
257                         if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
258                                 window->track_frame_number->disable();
259                         else
260                                 window->track_frame_number->enable();
261
262                         window->mode1->set_text(
263                                 Mode1::to_text(config.mode1));
264                         window->mode2->set_text(
265                                 Mode2::to_text(config.mode2));
266                         window->mode3->set_text(
267                                 Mode3::to_text(config.horizontal_only, config.vertical_only));
268                         window->master_layer->set_text(
269                                 MasterLayer::to_text(config.bottom_is_master));
270
271
272                         window->update_mode();
273                         window->unlock_window();
274                 }
275         }
276 }
277
278
279
280
281 void MotionCVMain::save_data(KeyFrame *keyframe)
282 {
283         FileXML output;
284
285 // cause data to be stored directly in text
286         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
287         output.tag.set_title("MOTION");
288
289         output.tag.set_property("BLOCK_COUNT", config.block_count);
290         output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
291         output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
292         output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
293         output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
294         output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
295         output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
296         output.tag.set_property("BLOCK_X", config.block_x);
297         output.tag.set_property("BLOCK_Y", config.block_y);
298         output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
299         output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
300         output.tag.set_property("ROTATION_RANGE", config.rotation_range);
301         output.tag.set_property("MAGNITUDE", config.magnitude);
302         output.tag.set_property("RETURN_SPEED", config.return_speed);
303         output.tag.set_property("MODE1", config.mode1);
304         output.tag.set_property("GLOBAL", config.global);
305         output.tag.set_property("ROTATE", config.rotate);
306         output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
307         output.tag.set_property("TRACKING_FILE", config.tracking_file);
308         output.tag.set_property("MODE2", config.mode2);
309         output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
310         output.tag.set_property("MODE3", config.mode3);
311         output.tag.set_property("TRACK_FRAME", config.track_frame);
312         output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
313         output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
314         output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
315         output.append_tag();
316         output.tag.set_title("/MOTION");
317         output.append_tag();
318         output.terminate_string();
319 }
320
321 void MotionCVMain::read_data(KeyFrame *keyframe)
322 {
323         FileXML input;
324
325         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
326
327         int result = 0;
328
329         while(!result)
330         {
331                 result = input.read_tag();
332
333                 if(!result)
334                 {
335                         if(input.tag.title_is("MOTION"))
336                         {
337                                 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
338                                 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
339                                 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
340                                 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
341                                 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
342                                 config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
343                                 config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
344                                 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
345                                 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
346                                 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
347                                 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
348                                 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
349                                 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
350                                 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
351                                 config.mode1 = input.tag.get_property("MODE1", config.mode1);
352                                 config.global = input.tag.get_property("GLOBAL", config.global);
353                                 config.rotate = input.tag.get_property("ROTATE", config.rotate);
354                                 config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
355                                 input.tag.get_property("TRACKING_FILE", config.tracking_file);
356                                 config.mode2 = input.tag.get_property("MODE2", config.mode2);
357                                 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
358                                 config.mode3 = input.tag.get_property("MODE3", config.mode3);
359                                 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
360                                 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
361                                 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
362                                 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
363                         }
364                 }
365         }
366         config.boundaries();
367 }
368
369
370
371
372
373
374
375
376
377 void MotionCVMain::allocate_temp(int w, int h, int color_model)
378 {
379         if(temp_frame && 
380                 (temp_frame->get_w() != w ||
381                 temp_frame->get_h() != h))
382         {
383                 delete temp_frame;
384                 temp_frame = 0;
385         }
386         if(!temp_frame)
387                 temp_frame = new VFrame(w, h, color_model);
388 }
389
390
391
392 void MotionCVMain::process_global()
393 {
394         if(!engine) engine = new MotionCVScan(this,
395                 PluginClient::get_project_smp() + 1,
396                 PluginClient::get_project_smp() + 1);
397
398 // Get the current motion vector between the previous and current frame
399         engine->scan_frame(current_global_ref, prev_global_ref);
400         current_dx = engine->dx_result;
401         current_dy = engine->dy_result;
402
403 // Add current motion vector to accumulation vector.
404         if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
405         {
406 // Retract over time
407                 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
408                 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
409                 total_dx += engine->dx_result;
410                 total_dy += engine->dy_result;
411         }
412         else
413 // Make accumulation vector current
414         {
415                 total_dx = engine->dx_result;
416                 total_dy = engine->dy_result;
417         }
418
419 // Clamp accumulation vector
420         if(config.magnitude < 100)
421         {
422                 //int block_w = (int64_t)config.global_block_w * 
423                 //              current_global_ref->get_w() / 100;
424                 //int block_h = (int64_t)config.global_block_h * 
425                 //              current_global_ref->get_h() / 100;
426                 int block_x_orig = (int64_t)(config.block_x * 
427                         current_global_ref->get_w() / 
428                         100);
429                 int block_y_orig = (int64_t)(config.block_y *
430                         current_global_ref->get_h() / 
431                         100);
432
433                 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
434                         OVERSAMPLE * 
435                         config.magnitude / 
436                         100;
437                 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
438                         OVERSAMPLE *
439                         config.magnitude / 
440                         100;
441                 int min_block_x = (int64_t)-block_x_orig * 
442                         OVERSAMPLE * 
443                         config.magnitude / 
444                         100;
445                 int min_block_y = (int64_t)-block_y_orig * 
446                         OVERSAMPLE * 
447                         config.magnitude / 
448                         100;
449
450                 CLAMP(total_dx, min_block_x, max_block_x);
451                 CLAMP(total_dy, min_block_y, max_block_y);
452         }
453
454 #ifdef DEBUG
455 printf("MotionCVMain::process_global 2 total_dx=%.02f total_dy=%.02f\n", 
456 (float)total_dx / OVERSAMPLE,
457 (float)total_dy / OVERSAMPLE);
458 #endif
459
460         if(config.mode3 != MotionCVConfig::TRACK_SINGLE && !config.rotate)
461         {
462 // Transfer current reference frame to previous reference frame and update
463 // counter.  Must wait for rotate to compare.
464                 prev_global_ref->copy_from(current_global_ref);
465                 previous_frame_number = get_source_position();
466         }
467
468 // Decide what to do with target based on requested operation
469         int interpolation = NEAREST_NEIGHBOR;
470         float dx = 0;
471         float dy = 0;
472         switch(config.mode1)
473         {
474                 case MotionCVConfig::NOTHING:
475                         global_target_dst->copy_from(global_target_src);
476                         break;
477                 case MotionCVConfig::TRACK_PIXEL:
478                         interpolation = NEAREST_NEIGHBOR;
479                         dx = (int)(total_dx / OVERSAMPLE);
480                         dy = (int)(total_dy / OVERSAMPLE);
481                         break;
482                 case MotionCVConfig::STABILIZE_PIXEL:
483                         interpolation = NEAREST_NEIGHBOR;
484                         dx = -(int)(total_dx / OVERSAMPLE);
485                         dy = -(int)(total_dy / OVERSAMPLE);
486                         break;
487                         break;
488                 case MotionCVConfig::TRACK:
489                         interpolation = CUBIC_LINEAR;
490                         dx = (float)total_dx / OVERSAMPLE;
491                         dy = (float)total_dy / OVERSAMPLE;
492                         break;
493                 case MotionCVConfig::STABILIZE:
494                         interpolation = CUBIC_LINEAR;
495                         dx = -(float)total_dx / OVERSAMPLE;
496                         dy = -(float)total_dy / OVERSAMPLE;
497                         break;
498         }
499
500
501         if(config.mode1 != MotionCVConfig::NOTHING)
502         {
503                 if(!overlayer) 
504                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
505                 global_target_dst->clear_frame();
506                 overlayer->overlay(global_target_dst,
507                         global_target_src,
508                         0,
509                         0,
510                         global_target_src->get_w(),
511                         global_target_src->get_h(),
512                         dx,
513                         dy,
514                         (float)global_target_src->get_w() + dx,
515                         (float)global_target_src->get_h() + dy,
516                         1,
517                         TRANSFER_REPLACE,
518                         interpolation);
519         }
520 }
521
522
523
524 void MotionCVMain::process_rotation()
525 {
526         int block_x;
527         int block_y;
528
529 // Convert the previous global reference into the previous rotation reference.
530 // Convert global target destination into rotation target source.
531         if(config.global)
532         {
533                 if(!overlayer) 
534                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
535                 float dx;
536                 float dy;
537                 if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
538                 {
539                         dx = (float)total_dx / OVERSAMPLE;
540                         dy = (float)total_dy / OVERSAMPLE;
541                 }
542                 else
543                 {
544                         dx = (float)current_dx / OVERSAMPLE;
545                         dy = (float)current_dy / OVERSAMPLE;
546                 }
547
548                 prev_rotate_ref->clear_frame();
549                 overlayer->overlay(prev_rotate_ref,
550                         prev_global_ref,
551                         0,
552                         0,
553                         prev_global_ref->get_w(),
554                         prev_global_ref->get_h(),
555                         dx,
556                         dy,
557                         (float)prev_global_ref->get_w() + dx,
558                         (float)prev_global_ref->get_h() + dy,
559                         1,
560                         TRANSFER_REPLACE,
561                         CUBIC_LINEAR);
562 // Pivot is destination global position
563                 block_x = (int)(prev_rotate_ref->get_w() * 
564                         config.block_x / 
565                         100 +
566                         (float)total_dx / 
567                         OVERSAMPLE);
568                 block_y = (int)(prev_rotate_ref->get_h() * 
569                         config.block_y / 
570                         100 +
571                         (float)total_dy / 
572                         OVERSAMPLE);
573 // Use the global target output as the rotation target input
574                 rotate_target_src->copy_from(global_target_dst);
575 // Transfer current reference frame to previous reference frame for global.
576                 if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
577                 {
578                         prev_global_ref->copy_from(current_global_ref);
579                         previous_frame_number = get_source_position();
580                 }
581         }
582         else
583         {
584 // Pivot is fixed
585                 block_x = (int)(prev_rotate_ref->get_w() * 
586                         config.block_x / 
587                         100);
588                 block_y = (int)(prev_rotate_ref->get_h() * 
589                         config.block_y / 
590                         100);
591         }
592
593
594
595 // Get rotation
596         if(!motion_rotate)
597                 motion_rotate = new RotateCVScan(this, 
598                         get_project_smp() + 1, 
599                         get_project_smp() + 1);
600
601         current_angle = motion_rotate->scan_frame(prev_rotate_ref, 
602                 current_rotate_ref,
603                 block_x,
604                 block_y);
605
606
607
608 // Add current rotation to accumulation
609         if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
610         {
611 // Retract over time
612                 total_angle = total_angle * (100 - config.return_speed) / 100;
613                 total_angle += current_angle;
614
615                 if(!config.global)
616                 {
617 // Transfer current reference frame to previous reference frame and update
618 // counter.
619                         prev_rotate_ref->copy_from(current_rotate_ref);
620                         previous_frame_number = get_source_position();
621                 }
622         }
623         else
624         {
625                 total_angle = current_angle;
626         }
627
628 #ifdef DEBUG
629 printf("MotionCVMain::process_rotation total_angle=%f\n", total_angle);
630 #endif
631
632
633 // Calculate rotation parameters based on requested operation
634         float angle = 0;
635         switch(config.mode1)
636         {
637                 case MotionCVConfig::NOTHING:
638                         rotate_target_dst->copy_from(rotate_target_src);
639                         break;
640                 case MotionCVConfig::TRACK:
641                 case MotionCVConfig::TRACK_PIXEL:
642                         angle = total_angle;
643                         break;
644                 case MotionCVConfig::STABILIZE:
645                 case MotionCVConfig::STABILIZE_PIXEL:
646                         angle = -total_angle;
647                         break;
648         }
649
650
651
652         if(config.mode1 != MotionCVConfig::NOTHING)
653         {
654                 if(!rotate_engine)
655                         rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
656                                 PluginClient::get_project_smp() + 1);
657
658                 rotate_target_dst->clear_frame();
659
660 // Determine pivot based on a number of factors.
661                 switch(config.mode1)
662                 {
663                         case MotionCVConfig::TRACK:
664                         case MotionCVConfig::TRACK_PIXEL:
665 // Use destination of global tracking.
666                                 rotate_engine->set_pivot(block_x, block_y);
667                                 break;
668
669                         case MotionCVConfig::STABILIZE:
670                         case MotionCVConfig::STABILIZE_PIXEL:
671                                 if(config.global)
672                                 {
673 // Use origin of global stabilize operation
674                                         rotate_engine->set_pivot((int)(rotate_target_dst->get_w() * 
675                                                         config.block_x / 
676                                                         100),
677                                                 (int)(rotate_target_dst->get_h() * 
678                                                         config.block_y / 
679                                                         100));
680                                 
681                                 }
682                                 else
683                                 {
684 // Use origin
685                                         rotate_engine->set_pivot(block_x, block_y);
686                                 }
687                                 break;
688                 }
689
690
691                 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
692 // overlayer->overlay(rotate_target_dst,
693 //      prev_rotate_ref,
694 //      0,
695 //      0,
696 //      prev_rotate_ref->get_w(),
697 //      prev_rotate_ref->get_h(),
698 //      0,
699 //      0,
700 //      prev_rotate_ref->get_w(),
701 //      prev_rotate_ref->get_h(),
702 //      1,
703 //      TRANSFER_NORMAL,
704 //      CUBIC_LINEAR);
705 // overlayer->overlay(rotate_target_dst,
706 //      current_rotate_ref,
707 //      0,
708 //      0,
709 //      prev_rotate_ref->get_w(),
710 //      prev_rotate_ref->get_h(),
711 //      0,
712 //      0,
713 //      prev_rotate_ref->get_w(),
714 //      prev_rotate_ref->get_h(),
715 //      1,
716 //      TRANSFER_NORMAL,
717 //      CUBIC_LINEAR);
718
719
720         }
721
722
723 }
724
725
726
727
728
729
730
731
732
733 int MotionCVMain::process_buffer(VFrame **frame,
734         int64_t start_position,
735         double frame_rate)
736 {
737         int prev_config_mode2 = config.mode2;
738         int need_reconfigure = load_configuration();
739         int color_model = frame[0]->get_color_model();
740         w = frame[0]->get_w();
741         h = frame[0]->get_h();
742         
743
744 #ifdef DEBUG
745 printf("MotionCVMain::process_buffer 1 start_position=%jd\n", start_position);
746 #endif
747
748
749 // Calculate the source and destination pointers for each of the operations.
750 // Get the layer to track motion in.
751         reference_layer = config.bottom_is_master ?
752                 PluginClient::total_in_buffers - 1 :
753                 0;
754 // Get the layer to apply motion in.
755         target_layer = config.bottom_is_master ?
756                 0 :
757                 PluginClient::total_in_buffers - 1;
758
759
760         output_frame = frame[target_layer];
761
762
763 // Get the position of previous reference frame.
764         int64_t actual_previous_number;
765 // Skip if match frame not available
766         int skip_current = 0;
767
768
769         if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
770         {
771                 actual_previous_number = config.track_frame;
772                 if(get_direction() == PLAY_REVERSE)
773                         actual_previous_number++;
774                 if(actual_previous_number == start_position)
775                         skip_current = 1;
776         }
777         else
778         {
779                 actual_previous_number = start_position;
780                 if(get_direction() == PLAY_FORWARD)
781                 {
782                         actual_previous_number--;
783                         if(actual_previous_number < get_source_start())
784                                 skip_current = 1;
785                         else
786                         {
787                                 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
788                                 if(keyframe->position > 0 &&
789                                         actual_previous_number < keyframe->position)
790                                         skip_current = 1;
791                         }
792                 }
793                 else
794                 {
795                         actual_previous_number++;
796                         if(actual_previous_number >= get_source_start() + get_total_len())
797                                 skip_current = 1;
798                         else
799                         {
800                                 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
801                                 if(keyframe->position > 0 &&
802                                         actual_previous_number >= keyframe->position)
803                                         skip_current = 1;
804                         }
805                 }
806
807 // Only count motion since last keyframe
808                 
809
810         }
811
812
813         if(!config.global && !config.rotate) skip_current = 1;
814
815
816
817
818 // printf("process_realtime %d %lld %lld\n", 
819 // skip_current, 
820 // previous_frame_number, 
821 // actual_previous_number);
822 // Load match frame and reset vectors
823         int need_reload = !skip_current && 
824                 (previous_frame_number != actual_previous_number ||
825                 need_reconfigure);
826         if(need_reload)
827         {
828                 total_dx = 0;
829                 total_dy = 0;
830                 total_angle = 0;
831                 previous_frame_number = actual_previous_number;
832         }
833         if( prev_config_mode2 != MotionCVConfig::SAVE &&
834             config.mode2 == MotionCVConfig::SAVE ) {
835 #ifdef DEBUG
836 printf("MotionCVMain::process_buffer 2 remove tracking file: %s\n", config.tracking_file);
837 #endif
838                 ::remove(config.tracking_file);
839         }
840
841
842         if(skip_current)
843         {
844                 total_dx = 0;
845                 total_dy = 0;
846                 current_dx = 0;
847                 current_dy = 0;
848                 total_angle = 0;
849                 current_angle = 0;
850         }
851
852
853
854
855 // Get the global pointers.  Here we walk through the sequence of events.
856         if(config.global)
857         {
858 // Assume global only.  Global reads previous frame and compares
859 // with current frame to get the current translation.
860 // The center of the search area is fixed in compensate mode or
861 // the user value + the accumulation vector in track mode.
862                 if(!prev_global_ref)
863                         prev_global_ref = new VFrame(w, h, color_model);
864                 if(!current_global_ref)
865                         current_global_ref = new VFrame(w, h, color_model);
866
867 // Global loads the current target frame into the src and 
868 // writes it to the dst frame with desired translation.
869                 if(!global_target_src)
870                         global_target_src = new VFrame(w, h, color_model);
871                 if(!global_target_dst)
872                         global_target_dst = new VFrame(w, h, color_model);
873
874
875 // Load the global frames
876                 if(need_reload)
877                 {
878                         read_frame(prev_global_ref, 
879                                 reference_layer, 
880                                 previous_frame_number, 
881                                 frame_rate,
882                                 0);
883                 }
884
885                 read_frame(current_global_ref, 
886                         reference_layer, 
887                         start_position, 
888                         frame_rate,
889                         0);
890                 read_frame(global_target_src,
891                         target_layer,
892                         start_position,
893                         frame_rate,
894                         0);
895
896
897
898 // Global followed by rotate
899                 if(config.rotate)
900                 {
901 // Must translate the previous global reference by the current global
902 // accumulation vector to match the current global reference.
903 // The center of the search area is always the user value + the accumulation
904 // vector.
905                         if(!prev_rotate_ref)
906                                 prev_rotate_ref = new VFrame(w, h, color_model);
907 // The current global reference is the current rotation reference.
908                         if(!current_rotate_ref)
909                                 current_rotate_ref = new VFrame(w, h, color_model);
910                         current_rotate_ref->copy_from(current_global_ref);
911
912 // The global target destination is copied to the rotation target source
913 // then written to the rotation output with rotation.
914 // The pivot for the rotation is the center of the search area 
915 // if we're tracking.
916 // The pivot is fixed to the user position if we're compensating.
917                         if(!rotate_target_src)
918                                 rotate_target_src = new VFrame(w, h, color_model);
919                         if(!rotate_target_dst)
920                                 rotate_target_dst = new VFrame(w,h , color_model);
921                 }
922         }
923         else
924 // Rotation only
925         if(config.rotate)
926         {
927 // Rotation reads the previous reference frame and compares it with current 
928 // reference frame.
929                 if(!prev_rotate_ref)
930                         prev_rotate_ref = new VFrame(w, h, color_model);
931                 if(!current_rotate_ref)
932                         current_rotate_ref = new VFrame(w, h, color_model);
933
934 // Rotation loads target frame to temporary, rotates it, and writes it to the
935 // target frame.  The pivot is always fixed.
936                 if(!rotate_target_src)
937                         rotate_target_src = new VFrame(w, h, color_model);
938                 if(!rotate_target_dst)
939                         rotate_target_dst = new VFrame(w,h , color_model);
940
941
942 // Load the rotate frames
943                 if(need_reload)
944                 {
945                         read_frame(prev_rotate_ref, 
946                                 reference_layer, 
947                                 previous_frame_number, 
948                                 frame_rate,
949                                 0);
950                 }
951                 read_frame(current_rotate_ref, 
952                         reference_layer, 
953                         start_position, 
954                         frame_rate,
955                         0);
956                 read_frame(rotate_target_src,
957                         target_layer,
958                         start_position,
959                         frame_rate,
960                         0);
961         }
962
963         if(!skip_current)
964         {
965
966                 if( config.mode2 == MotionCVConfig::LOAD ) {
967                         char line[BCTEXTLEN];
968                         int64_t frame_no, no;  int dx, dy;  float dt;
969                         if( config.addtrackedframeoffset && config.track_frame != tracking_frame ) {
970                                 tracking_frame = frame_no = config.track_frame;
971                                 if( !get_line_key(config.tracking_file, frame_no, line, sizeof(line)) &&
972                                     sscanf(line, "%jd %d %d %f", &no, &dx, &dy, &dt) == 4 ) {
973                                         dx_offset = dx;  dy_offset = dy;
974                                 }
975                                 else {
976 #ifdef DEBUG
977                                         printf("MotionCVMain::process_buffer: no offset data frame %jd\n", frame_no);
978 #endif
979                                         dx_offset = 0;  dy_offset = 0;
980                                 }
981                         }
982 // Load result from disk
983                         load_ok= 0;
984                         frame_no = get_source_position();
985                         if( !get_line_key(config.tracking_file, frame_no, line, sizeof(line)) &&
986                             sscanf(line, "%jd %d %d %f", &frame_no, &dx, &dy, &dt) == 4 ) {
987                                 load_ok= 1;  load_dx = dx;  load_dy = dy;  load_dt = dt;
988                         }
989                         else {
990 #ifdef DEBUG
991                                 printf("MotionCVMain::process_buffer: no tracking data frame %jd\n", frame_no);
992 #endif
993                         }
994                 }
995
996 // Get position change from previous frame to current frame
997                 if(config.global) process_global();
998 // Get rotation change from previous frame to current frame
999                 if(config.rotate) process_rotation();
1000 //frame[target_layer]->copy_from(prev_rotate_ref);
1001 //frame[target_layer]->copy_from(current_rotate_ref);
1002
1003 // write results to disk
1004                 if( config.mode2 == MotionCVConfig::SAVE ) {
1005                         FILE *output = fopen(config.tracking_file, "aw");
1006                         if( output ) {
1007                                 int64_t frame_no = get_source_position();
1008                                 fprintf(output, "%jd %d %d %f\n", frame_no, save_dx, save_dy, save_dt);
1009                                 fclose(output);
1010                         }
1011                         else {
1012                                 perror("MotionCVMain::process buffer save");
1013                         }
1014                 }
1015         }
1016
1017
1018 // Transfer the relevant target frame to the output
1019         if(!skip_current)
1020         {
1021                 if(config.rotate)
1022                 {
1023                         frame[target_layer]->copy_from(rotate_target_dst);
1024                 }
1025                 else
1026                 {
1027                         frame[target_layer]->copy_from(global_target_dst);
1028                 }
1029         }
1030         else
1031 // Read the target destination directly
1032         {
1033                 read_frame(frame[target_layer],
1034                         target_layer,
1035                         start_position,
1036                         frame_rate,
1037                         0);
1038         }
1039
1040         if(config.draw_vectors)
1041         {
1042                 draw_vectors(frame[target_layer]);
1043         }
1044
1045 #ifdef DEBUG
1046 printf("MotionCVMain::process_buffer 100\n");
1047 #endif
1048         return 0;
1049 }
1050
1051
1052 void MotionCVMain::clamp_scan(int w, 
1053         int h, 
1054         int *block_x1,
1055         int *block_y1,
1056         int *block_x2,
1057         int *block_y2,
1058         int *scan_x1,
1059         int *scan_y1,
1060         int *scan_x2,
1061         int *scan_y2,
1062         int use_absolute)
1063 {
1064 // printf("MotionCVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1065 // w,
1066 // h,
1067 // *block_x1,
1068 // *block_y1,
1069 // *block_x2,
1070 // *block_y2,
1071 // *scan_x1,
1072 // *scan_y1,
1073 // *scan_x2,
1074 // *scan_y2,
1075 // use_absolute);
1076
1077         if(use_absolute)
1078         {
1079 // scan is always out of range before block.
1080                 if(*scan_x1 < 0)
1081                 {
1082                         int difference = -*scan_x1;
1083                         *block_x1 += difference;
1084                         *scan_x1 = 0;
1085                 }
1086
1087                 if(*scan_y1 < 0)
1088                 {
1089                         int difference = -*scan_y1;
1090                         *block_y1 += difference;
1091                         *scan_y1 = 0;
1092                 }
1093
1094                 if(*scan_x2 > w)
1095                 {
1096                         int difference = *scan_x2 - w;
1097                         *block_x2 -= difference;
1098                         *scan_x2 -= difference;
1099                 }
1100
1101                 if(*scan_y2 > h)
1102                 {
1103                         int difference = *scan_y2 - h;
1104                         *block_y2 -= difference;
1105                         *scan_y2 -= difference;
1106                 }
1107
1108                 CLAMP(*scan_x1, 0, w);
1109                 CLAMP(*scan_y1, 0, h);
1110                 CLAMP(*scan_x2, 0, w);
1111                 CLAMP(*scan_y2, 0, h);
1112         }
1113         else
1114         {
1115                 if(*scan_x1 < 0)
1116                 {
1117                         int difference = -*scan_x1;
1118                         *block_x1 += difference;
1119                         *scan_x2 += difference;
1120                         *scan_x1 = 0;
1121                 }
1122
1123                 if(*scan_y1 < 0)
1124                 {
1125                         int difference = -*scan_y1;
1126                         *block_y1 += difference;
1127                         *scan_y2 += difference;
1128                         *scan_y1 = 0;
1129                 }
1130
1131                 if(*scan_x2 - *block_x1 + *block_x2 > w)
1132                 {
1133                         int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1134                         *block_x2 -= difference;
1135                 }
1136
1137                 if(*scan_y2 - *block_y1 + *block_y2 > h)
1138                 {
1139                         int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1140                         *block_y2 -= difference;
1141                 }
1142
1143 //              CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1144 //              CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1145 //              CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1146 //              CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1147         }
1148
1149 // Sanity checks which break the calculation but should never happen if the
1150 // center of the block is inside the frame.
1151         CLAMP(*block_x1, 0, w);
1152         CLAMP(*block_x2, 0, w);
1153         CLAMP(*block_y1, 0, h);
1154         CLAMP(*block_y2, 0, h);
1155
1156 // printf("MotionCVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1157 // w,
1158 // h,
1159 // *block_x1,
1160 // *block_y1,
1161 // *block_x2,
1162 // *block_y2,
1163 // *scan_x1,
1164 // *scan_y1,
1165 // *scan_x2,
1166 // *scan_y2,
1167 // use_absolute);
1168 }
1169
1170
1171
1172 void MotionCVMain::draw_vectors(VFrame *frame)
1173 {
1174         int w = frame->get_w();
1175         int h = frame->get_h();
1176         int global_x1, global_y1;
1177         int global_x2, global_y2;
1178         int block_x, block_y;
1179         int block_w, block_h;
1180         int block_x1, block_y1;
1181         int block_x2, block_y2;
1182         int block_x3, block_y3;
1183         int block_x4, block_y4;
1184         int search_w, search_h;
1185         int search_x1, search_y1;
1186         int search_x2, search_y2;
1187         //int search_x3, search_y3;
1188         //int search_x4, search_y4;
1189
1190         if(config.global)
1191         {
1192 // Get vector
1193 // Start of vector is center of previous block.
1194 // End of vector is total accumulation.
1195                 if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
1196                 {
1197                         global_x1 = (int64_t)(config.block_x * 
1198                                 w / 
1199                                 100);
1200                         global_y1 = (int64_t)(config.block_y *
1201                                 h / 
1202                                 100);
1203                         global_x2 = global_x1 + total_dx / OVERSAMPLE;
1204                         global_y2 = global_y1 + total_dy / OVERSAMPLE;
1205 //printf("MotionCVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1206                 }
1207                 else
1208 // Start of vector is center of previous block.
1209 // End of vector is current change.
1210                 if(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK)
1211                 {
1212                         global_x1 = (int64_t)(config.block_x * 
1213                                 w / 
1214                                 100);
1215                         global_y1 = (int64_t)(config.block_y *
1216                                 h / 
1217                                 100);
1218                         global_x2 = global_x1 + current_dx / OVERSAMPLE;
1219                         global_y2 = global_y1 + current_dy / OVERSAMPLE;
1220                 }
1221                 else
1222                 {
1223                         global_x1 = (int64_t)(config.block_x * 
1224                                 w / 
1225                                 100 + 
1226                                 (total_dx - current_dx) / 
1227                                 OVERSAMPLE);
1228                         global_y1 = (int64_t)(config.block_y *
1229                                 h / 
1230                                 100 +
1231                                 (total_dy - current_dy) /
1232                                 OVERSAMPLE);
1233                         global_x2 = (int64_t)(config.block_x * 
1234                                 w / 
1235                                 100 + 
1236                                 total_dx / 
1237                                 OVERSAMPLE);
1238                         global_y2 = (int64_t)(config.block_y *
1239                                 h / 
1240                                 100 +
1241                                 total_dy /
1242                                 OVERSAMPLE);
1243                 }
1244
1245                 block_x = global_x1;
1246                 block_y = global_y1;
1247                 block_w = config.global_block_w * w / 100;
1248                 block_h = config.global_block_h * h / 100;
1249                 block_x1 = block_x - block_w / 2;
1250                 block_y1 = block_y - block_h / 2;
1251                 block_x2 = block_x + block_w / 2;
1252                 block_y2 = block_y + block_h / 2;
1253                 search_w = config.global_range_w * w / 100;
1254                 search_h = config.global_range_h * h / 100;
1255                 search_x1 = block_x1 - search_w / 2;
1256                 search_y1 = block_y1 - search_h / 2;
1257                 search_x2 = block_x2 + search_w / 2;
1258                 search_y2 = block_y2 + search_h / 2;
1259
1260 // printf("MotionCVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1261 // global_x1,
1262 // global_y1,
1263 // block_w,
1264 // block_h,
1265 // block_x1,
1266 // block_y1,
1267 // block_x2,
1268 // block_y2,
1269 // search_x1,
1270 // search_y1,
1271 // search_x2,
1272 // search_y2);
1273
1274                 clamp_scan(w, 
1275                         h, 
1276                         &block_x1,
1277                         &block_y1,
1278                         &block_x2,
1279                         &block_y2,
1280                         &search_x1,
1281                         &search_y1,
1282                         &search_x2,
1283                         &search_y2,
1284                         1);
1285
1286 // Vector
1287                 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1288
1289 // Macroblock
1290                 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1291                 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1292                 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1293                 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1294
1295
1296 // Search area
1297                 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1298                 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1299                 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1300                 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1301
1302 // Block should be endpoint of motion
1303                 if(config.rotate)
1304                 {
1305                         block_x = global_x2;
1306                         block_y = global_y2;
1307                 }
1308         }
1309         else
1310         {
1311                 block_x = (int64_t)(config.block_x * w / 100);
1312                 block_y = (int64_t)(config.block_y * h / 100);
1313         }
1314
1315         block_w = config.rotation_block_w * w / 100;
1316         block_h = config.rotation_block_h * h / 100;
1317         if(config.rotate)
1318         {
1319                 float angle = total_angle * 2 * M_PI / 360;
1320                 double base_angle1 = atan((float)block_h / block_w);
1321                 double base_angle2 = atan((float)block_w / block_h);
1322                 double target_angle1 = base_angle1 + angle;
1323                 double target_angle2 = base_angle2 + angle;
1324                 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1325                 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1326                 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1327                 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1328                 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1329                 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1330                 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1331                 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1332                 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1333
1334                 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1335                 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1336                 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1337                 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1338
1339
1340 // Center
1341                 if(!config.global)
1342                 {
1343                         draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1344                         draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1345                 }
1346         }
1347 }
1348
1349
1350
1351 void MotionCVMain::draw_pixel(VFrame *frame, int x, int y)
1352 {
1353         if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1354
1355 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1356 { \
1357         type **rows = (type**)frame->get_rows(); \
1358         rows[y][x * components] = max - rows[y][x * components]; \
1359         if(!do_yuv) \
1360         { \
1361                 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1362                 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1363         } \
1364         else \
1365         { \
1366                 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1367                 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1368         } \
1369         if(components == 4) \
1370                 rows[y][x * components + 3] = max; \
1371 }
1372
1373
1374         switch(frame->get_color_model())
1375         {
1376                 case BC_RGB888:
1377                         DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1378                         break;
1379                 case BC_RGBA8888:
1380                         DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1381                         break;
1382                 case BC_RGB_FLOAT:
1383                         DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1384                         break;
1385                 case BC_RGBA_FLOAT:
1386                         DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1387                         break;
1388                 case BC_YUV888:
1389                         DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1390                         break;
1391                 case BC_YUVA8888:
1392                         DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1393                         break;
1394                 case BC_RGB161616:
1395                         DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1396                         break;
1397                 case BC_YUV161616:
1398                         DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1399                         break;
1400                 case BC_RGBA16161616:
1401                         DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1402                         break;
1403                 case BC_YUVA16161616:
1404                         DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1405                         break;
1406         }
1407 }
1408
1409
1410 void MotionCVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1411 {
1412         int w = labs(x2 - x1);
1413         int h = labs(y2 - y1);
1414 //printf("MotionCVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1415
1416         if(!w && !h)
1417         {
1418                 draw_pixel(frame, x1, y1);
1419         }
1420         else
1421         if(w > h)
1422         {
1423 // Flip coordinates so x1 < x2
1424                 if(x2 < x1)
1425                 {
1426                         y2 ^= y1;
1427                         y1 ^= y2;
1428                         y2 ^= y1;
1429                         x1 ^= x2;
1430                         x2 ^= x1;
1431                         x1 ^= x2;
1432                 }
1433                 int numerator = y2 - y1;
1434                 int denominator = x2 - x1;
1435                 for(int i = x1; i < x2; i++)
1436                 {
1437                         int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1438                         draw_pixel(frame, i, y);
1439                 }
1440         }
1441         else
1442         {
1443 // Flip coordinates so y1 < y2
1444                 if(y2 < y1)
1445                 {
1446                         y2 ^= y1;
1447                         y1 ^= y2;
1448                         y2 ^= y1;
1449                         x1 ^= x2;
1450                         x2 ^= x1;
1451                         x1 ^= x2;
1452                 }
1453                 int numerator = x2 - x1;
1454                 int denominator = y2 - y1;
1455                 for(int i = y1; i < y2; i++)
1456                 {
1457                         int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1458                         draw_pixel(frame, x, i);
1459                 }
1460         }
1461 //printf("MotionCVMain::draw_line 2\n");
1462 }
1463
1464 #define ARROW_SIZE 10
1465 void MotionCVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1466 {
1467         double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1468         double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1469         double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1470         int x3;
1471         int y3;
1472         int x4;
1473         int y4;
1474         if(x2 < x1)
1475         {
1476                 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1477                 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1478                 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1479                 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1480         }
1481         else
1482         {
1483                 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1484                 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1485                 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1486                 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1487         }
1488
1489 // Main vector
1490         draw_line(frame, x1, y1, x2, y2);
1491 //      draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1492
1493 // Arrow line
1494         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1495 //      draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1496 // Arrow line
1497         if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1498 //      draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1499 }
1500
1501
1502
1503
1504 #define ABS_DIFF(type, temp_type, multiplier, components) \
1505 { \
1506         temp_type result_temp = 0; \
1507         for(int i = 0; i < h; i++) \
1508         { \
1509                 type *prev_row = (type*)prev_ptr; \
1510                 type *current_row = (type*)current_ptr; \
1511                 for(int j = 0; j < w; j++) \
1512                 { \
1513                         for(int k = 0; k < 3; k++) \
1514                         { \
1515                                 temp_type difference; \
1516                                 difference = *prev_row++ - *current_row++; \
1517                                 if(difference < 0) \
1518                                         result_temp -= difference; \
1519                                 else \
1520                                         result_temp += difference; \
1521                         } \
1522                         if(components == 4) \
1523                         { \
1524                                 prev_row++; \
1525                                 current_row++; \
1526                         } \
1527                 } \
1528                 prev_ptr += row_bytes; \
1529                 current_ptr += row_bytes; \
1530         } \
1531         result = (int64_t)(result_temp * multiplier); \
1532 }
1533
1534 int64_t MotionCVMain::abs_diff(unsigned char *prev_ptr,
1535         unsigned char *current_ptr,
1536         int row_bytes,
1537         int w,
1538         int h,
1539         int color_model)
1540 {
1541         int64_t result = 0;
1542         switch(color_model)
1543         {
1544                 case BC_RGB888:
1545                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1546                         break;
1547                 case BC_RGBA8888:
1548                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1549                         break;
1550                 case BC_RGB_FLOAT:
1551                         ABS_DIFF(float, double, 0x10000, 3)
1552                         break;
1553                 case BC_RGBA_FLOAT:
1554                         ABS_DIFF(float, double, 0x10000, 4)
1555                         break;
1556                 case BC_YUV888:
1557                         ABS_DIFF(unsigned char, int64_t, 1, 3)
1558                         break;
1559                 case BC_YUVA8888:
1560                         ABS_DIFF(unsigned char, int64_t, 1, 4)
1561                         break;
1562                 case BC_YUV161616:
1563                         ABS_DIFF(uint16_t, int64_t, 1, 3)
1564                         break;
1565                 case BC_YUVA16161616:
1566                         ABS_DIFF(uint16_t, int64_t, 1, 4)
1567                         break;
1568         }
1569         return result;
1570 }
1571
1572
1573
1574 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1575 { \
1576         temp_type result_temp = 0; \
1577         temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1578         temp_type y1_fraction = 0x100 - y2_fraction; \
1579         temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1580         temp_type x1_fraction = 0x100 - x2_fraction; \
1581         for(int i = 0; i < h_sub; i++) \
1582         { \
1583                 type *prev_row1 = (type*)prev_ptr; \
1584                 type *prev_row2 = (type*)prev_ptr + components; \
1585                 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1586                 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1587                 type *current_row = (type*)current_ptr; \
1588                 for(int j = 0; j < w_sub; j++) \
1589                 { \
1590                         for(int k = 0; k < 3; k++) \
1591                         { \
1592                                 temp_type difference; \
1593                                 temp_type prev_value = \
1594                                         (*prev_row1++ * x1_fraction * y1_fraction + \
1595                                         *prev_row2++ * x2_fraction * y1_fraction + \
1596                                         *prev_row3++ * x1_fraction * y2_fraction + \
1597                                         *prev_row4++ * x2_fraction * y2_fraction) / \
1598                                         0x100 / 0x100; \
1599                                 temp_type current_value = *current_row++; \
1600                                 difference = prev_value - current_value; \
1601                                 if(difference < 0) \
1602                                         result_temp -= difference; \
1603                                 else \
1604                                         result_temp += difference; \
1605                         } \
1606  \
1607                         if(components == 4) \
1608                         { \
1609                                 prev_row1++; \
1610                                 prev_row2++; \
1611                                 prev_row3++; \
1612                                 prev_row4++; \
1613                                 current_row++; \
1614                         } \
1615                 } \
1616                 prev_ptr += row_bytes; \
1617                 current_ptr += row_bytes; \
1618         } \
1619         result = (int64_t)(result_temp * multiplier); \
1620 }
1621
1622
1623
1624
1625 int64_t MotionCVMain::abs_diff_sub(unsigned char *prev_ptr,
1626         unsigned char *current_ptr,
1627         int row_bytes,
1628         int w,
1629         int h,
1630         int color_model,
1631         int sub_x,
1632         int sub_y)
1633 {
1634         int h_sub = h - 1;
1635         int w_sub = w - 1;
1636         int64_t result = 0;
1637
1638         switch(color_model)
1639         {
1640                 case BC_RGB888:
1641                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1642                         break;
1643                 case BC_RGBA8888:
1644                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1645                         break;
1646                 case BC_RGB_FLOAT:
1647                         ABS_DIFF_SUB(float, double, 0x10000, 3)
1648                         break;
1649                 case BC_RGBA_FLOAT:
1650                         ABS_DIFF_SUB(float, double, 0x10000, 4)
1651                         break;
1652                 case BC_YUV888:
1653                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1654                         break;
1655                 case BC_YUVA8888:
1656                         ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1657                         break;
1658                 case BC_YUV161616:
1659                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
1660                         break;
1661                 case BC_YUVA16161616:
1662                         ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
1663                         break;
1664         }
1665         return result;
1666 }
1667
1668 int MotionCVMain::get_line_key(const char *filename, int64_t key, char *line, int len)
1669 {
1670         if( active_fp && strcmp(active_file, filename) ) {
1671                 fclose(active_fp);  active_fp = 0;  active_file[0] = 0;
1672         }
1673         if( !active_fp ) {
1674                 if( !(active_fp = fopen(filename, "r")) ) {
1675                         perror("open motion file");
1676                         fprintf(stderr,"err reading key %jd\n", key);
1677                         return -1;
1678                 }
1679                 strcpy(active_file, filename);
1680                 tracking_frame = -1;
1681         }
1682         int64_t recd = 0;
1683         if( fgets(line, len, active_fp) && (recd=strtol(line,0,0)) == key )
1684                 return 0;
1685 // binary search file
1686         fseek(active_fp, 0, SEEK_END);
1687         int64_t l = -1, r = ftell(active_fp);
1688         while( (r-l) > 1 ) {
1689                 int64_t m = (l+r) / 2;
1690                 fseek(active_fp, m, SEEK_SET);
1691                 if( m > 0 && !fgets(line, len, active_fp) ) {
1692                         fprintf(stderr,"err reading key %jd\n", key);
1693                         return -1;
1694                 }
1695                 if( fgets(line, len, active_fp) ) {
1696                         recd = strtol(line,0,0);
1697                         if( recd == key ) return 0;
1698                         if( recd < key ) { l = m; continue; }
1699                 }
1700                 r = m;
1701         }
1702         return 1;
1703 }
1704
1705
1706
1707 MotionCVScanPackage::MotionCVScanPackage()
1708  : LoadPackage()
1709 {
1710         valid = 1;
1711 }
1712
1713
1714 MotionCVScanUnit::MotionCVScanUnit(MotionCVScan *server, 
1715         MotionCVMain *plugin)
1716  : LoadClient(server)
1717 {
1718         this->plugin = plugin;
1719         this->server = server;
1720         cache_lock = new Mutex("MotionCVScanUnit::cache_lock");
1721 }
1722
1723 MotionCVScanUnit::~MotionCVScanUnit()
1724 {
1725         delete cache_lock;
1726 }
1727
1728
1729
1730 void MotionCVScanUnit::process_package(LoadPackage *package)
1731 {
1732         MotionCVScanPackage *pkg = (MotionCVScanPackage*)package;
1733         //int w = server->current_frame->get_w();
1734         //int h = server->current_frame->get_h();
1735         int color_model = server->current_frame->get_color_model();
1736         int pixel_size = BC_CModels::calculate_pixelsize(color_model);
1737         int row_bytes = server->current_frame->get_bytes_per_line();
1738
1739 // Single pixel
1740         if(!server->subpixel)
1741         {
1742                 int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1));
1743                 int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1));
1744
1745 // Try cache
1746                 pkg->difference1 = server->get_cache(search_x, search_y);
1747                 if(pkg->difference1 < 0)
1748                 {
1749 //printf("MotionCVScanUnit::process_package 1 %d %d\n", 
1750 //search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1);
1751 // Pointers to first pixel in each block
1752                         unsigned char *prev_ptr = server->previous_frame->get_rows()[
1753                                 search_y] +     
1754                                 search_x * pixel_size;
1755                         unsigned char *current_ptr = server->current_frame->get_rows()[
1756                                 pkg->block_y1] +
1757                                 pkg->block_x1 * pixel_size;
1758 // Scan block
1759                         pkg->difference1 = plugin->abs_diff(prev_ptr,
1760                                 current_ptr,
1761                                 row_bytes,
1762                                 pkg->block_x2 - pkg->block_x1,
1763                                 pkg->block_y2 - pkg->block_y1,
1764                                 color_model);
1765 //printf("MotionCVScanUnit::process_package 2\n");
1766                         server->put_cache(search_x, search_y, pkg->difference1);
1767                 }
1768         }
1769         else
1770 // Sub pixel
1771         {
1772                 int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1;
1773                 int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1;
1774
1775                 if(plugin->config.horizontal_only)
1776                 {
1777                         sub_y = 0;
1778                 }
1779
1780                 if(plugin->config.vertical_only)
1781                 {
1782                         sub_x = 0;
1783                 }
1784
1785                 int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE;
1786                 int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE;
1787                 sub_x %= OVERSAMPLE;
1788                 sub_y %= OVERSAMPLE;
1789
1790
1791                 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1792                         search_y] +
1793                         search_x * pixel_size;
1794                 unsigned char *current_ptr = server->current_frame->get_rows()[
1795                         pkg->block_y1] +
1796                         pkg->block_x1 * pixel_size;
1797
1798 // With subpixel, there are two ways to compare each position, one by shifting
1799 // the previous frame and two by shifting the current frame.
1800                 pkg->difference1 = plugin->abs_diff_sub(prev_ptr,
1801                         current_ptr,
1802                         row_bytes,
1803                         pkg->block_x2 - pkg->block_x1,
1804                         pkg->block_y2 - pkg->block_y1,
1805                         color_model,
1806                         sub_x,
1807                         sub_y);
1808                 pkg->difference2 = plugin->abs_diff_sub(current_ptr,
1809                         prev_ptr,
1810                         row_bytes,
1811                         pkg->block_x2 - pkg->block_x1,
1812                         pkg->block_y2 - pkg->block_y1,
1813                         color_model,
1814                         sub_x,
1815                         sub_y);
1816 // printf("MotionCVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
1817 // sub_x,
1818 // sub_y,
1819 // search_x,
1820 // search_y,
1821 // pkg->difference1,
1822 // pkg->difference2);
1823         }
1824
1825
1826
1827
1828 }
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839 int64_t MotionCVScanUnit::get_cache(int x, int y)
1840 {
1841         int64_t result = -1;
1842         cache_lock->lock("MotionCVScanUnit::get_cache");
1843         for(int i = 0; i < cache.total; i++)
1844         {
1845                 MotionCVScanCache *ptr = cache.values[i];
1846                 if(ptr->x == x && ptr->y == y)
1847                 {
1848                         result = ptr->difference;
1849                         break;
1850                 }
1851         }
1852         cache_lock->unlock();
1853         return result;
1854 }
1855
1856 void MotionCVScanUnit::put_cache(int x, int y, int64_t difference)
1857 {
1858         MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference);
1859         cache_lock->lock("MotionCVScanUnit::put_cache");
1860         cache.append(ptr);
1861         cache_lock->unlock();
1862 }
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874 MotionCVScan::MotionCVScan(MotionCVMain *plugin, 
1875         int total_clients,
1876         int total_packages)
1877  : LoadServer(
1878 //1, 1 
1879 total_clients, total_packages 
1880 )
1881 {
1882         this->plugin = plugin;
1883         cache_lock = new Mutex("MotionCVScan::cache_lock");
1884 }
1885
1886 MotionCVScan::~MotionCVScan()
1887 {
1888         delete cache_lock;
1889 }
1890
1891
1892 void MotionCVScan::init_packages()
1893 {
1894 // Set package coords
1895         for(int i = 0; i < get_total_packages(); i++)
1896         {
1897                 MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
1898
1899                 pkg->block_x1 = block_x1;
1900                 pkg->block_x2 = block_x2;
1901                 pkg->block_y1 = block_y1;
1902                 pkg->block_y2 = block_y2;
1903                 pkg->scan_x1 = scan_x1;
1904                 pkg->scan_x2 = scan_x2;
1905                 pkg->scan_y1 = scan_y1;
1906                 pkg->scan_y2 = scan_y2;
1907                 pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps;
1908                 pkg->difference1 = 0;
1909                 pkg->difference2 = 0;
1910                 pkg->dx = 0;
1911                 pkg->dy = 0;
1912                 pkg->valid = 1;
1913         }
1914 }
1915
1916 LoadClient* MotionCVScan::new_client()
1917 {
1918         return new MotionCVScanUnit(this, plugin);
1919 }
1920
1921 LoadPackage* MotionCVScan::new_package()
1922 {
1923         return new MotionCVScanPackage;
1924 }
1925
1926
1927 void MotionCVScan::scan_frame(VFrame *previous_frame,
1928         VFrame *current_frame)
1929 {
1930         this->previous_frame = previous_frame;
1931         this->current_frame = current_frame;
1932         subpixel = 0;
1933
1934         cache.remove_all_objects();
1935
1936
1937 // Single macroblock
1938         int w = current_frame->get_w();
1939         int h = current_frame->get_h();
1940
1941 // Initial search parameters
1942         int scan_w = w * plugin->config.global_range_w / 100;
1943         int scan_h = h * plugin->config.global_range_h / 100;
1944         int block_w = w * plugin->config.global_block_w / 100;
1945         int block_h = h * plugin->config.global_block_h / 100;
1946
1947 // Location of block in previous frame
1948         block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2);
1949         block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2);
1950         block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2);
1951         block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2);
1952
1953 // Offset to location of previous block.  This offset needn't be very accurate
1954 // since it's the offset of the previous image and current image we want.
1955         if(plugin->config.mode3 == MotionCVConfig::TRACK_PREVIOUS)
1956         {
1957                 block_x1 += plugin->total_dx / OVERSAMPLE;
1958                 block_y1 += plugin->total_dy / OVERSAMPLE;
1959                 block_x2 += plugin->total_dx / OVERSAMPLE;
1960                 block_y2 += plugin->total_dy / OVERSAMPLE;
1961         }
1962
1963         skip = 0;
1964
1965         switch(plugin->config.mode2)
1966         {
1967 // Don't calculate
1968                 case MotionCVConfig::NO_CALCULATE:
1969                         dx_result = 0;
1970                         dy_result = 0;
1971                         skip = 1;
1972                         break;
1973
1974                 case MotionCVConfig::LOAD:
1975                 {
1976                         if( plugin->load_ok ) {
1977                                 dx_result = plugin->load_dx;
1978                                 dy_result = plugin->load_dy;
1979                                 skip = 1;
1980                         }
1981                         break;
1982                 }
1983
1984 // Scan from scratch
1985                 default:
1986                         skip = 0;
1987                         break;
1988         }
1989
1990 // Perform scan
1991         if(!skip)
1992         {
1993 // Location of block in current frame
1994                 int x_result = block_x1;
1995                 int y_result = block_y1;
1996
1997 // printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
1998 // block_x1 + block_w / 2,
1999 // block_y1 + block_h / 2,
2000 // block_w,
2001 // block_h,
2002 // block_x1,
2003 // block_y1,
2004 // block_x2,
2005 // block_y2);
2006
2007                 while(1)
2008                 {
2009                         scan_x1 = x_result - scan_w / 2;
2010                         scan_y1 = y_result - scan_h / 2;
2011                         scan_x2 = x_result + scan_w / 2;
2012                         scan_y2 = y_result + scan_h / 2;
2013
2014
2015
2016 // Zero out requested values
2017                         if(plugin->config.horizontal_only)
2018                         {
2019                                 scan_y1 = block_y1;
2020                                 scan_y2 = block_y1 + 1;
2021                         }
2022                         if(plugin->config.vertical_only)
2023                         {
2024                                 scan_x1 = block_x1;
2025                                 scan_x2 = block_x1 + 1;
2026                         }
2027
2028 // printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2029 // block_x1,
2030 // block_y1,
2031 // block_x2,
2032 // block_y2,
2033 // scan_x1,
2034 // scan_y1,
2035 // scan_x2,
2036 // scan_y2);
2037 // Clamp the block coords before the scan so we get useful scan coords.
2038                         MotionCVMain::clamp_scan(w, 
2039                                 h, 
2040                                 &block_x1,
2041                                 &block_y1,
2042                                 &block_x2,
2043                                 &block_y2,
2044                                 &scan_x1,
2045                                 &scan_y1,
2046                                 &scan_x2,
2047                                 &scan_y2,
2048                                 0);
2049 // printf("MotionCVScan::scan_frame 1\n    block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n    scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n    x_result=%d y_result=%d\n", 
2050 // block_x1,
2051 // block_y1,
2052 // block_x2,
2053 // block_y2,
2054 // scan_x1, 
2055 // scan_y1, 
2056 // scan_x2, 
2057 // scan_y2, 
2058 // x_result, 
2059 // y_result);
2060
2061
2062 // Give up if invalid coords.
2063                         if(scan_y2 <= scan_y1 ||
2064                                 scan_x2 <= scan_x1 ||
2065                                 block_x2 <= block_x1 ||
2066                                 block_y2 <= block_y1)
2067                                 break;
2068
2069 // For subpixel, the top row and left column are skipped
2070                         if(subpixel)
2071                         {
2072                                 if(plugin->config.horizontal_only ||
2073                                         plugin->config.vertical_only)
2074                                 {
2075                                         total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE;
2076                                 }
2077                                 else
2078                                 {
2079                                         total_pixels = 4 * OVERSAMPLE;
2080                                 }
2081
2082                                 total_steps = total_pixels;
2083
2084                                 set_package_count(total_steps);
2085                                 process_packages();
2086
2087 // Get least difference
2088                                 int64_t min_difference = -1;
2089                                 for(int i = 0; i < get_total_packages(); i++)
2090                                 {
2091                                         MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
2092                                         if(pkg->difference1 < min_difference || min_difference == -1)
2093                                         {
2094                                                 min_difference = pkg->difference1;
2095
2096                                                 if(plugin->config.vertical_only)
2097                                                         x_result = scan_x1 * OVERSAMPLE;
2098                                                 else
2099                                                         x_result = scan_x1 * OVERSAMPLE + 
2100                                                                 (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1;
2101                                                 
2102                                                 if(plugin->config.horizontal_only)
2103                                                         y_result = scan_y1 * OVERSAMPLE;
2104                                                 else
2105                                                         y_result = scan_y1 * OVERSAMPLE + 
2106                                                                 (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1;
2107
2108
2109 // Fill in results
2110                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2111                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2112                                         }
2113
2114                                         if(pkg->difference2 < min_difference)
2115                                         {
2116                                                 min_difference = pkg->difference2;
2117
2118                                                 if(plugin->config.vertical_only)
2119                                                         x_result = scan_x1 * OVERSAMPLE;
2120                                                 else
2121                                                         x_result = scan_x2 * OVERSAMPLE -
2122                                                                 ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1);
2123
2124                                                 if(plugin->config.horizontal_only)
2125                                                         y_result = scan_y1 * OVERSAMPLE;
2126                                                 else
2127                                                         y_result = scan_y2 * OVERSAMPLE -
2128                                                                 ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1);
2129
2130                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2131                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2132                                         }
2133                                 }
2134
2135 //printf("MotionCVScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result);
2136                                 break;
2137                         }
2138                         else
2139                         {
2140                                 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
2141                                 total_steps = MIN(plugin->config.global_positions, total_pixels);
2142
2143                                 set_package_count(total_steps);
2144                                 process_packages();
2145
2146 // Get least difference
2147                                 int64_t min_difference = -1;
2148                                 for(int i = 0; i < get_total_packages(); i++)
2149                                 {
2150                                         MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
2151                                         if(pkg->difference1 < min_difference || min_difference == -1)
2152                                         {
2153                                                 min_difference = pkg->difference1;
2154                                                 x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1));
2155                                                 y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1));
2156                                                 x_result *= OVERSAMPLE;
2157                                                 y_result *= OVERSAMPLE;
2158                                         }
2159                                 }
2160
2161 // printf("MotionCVScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n",
2162 // total_steps, 
2163 // total_pixels,
2164 // subpixel);
2165 // 
2166 // printf("     scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n",
2167 // scan_w,
2168 // scan_h, 
2169 // scan_x1,
2170 // scan_y1,
2171 // scan_x2,
2172 // scan_y2);
2173 // 
2174 // printf("MotionCVScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n", 
2175 // block_x1, 
2176 // block_y1, 
2177 // block_x2,
2178 // block_y2,
2179 // (float)x_result / 4, 
2180 // (float)y_result / 4);
2181
2182
2183 // If a new search is required, rescale results back to pixels.
2184                                 if(total_steps >= total_pixels)
2185                                 {
2186 // Single pixel accuracy reached.  Now do exhaustive subpixel search.
2187                                         if(plugin->config.mode1 == MotionCVConfig::STABILIZE ||
2188                                                 plugin->config.mode1 == MotionCVConfig::TRACK ||
2189                                                 plugin->config.mode1 == MotionCVConfig::NOTHING)
2190                                         {
2191                                                 x_result /= OVERSAMPLE;
2192                                                 y_result /= OVERSAMPLE;
2193                                                 scan_w = 2;
2194                                                 scan_h = 2;
2195                                                 subpixel = 1;
2196                                         }
2197                                         else
2198                                         {
2199 // Fill in results and quit
2200                                                 dx_result = block_x1 * OVERSAMPLE - x_result;
2201                                                 dy_result = block_y1 * OVERSAMPLE - y_result;
2202                                                 break;
2203                                         }
2204                                 }
2205                                 else
2206 // Reduce scan area and try again
2207                                 {
2208                                         scan_w = (scan_x2 - scan_x1) / 2;
2209                                         scan_h = (scan_y2 - scan_y1) / 2;
2210                                         x_result /= OVERSAMPLE;
2211                                         y_result /= OVERSAMPLE;
2212                                 }
2213                         }
2214                 }
2215
2216                 // Add offsets from the "tracked single frame"
2217                 dx_result = plugin->dx_offset - dx_result;
2218                 dy_result = plugin->dy_offset - dy_result;
2219
2220                 if(plugin->config.mode2 == MotionCVConfig::SAVE)
2221                 {
2222                         plugin->save_dx = dx_result;
2223                         plugin->save_dy = dy_result;
2224                 }
2225         }
2226
2227 #ifdef DEBUG
2228 printf("MotionCVScan::scan_frame 10 dx=%.2f dy=%.2f\n", 
2229 (float)this->dx_result / OVERSAMPLE,
2230 (float)this->dy_result / OVERSAMPLE);
2231 #endif
2232 }
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250 int64_t MotionCVScan::get_cache(int x, int y)
2251 {
2252         int64_t result = -1;
2253         cache_lock->lock("MotionCVScan::get_cache");
2254         for(int i = 0; i < cache.total; i++)
2255         {
2256                 MotionCVScanCache *ptr = cache.values[i];
2257                 if(ptr->x == x && ptr->y == y)
2258                 {
2259                         result = ptr->difference;
2260                         break;
2261                 }
2262         }
2263         cache_lock->unlock();
2264         return result;
2265 }
2266
2267 void MotionCVScan::put_cache(int x, int y, int64_t difference)
2268 {
2269         MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference);
2270         cache_lock->lock("MotionCVScan::put_cache");
2271         cache.append(ptr);
2272         cache_lock->unlock();
2273 }
2274
2275
2276
2277
2278
2279 MotionCVScanCache::MotionCVScanCache(int x, int y, int64_t difference)
2280 {
2281         this->x = x;
2282         this->y = y;
2283         this->difference = difference;
2284 }
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299 RotateCVScanPackage::RotateCVScanPackage()
2300 {
2301 }
2302
2303
2304 RotateCVScanUnit::RotateCVScanUnit(RotateCVScan *server, MotionCVMain *plugin)
2305  : LoadClient(server)
2306 {
2307         this->server = server;
2308         this->plugin = plugin;
2309         rotater = 0;
2310         temp = 0;
2311 }
2312
2313 RotateCVScanUnit::~RotateCVScanUnit()
2314 {
2315         delete rotater;
2316         delete temp;
2317 }
2318
2319 void RotateCVScanUnit::process_package(LoadPackage *package)
2320 {
2321         if(server->skip) return;
2322         RotateCVScanPackage *pkg = (RotateCVScanPackage*)package;
2323
2324         if((pkg->difference = server->get_cache(pkg->angle)) < 0)
2325         {
2326 //printf("RotateCVScanUnit::process_package 1\n");
2327                 int color_model = server->previous_frame->get_color_model();
2328                 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
2329                 int row_bytes = server->previous_frame->get_bytes_per_line();
2330
2331                 if(!rotater)
2332                         rotater = new AffineEngine(1, 1);
2333                 if(!temp) temp = new VFrame(
2334                         server->previous_frame->get_w(),
2335                         server->previous_frame->get_h(),
2336                         color_model);
2337
2338
2339 // RotateCV original block size
2340                 rotater->set_viewport(server->block_x1, 
2341                         server->block_y1,
2342                         server->block_x2 - server->block_x1,
2343                         server->block_y2 - server->block_y1);
2344                 rotater->set_pivot(server->block_x, server->block_y);
2345 //pkg->angle = 2;
2346                 rotater->rotate(temp,
2347                         server->previous_frame,
2348                         pkg->angle);
2349 // Clamp coordinates
2350                 int x1 = server->scan_x;
2351                 int y1 = server->scan_y;
2352                 int x2 = x1 + server->scan_w;
2353                 int y2 = y1 + server->scan_h;
2354                 x2 = MIN(temp->get_w(), x2);
2355                 y2 = MIN(temp->get_h(), y2);
2356                 x2 = MIN(server->current_frame->get_w(), x2);
2357                 y2 = MIN(server->current_frame->get_h(), y2);
2358                 x1 = MAX(0, x1);
2359                 y1 = MAX(0, y1);
2360
2361                 if(x2 > x1 && y2 > y1)
2362                 {
2363                         pkg->difference = plugin->abs_diff(
2364                                 temp->get_rows()[y1] + x1 * pixel_size,
2365                                 server->current_frame->get_rows()[y1] + x1 * pixel_size,
2366                                 row_bytes,
2367                                 x2 - x1,
2368                                 y2 - y1,
2369                                 color_model);
2370 //printf("RotateCVScanUnit::process_package %d\n", __LINE__);
2371                         server->put_cache(pkg->angle, pkg->difference);
2372                 }
2373
2374 // printf("RotateCVScanUnit::process_package 10 x=%d y=%d w=%d h=%d block_x=%d block_y=%d angle=%f scan_w=%d scan_h=%d diff=%lld\n", 
2375 // server->block_x1, 
2376 // server->block_y1,
2377 // server->block_x2 - server->block_x1,
2378 // server->block_y2 - server->block_y1,
2379 // server->block_x,
2380 // server->block_y,
2381 // pkg->angle, 
2382 // server->scan_w,
2383 // server->scan_h,
2384 // pkg->difference);
2385         }
2386 }
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409 RotateCVScan::RotateCVScan(MotionCVMain *plugin, 
2410         int total_clients, 
2411         int total_packages)
2412  : LoadServer(
2413 //1, 1 
2414 total_clients, total_packages 
2415 )
2416 {
2417         this->plugin = plugin;
2418         cache_lock = new Mutex("RotateCVScan::cache_lock");
2419 }
2420
2421
2422 RotateCVScan::~RotateCVScan()
2423 {
2424         delete cache_lock;
2425 }
2426
2427 void RotateCVScan::init_packages()
2428 {
2429         for(int i = 0; i < get_total_packages(); i++)
2430         {
2431                 RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i);
2432                 pkg->angle = i * 
2433                         (scan_angle2 - scan_angle1) / 
2434                         (total_steps - 1) + 
2435                         scan_angle1;
2436         }
2437 }
2438
2439 LoadClient* RotateCVScan::new_client()
2440 {
2441         return new RotateCVScanUnit(this, plugin);
2442 }
2443
2444 LoadPackage* RotateCVScan::new_package()
2445 {
2446         return new RotateCVScanPackage;
2447 }
2448
2449
2450 float RotateCVScan::scan_frame(VFrame *previous_frame,
2451         VFrame *current_frame,
2452         int block_x,
2453         int block_y)
2454 {
2455         skip = 0;
2456         this->block_x = block_x;
2457         this->block_y = block_y;
2458
2459         switch(plugin->config.mode2)
2460         {
2461                 case MotionCVConfig::NO_CALCULATE:
2462                         result = 0;
2463                         skip = 1;
2464                         break;
2465
2466                 case MotionCVConfig::LOAD:
2467                 {
2468                         if( plugin->load_ok ) {
2469                                 result = plugin->load_dt;
2470                                 skip = 1;
2471                         }
2472                         break;
2473                 }
2474         }
2475
2476
2477         this->previous_frame = previous_frame;
2478         this->current_frame = current_frame;
2479         int w = current_frame->get_w();
2480         int h = current_frame->get_h();
2481         int block_w = w * plugin->config.rotation_block_w / 100;
2482         int block_h = h * plugin->config.rotation_block_h / 100;
2483
2484         if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
2485         if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
2486         if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
2487         if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
2488
2489         block_x1 = this->block_x - block_w / 2;
2490         block_x2 = this->block_x + block_w / 2;
2491         block_y1 = this->block_y - block_h / 2;
2492         block_y2 = this->block_y + block_h / 2;
2493
2494
2495 // Calculate the maximum area available to scan after rotation.
2496 // Must be calculated from the starting range because of cache.
2497 // Get coords of rectangle after rotation.
2498         double center_x = this->block_x;
2499         double center_y = this->block_y;
2500         double max_angle = plugin->config.rotation_range;
2501         double base_angle1 = atan((float)block_h / block_w);
2502         double base_angle2 = atan((float)block_w / block_h);
2503         double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
2504         double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
2505         double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
2506         double x1 = center_x - cos(target_angle1) * radius;
2507         double y1 = center_y - sin(target_angle1) * radius;
2508         double x2 = center_x + sin(target_angle2) * radius;
2509         double y2 = center_y - cos(target_angle2) * radius;
2510         double x3 = center_x - sin(target_angle2) * radius;
2511         double y3 = center_y + cos(target_angle2) * radius;
2512
2513 // Track top edge to find greatest area.
2514         double max_area1 = 0;
2515         //double max_x1 = 0;
2516         double max_y1 = 0;
2517         for(double x = x1; x < x2; x++)
2518         {
2519                 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
2520                 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
2521                 {
2522                         double area = fabs(x - center_x) * fabs(y - center_y);
2523                         if(area > max_area1)
2524                         {
2525                                 max_area1 = area;
2526                                 //max_x1 = x;
2527                                 max_y1 = y;
2528                         }
2529                 }
2530         }
2531
2532 // Track left edge to find greatest area.
2533         double max_area2 = 0;
2534         double max_x2 = 0;
2535         //double max_y2 = 0;
2536         for(double y = y1; y < y3; y++)
2537         {
2538                 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
2539                 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
2540                 {
2541                         double area = fabs(x - center_x) * fabs(y - center_y);
2542                         if(area > max_area2)
2543                         {
2544                                 max_area2 = area;
2545                                 max_x2 = x;
2546                                 //max_y2 = y;
2547                         }
2548                 }
2549         }
2550
2551         double max_x, max_y;
2552         max_x = max_x2;
2553         max_y = max_y1;
2554
2555 // Get reduced scan coords
2556         scan_w = (int)(fabs(max_x - center_x) * 2);
2557         scan_h = (int)(fabs(max_y - center_y) * 2);
2558         scan_x = (int)(center_x - scan_w / 2);
2559         scan_y = (int)(center_y - scan_h / 2);
2560 // printf("RotateCVScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n", 
2561 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
2562 // printf("    angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
2563
2564 // Determine min angle from size of block
2565         double angle1 = atan((double)block_h / block_w);
2566         double angle2 = atan((double)(block_h - 1) / (block_w + 1));
2567         double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
2568         min_angle = MAX(min_angle, MIN_ANGLE);
2569
2570 #ifdef DEBUG
2571 printf("RotateCVScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI);
2572 #endif
2573
2574         cache.remove_all_objects();
2575         if(!skip)
2576         {
2577 // Initial search range
2578                 float angle_range = (float)plugin->config.rotation_range;
2579                 result = 0;
2580                 total_steps = plugin->config.rotate_positions;
2581
2582
2583                 while(angle_range >= min_angle * total_steps)
2584                 {
2585                         scan_angle1 = result - angle_range;
2586                         scan_angle2 = result + angle_range;
2587
2588
2589                         set_package_count(total_steps);
2590 //set_package_count(1);
2591                         process_packages();
2592
2593                         int64_t min_difference = -1;
2594                         for(int i = 0; i < get_total_packages(); i++)
2595                         {
2596                                 RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i);
2597                                 if(pkg->difference < min_difference || min_difference == -1)
2598                                 {
2599                                         min_difference = pkg->difference;
2600                                         result = pkg->angle;
2601                                 }
2602 //break;
2603                         }
2604
2605                         angle_range /= 2;
2606
2607 //break;
2608                 }
2609
2610                 if(plugin->config.mode2 == MotionCVConfig::SAVE) {
2611                         plugin->save_dt = result;
2612                 }
2613         }
2614
2615 #ifdef DEBUG
2616 printf("RotateCVScan::scan_frame 10 angle=%f\n", result);
2617 #endif
2618
2619         return result;
2620 }
2621
2622 int64_t RotateCVScan::get_cache(float angle)
2623 {
2624         int64_t result = -1;
2625         cache_lock->lock("RotateCVScan::get_cache");
2626         for(int i = 0; i < cache.total; i++)
2627         {
2628                 RotateCVScanCache *ptr = cache.values[i];
2629                 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
2630                 {
2631                         result = ptr->difference;
2632                         break;
2633                 }
2634         }
2635         cache_lock->unlock();
2636         return result;
2637 }
2638
2639 void RotateCVScan::put_cache(float angle, int64_t difference)
2640 {
2641         RotateCVScanCache *ptr = new RotateCVScanCache(angle, difference);
2642         cache_lock->lock("RotateCVScan::put_cache");
2643         cache.append(ptr);
2644         cache_lock->unlock();
2645 }
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655 RotateCVScanCache::RotateCVScanCache(float angle, int64_t difference)
2656 {
2657         this->angle = angle;
2658         this->difference = difference;
2659 }
2660
2661
2662