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