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