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