2 #include "bcdisplayinfo.h"
9 #include "motionwindow.h"
11 #include "overlayframe.h"
12 #include "picon_png.h"
13 #include "rotateframe.h"
14 #include "transportque.h"
20 REGISTER_PLUGIN(MotionMain)
28 static void sort(int *array, int total)
34 for(int i = 0; i < total - 1; i++)
36 if(array[i] > array[i + 1])
38 array[i] ^= array[i + 1];
39 array[i + 1] ^= array[i];
40 array[i] ^= array[i + 1];
49 MotionConfig::MotionConfig()
55 global_block_w = MIN_BLOCK;
56 global_block_h = MIN_BLOCK;
57 rotation_block_w = MIN_BLOCK;
58 rotation_block_h = MIN_BLOCK;
61 global_positions = 256;
70 mode3 = MotionConfig::TRACK_SINGLE;
77 void MotionConfig::boundaries()
79 CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
80 CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
81 CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
82 CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
83 CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
84 CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
85 CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
86 CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
89 int MotionConfig::equivalent(MotionConfig &that)
91 return global_range_w == that.global_range_w &&
92 global_range_h == that.global_range_h &&
93 rotation_range == that.rotation_range &&
94 mode1 == that.mode1 &&
95 global == that.global &&
96 rotate == that.rotate &&
97 draw_vectors == that.draw_vectors &&
98 block_count == that.block_count &&
99 global_block_w == that.global_block_w &&
100 global_block_h == that.global_block_h &&
101 rotation_block_w == that.rotation_block_w &&
102 rotation_block_h == that.rotation_block_h &&
103 EQUIV(block_x, that.block_x) &&
104 EQUIV(block_y, that.block_y) &&
105 global_positions == that.global_positions &&
106 rotate_positions == that.rotate_positions &&
107 magnitude == that.magnitude &&
108 return_speed == that.return_speed &&
109 mode3 == that.mode3 &&
110 track_frame == that.track_frame &&
111 bottom_is_master == that.bottom_is_master &&
112 horizontal_only == that.horizontal_only &&
113 vertical_only == that.vertical_only;
116 void MotionConfig::copy_from(MotionConfig &that)
118 global_range_w = that.global_range_w;
119 global_range_h = that.global_range_h;
120 rotation_range = that.rotation_range;
122 global = that.global;
123 rotate = that.rotate;
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;
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;
144 void MotionConfig::interpolate(MotionConfig &prev,
148 int64_t current_frame)
150 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
151 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
152 this->block_x = prev.block_x;
153 this->block_y = prev.block_y;
154 global_range_w = prev.global_range_w;
155 global_range_h = prev.global_range_h;
156 rotation_range = prev.rotation_range;
158 global = prev.global;
159 rotate = prev.rotate;
161 draw_vectors = prev.draw_vectors;
162 block_count = prev.block_count;
163 global_positions = prev.global_positions;
164 rotate_positions = prev.rotate_positions;
165 global_block_w = prev.global_block_w;
166 global_block_h = prev.global_block_h;
167 rotation_block_w = prev.rotation_block_w;
168 rotation_block_h = prev.rotation_block_h;
169 magnitude = prev.magnitude;
170 return_speed = prev.return_speed;
172 track_frame = prev.track_frame;
173 bottom_is_master = prev.bottom_is_master;
174 horizontal_only = prev.horizontal_only;
175 vertical_only = prev.vertical_only;
196 MotionMain::MotionMain(PluginServer *server)
197 : PluginVClient(server)
199 PLUGIN_CONSTRUCTOR_MACRO
210 previous_frame_number = -1;
213 current_global_ref = 0;
214 global_target_src = 0;
215 global_target_dst = 0;
218 current_rotate_ref = 0;
219 rotate_target_src = 0;
220 rotate_target_dst = 0;
223 MotionMain::~MotionMain()
225 PLUGIN_DESTRUCTOR_MACRO
228 delete [] search_area;
230 delete rotate_engine;
231 delete motion_rotate;
234 delete prev_global_ref;
235 delete current_global_ref;
236 delete global_target_src;
237 delete global_target_dst;
239 delete prev_rotate_ref;
240 delete current_rotate_ref;
241 delete rotate_target_src;
242 delete rotate_target_dst;
245 char* MotionMain::plugin_title() { return N_("Motion"); }
246 int MotionMain::is_realtime() { return 1; }
247 int MotionMain::is_multichannel() { return 1; }
249 NEW_PICON_MACRO(MotionMain)
251 SHOW_GUI_MACRO(MotionMain, MotionThread)
253 SET_STRING_MACRO(MotionMain)
255 RAISE_WINDOW_MACRO(MotionMain)
257 LOAD_CONFIGURATION_MACRO(MotionMain, MotionConfig)
261 void MotionMain::update_gui()
265 if(load_configuration())
267 thread->window->lock_window("MotionMain::update_gui");
269 char string[BCTEXTLEN];
270 sprintf(string, "%d", config.global_positions);
271 thread->window->global_search_positions->set_text(string);
272 sprintf(string, "%d", config.rotate_positions);
273 thread->window->rotation_search_positions->set_text(string);
275 thread->window->global_block_w->update(config.global_block_w);
276 thread->window->global_block_h->update(config.global_block_h);
277 thread->window->rotation_block_w->update(config.rotation_block_w);
278 thread->window->rotation_block_h->update(config.rotation_block_h);
279 thread->window->block_x->update(config.block_x);
280 thread->window->block_y->update(config.block_y);
281 thread->window->block_x_text->update((float)config.block_x);
282 thread->window->block_y_text->update((float)config.block_y);
283 thread->window->magnitude->update(config.magnitude);
284 thread->window->return_speed->update(config.return_speed);
287 thread->window->track_single->update(config.mode3 == MotionConfig::TRACK_SINGLE);
288 thread->window->track_frame_number->update(config.track_frame);
289 thread->window->track_previous->update(config.mode3 == MotionConfig::TRACK_PREVIOUS);
290 thread->window->previous_same->update(config.mode3 == MotionConfig::PREVIOUS_SAME_BLOCK);
291 if(config.mode3 != MotionConfig::TRACK_SINGLE)
292 thread->window->track_frame_number->disable();
294 thread->window->track_frame_number->enable();
296 thread->window->mode1->set_text(
297 Mode1::to_text(config.mode1));
298 thread->window->mode2->set_text(
299 Mode2::to_text(config.mode2));
300 thread->window->mode3->set_text(
301 Mode3::to_text(config.horizontal_only, config.vertical_only));
302 thread->window->master_layer->set_text(
303 MasterLayer::to_text(config.bottom_is_master));
306 thread->window->update_mode();
307 thread->window->unlock_window();
313 int MotionMain::load_defaults()
315 char directory[BCTEXTLEN], string[BCTEXTLEN];
316 // set the default directory
317 sprintf(directory, "%smotion.rc", BCASTDIR);
320 defaults = new BC_Hash(directory);
323 config.block_count = defaults->get("BLOCK_COUNT", config.block_count);
324 config.global_positions = defaults->get("GLOBAL_POSITIONS", config.global_positions);
325 config.rotate_positions = defaults->get("ROTATE_POSITIONS", config.rotate_positions);
326 config.global_block_w = defaults->get("GLOBAL_BLOCK_W", config.global_block_w);
327 config.global_block_h = defaults->get("GLOBAL_BLOCK_H", config.global_block_h);
328 config.rotation_block_w = defaults->get("ROTATION_BLOCK_W", config.rotation_block_w);
329 config.rotation_block_h = defaults->get("ROTATION_BLOCK_H", config.rotation_block_h);
330 config.block_x = defaults->get("BLOCK_X", config.block_x);
331 config.block_y = defaults->get("BLOCK_Y", config.block_y);
332 config.global_range_w = defaults->get("GLOBAL_RANGE_W", config.global_range_w);
333 config.global_range_h = defaults->get("GLOBAL_RANGE_H", config.global_range_h);
334 config.rotation_range = defaults->get("ROTATION_RANGE", config.rotation_range);
335 config.magnitude = defaults->get("MAGNITUDE", config.magnitude);
336 config.return_speed = defaults->get("RETURN_SPEED", config.return_speed);
337 config.mode1 = defaults->get("MODE1", config.mode1);
338 config.global = defaults->get("GLOBAL", config.global);
339 config.rotate = defaults->get("ROTATE", config.rotate);
340 config.mode2 = defaults->get("MODE2", config.mode2);
341 config.draw_vectors = defaults->get("DRAW_VECTORS", config.draw_vectors);
342 config.mode3 = defaults->get("MODE3", config.mode3);
343 config.track_frame = defaults->get("TRACK_FRAME", config.track_frame);
344 config.bottom_is_master = defaults->get("BOTTOM_IS_MASTER", config.bottom_is_master);
345 config.horizontal_only = defaults->get("HORIZONTAL_ONLY", config.horizontal_only);
346 config.vertical_only = defaults->get("VERTICAL_ONLY", config.vertical_only);
352 int MotionMain::save_defaults()
354 defaults->update("BLOCK_COUNT", config.block_count);
355 defaults->update("GLOBAL_POSITIONS", config.global_positions);
356 defaults->update("ROTATE_POSITIONS", config.rotate_positions);
357 defaults->update("GLOBAL_BLOCK_W", config.global_block_w);
358 defaults->update("GLOBAL_BLOCK_H", config.global_block_h);
359 defaults->update("ROTATION_BLOCK_W", config.rotation_block_w);
360 defaults->update("ROTATION_BLOCK_H", config.rotation_block_h);
361 defaults->update("BLOCK_X", config.block_x);
362 defaults->update("BLOCK_Y", config.block_y);
363 defaults->update("GLOBAL_RANGE_W", config.global_range_w);
364 defaults->update("GLOBAL_RANGE_H", config.global_range_h);
365 defaults->update("ROTATION_RANGE", config.rotation_range);
366 defaults->update("MAGNITUDE", config.magnitude);
367 defaults->update("RETURN_SPEED", config.return_speed);
368 defaults->update("MODE1", config.mode1);
369 defaults->update("GLOBAL", config.global);
370 defaults->update("ROTATE", config.rotate);
371 defaults->update("MODE2", config.mode2);
372 defaults->update("DRAW_VECTORS", config.draw_vectors);
373 defaults->update("MODE3", config.mode3);
374 defaults->update("TRACK_FRAME", config.track_frame);
375 defaults->update("BOTTOM_IS_MASTER", config.bottom_is_master);
376 defaults->update("HORIZONTAL_ONLY", config.horizontal_only);
377 defaults->update("VERTICAL_ONLY", config.vertical_only);
384 void MotionMain::save_data(KeyFrame *keyframe)
388 // cause data to be stored directly in text
389 output.set_shared_string(keyframe->data, MESSAGESIZE);
390 output.tag.set_title("MOTION");
392 output.tag.set_property("BLOCK_COUNT", config.block_count);
393 output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
394 output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
395 output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
396 output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
397 output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
398 output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
399 output.tag.set_property("BLOCK_X", config.block_x);
400 output.tag.set_property("BLOCK_Y", config.block_y);
401 output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
402 output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
403 output.tag.set_property("ROTATION_RANGE", config.rotation_range);
404 output.tag.set_property("MAGNITUDE", config.magnitude);
405 output.tag.set_property("RETURN_SPEED", config.return_speed);
406 output.tag.set_property("MODE1", config.mode1);
407 output.tag.set_property("GLOBAL", config.global);
408 output.tag.set_property("ROTATE", config.rotate);
409 output.tag.set_property("MODE2", config.mode2);
410 output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
411 output.tag.set_property("MODE3", config.mode3);
412 output.tag.set_property("TRACK_FRAME", config.track_frame);
413 output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
414 output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
415 output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
417 output.tag.set_title("/MOTION");
419 output.terminate_string();
422 void MotionMain::read_data(KeyFrame *keyframe)
426 input.set_shared_string(keyframe->data, strlen(keyframe->data));
432 result = input.read_tag();
436 if(input.tag.title_is("MOTION"))
438 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
439 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
440 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
441 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
442 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
443 config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
444 config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
445 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
446 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
447 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
448 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
449 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
450 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
451 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
452 config.mode1 = input.tag.get_property("MODE1", config.mode1);
453 config.global = input.tag.get_property("GLOBAL", config.global);
454 config.rotate = input.tag.get_property("ROTATE", config.rotate);
455 config.mode2 = input.tag.get_property("MODE2", config.mode2);
456 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
457 config.mode3 = input.tag.get_property("MODE3", config.mode3);
458 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
459 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
460 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
461 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
476 void MotionMain::allocate_temp(int w, int h, int color_model)
479 (temp_frame->get_w() != w ||
480 temp_frame->get_h() != h))
486 temp_frame = new VFrame(0, w, h, color_model);
491 void MotionMain::process_global()
493 if(!engine) engine = new MotionScan(this,
494 PluginClient::get_project_smp() + 1,
495 PluginClient::get_project_smp() + 1);
497 // Get the current motion vector between the previous and current frame
498 engine->scan_frame(current_global_ref, prev_global_ref);
499 current_dx = engine->dx_result;
500 current_dy = engine->dy_result;
502 // Add current motion vector to accumulation vector.
503 if(config.mode3 != MotionConfig::TRACK_SINGLE)
506 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
507 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
508 total_dx += engine->dx_result;
509 total_dy += engine->dy_result;
512 // Make accumulation vector current
514 total_dx = engine->dx_result;
515 total_dy = engine->dy_result;
518 // Clamp accumulation vector
519 if(config.magnitude < 100)
521 int block_w = (int64_t)config.global_block_w *
522 current_global_ref->get_w() / 100;
523 int block_h = (int64_t)config.global_block_h *
524 current_global_ref->get_h() / 100;
525 int block_x_orig = (int64_t)(config.block_x *
526 current_global_ref->get_w() /
528 int block_y_orig = (int64_t)(config.block_y *
529 current_global_ref->get_h() /
532 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
536 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
540 int min_block_x = (int64_t)-block_x_orig *
544 int min_block_y = (int64_t)-block_y_orig *
549 CLAMP(total_dx, min_block_x, max_block_x);
550 CLAMP(total_dy, min_block_y, max_block_y);
554 printf("MotionMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
555 (float)total_dx / OVERSAMPLE,
556 (float)total_dy / OVERSAMPLE);
559 if(config.mode3 != MotionConfig::TRACK_SINGLE && !config.rotate)
561 // Transfer current reference frame to previous reference frame and update
562 // counter. Must wait for rotate to compare.
563 prev_global_ref->copy_from(current_global_ref);
564 previous_frame_number = get_source_position();
567 // Decide what to do with target based on requested operation
573 case MotionConfig::NOTHING:
574 global_target_dst->copy_from(global_target_src);
576 case MotionConfig::TRACK_PIXEL:
577 interpolation = NEAREST_NEIGHBOR;
578 dx = (int)(total_dx / OVERSAMPLE);
579 dy = (int)(total_dy / OVERSAMPLE);
581 case MotionConfig::STABILIZE_PIXEL:
582 interpolation = NEAREST_NEIGHBOR;
583 dx = -(int)(total_dx / OVERSAMPLE);
584 dy = -(int)(total_dy / OVERSAMPLE);
587 case MotionConfig::TRACK:
588 interpolation = CUBIC_LINEAR;
589 dx = (float)total_dx / OVERSAMPLE;
590 dy = (float)total_dy / OVERSAMPLE;
592 case MotionConfig::STABILIZE:
593 interpolation = CUBIC_LINEAR;
594 dx = -(float)total_dx / OVERSAMPLE;
595 dy = -(float)total_dy / OVERSAMPLE;
600 if(config.mode1 != MotionConfig::NOTHING)
603 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
604 global_target_dst->clear_frame();
605 overlayer->overlay(global_target_dst,
609 global_target_src->get_w(),
610 global_target_src->get_h(),
613 (float)global_target_src->get_w() + dx,
614 (float)global_target_src->get_h() + dy,
623 void MotionMain::process_rotation()
628 // Convert the previous global reference into the previous rotation reference.
629 // Convert global target destination into rotation target source.
633 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
636 if(config.mode3 == MotionConfig::TRACK_SINGLE)
638 dx = (float)total_dx / OVERSAMPLE;
639 dy = (float)total_dy / OVERSAMPLE;
643 dx = (float)current_dx / OVERSAMPLE;
644 dy = (float)current_dy / OVERSAMPLE;
647 prev_rotate_ref->clear_frame();
648 overlayer->overlay(prev_rotate_ref,
652 prev_global_ref->get_w(),
653 prev_global_ref->get_h(),
656 (float)prev_global_ref->get_w() + dx,
657 (float)prev_global_ref->get_h() + dy,
661 // Pivot is destination global position
662 block_x = (int)(prev_rotate_ref->get_w() *
667 block_y = (int)(prev_rotate_ref->get_h() *
672 // Use the global target output as the rotation target input
673 rotate_target_src->copy_from(global_target_dst);
674 // Transfer current reference frame to previous reference frame for global.
675 if(config.mode3 != MotionConfig::TRACK_SINGLE)
677 prev_global_ref->copy_from(current_global_ref);
678 previous_frame_number = get_source_position();
684 block_x = (int)(prev_rotate_ref->get_w() *
687 block_y = (int)(prev_rotate_ref->get_h() *
696 motion_rotate = new RotateScan(this,
697 get_project_smp() + 1,
698 get_project_smp() + 1);
700 current_angle = motion_rotate->scan_frame(prev_rotate_ref,
707 // Add current rotation to accumulation
708 if(config.mode3 != MotionConfig::TRACK_SINGLE)
711 total_angle = total_angle * (100 - config.return_speed) / 100;
712 total_angle += current_angle;
716 // Transfer current reference frame to previous reference frame and update
718 prev_rotate_ref->copy_from(current_rotate_ref);
719 previous_frame_number = get_source_position();
724 total_angle = current_angle;
728 printf("MotionMain::process_rotation total_angle=%f\n", total_angle);
732 // Calculate rotation parameters based on requested operation
736 case MotionConfig::NOTHING:
737 rotate_target_dst->copy_from(rotate_target_src);
739 case MotionConfig::TRACK:
740 case MotionConfig::TRACK_PIXEL:
743 case MotionConfig::STABILIZE:
744 case MotionConfig::STABILIZE_PIXEL:
745 angle = -total_angle;
751 if(config.mode1 != MotionConfig::NOTHING)
754 rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
755 PluginClient::get_project_smp() + 1);
757 rotate_target_dst->clear_frame();
759 // Determine pivot based on a number of factors.
762 case MotionConfig::TRACK:
763 case MotionConfig::TRACK_PIXEL:
764 // Use destination of global tracking.
765 rotate_engine->set_pivot(block_x, block_y);
768 case MotionConfig::STABILIZE:
769 case MotionConfig::STABILIZE_PIXEL:
772 // Use origin of global stabilize operation
773 rotate_engine->set_pivot((int)(rotate_target_dst->get_w() *
776 (int)(rotate_target_dst->get_h() *
784 rotate_engine->set_pivot(block_x, block_y);
790 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
791 // overlayer->overlay(rotate_target_dst,
795 // prev_rotate_ref->get_w(),
796 // prev_rotate_ref->get_h(),
799 // prev_rotate_ref->get_w(),
800 // prev_rotate_ref->get_h(),
804 // overlayer->overlay(rotate_target_dst,
805 // current_rotate_ref,
808 // prev_rotate_ref->get_w(),
809 // prev_rotate_ref->get_h(),
812 // prev_rotate_ref->get_w(),
813 // prev_rotate_ref->get_h(),
832 int MotionMain::process_buffer(VFrame **frame,
833 int64_t start_position,
836 int need_reconfigure = load_configuration();
837 int color_model = frame[0]->get_color_model();
838 w = frame[0]->get_w();
839 h = frame[0]->get_h();
843 printf("MotionMain::process_buffer 1 start_position=%lld\n", start_position);
847 // Calculate the source and destination pointers for each of the operations.
848 // Get the layer to track motion in.
849 reference_layer = config.bottom_is_master ?
850 PluginClient::total_in_buffers - 1 :
852 // Get the layer to apply motion in.
853 target_layer = config.bottom_is_master ?
855 PluginClient::total_in_buffers - 1;
858 output_frame = frame[target_layer];
861 // Get the position of previous reference frame.
862 int64_t actual_previous_number;
863 // Skip if match frame not available
864 int skip_current = 0;
867 if(config.mode3 == MotionConfig::TRACK_SINGLE)
869 actual_previous_number = config.track_frame;
870 if(get_direction() == PLAY_REVERSE)
871 actual_previous_number++;
872 if(actual_previous_number == start_position)
877 actual_previous_number = start_position;
878 if(get_direction() == PLAY_FORWARD)
880 actual_previous_number--;
881 if(actual_previous_number < get_source_start())
885 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
886 if(keyframe->position > 0 &&
887 actual_previous_number < keyframe->position)
893 actual_previous_number++;
894 if(actual_previous_number >= get_source_start() + get_total_len())
898 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
899 if(keyframe->position > 0 &&
900 actual_previous_number >= keyframe->position)
905 // Only count motion since last keyframe
911 if(!config.global && !config.rotate) skip_current = 1;
916 // printf("process_realtime %d %lld %lld\n",
918 // previous_frame_number,
919 // actual_previous_number);
920 // Load match frame and reset vectors
921 int need_reload = !skip_current &&
922 (previous_frame_number != actual_previous_number ||
929 previous_frame_number = actual_previous_number;
946 // Get the global pointers. Here we walk through the sequence of events.
949 // Assume global only. Global reads previous frame and compares
950 // with current frame to get the current translation.
951 // The center of the search area is fixed in compensate mode or
952 // the user value + the accumulation vector in track mode.
954 prev_global_ref = new VFrame(0, w, h, color_model);
955 if(!current_global_ref)
956 current_global_ref = new VFrame(0, w, h, color_model);
958 // Global loads the current target frame into the src and
959 // writes it to the dst frame with desired translation.
960 if(!global_target_src)
961 global_target_src = new VFrame(0, w, h, color_model);
962 if(!global_target_dst)
963 global_target_dst = new VFrame(0, w, h, color_model);
966 // Load the global frames
969 read_frame(prev_global_ref,
971 previous_frame_number,
975 read_frame(current_global_ref,
979 read_frame(global_target_src,
986 // Global followed by rotate
989 // Must translate the previous global reference by the current global
990 // accumulation vector to match the current global reference.
991 // The center of the search area is always the user value + the accumulation
994 prev_rotate_ref = new VFrame(0, w, h, color_model);
995 // The current global reference is the current rotation reference.
996 if(!current_rotate_ref)
997 current_rotate_ref = new VFrame(0, w, h, color_model);
998 current_rotate_ref->copy_from(current_global_ref);
1000 // The global target destination is copied to the rotation target source
1001 // then written to the rotation output with rotation.
1002 // The pivot for the rotation is the center of the search area
1003 // if we're tracking.
1004 // The pivot is fixed to the user position if we're compensating.
1005 if(!rotate_target_src)
1006 rotate_target_src = new VFrame(0, w, h, color_model);
1007 if(!rotate_target_dst)
1008 rotate_target_dst = new VFrame(0, w,h , color_model);
1015 // Rotation reads the previous reference frame and compares it with current
1017 if(!prev_rotate_ref)
1018 prev_rotate_ref = new VFrame(0, w, h, color_model);
1019 if(!current_rotate_ref)
1020 current_rotate_ref = new VFrame(0, w, h, color_model);
1022 // Rotation loads target frame to temporary, rotates it, and writes it to the
1023 // target frame. The pivot is always fixed.
1024 if(!rotate_target_src)
1025 rotate_target_src = new VFrame(0, w, h, color_model);
1026 if(!rotate_target_dst)
1027 rotate_target_dst = new VFrame(0, w,h , color_model);
1030 // Load the rotate frames
1033 read_frame(prev_rotate_ref,
1035 previous_frame_number,
1038 read_frame(current_rotate_ref,
1042 read_frame(rotate_target_src,
1059 // Get position change from previous frame to current frame
1060 if(config.global) process_global();
1061 // Get rotation change from previous frame to current frame
1062 if(config.rotate) process_rotation();
1063 //frame[target_layer]->copy_from(prev_rotate_ref);
1064 //frame[target_layer]->copy_from(current_rotate_ref);
1072 // Transfer the relevant target frame to the output
1077 frame[target_layer]->copy_from(rotate_target_dst);
1081 frame[target_layer]->copy_from(global_target_dst);
1085 // Read the target destination directly
1087 read_frame(frame[target_layer],
1093 if(config.draw_vectors)
1095 draw_vectors(frame[target_layer]);
1099 printf("MotionMain::process_buffer 100\n");
1105 void MotionMain::clamp_scan(int w,
1117 // printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1132 // scan is always out of range before block.
1135 int difference = -*scan_x1;
1136 *block_x1 += difference;
1142 int difference = -*scan_y1;
1143 *block_y1 += difference;
1149 int difference = *scan_x2 - w;
1150 *block_x2 -= difference;
1151 *scan_x2 -= difference;
1156 int difference = *scan_y2 - h;
1157 *block_y2 -= difference;
1158 *scan_y2 -= difference;
1161 CLAMP(*scan_x1, 0, w);
1162 CLAMP(*scan_y1, 0, h);
1163 CLAMP(*scan_x2, 0, w);
1164 CLAMP(*scan_y2, 0, h);
1170 int difference = -*scan_x1;
1171 *block_x1 += difference;
1172 *scan_x2 += difference;
1178 int difference = -*scan_y1;
1179 *block_y1 += difference;
1180 *scan_y2 += difference;
1184 if(*scan_x2 - *block_x1 + *block_x2 > w)
1186 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1187 *block_x2 -= difference;
1190 if(*scan_y2 - *block_y1 + *block_y2 > h)
1192 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1193 *block_y2 -= difference;
1196 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1197 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1198 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1199 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1202 // Sanity checks which break the calculation but should never happen if the
1203 // center of the block is inside the frame.
1204 CLAMP(*block_x1, 0, w);
1205 CLAMP(*block_x2, 0, w);
1206 CLAMP(*block_y1, 0, h);
1207 CLAMP(*block_y2, 0, h);
1209 // printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1225 void MotionMain::draw_vectors(VFrame *frame)
1227 int w = frame->get_w();
1228 int h = frame->get_h();
1229 int global_x1, global_y1;
1230 int global_x2, global_y2;
1231 int block_x, block_y;
1232 int block_w, block_h;
1233 int block_x1, block_y1;
1234 int block_x2, block_y2;
1235 int block_x3, block_y3;
1236 int block_x4, block_y4;
1237 int search_w, search_h;
1238 int search_x1, search_y1;
1239 int search_x2, search_y2;
1240 int search_x3, search_y3;
1241 int search_x4, search_y4;
1246 // Start of vector is center of previous block.
1247 // End of vector is total accumulation.
1248 if(config.mode3 == MotionConfig::TRACK_SINGLE)
1250 global_x1 = (int64_t)(config.block_x *
1253 global_y1 = (int64_t)(config.block_y *
1256 global_x2 = global_x1 + total_dx / OVERSAMPLE;
1257 global_y2 = global_y1 + total_dy / OVERSAMPLE;
1258 //printf("MotionMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1261 // Start of vector is center of previous block.
1262 // End of vector is current change.
1263 if(config.mode3 == MotionConfig::PREVIOUS_SAME_BLOCK)
1265 global_x1 = (int64_t)(config.block_x *
1268 global_y1 = (int64_t)(config.block_y *
1271 global_x2 = global_x1 + current_dx / OVERSAMPLE;
1272 global_y2 = global_y1 + current_dy / OVERSAMPLE;
1276 global_x1 = (int64_t)(config.block_x *
1279 (total_dx - current_dx) /
1281 global_y1 = (int64_t)(config.block_y *
1284 (total_dy - current_dy) /
1286 global_x2 = (int64_t)(config.block_x *
1291 global_y2 = (int64_t)(config.block_y *
1298 block_x = global_x1;
1299 block_y = global_y1;
1300 block_w = config.global_block_w * w / 100;
1301 block_h = config.global_block_h * h / 100;
1302 block_x1 = block_x - block_w / 2;
1303 block_y1 = block_y - block_h / 2;
1304 block_x2 = block_x + block_w / 2;
1305 block_y2 = block_y + block_h / 2;
1306 search_w = config.global_range_w * w / 100;
1307 search_h = config.global_range_h * h / 100;
1308 search_x1 = block_x1 - search_w / 2;
1309 search_y1 = block_y1 - search_h / 2;
1310 search_x2 = block_x2 + search_w / 2;
1311 search_y2 = block_y2 + search_h / 2;
1313 // printf("MotionMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1340 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1343 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1344 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1345 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1346 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1350 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1351 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1352 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1353 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1355 // Block should be endpoint of motion
1358 block_x = global_x2;
1359 block_y = global_y2;
1364 block_x = (int64_t)(config.block_x * w / 100);
1365 block_y = (int64_t)(config.block_y * h / 100);
1368 block_w = config.rotation_block_w * w / 100;
1369 block_h = config.rotation_block_h * h / 100;
1372 float angle = total_angle * 2 * M_PI / 360;
1373 double base_angle1 = atan((float)block_h / block_w);
1374 double base_angle2 = atan((float)block_w / block_h);
1375 double target_angle1 = base_angle1 + angle;
1376 double target_angle2 = base_angle2 + angle;
1377 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1378 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1379 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1380 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1381 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1382 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1383 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1384 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1385 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1387 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1388 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1389 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1390 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1396 draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1397 draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1404 void MotionMain::draw_pixel(VFrame *frame, int x, int y)
1406 if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1408 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1410 type **rows = (type**)frame->get_rows(); \
1411 rows[y][x * components] = max - rows[y][x * components]; \
1414 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1415 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1419 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1420 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1422 if(components == 4) \
1423 rows[y][x * components + 3] = max; \
1427 switch(frame->get_color_model())
1430 DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1433 DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1436 DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1439 DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1442 DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1445 DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1448 DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1451 DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1453 case BC_RGBA16161616:
1454 DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1456 case BC_YUVA16161616:
1457 DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1463 void MotionMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1465 int w = labs(x2 - x1);
1466 int h = labs(y2 - y1);
1467 //printf("MotionMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1471 draw_pixel(frame, x1, y1);
1476 // Flip coordinates so x1 < x2
1486 int numerator = y2 - y1;
1487 int denominator = x2 - x1;
1488 for(int i = x1; i < x2; i++)
1490 int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1491 draw_pixel(frame, i, y);
1496 // Flip coordinates so y1 < y2
1506 int numerator = x2 - x1;
1507 int denominator = y2 - y1;
1508 for(int i = y1; i < y2; i++)
1510 int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1511 draw_pixel(frame, x, i);
1514 //printf("MotionMain::draw_line 2\n");
1517 #define ARROW_SIZE 10
1518 void MotionMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1520 double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1521 double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1522 double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1529 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1530 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1531 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1532 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1536 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1537 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1538 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1539 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1543 draw_line(frame, x1, y1, x2, y2);
1544 // draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1547 if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1548 // draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1550 if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1551 // draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1557 #define ABS_DIFF(type, temp_type, multiplier, components) \
1559 temp_type result_temp = 0; \
1560 for(int i = 0; i < h; i++) \
1562 type *prev_row = (type*)prev_ptr; \
1563 type *current_row = (type*)current_ptr; \
1564 for(int j = 0; j < w; j++) \
1566 for(int k = 0; k < 3; k++) \
1568 temp_type difference; \
1569 difference = *prev_row++ - *current_row++; \
1570 if(difference < 0) \
1571 result_temp -= difference; \
1573 result_temp += difference; \
1575 if(components == 4) \
1581 prev_ptr += row_bytes; \
1582 current_ptr += row_bytes; \
1584 result = (int64_t)(result_temp * multiplier); \
1587 int64_t MotionMain::abs_diff(unsigned char *prev_ptr,
1588 unsigned char *current_ptr,
1598 ABS_DIFF(unsigned char, int64_t, 1, 3)
1601 ABS_DIFF(unsigned char, int64_t, 1, 4)
1604 ABS_DIFF(float, double, 0x10000, 3)
1607 ABS_DIFF(float, double, 0x10000, 4)
1610 ABS_DIFF(unsigned char, int64_t, 1, 3)
1613 ABS_DIFF(unsigned char, int64_t, 1, 4)
1616 ABS_DIFF(uint16_t, int64_t, 1, 3)
1618 case BC_YUVA16161616:
1619 ABS_DIFF(uint16_t, int64_t, 1, 4)
1627 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1629 temp_type result_temp = 0; \
1630 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1631 temp_type y1_fraction = 0x100 - y2_fraction; \
1632 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1633 temp_type x1_fraction = 0x100 - x2_fraction; \
1634 for(int i = 0; i < h_sub; i++) \
1636 type *prev_row1 = (type*)prev_ptr; \
1637 type *prev_row2 = (type*)prev_ptr + components; \
1638 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1639 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1640 type *current_row = (type*)current_ptr; \
1641 for(int j = 0; j < w_sub; j++) \
1643 for(int k = 0; k < 3; k++) \
1645 temp_type difference; \
1646 temp_type prev_value = \
1647 (*prev_row1++ * x1_fraction * y1_fraction + \
1648 *prev_row2++ * x2_fraction * y1_fraction + \
1649 *prev_row3++ * x1_fraction * y2_fraction + \
1650 *prev_row4++ * x2_fraction * y2_fraction) / \
1652 temp_type current_value = *current_row++; \
1653 difference = prev_value - current_value; \
1654 if(difference < 0) \
1655 result_temp -= difference; \
1657 result_temp += difference; \
1660 if(components == 4) \
1669 prev_ptr += row_bytes; \
1670 current_ptr += row_bytes; \
1672 result = (int64_t)(result_temp * multiplier); \
1678 int64_t MotionMain::abs_diff_sub(unsigned char *prev_ptr,
1679 unsigned char *current_ptr,
1694 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1697 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1700 ABS_DIFF_SUB(float, double, 0x10000, 3)
1703 ABS_DIFF_SUB(float, double, 0x10000, 4)
1706 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1709 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1712 ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
1714 case BC_YUVA16161616:
1715 ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
1725 MotionScanPackage::MotionScanPackage()
1736 MotionScanUnit::MotionScanUnit(MotionScan *server,
1738 : LoadClient(server)
1740 this->plugin = plugin;
1741 this->server = server;
1742 cache_lock = new Mutex("MotionScanUnit::cache_lock");
1745 MotionScanUnit::~MotionScanUnit()
1752 void MotionScanUnit::process_package(LoadPackage *package)
1754 MotionScanPackage *pkg = (MotionScanPackage*)package;
1755 int w = server->current_frame->get_w();
1756 int h = server->current_frame->get_h();
1757 int color_model = server->current_frame->get_color_model();
1758 int pixel_size = cmodel_calculate_pixelsize(color_model);
1759 int row_bytes = server->current_frame->get_bytes_per_line();
1773 if(!server->subpixel)
1775 int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1));
1776 int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1));
1779 pkg->difference1 = server->get_cache(search_x, search_y);
1780 if(pkg->difference1 < 0)
1782 //printf("MotionScanUnit::process_package 1 %d %d\n",
1783 //search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1);
1784 // Pointers to first pixel in each block
1785 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1787 search_x * pixel_size;
1788 unsigned char *current_ptr = server->current_frame->get_rows()[
1790 pkg->block_x1 * pixel_size;
1792 pkg->difference1 = plugin->abs_diff(prev_ptr,
1795 pkg->block_x2 - pkg->block_x1,
1796 pkg->block_y2 - pkg->block_y1,
1798 //printf("MotionScanUnit::process_package 2\n");
1799 server->put_cache(search_x, search_y, pkg->difference1);
1820 int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1;
1821 int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1;
1823 if(plugin->config.horizontal_only)
1828 if(plugin->config.vertical_only)
1833 int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE;
1834 int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE;
1835 sub_x %= OVERSAMPLE;
1836 sub_y %= OVERSAMPLE;
1839 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1841 search_x * pixel_size;
1842 unsigned char *current_ptr = server->current_frame->get_rows()[
1844 pkg->block_x1 * pixel_size;
1846 // With subpixel, there are two ways to compare each position, one by shifting
1847 // the previous frame and two by shifting the current frame.
1848 pkg->difference1 = plugin->abs_diff_sub(prev_ptr,
1851 pkg->block_x2 - pkg->block_x1,
1852 pkg->block_y2 - pkg->block_y1,
1856 pkg->difference2 = plugin->abs_diff_sub(current_ptr,
1859 pkg->block_x2 - pkg->block_x1,
1860 pkg->block_y2 - pkg->block_y1,
1864 // printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
1869 // pkg->difference1,
1870 // pkg->difference2);
1887 int64_t MotionScanUnit::get_cache(int x, int y)
1889 int64_t result = -1;
1890 cache_lock->lock("MotionScanUnit::get_cache");
1891 for(int i = 0; i < cache.total; i++)
1893 MotionScanCache *ptr = cache.values[i];
1894 if(ptr->x == x && ptr->y == y)
1896 result = ptr->difference;
1900 cache_lock->unlock();
1904 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
1906 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
1907 cache_lock->lock("MotionScanUnit::put_cache");
1909 cache_lock->unlock();
1922 MotionScan::MotionScan(MotionMain *plugin,
1927 total_clients, total_packages
1930 this->plugin = plugin;
1931 cache_lock = new Mutex("MotionScan::cache_lock");
1934 MotionScan::~MotionScan()
1940 void MotionScan::init_packages()
1942 // Set package coords
1943 for(int i = 0; i < get_total_packages(); i++)
1945 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
1947 pkg->block_x1 = block_x1;
1948 pkg->block_x2 = block_x2;
1949 pkg->block_y1 = block_y1;
1950 pkg->block_y2 = block_y2;
1951 pkg->scan_x1 = scan_x1;
1952 pkg->scan_x2 = scan_x2;
1953 pkg->scan_y1 = scan_y1;
1954 pkg->scan_y2 = scan_y2;
1955 pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps;
1956 pkg->difference1 = 0;
1957 pkg->difference2 = 0;
1964 LoadClient* MotionScan::new_client()
1966 return new MotionScanUnit(this, plugin);
1969 LoadPackage* MotionScan::new_package()
1971 return new MotionScanPackage;
1975 void MotionScan::scan_frame(VFrame *previous_frame,
1976 VFrame *current_frame)
1978 this->previous_frame = previous_frame;
1979 this->current_frame = current_frame;
1982 cache.remove_all_objects();
1985 // Single macroblock
1986 int w = current_frame->get_w();
1987 int h = current_frame->get_h();
1989 // Initial search parameters
1990 int scan_w = w * plugin->config.global_range_w / 100;
1991 int scan_h = h * plugin->config.global_range_h / 100;
1992 int block_w = w * plugin->config.global_block_w / 100;
1993 int block_h = h * plugin->config.global_block_h / 100;
1995 // Location of block in previous frame
1996 block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2);
1997 block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2);
1998 block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2);
1999 block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2);
2001 // Offset to location of previous block. This offset needn't be very accurate
2002 // since it's the offset of the previous image and current image we want.
2003 if(plugin->config.mode3 == MotionConfig::TRACK_PREVIOUS)
2005 block_x1 += plugin->total_dx / OVERSAMPLE;
2006 block_y1 += plugin->total_dy / OVERSAMPLE;
2007 block_x2 += plugin->total_dx / OVERSAMPLE;
2008 block_y2 += plugin->total_dy / OVERSAMPLE;
2013 switch(plugin->config.mode2)
2016 case MotionConfig::NO_CALCULATE:
2022 case MotionConfig::LOAD:
2024 // Load result from disk
2025 char string[BCTEXTLEN];
2026 sprintf(string, "%s%06d", MOTION_FILE, plugin->get_source_position());
2027 FILE *input = fopen(string, "r");
2040 // Scan from scratch
2049 // Location of block in current frame
2050 int x_result = block_x1;
2051 int y_result = block_y1;
2053 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2054 // block_x1 + block_w / 2,
2055 // block_y1 + block_h / 2,
2065 scan_x1 = x_result - scan_w / 2;
2066 scan_y1 = y_result - scan_h / 2;
2067 scan_x2 = x_result + scan_w / 2;
2068 scan_y2 = y_result + scan_h / 2;
2072 // Zero out requested values
2073 if(plugin->config.horizontal_only)
2076 scan_y2 = block_y1 + 1;
2078 if(plugin->config.vertical_only)
2081 scan_x2 = block_x1 + 1;
2084 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2093 // Clamp the block coords before the scan so we get useful scan coords.
2094 MotionMain::clamp_scan(w,
2105 // printf("MotionScan::scan_frame 1\n block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n",
2118 // Give up if invalid coords.
2119 if(scan_y2 <= scan_y1 ||
2120 scan_x2 <= scan_x1 ||
2121 block_x2 <= block_x1 ||
2122 block_y2 <= block_y1)
2125 // For subpixel, the top row and left column are skipped
2128 if(plugin->config.horizontal_only ||
2129 plugin->config.vertical_only)
2131 total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE;
2135 total_pixels = 4 * OVERSAMPLE;
2138 total_steps = total_pixels;
2140 set_package_count(total_steps);
2143 // Get least difference
2144 int64_t min_difference = -1;
2145 for(int i = 0; i < get_total_packages(); i++)
2147 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2148 if(pkg->difference1 < min_difference || min_difference == -1)
2150 min_difference = pkg->difference1;
2152 if(plugin->config.vertical_only)
2153 x_result = scan_x1 * OVERSAMPLE;
2155 x_result = scan_x1 * OVERSAMPLE +
2156 (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1;
2158 if(plugin->config.horizontal_only)
2159 y_result = scan_y1 * OVERSAMPLE;
2161 y_result = scan_y1 * OVERSAMPLE +
2162 (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1;
2166 dx_result = block_x1 * OVERSAMPLE - x_result;
2167 dy_result = block_y1 * OVERSAMPLE - y_result;
2170 if(pkg->difference2 < min_difference)
2172 min_difference = pkg->difference2;
2174 if(plugin->config.vertical_only)
2175 x_result = scan_x1 * OVERSAMPLE;
2177 x_result = scan_x2 * OVERSAMPLE -
2178 ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1);
2180 if(plugin->config.horizontal_only)
2181 y_result = scan_y1 * OVERSAMPLE;
2183 y_result = scan_y2 * OVERSAMPLE -
2184 ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1);
2186 dx_result = block_x1 * OVERSAMPLE - x_result;
2187 dy_result = block_y1 * OVERSAMPLE - y_result;
2191 //printf("MotionScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result);
2196 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
2197 total_steps = MIN(plugin->config.global_positions, total_pixels);
2199 set_package_count(total_steps);
2202 // Get least difference
2203 int64_t min_difference = -1;
2204 for(int i = 0; i < get_total_packages(); i++)
2206 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
2207 if(pkg->difference1 < min_difference || min_difference == -1)
2209 min_difference = pkg->difference1;
2210 x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1));
2211 y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1));
2212 x_result *= OVERSAMPLE;
2213 y_result *= OVERSAMPLE;
2217 // printf("MotionScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n",
2222 // printf(" scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n",
2230 // printf("MotionScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n",
2235 // (float)x_result / 4,
2236 // (float)y_result / 4);
2239 // If a new search is required, rescale results back to pixels.
2240 if(total_steps >= total_pixels)
2242 // Single pixel accuracy reached. Now do exhaustive subpixel search.
2243 if(plugin->config.mode1 == MotionConfig::STABILIZE ||
2244 plugin->config.mode1 == MotionConfig::TRACK ||
2245 plugin->config.mode1 == MotionConfig::NOTHING)
2247 x_result /= OVERSAMPLE;
2248 y_result /= OVERSAMPLE;
2255 // Fill in results and quit
2256 dx_result = block_x1 * OVERSAMPLE - x_result;
2257 dy_result = block_y1 * OVERSAMPLE - y_result;
2262 // Reduce scan area and try again
2264 scan_w = (scan_x2 - scan_x1) / 2;
2265 scan_h = (scan_y2 - scan_y1) / 2;
2266 x_result /= OVERSAMPLE;
2267 y_result /= OVERSAMPLE;
2282 if(plugin->config.mode2 == MotionConfig::SAVE)
2284 char string[BCTEXTLEN];
2288 plugin->get_source_position());
2289 FILE *output = fopen(string, "w");
2300 perror("MotionScan::scan_frame SAVE 1");
2305 printf("MotionScan::scan_frame 10 dx=%.2f dy=%.2f\n",
2306 (float)this->dx_result / OVERSAMPLE,
2307 (float)this->dy_result / OVERSAMPLE);
2327 int64_t MotionScan::get_cache(int x, int y)
2329 int64_t result = -1;
2330 cache_lock->lock("MotionScan::get_cache");
2331 for(int i = 0; i < cache.total; i++)
2333 MotionScanCache *ptr = cache.values[i];
2334 if(ptr->x == x && ptr->y == y)
2336 result = ptr->difference;
2340 cache_lock->unlock();
2344 void MotionScan::put_cache(int x, int y, int64_t difference)
2346 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
2347 cache_lock->lock("MotionScan::put_cache");
2349 cache_lock->unlock();
2356 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
2360 this->difference = difference;
2376 RotateScanPackage::RotateScanPackage()
2381 RotateScanUnit::RotateScanUnit(RotateScan *server, MotionMain *plugin)
2382 : LoadClient(server)
2384 this->server = server;
2385 this->plugin = plugin;
2390 RotateScanUnit::~RotateScanUnit()
2396 void RotateScanUnit::process_package(LoadPackage *package)
2398 if(server->skip) return;
2399 RotateScanPackage *pkg = (RotateScanPackage*)package;
2401 if((pkg->difference = server->get_cache(pkg->angle)) < 0)
2403 //printf("RotateScanUnit::process_package 1\n");
2404 int color_model = server->previous_frame->get_color_model();
2405 int pixel_size = cmodel_calculate_pixelsize(color_model);
2406 int row_bytes = server->previous_frame->get_bytes_per_line();
2409 rotater = new AffineEngine(1, 1);
2410 if(!temp) temp = new VFrame(0,
2411 server->previous_frame->get_w(),
2412 server->previous_frame->get_h(),
2416 // Rotate original block size
2417 rotater->set_viewport(server->block_x1,
2419 server->block_x2 - server->block_x1,
2420 server->block_y2 - server->block_y1);
2421 rotater->set_pivot(server->block_x, server->block_y);
2423 rotater->rotate(temp,
2424 server->previous_frame,
2427 // Scan reduced block size
2428 //plugin->output_frame->copy_from(server->current_frame);
2429 //plugin->output_frame->copy_from(temp);
2430 pkg->difference = plugin->abs_diff(
2431 temp->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2432 server->current_frame->get_rows()[server->scan_y] + server->scan_x * pixel_size,
2437 server->put_cache(pkg->angle, pkg->difference);
2439 // 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=%lld\n",
2440 // server->block_x1,
2441 // server->block_y1,
2442 // server->block_x2 - server->block_x1,
2443 // server->block_y2 - server->block_y1,
2449 // pkg->difference);
2474 RotateScan::RotateScan(MotionMain *plugin,
2479 total_clients, total_packages
2482 this->plugin = plugin;
2483 cache_lock = new Mutex("RotateScan::cache_lock");
2487 RotateScan::~RotateScan()
2492 void RotateScan::init_packages()
2494 for(int i = 0; i < get_total_packages(); i++)
2496 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2498 (scan_angle2 - scan_angle1) /
2504 LoadClient* RotateScan::new_client()
2506 return new RotateScanUnit(this, plugin);
2509 LoadPackage* RotateScan::new_package()
2511 return new RotateScanPackage;
2515 float RotateScan::scan_frame(VFrame *previous_frame,
2516 VFrame *current_frame,
2521 this->block_x = block_x;
2522 this->block_y = block_y;
2524 switch(plugin->config.mode2)
2526 case MotionConfig::NO_CALCULATE:
2531 case MotionConfig::LOAD:
2533 char string[BCTEXTLEN];
2534 sprintf(string, "%s%06d", ROTATION_FILE, plugin->get_source_position());
2535 FILE *input = fopen(string, "r");
2538 fscanf(input, "%f", &result);
2544 perror("RotateScan::scan_frame LOAD");
2557 this->previous_frame = previous_frame;
2558 this->current_frame = current_frame;
2559 int w = current_frame->get_w();
2560 int h = current_frame->get_h();
2561 int block_w = w * plugin->config.rotation_block_w / 100;
2562 int block_h = h * plugin->config.rotation_block_h / 100;
2564 if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
2565 if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
2566 if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
2567 if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
2569 block_x1 = this->block_x - block_w / 2;
2570 block_x2 = this->block_x + block_w / 2;
2571 block_y1 = this->block_y - block_h / 2;
2572 block_y2 = this->block_y + block_h / 2;
2575 // Calculate the maximum area available to scan after rotation.
2576 // Must be calculated from the starting range because of cache.
2577 // Get coords of rectangle after rotation.
2578 double center_x = this->block_x;
2579 double center_y = this->block_y;
2580 double max_angle = plugin->config.rotation_range;
2581 double base_angle1 = atan((float)block_h / block_w);
2582 double base_angle2 = atan((float)block_w / block_h);
2583 double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
2584 double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
2585 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
2586 double x1 = center_x - cos(target_angle1) * radius;
2587 double y1 = center_y - sin(target_angle1) * radius;
2588 double x2 = center_x + sin(target_angle2) * radius;
2589 double y2 = center_y - cos(target_angle2) * radius;
2590 double x3 = center_x - sin(target_angle2) * radius;
2591 double y3 = center_y + cos(target_angle2) * radius;
2593 // Track top edge to find greatest area.
2594 double max_area1 = 0;
2597 for(double x = x1; x < x2; x++)
2599 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
2600 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
2602 double area = fabs(x - center_x) * fabs(y - center_y);
2603 if(area > max_area1)
2612 // Track left edge to find greatest area.
2613 double max_area2 = 0;
2616 for(double y = y1; y < y3; y++)
2618 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
2619 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
2621 double area = fabs(x - center_x) * fabs(y - center_y);
2622 if(area > max_area2)
2631 double max_x, max_y;
2635 // Get reduced scan coords
2636 scan_w = (int)(fabs(max_x - center_x) * 2);
2637 scan_h = (int)(fabs(max_y - center_y) * 2);
2638 scan_x = (int)(center_x - scan_w / 2);
2639 scan_y = (int)(center_y - scan_h / 2);
2640 // printf("RotateScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n",
2641 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
2642 // printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
2644 // Determine min angle from size of block
2645 double angle1 = atan((double)block_h / block_w);
2646 double angle2 = atan((double)(block_h - 1) / (block_w + 1));
2647 double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
2648 min_angle = MAX(min_angle, MIN_ANGLE);
2651 printf("RotateScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI);
2654 cache.remove_all_objects();
2657 // Initial search range
2658 float angle_range = (float)plugin->config.rotation_range;
2660 total_steps = plugin->config.rotate_positions;
2663 while(angle_range >= min_angle * total_steps)
2665 scan_angle1 = result - angle_range;
2666 scan_angle2 = result + angle_range;
2669 set_package_count(total_steps);
2670 //set_package_count(1);
2673 int64_t min_difference = -1;
2674 for(int i = 0; i < get_total_packages(); i++)
2676 RotateScanPackage *pkg = (RotateScanPackage*)get_package(i);
2677 if(pkg->difference < min_difference || min_difference == -1)
2679 min_difference = pkg->difference;
2680 result = pkg->angle;
2692 if(!skip && plugin->config.mode2 == MotionConfig::SAVE)
2694 char string[BCTEXTLEN];
2698 plugin->get_source_position());
2699 FILE *output = fopen(string, "w");
2702 fprintf(output, "%f\n", result);
2707 perror("RotateScan::scan_frame SAVE");
2712 printf("RotateScan::scan_frame 10 angle=%f\n", result);
2720 int64_t RotateScan::get_cache(float angle)
2722 int64_t result = -1;
2723 cache_lock->lock("RotateScan::get_cache");
2724 for(int i = 0; i < cache.total; i++)
2726 RotateScanCache *ptr = cache.values[i];
2727 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
2729 result = ptr->difference;
2733 cache_lock->unlock();
2737 void RotateScan::put_cache(float angle, int64_t difference)
2739 RotateScanCache *ptr = new RotateScanCache(angle, difference);
2740 cache_lock->lock("RotateScan::put_cache");
2742 cache_lock->unlock();
2753 RotateScanCache::RotateScanCache(float angle, int64_t difference)
2755 this->angle = angle;
2756 this->difference = difference;