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