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