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