4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #include "bcdisplayinfo.h"
26 #include "bcsignals.h"
30 #include "motion-cv.h"
31 #include "motionwindow-cv.h"
33 #include "overlayframe.h"
34 #include "rotateframe.h"
35 #include "transportque.h"
41 REGISTER_PLUGIN(MotionCVMain)
50 MotionCVConfig::MotionCVConfig()
56 global_block_w = MIN_BLOCK;
57 global_block_h = MIN_BLOCK;
58 rotation_block_w = MIN_BLOCK;
59 rotation_block_h = MIN_BLOCK;
62 global_positions = 256;
69 addtrackedframeoffset = 0;
72 mode3 = MotionCVConfig::TRACK_SINGLE;
79 void MotionCVConfig::boundaries()
81 CLAMP(global_range_w, MIN_RADIUS, MAX_RADIUS);
82 CLAMP(global_range_h, MIN_RADIUS, MAX_RADIUS);
83 CLAMP(rotation_range, MIN_ROTATION, MAX_ROTATION);
84 CLAMP(block_count, MIN_BLOCKS, MAX_BLOCKS);
85 CLAMP(global_block_w, MIN_BLOCK, MAX_BLOCK);
86 CLAMP(global_block_h, MIN_BLOCK, MAX_BLOCK);
87 CLAMP(rotation_block_w, MIN_BLOCK, MAX_BLOCK);
88 CLAMP(rotation_block_h, MIN_BLOCK, MAX_BLOCK);
91 int MotionCVConfig::equivalent(MotionCVConfig &that)
93 return global_range_w == that.global_range_w &&
94 global_range_h == that.global_range_h &&
95 rotation_range == that.rotation_range &&
96 mode1 == that.mode1 &&
97 global == that.global &&
98 rotate == that.rotate &&
99 addtrackedframeoffset == that.addtrackedframeoffset &&
100 draw_vectors == that.draw_vectors &&
101 block_count == that.block_count &&
102 global_block_w == that.global_block_w &&
103 global_block_h == that.global_block_h &&
104 rotation_block_w == that.rotation_block_w &&
105 rotation_block_h == that.rotation_block_h &&
106 EQUIV(block_x, that.block_x) &&
107 EQUIV(block_y, that.block_y) &&
108 global_positions == that.global_positions &&
109 rotate_positions == that.rotate_positions &&
110 magnitude == that.magnitude &&
111 return_speed == that.return_speed &&
112 mode3 == that.mode3 &&
113 track_frame == that.track_frame &&
114 bottom_is_master == that.bottom_is_master &&
115 horizontal_only == that.horizontal_only &&
116 vertical_only == that.vertical_only;
119 void MotionCVConfig::copy_from(MotionCVConfig &that)
121 global_range_w = that.global_range_w;
122 global_range_h = that.global_range_h;
123 rotation_range = that.rotation_range;
125 global = that.global;
126 rotate = that.rotate;
127 addtrackedframeoffset = that.addtrackedframeoffset;
129 draw_vectors = that.draw_vectors;
130 block_count = that.block_count;
131 block_x = that.block_x;
132 block_y = that.block_y;
133 global_positions = that.global_positions;
134 rotate_positions = that.rotate_positions;
135 global_block_w = that.global_block_w;
136 global_block_h = that.global_block_h;
137 rotation_block_w = that.rotation_block_w;
138 rotation_block_h = that.rotation_block_h;
139 magnitude = that.magnitude;
140 return_speed = that.return_speed;
142 track_frame = that.track_frame;
143 bottom_is_master = that.bottom_is_master;
144 horizontal_only = that.horizontal_only;
145 vertical_only = that.vertical_only;
148 void MotionCVConfig::interpolate(MotionCVConfig &prev,
149 MotionCVConfig &next,
152 int64_t current_frame)
154 //double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
155 //double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
156 this->block_x = prev.block_x;
157 this->block_y = prev.block_y;
158 global_range_w = prev.global_range_w;
159 global_range_h = prev.global_range_h;
160 rotation_range = prev.rotation_range;
162 global = prev.global;
163 rotate = prev.rotate;
164 addtrackedframeoffset = prev.addtrackedframeoffset;
166 draw_vectors = prev.draw_vectors;
167 block_count = prev.block_count;
168 global_positions = prev.global_positions;
169 rotate_positions = prev.rotate_positions;
170 global_block_w = prev.global_block_w;
171 global_block_h = prev.global_block_h;
172 rotation_block_w = prev.rotation_block_w;
173 rotation_block_h = prev.rotation_block_h;
174 magnitude = prev.magnitude;
175 return_speed = prev.return_speed;
177 track_frame = prev.track_frame;
178 bottom_is_master = prev.bottom_is_master;
179 horizontal_only = prev.horizontal_only;
180 vertical_only = prev.vertical_only;
201 MotionCVMain::MotionCVMain(PluginServer *server)
202 : PluginVClient(server)
214 previous_frame_number = -1;
217 current_global_ref = 0;
218 global_target_src = 0;
219 global_target_dst = 0;
222 current_rotate_ref = 0;
223 rotate_target_src = 0;
224 rotate_target_dst = 0;
227 MotionCVMain::~MotionCVMain()
232 delete [] search_area;
234 delete rotate_engine;
235 delete motion_rotate;
238 delete prev_global_ref;
239 delete current_global_ref;
240 delete global_target_src;
241 delete global_target_dst;
243 delete prev_rotate_ref;
244 delete current_rotate_ref;
245 delete rotate_target_src;
246 delete rotate_target_dst;
249 const char* MotionCVMain::plugin_title() { return _("MotionCV"); }
250 int MotionCVMain::is_realtime() { return 1; }
251 int MotionCVMain::is_multichannel() { return 1; }
254 NEW_WINDOW_MACRO(MotionCVMain, MotionCVWindow)
256 LOAD_CONFIGURATION_MACRO(MotionCVMain, MotionCVConfig)
260 void MotionCVMain::update_gui()
264 if(load_configuration())
266 thread->window->lock_window("MotionCVMain::update_gui");
267 MotionCVWindow *window = (MotionCVWindow *)thread->window;
269 char string[BCTEXTLEN];
270 sprintf(string, "%d", config.global_positions);
271 window->global_search_positions->set_text(string);
272 sprintf(string, "%d", config.rotate_positions);
273 window->rotation_search_positions->set_text(string);
275 window->global_block_w->update(config.global_block_w);
276 window->global_block_h->update(config.global_block_h);
277 window->rotation_block_w->update(config.rotation_block_w);
278 window->rotation_block_h->update(config.rotation_block_h);
279 window->block_x->update(config.block_x);
280 window->block_y->update(config.block_y);
281 window->block_x_text->update((float)config.block_x);
282 window->block_y_text->update((float)config.block_y);
283 window->magnitude->update(config.magnitude);
284 window->return_speed->update(config.return_speed);
287 window->track_single->update(config.mode3 == MotionCVConfig::TRACK_SINGLE);
288 window->track_frame_number->update(config.track_frame);
289 window->track_previous->update(config.mode3 == MotionCVConfig::TRACK_PREVIOUS);
290 window->previous_same->update(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK);
291 if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
292 window->track_frame_number->disable();
294 window->track_frame_number->enable();
296 window->mode1->set_text(
297 Mode1::to_text(config.mode1));
298 window->mode2->set_text(
299 Mode2::to_text(config.mode2));
300 window->mode3->set_text(
301 Mode3::to_text(config.horizontal_only, config.vertical_only));
302 window->master_layer->set_text(
303 MasterLayer::to_text(config.bottom_is_master));
306 window->update_mode();
307 window->unlock_window();
315 void MotionCVMain::save_data(KeyFrame *keyframe)
319 // cause data to be stored directly in text
320 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
321 output.tag.set_title("MOTION");
323 output.tag.set_property("BLOCK_COUNT", config.block_count);
324 output.tag.set_property("GLOBAL_POSITIONS", config.global_positions);
325 output.tag.set_property("ROTATE_POSITIONS", config.rotate_positions);
326 output.tag.set_property("GLOBAL_BLOCK_W", config.global_block_w);
327 output.tag.set_property("GLOBAL_BLOCK_H", config.global_block_h);
328 output.tag.set_property("ROTATION_BLOCK_W", config.rotation_block_w);
329 output.tag.set_property("ROTATION_BLOCK_H", config.rotation_block_h);
330 output.tag.set_property("BLOCK_X", config.block_x);
331 output.tag.set_property("BLOCK_Y", config.block_y);
332 output.tag.set_property("GLOBAL_RANGE_W", config.global_range_w);
333 output.tag.set_property("GLOBAL_RANGE_H", config.global_range_h);
334 output.tag.set_property("ROTATION_RANGE", config.rotation_range);
335 output.tag.set_property("MAGNITUDE", config.magnitude);
336 output.tag.set_property("RETURN_SPEED", config.return_speed);
337 output.tag.set_property("MODE1", config.mode1);
338 output.tag.set_property("GLOBAL", config.global);
339 output.tag.set_property("ROTATE", config.rotate);
340 output.tag.set_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
341 output.tag.set_property("MODE2", config.mode2);
342 output.tag.set_property("DRAW_VECTORS", config.draw_vectors);
343 output.tag.set_property("MODE3", config.mode3);
344 output.tag.set_property("TRACK_FRAME", config.track_frame);
345 output.tag.set_property("BOTTOM_IS_MASTER", config.bottom_is_master);
346 output.tag.set_property("HORIZONTAL_ONLY", config.horizontal_only);
347 output.tag.set_property("VERTICAL_ONLY", config.vertical_only);
349 output.tag.set_title("/MOTION");
351 output.terminate_string();
354 void MotionCVMain::read_data(KeyFrame *keyframe)
358 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
364 result = input.read_tag();
368 if(input.tag.title_is("MOTION"))
370 config.block_count = input.tag.get_property("BLOCK_COUNT", config.block_count);
371 config.global_positions = input.tag.get_property("GLOBAL_POSITIONS", config.global_positions);
372 config.rotate_positions = input.tag.get_property("ROTATE_POSITIONS", config.rotate_positions);
373 config.global_block_w = input.tag.get_property("GLOBAL_BLOCK_W", config.global_block_w);
374 config.global_block_h = input.tag.get_property("GLOBAL_BLOCK_H", config.global_block_h);
375 config.rotation_block_w = input.tag.get_property("ROTATION_BLOCK_W", config.rotation_block_w);
376 config.rotation_block_h = input.tag.get_property("ROTATION_BLOCK_H", config.rotation_block_h);
377 config.block_x = input.tag.get_property("BLOCK_X", config.block_x);
378 config.block_y = input.tag.get_property("BLOCK_Y", config.block_y);
379 config.global_range_w = input.tag.get_property("GLOBAL_RANGE_W", config.global_range_w);
380 config.global_range_h = input.tag.get_property("GLOBAL_RANGE_H", config.global_range_h);
381 config.rotation_range = input.tag.get_property("ROTATION_RANGE", config.rotation_range);
382 config.magnitude = input.tag.get_property("MAGNITUDE", config.magnitude);
383 config.return_speed = input.tag.get_property("RETURN_SPEED", config.return_speed);
384 config.mode1 = input.tag.get_property("MODE1", config.mode1);
385 config.global = input.tag.get_property("GLOBAL", config.global);
386 config.rotate = input.tag.get_property("ROTATE", config.rotate);
387 config.addtrackedframeoffset = input.tag.get_property("ADDTRACKEDFRAMEOFFSET", config.addtrackedframeoffset);
388 config.mode2 = input.tag.get_property("MODE2", config.mode2);
389 config.draw_vectors = input.tag.get_property("DRAW_VECTORS", config.draw_vectors);
390 config.mode3 = input.tag.get_property("MODE3", config.mode3);
391 config.track_frame = input.tag.get_property("TRACK_FRAME", config.track_frame);
392 config.bottom_is_master = input.tag.get_property("BOTTOM_IS_MASTER", config.bottom_is_master);
393 config.horizontal_only = input.tag.get_property("HORIZONTAL_ONLY", config.horizontal_only);
394 config.vertical_only = input.tag.get_property("VERTICAL_ONLY", config.vertical_only);
409 void MotionCVMain::allocate_temp(int w, int h, int color_model)
412 (temp_frame->get_w() != w ||
413 temp_frame->get_h() != h))
419 temp_frame = new VFrame(w, h, color_model);
424 void MotionCVMain::process_global()
426 if(!engine) engine = new MotionCVScan(this,
427 PluginClient::get_project_smp() + 1,
428 PluginClient::get_project_smp() + 1);
430 // Get the current motion vector between the previous and current frame
431 engine->scan_frame(current_global_ref, prev_global_ref);
432 current_dx = engine->dx_result;
433 current_dy = engine->dy_result;
435 // Add current motion vector to accumulation vector.
436 if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
439 total_dx = (int64_t)total_dx * (100 - config.return_speed) / 100;
440 total_dy = (int64_t)total_dy * (100 - config.return_speed) / 100;
441 total_dx += engine->dx_result;
442 total_dy += engine->dy_result;
445 // Make accumulation vector current
447 total_dx = engine->dx_result;
448 total_dy = engine->dy_result;
451 // Clamp accumulation vector
452 if(config.magnitude < 100)
454 //int block_w = (int64_t)config.global_block_w *
455 // current_global_ref->get_w() / 100;
456 //int block_h = (int64_t)config.global_block_h *
457 // current_global_ref->get_h() / 100;
458 int block_x_orig = (int64_t)(config.block_x *
459 current_global_ref->get_w() /
461 int block_y_orig = (int64_t)(config.block_y *
462 current_global_ref->get_h() /
465 int max_block_x = (int64_t)(current_global_ref->get_w() - block_x_orig) *
469 int max_block_y = (int64_t)(current_global_ref->get_h() - block_y_orig) *
473 int min_block_x = (int64_t)-block_x_orig *
477 int min_block_y = (int64_t)-block_y_orig *
482 CLAMP(total_dx, min_block_x, max_block_x);
483 CLAMP(total_dy, min_block_y, max_block_y);
487 printf("MotionCVMain::process_global 2 total_dx=%.02f total_dy=%.02f\n",
488 (float)total_dx / OVERSAMPLE,
489 (float)total_dy / OVERSAMPLE);
492 if(config.mode3 != MotionCVConfig::TRACK_SINGLE && !config.rotate)
494 // Transfer current reference frame to previous reference frame and update
495 // counter. Must wait for rotate to compare.
496 prev_global_ref->copy_from(current_global_ref);
497 previous_frame_number = get_source_position();
500 // Decide what to do with target based on requested operation
501 int interpolation = NEAREST_NEIGHBOR;
506 case MotionCVConfig::NOTHING:
507 global_target_dst->copy_from(global_target_src);
509 case MotionCVConfig::TRACK_PIXEL:
510 interpolation = NEAREST_NEIGHBOR;
511 dx = (int)(total_dx / OVERSAMPLE);
512 dy = (int)(total_dy / OVERSAMPLE);
514 case MotionCVConfig::STABILIZE_PIXEL:
515 interpolation = NEAREST_NEIGHBOR;
516 dx = -(int)(total_dx / OVERSAMPLE);
517 dy = -(int)(total_dy / OVERSAMPLE);
520 case MotionCVConfig::TRACK:
521 interpolation = CUBIC_LINEAR;
522 dx = (float)total_dx / OVERSAMPLE;
523 dy = (float)total_dy / OVERSAMPLE;
525 case MotionCVConfig::STABILIZE:
526 interpolation = CUBIC_LINEAR;
527 dx = -(float)total_dx / OVERSAMPLE;
528 dy = -(float)total_dy / OVERSAMPLE;
533 if(config.mode1 != MotionCVConfig::NOTHING)
536 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
537 global_target_dst->clear_frame();
538 overlayer->overlay(global_target_dst,
542 global_target_src->get_w(),
543 global_target_src->get_h(),
546 (float)global_target_src->get_w() + dx,
547 (float)global_target_src->get_h() + dy,
556 void MotionCVMain::process_rotation()
561 // Convert the previous global reference into the previous rotation reference.
562 // Convert global target destination into rotation target source.
566 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
569 if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
571 dx = (float)total_dx / OVERSAMPLE;
572 dy = (float)total_dy / OVERSAMPLE;
576 dx = (float)current_dx / OVERSAMPLE;
577 dy = (float)current_dy / OVERSAMPLE;
580 prev_rotate_ref->clear_frame();
581 overlayer->overlay(prev_rotate_ref,
585 prev_global_ref->get_w(),
586 prev_global_ref->get_h(),
589 (float)prev_global_ref->get_w() + dx,
590 (float)prev_global_ref->get_h() + dy,
594 // Pivot is destination global position
595 block_x = (int)(prev_rotate_ref->get_w() *
600 block_y = (int)(prev_rotate_ref->get_h() *
605 // Use the global target output as the rotation target input
606 rotate_target_src->copy_from(global_target_dst);
607 // Transfer current reference frame to previous reference frame for global.
608 if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
610 prev_global_ref->copy_from(current_global_ref);
611 previous_frame_number = get_source_position();
617 block_x = (int)(prev_rotate_ref->get_w() *
620 block_y = (int)(prev_rotate_ref->get_h() *
629 motion_rotate = new RotateCVScan(this,
630 get_project_smp() + 1,
631 get_project_smp() + 1);
633 current_angle = motion_rotate->scan_frame(prev_rotate_ref,
640 // Add current rotation to accumulation
641 if(config.mode3 != MotionCVConfig::TRACK_SINGLE)
644 total_angle = total_angle * (100 - config.return_speed) / 100;
645 total_angle += current_angle;
649 // Transfer current reference frame to previous reference frame and update
651 prev_rotate_ref->copy_from(current_rotate_ref);
652 previous_frame_number = get_source_position();
657 total_angle = current_angle;
661 printf("MotionCVMain::process_rotation total_angle=%f\n", total_angle);
665 // Calculate rotation parameters based on requested operation
669 case MotionCVConfig::NOTHING:
670 rotate_target_dst->copy_from(rotate_target_src);
672 case MotionCVConfig::TRACK:
673 case MotionCVConfig::TRACK_PIXEL:
676 case MotionCVConfig::STABILIZE:
677 case MotionCVConfig::STABILIZE_PIXEL:
678 angle = -total_angle;
684 if(config.mode1 != MotionCVConfig::NOTHING)
687 rotate_engine = new AffineEngine(PluginClient::get_project_smp() + 1,
688 PluginClient::get_project_smp() + 1);
690 rotate_target_dst->clear_frame();
692 // Determine pivot based on a number of factors.
695 case MotionCVConfig::TRACK:
696 case MotionCVConfig::TRACK_PIXEL:
697 // Use destination of global tracking.
698 rotate_engine->set_pivot(block_x, block_y);
701 case MotionCVConfig::STABILIZE:
702 case MotionCVConfig::STABILIZE_PIXEL:
705 // Use origin of global stabilize operation
706 rotate_engine->set_pivot((int)(rotate_target_dst->get_w() *
709 (int)(rotate_target_dst->get_h() *
717 rotate_engine->set_pivot(block_x, block_y);
723 rotate_engine->rotate(rotate_target_dst, rotate_target_src, angle);
724 // overlayer->overlay(rotate_target_dst,
728 // prev_rotate_ref->get_w(),
729 // prev_rotate_ref->get_h(),
732 // prev_rotate_ref->get_w(),
733 // prev_rotate_ref->get_h(),
737 // overlayer->overlay(rotate_target_dst,
738 // current_rotate_ref,
741 // prev_rotate_ref->get_w(),
742 // prev_rotate_ref->get_h(),
745 // prev_rotate_ref->get_w(),
746 // prev_rotate_ref->get_h(),
765 int MotionCVMain::process_buffer(VFrame **frame,
766 int64_t start_position,
769 int need_reconfigure = load_configuration();
770 int color_model = frame[0]->get_color_model();
771 w = frame[0]->get_w();
772 h = frame[0]->get_h();
776 printf("MotionCVMain::process_buffer 1 start_position=%jd\n", start_position);
780 // Calculate the source and destination pointers for each of the operations.
781 // Get the layer to track motion in.
782 reference_layer = config.bottom_is_master ?
783 PluginClient::total_in_buffers - 1 :
785 // Get the layer to apply motion in.
786 target_layer = config.bottom_is_master ?
788 PluginClient::total_in_buffers - 1;
791 output_frame = frame[target_layer];
794 // Get the position of previous reference frame.
795 int64_t actual_previous_number;
796 // Skip if match frame not available
797 int skip_current = 0;
800 if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
802 actual_previous_number = config.track_frame;
803 if(get_direction() == PLAY_REVERSE)
804 actual_previous_number++;
805 if(actual_previous_number == start_position)
810 actual_previous_number = start_position;
811 if(get_direction() == PLAY_FORWARD)
813 actual_previous_number--;
814 if(actual_previous_number < get_source_start())
818 KeyFrame *keyframe = get_prev_keyframe(start_position, 1);
819 if(keyframe->position > 0 &&
820 actual_previous_number < keyframe->position)
826 actual_previous_number++;
827 if(actual_previous_number >= get_source_start() + get_total_len())
831 KeyFrame *keyframe = get_next_keyframe(start_position, 1);
832 if(keyframe->position > 0 &&
833 actual_previous_number >= keyframe->position)
838 // Only count motion since last keyframe
844 if(!config.global && !config.rotate) skip_current = 1;
849 // printf("process_realtime %d %lld %lld\n",
851 // previous_frame_number,
852 // actual_previous_number);
853 // Load match frame and reset vectors
854 int need_reload = !skip_current &&
855 (previous_frame_number != actual_previous_number ||
862 previous_frame_number = actual_previous_number;
879 // Get the global pointers. Here we walk through the sequence of events.
882 // Assume global only. Global reads previous frame and compares
883 // with current frame to get the current translation.
884 // The center of the search area is fixed in compensate mode or
885 // the user value + the accumulation vector in track mode.
887 prev_global_ref = new VFrame(w, h, color_model);
888 if(!current_global_ref)
889 current_global_ref = new VFrame(w, h, color_model);
891 // Global loads the current target frame into the src and
892 // writes it to the dst frame with desired translation.
893 if(!global_target_src)
894 global_target_src = new VFrame(w, h, color_model);
895 if(!global_target_dst)
896 global_target_dst = new VFrame(w, h, color_model);
899 // Load the global frames
902 read_frame(prev_global_ref,
904 previous_frame_number,
909 read_frame(current_global_ref,
914 read_frame(global_target_src,
922 // Global followed by rotate
925 // Must translate the previous global reference by the current global
926 // accumulation vector to match the current global reference.
927 // The center of the search area is always the user value + the accumulation
930 prev_rotate_ref = new VFrame(w, h, color_model);
931 // The current global reference is the current rotation reference.
932 if(!current_rotate_ref)
933 current_rotate_ref = new VFrame(w, h, color_model);
934 current_rotate_ref->copy_from(current_global_ref);
936 // The global target destination is copied to the rotation target source
937 // then written to the rotation output with rotation.
938 // The pivot for the rotation is the center of the search area
939 // if we're tracking.
940 // The pivot is fixed to the user position if we're compensating.
941 if(!rotate_target_src)
942 rotate_target_src = new VFrame(w, h, color_model);
943 if(!rotate_target_dst)
944 rotate_target_dst = new VFrame(w,h , color_model);
951 // Rotation reads the previous reference frame and compares it with current
954 prev_rotate_ref = new VFrame(w, h, color_model);
955 if(!current_rotate_ref)
956 current_rotate_ref = new VFrame(w, h, color_model);
958 // Rotation loads target frame to temporary, rotates it, and writes it to the
959 // target frame. The pivot is always fixed.
960 if(!rotate_target_src)
961 rotate_target_src = new VFrame(w, h, color_model);
962 if(!rotate_target_dst)
963 rotate_target_dst = new VFrame(w,h , color_model);
966 // Load the rotate frames
969 read_frame(prev_rotate_ref,
971 previous_frame_number,
975 read_frame(current_rotate_ref,
980 read_frame(rotate_target_src,
998 // Get position change from previous frame to current frame
999 if(config.global) process_global();
1000 // Get rotation change from previous frame to current frame
1001 if(config.rotate) process_rotation();
1002 //frame[target_layer]->copy_from(prev_rotate_ref);
1003 //frame[target_layer]->copy_from(current_rotate_ref);
1011 // Transfer the relevant target frame to the output
1016 frame[target_layer]->copy_from(rotate_target_dst);
1020 frame[target_layer]->copy_from(global_target_dst);
1024 // Read the target destination directly
1026 read_frame(frame[target_layer],
1033 if(config.draw_vectors)
1035 draw_vectors(frame[target_layer]);
1039 printf("MotionCVMain::process_buffer 100\n");
1045 void MotionCVMain::clamp_scan(int w,
1057 // printf("MotionCVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1072 // scan is always out of range before block.
1075 int difference = -*scan_x1;
1076 *block_x1 += difference;
1082 int difference = -*scan_y1;
1083 *block_y1 += difference;
1089 int difference = *scan_x2 - w;
1090 *block_x2 -= difference;
1091 *scan_x2 -= difference;
1096 int difference = *scan_y2 - h;
1097 *block_y2 -= difference;
1098 *scan_y2 -= difference;
1101 CLAMP(*scan_x1, 0, w);
1102 CLAMP(*scan_y1, 0, h);
1103 CLAMP(*scan_x2, 0, w);
1104 CLAMP(*scan_y2, 0, h);
1110 int difference = -*scan_x1;
1111 *block_x1 += difference;
1112 *scan_x2 += difference;
1118 int difference = -*scan_y1;
1119 *block_y1 += difference;
1120 *scan_y2 += difference;
1124 if(*scan_x2 - *block_x1 + *block_x2 > w)
1126 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1127 *block_x2 -= difference;
1130 if(*scan_y2 - *block_y1 + *block_y2 > h)
1132 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1133 *block_y2 -= difference;
1136 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1137 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1138 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1139 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1142 // Sanity checks which break the calculation but should never happen if the
1143 // center of the block is inside the frame.
1144 CLAMP(*block_x1, 0, w);
1145 CLAMP(*block_x2, 0, w);
1146 CLAMP(*block_y1, 0, h);
1147 CLAMP(*block_y2, 0, h);
1149 // printf("MotionCVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1165 void MotionCVMain::draw_vectors(VFrame *frame)
1167 int w = frame->get_w();
1168 int h = frame->get_h();
1169 int global_x1, global_y1;
1170 int global_x2, global_y2;
1171 int block_x, block_y;
1172 int block_w, block_h;
1173 int block_x1, block_y1;
1174 int block_x2, block_y2;
1175 int block_x3, block_y3;
1176 int block_x4, block_y4;
1177 int search_w, search_h;
1178 int search_x1, search_y1;
1179 int search_x2, search_y2;
1180 //int search_x3, search_y3;
1181 //int search_x4, search_y4;
1186 // Start of vector is center of previous block.
1187 // End of vector is total accumulation.
1188 if(config.mode3 == MotionCVConfig::TRACK_SINGLE)
1190 global_x1 = (int64_t)(config.block_x *
1193 global_y1 = (int64_t)(config.block_y *
1196 global_x2 = global_x1 + total_dx / OVERSAMPLE;
1197 global_y2 = global_y1 + total_dy / OVERSAMPLE;
1198 //printf("MotionCVMain::draw_vectors %d %d %d %d %d %d\n", total_dx, total_dy, global_x1, global_y1, global_x2, global_y2);
1201 // Start of vector is center of previous block.
1202 // End of vector is current change.
1203 if(config.mode3 == MotionCVConfig::PREVIOUS_SAME_BLOCK)
1205 global_x1 = (int64_t)(config.block_x *
1208 global_y1 = (int64_t)(config.block_y *
1211 global_x2 = global_x1 + current_dx / OVERSAMPLE;
1212 global_y2 = global_y1 + current_dy / OVERSAMPLE;
1216 global_x1 = (int64_t)(config.block_x *
1219 (total_dx - current_dx) /
1221 global_y1 = (int64_t)(config.block_y *
1224 (total_dy - current_dy) /
1226 global_x2 = (int64_t)(config.block_x *
1231 global_y2 = (int64_t)(config.block_y *
1238 block_x = global_x1;
1239 block_y = global_y1;
1240 block_w = config.global_block_w * w / 100;
1241 block_h = config.global_block_h * h / 100;
1242 block_x1 = block_x - block_w / 2;
1243 block_y1 = block_y - block_h / 2;
1244 block_x2 = block_x + block_w / 2;
1245 block_y2 = block_y + block_h / 2;
1246 search_w = config.global_range_w * w / 100;
1247 search_h = config.global_range_h * h / 100;
1248 search_x1 = block_x1 - search_w / 2;
1249 search_y1 = block_y1 - search_h / 2;
1250 search_x2 = block_x2 + search_w / 2;
1251 search_y2 = block_y2 + search_h / 2;
1253 // printf("MotionCVMain::draw_vectors %d %d %d %d %d %d %d %d %d %d %d %d\n",
1280 draw_arrow(frame, global_x1, global_y1, global_x2, global_y2);
1283 draw_line(frame, block_x1, block_y1, block_x2, block_y1);
1284 draw_line(frame, block_x2, block_y1, block_x2, block_y2);
1285 draw_line(frame, block_x2, block_y2, block_x1, block_y2);
1286 draw_line(frame, block_x1, block_y2, block_x1, block_y1);
1290 draw_line(frame, search_x1, search_y1, search_x2, search_y1);
1291 draw_line(frame, search_x2, search_y1, search_x2, search_y2);
1292 draw_line(frame, search_x2, search_y2, search_x1, search_y2);
1293 draw_line(frame, search_x1, search_y2, search_x1, search_y1);
1295 // Block should be endpoint of motion
1298 block_x = global_x2;
1299 block_y = global_y2;
1304 block_x = (int64_t)(config.block_x * w / 100);
1305 block_y = (int64_t)(config.block_y * h / 100);
1308 block_w = config.rotation_block_w * w / 100;
1309 block_h = config.rotation_block_h * h / 100;
1312 float angle = total_angle * 2 * M_PI / 360;
1313 double base_angle1 = atan((float)block_h / block_w);
1314 double base_angle2 = atan((float)block_w / block_h);
1315 double target_angle1 = base_angle1 + angle;
1316 double target_angle2 = base_angle2 + angle;
1317 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
1318 block_x1 = (int)(block_x - cos(target_angle1) * radius);
1319 block_y1 = (int)(block_y - sin(target_angle1) * radius);
1320 block_x2 = (int)(block_x + sin(target_angle2) * radius);
1321 block_y2 = (int)(block_y - cos(target_angle2) * radius);
1322 block_x3 = (int)(block_x - sin(target_angle2) * radius);
1323 block_y3 = (int)(block_y + cos(target_angle2) * radius);
1324 block_x4 = (int)(block_x + cos(target_angle1) * radius);
1325 block_y4 = (int)(block_y + sin(target_angle1) * radius);
1327 draw_line(frame, block_x1, block_y1, block_x2, block_y2);
1328 draw_line(frame, block_x2, block_y2, block_x4, block_y4);
1329 draw_line(frame, block_x4, block_y4, block_x3, block_y3);
1330 draw_line(frame, block_x3, block_y3, block_x1, block_y1);
1336 draw_line(frame, block_x, block_y - 5, block_x, block_y + 6);
1337 draw_line(frame, block_x - 5, block_y, block_x + 6, block_y);
1344 void MotionCVMain::draw_pixel(VFrame *frame, int x, int y)
1346 if(!(x >= 0 && y >= 0 && x < frame->get_w() && y < frame->get_h())) return;
1348 #define DRAW_PIXEL(x, y, components, do_yuv, max, type) \
1350 type **rows = (type**)frame->get_rows(); \
1351 rows[y][x * components] = max - rows[y][x * components]; \
1354 rows[y][x * components + 1] = max - rows[y][x * components + 1]; \
1355 rows[y][x * components + 2] = max - rows[y][x * components + 2]; \
1359 rows[y][x * components + 1] = (max / 2 + 1) - rows[y][x * components + 1]; \
1360 rows[y][x * components + 2] = (max / 2 + 1) - rows[y][x * components + 2]; \
1362 if(components == 4) \
1363 rows[y][x * components + 3] = max; \
1367 switch(frame->get_color_model())
1370 DRAW_PIXEL(x, y, 3, 0, 0xff, unsigned char);
1373 DRAW_PIXEL(x, y, 4, 0, 0xff, unsigned char);
1376 DRAW_PIXEL(x, y, 3, 0, 1.0, float);
1379 DRAW_PIXEL(x, y, 4, 0, 1.0, float);
1382 DRAW_PIXEL(x, y, 3, 1, 0xff, unsigned char);
1385 DRAW_PIXEL(x, y, 4, 1, 0xff, unsigned char);
1388 DRAW_PIXEL(x, y, 3, 0, 0xffff, uint16_t);
1391 DRAW_PIXEL(x, y, 3, 1, 0xffff, uint16_t);
1393 case BC_RGBA16161616:
1394 DRAW_PIXEL(x, y, 4, 0, 0xffff, uint16_t);
1396 case BC_YUVA16161616:
1397 DRAW_PIXEL(x, y, 4, 1, 0xffff, uint16_t);
1403 void MotionCVMain::draw_line(VFrame *frame, int x1, int y1, int x2, int y2)
1405 int w = labs(x2 - x1);
1406 int h = labs(y2 - y1);
1407 //printf("MotionCVMain::draw_line 1 %d %d %d %d\n", x1, y1, x2, y2);
1411 draw_pixel(frame, x1, y1);
1416 // Flip coordinates so x1 < x2
1426 int numerator = y2 - y1;
1427 int denominator = x2 - x1;
1428 for(int i = x1; i < x2; i++)
1430 int y = y1 + (int64_t)(i - x1) * (int64_t)numerator / (int64_t)denominator;
1431 draw_pixel(frame, i, y);
1436 // Flip coordinates so y1 < y2
1446 int numerator = x2 - x1;
1447 int denominator = y2 - y1;
1448 for(int i = y1; i < y2; i++)
1450 int x = x1 + (int64_t)(i - y1) * (int64_t)numerator / (int64_t)denominator;
1451 draw_pixel(frame, x, i);
1454 //printf("MotionCVMain::draw_line 2\n");
1457 #define ARROW_SIZE 10
1458 void MotionCVMain::draw_arrow(VFrame *frame, int x1, int y1, int x2, int y2)
1460 double angle = atan((float)(y2 - y1) / (float)(x2 - x1));
1461 double angle1 = angle + (float)145 / 360 * 2 * 3.14159265;
1462 double angle2 = angle - (float)145 / 360 * 2 * 3.14159265;
1469 x3 = x2 - (int)(ARROW_SIZE * cos(angle1));
1470 y3 = y2 - (int)(ARROW_SIZE * sin(angle1));
1471 x4 = x2 - (int)(ARROW_SIZE * cos(angle2));
1472 y4 = y2 - (int)(ARROW_SIZE * sin(angle2));
1476 x3 = x2 + (int)(ARROW_SIZE * cos(angle1));
1477 y3 = y2 + (int)(ARROW_SIZE * sin(angle1));
1478 x4 = x2 + (int)(ARROW_SIZE * cos(angle2));
1479 y4 = y2 + (int)(ARROW_SIZE * sin(angle2));
1483 draw_line(frame, x1, y1, x2, y2);
1484 // draw_line(frame, x1, y1 + 1, x2, y2 + 1);
1487 if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x3, y3);
1488 // draw_line(frame, x2, y2 + 1, x3, y3 + 1);
1490 if(abs(y2 - y1) || abs(x2 - x1)) draw_line(frame, x2, y2, x4, y4);
1491 // draw_line(frame, x2, y2 + 1, x4, y4 + 1);
1497 #define ABS_DIFF(type, temp_type, multiplier, components) \
1499 temp_type result_temp = 0; \
1500 for(int i = 0; i < h; i++) \
1502 type *prev_row = (type*)prev_ptr; \
1503 type *current_row = (type*)current_ptr; \
1504 for(int j = 0; j < w; j++) \
1506 for(int k = 0; k < 3; k++) \
1508 temp_type difference; \
1509 difference = *prev_row++ - *current_row++; \
1510 if(difference < 0) \
1511 result_temp -= difference; \
1513 result_temp += difference; \
1515 if(components == 4) \
1521 prev_ptr += row_bytes; \
1522 current_ptr += row_bytes; \
1524 result = (int64_t)(result_temp * multiplier); \
1527 int64_t MotionCVMain::abs_diff(unsigned char *prev_ptr,
1528 unsigned char *current_ptr,
1538 ABS_DIFF(unsigned char, int64_t, 1, 3)
1541 ABS_DIFF(unsigned char, int64_t, 1, 4)
1544 ABS_DIFF(float, double, 0x10000, 3)
1547 ABS_DIFF(float, double, 0x10000, 4)
1550 ABS_DIFF(unsigned char, int64_t, 1, 3)
1553 ABS_DIFF(unsigned char, int64_t, 1, 4)
1556 ABS_DIFF(uint16_t, int64_t, 1, 3)
1558 case BC_YUVA16161616:
1559 ABS_DIFF(uint16_t, int64_t, 1, 4)
1567 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1569 temp_type result_temp = 0; \
1570 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1571 temp_type y1_fraction = 0x100 - y2_fraction; \
1572 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1573 temp_type x1_fraction = 0x100 - x2_fraction; \
1574 for(int i = 0; i < h_sub; i++) \
1576 type *prev_row1 = (type*)prev_ptr; \
1577 type *prev_row2 = (type*)prev_ptr + components; \
1578 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1579 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1580 type *current_row = (type*)current_ptr; \
1581 for(int j = 0; j < w_sub; j++) \
1583 for(int k = 0; k < 3; k++) \
1585 temp_type difference; \
1586 temp_type prev_value = \
1587 (*prev_row1++ * x1_fraction * y1_fraction + \
1588 *prev_row2++ * x2_fraction * y1_fraction + \
1589 *prev_row3++ * x1_fraction * y2_fraction + \
1590 *prev_row4++ * x2_fraction * y2_fraction) / \
1592 temp_type current_value = *current_row++; \
1593 difference = prev_value - current_value; \
1594 if(difference < 0) \
1595 result_temp -= difference; \
1597 result_temp += difference; \
1600 if(components == 4) \
1609 prev_ptr += row_bytes; \
1610 current_ptr += row_bytes; \
1612 result = (int64_t)(result_temp * multiplier); \
1618 int64_t MotionCVMain::abs_diff_sub(unsigned char *prev_ptr,
1619 unsigned char *current_ptr,
1634 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1637 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1640 ABS_DIFF_SUB(float, double, 0x10000, 3)
1643 ABS_DIFF_SUB(float, double, 0x10000, 4)
1646 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1649 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1652 ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
1654 case BC_YUVA16161616:
1655 ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
1665 MotionCVScanPackage::MotionCVScanPackage()
1676 MotionCVScanUnit::MotionCVScanUnit(MotionCVScan *server,
1677 MotionCVMain *plugin)
1678 : LoadClient(server)
1680 this->plugin = plugin;
1681 this->server = server;
1682 cache_lock = new Mutex("MotionCVScanUnit::cache_lock");
1685 MotionCVScanUnit::~MotionCVScanUnit()
1692 void MotionCVScanUnit::process_package(LoadPackage *package)
1694 MotionCVScanPackage *pkg = (MotionCVScanPackage*)package;
1695 //int w = server->current_frame->get_w();
1696 //int h = server->current_frame->get_h();
1697 int color_model = server->current_frame->get_color_model();
1698 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
1699 int row_bytes = server->current_frame->get_bytes_per_line();
1713 if(!server->subpixel)
1715 int search_x = pkg->scan_x1 + (pkg->pixel % (pkg->scan_x2 - pkg->scan_x1));
1716 int search_y = pkg->scan_y1 + (pkg->pixel / (pkg->scan_x2 - pkg->scan_x1));
1719 pkg->difference1 = server->get_cache(search_x, search_y);
1720 if(pkg->difference1 < 0)
1722 //printf("MotionCVScanUnit::process_package 1 %d %d\n",
1723 //search_x, search_y, pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1);
1724 // Pointers to first pixel in each block
1725 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1727 search_x * pixel_size;
1728 unsigned char *current_ptr = server->current_frame->get_rows()[
1730 pkg->block_x1 * pixel_size;
1732 pkg->difference1 = plugin->abs_diff(prev_ptr,
1735 pkg->block_x2 - pkg->block_x1,
1736 pkg->block_y2 - pkg->block_y1,
1738 //printf("MotionCVScanUnit::process_package 2\n");
1739 server->put_cache(search_x, search_y, pkg->difference1);
1760 int sub_x = pkg->pixel % (OVERSAMPLE * 2 - 1) + 1;
1761 int sub_y = pkg->pixel / (OVERSAMPLE * 2 - 1) + 1;
1763 if(plugin->config.horizontal_only)
1768 if(plugin->config.vertical_only)
1773 int search_x = pkg->scan_x1 + sub_x / OVERSAMPLE;
1774 int search_y = pkg->scan_y1 + sub_y / OVERSAMPLE;
1775 sub_x %= OVERSAMPLE;
1776 sub_y %= OVERSAMPLE;
1779 unsigned char *prev_ptr = server->previous_frame->get_rows()[
1781 search_x * pixel_size;
1782 unsigned char *current_ptr = server->current_frame->get_rows()[
1784 pkg->block_x1 * pixel_size;
1786 // With subpixel, there are two ways to compare each position, one by shifting
1787 // the previous frame and two by shifting the current frame.
1788 pkg->difference1 = plugin->abs_diff_sub(prev_ptr,
1791 pkg->block_x2 - pkg->block_x1,
1792 pkg->block_y2 - pkg->block_y1,
1796 pkg->difference2 = plugin->abs_diff_sub(current_ptr,
1799 pkg->block_x2 - pkg->block_x1,
1800 pkg->block_y2 - pkg->block_y1,
1804 // printf("MotionCVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
1809 // pkg->difference1,
1810 // pkg->difference2);
1827 int64_t MotionCVScanUnit::get_cache(int x, int y)
1829 int64_t result = -1;
1830 cache_lock->lock("MotionCVScanUnit::get_cache");
1831 for(int i = 0; i < cache.total; i++)
1833 MotionCVScanCache *ptr = cache.values[i];
1834 if(ptr->x == x && ptr->y == y)
1836 result = ptr->difference;
1840 cache_lock->unlock();
1844 void MotionCVScanUnit::put_cache(int x, int y, int64_t difference)
1846 MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference);
1847 cache_lock->lock("MotionCVScanUnit::put_cache");
1849 cache_lock->unlock();
1862 MotionCVScan::MotionCVScan(MotionCVMain *plugin,
1867 total_clients, total_packages
1870 this->plugin = plugin;
1871 cache_lock = new Mutex("MotionCVScan::cache_lock");
1874 MotionCVScan::~MotionCVScan()
1880 void MotionCVScan::init_packages()
1882 // Set package coords
1883 for(int i = 0; i < get_total_packages(); i++)
1885 MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
1887 pkg->block_x1 = block_x1;
1888 pkg->block_x2 = block_x2;
1889 pkg->block_y1 = block_y1;
1890 pkg->block_y2 = block_y2;
1891 pkg->scan_x1 = scan_x1;
1892 pkg->scan_x2 = scan_x2;
1893 pkg->scan_y1 = scan_y1;
1894 pkg->scan_y2 = scan_y2;
1895 pkg->pixel = (int64_t)i * (int64_t)total_pixels / (int64_t)total_steps;
1896 pkg->difference1 = 0;
1897 pkg->difference2 = 0;
1904 LoadClient* MotionCVScan::new_client()
1906 return new MotionCVScanUnit(this, plugin);
1909 LoadPackage* MotionCVScan::new_package()
1911 return new MotionCVScanPackage;
1915 void MotionCVScan::scan_frame(VFrame *previous_frame,
1916 VFrame *current_frame)
1918 this->previous_frame = previous_frame;
1919 this->current_frame = current_frame;
1922 cache.remove_all_objects();
1925 // Single macroblock
1926 int w = current_frame->get_w();
1927 int h = current_frame->get_h();
1929 // Initial search parameters
1930 int scan_w = w * plugin->config.global_range_w / 100;
1931 int scan_h = h * plugin->config.global_range_h / 100;
1932 int block_w = w * plugin->config.global_block_w / 100;
1933 int block_h = h * plugin->config.global_block_h / 100;
1935 // Location of block in previous frame
1936 block_x1 = (int)(w * plugin->config.block_x / 100 - block_w / 2);
1937 block_y1 = (int)(h * plugin->config.block_y / 100 - block_h / 2);
1938 block_x2 = (int)(w * plugin->config.block_x / 100 + block_w / 2);
1939 block_y2 = (int)(h * plugin->config.block_y / 100 + block_h / 2);
1941 // Offset to location of previous block. This offset needn't be very accurate
1942 // since it's the offset of the previous image and current image we want.
1943 if(plugin->config.mode3 == MotionCVConfig::TRACK_PREVIOUS)
1945 block_x1 += plugin->total_dx / OVERSAMPLE;
1946 block_y1 += plugin->total_dy / OVERSAMPLE;
1947 block_x2 += plugin->total_dx / OVERSAMPLE;
1948 block_y2 += plugin->total_dy / OVERSAMPLE;
1953 switch(plugin->config.mode2)
1956 case MotionCVConfig::NO_CALCULATE:
1962 case MotionCVConfig::LOAD:
1964 // Load result from disk
1965 char string[BCTEXTLEN];
1966 sprintf(string, "%s%06jd", MOTION_FILE, plugin->get_source_position());
1967 FILE *input = fopen(string, "r");
1980 // Scan from scratch
1989 // Location of block in current frame
1990 int x_result = block_x1;
1991 int y_result = block_y1;
1993 // printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
1994 // block_x1 + block_w / 2,
1995 // block_y1 + block_h / 2,
2005 scan_x1 = x_result - scan_w / 2;
2006 scan_y1 = y_result - scan_h / 2;
2007 scan_x2 = x_result + scan_w / 2;
2008 scan_y2 = y_result + scan_h / 2;
2012 // Zero out requested values
2013 if(plugin->config.horizontal_only)
2016 scan_y2 = block_y1 + 1;
2018 if(plugin->config.vertical_only)
2021 scan_x2 = block_x1 + 1;
2024 // printf("MotionCVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
2033 // Clamp the block coords before the scan so we get useful scan coords.
2034 MotionCVMain::clamp_scan(w,
2045 // printf("MotionCVScan::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",
2058 // Give up if invalid coords.
2059 if(scan_y2 <= scan_y1 ||
2060 scan_x2 <= scan_x1 ||
2061 block_x2 <= block_x1 ||
2062 block_y2 <= block_y1)
2065 // For subpixel, the top row and left column are skipped
2068 if(plugin->config.horizontal_only ||
2069 plugin->config.vertical_only)
2071 total_pixels = 4 * OVERSAMPLE * OVERSAMPLE - 4 * OVERSAMPLE;
2075 total_pixels = 4 * OVERSAMPLE;
2078 total_steps = total_pixels;
2080 set_package_count(total_steps);
2083 // Get least difference
2084 int64_t min_difference = -1;
2085 for(int i = 0; i < get_total_packages(); i++)
2087 MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
2088 if(pkg->difference1 < min_difference || min_difference == -1)
2090 min_difference = pkg->difference1;
2092 if(plugin->config.vertical_only)
2093 x_result = scan_x1 * OVERSAMPLE;
2095 x_result = scan_x1 * OVERSAMPLE +
2096 (pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1;
2098 if(plugin->config.horizontal_only)
2099 y_result = scan_y1 * OVERSAMPLE;
2101 y_result = scan_y1 * OVERSAMPLE +
2102 (pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1;
2106 dx_result = block_x1 * OVERSAMPLE - x_result;
2107 dy_result = block_y1 * OVERSAMPLE - y_result;
2110 if(pkg->difference2 < min_difference)
2112 min_difference = pkg->difference2;
2114 if(plugin->config.vertical_only)
2115 x_result = scan_x1 * OVERSAMPLE;
2117 x_result = scan_x2 * OVERSAMPLE -
2118 ((pkg->pixel % (OVERSAMPLE * 2 - 1)) + 1);
2120 if(plugin->config.horizontal_only)
2121 y_result = scan_y1 * OVERSAMPLE;
2123 y_result = scan_y2 * OVERSAMPLE -
2124 ((pkg->pixel / (OVERSAMPLE * 2 - 1)) + 1);
2126 dx_result = block_x1 * OVERSAMPLE - x_result;
2127 dy_result = block_y1 * OVERSAMPLE - y_result;
2131 //printf("MotionCVScan::scan_frame 1 %d %d %d %d\n", block_x1, block_y1, x_result, y_result);
2136 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
2137 total_steps = MIN(plugin->config.global_positions, total_pixels);
2139 set_package_count(total_steps);
2142 // Get least difference
2143 int64_t min_difference = -1;
2144 for(int i = 0; i < get_total_packages(); i++)
2146 MotionCVScanPackage *pkg = (MotionCVScanPackage*)get_package(i);
2147 if(pkg->difference1 < min_difference || min_difference == -1)
2149 min_difference = pkg->difference1;
2150 x_result = scan_x1 + (pkg->pixel % (scan_x2 - scan_x1));
2151 y_result = scan_y1 + (pkg->pixel / (scan_x2 - scan_x1));
2152 x_result *= OVERSAMPLE;
2153 y_result *= OVERSAMPLE;
2157 // printf("MotionCVScan::scan_frame 10 total_steps=%d total_pixels=%d subpixel=%d\n",
2162 // printf(" scan w=%d h=%d scan x1=%d y1=%d x2=%d y2=%d\n",
2170 // printf("MotionCVScan::scan_frame 2 block x1=%d y1=%d x2=%d y2=%d result x=%.2f y=%.2f\n",
2175 // (float)x_result / 4,
2176 // (float)y_result / 4);
2179 // If a new search is required, rescale results back to pixels.
2180 if(total_steps >= total_pixels)
2182 // Single pixel accuracy reached. Now do exhaustive subpixel search.
2183 if(plugin->config.mode1 == MotionCVConfig::STABILIZE ||
2184 plugin->config.mode1 == MotionCVConfig::TRACK ||
2185 plugin->config.mode1 == MotionCVConfig::NOTHING)
2187 x_result /= OVERSAMPLE;
2188 y_result /= OVERSAMPLE;
2195 // Fill in results and quit
2196 dx_result = block_x1 * OVERSAMPLE - x_result;
2197 dy_result = block_y1 * OVERSAMPLE - y_result;
2202 // Reduce scan area and try again
2204 scan_w = (scan_x2 - scan_x1) / 2;
2205 scan_h = (scan_y2 - scan_y1) / 2;
2206 x_result /= OVERSAMPLE;
2207 y_result /= OVERSAMPLE;
2215 // Add offsets from the "tracked single frame"
2216 if (plugin->config.addtrackedframeoffset) {
2217 int tf_dx_result, tf_dy_result;
2218 char string[BCTEXTLEN];
2219 sprintf(string, "%s%06jd", MOTION_FILE, plugin->config.track_frame);
2220 FILE *input = fopen(string, "r");
2227 dx_result += tf_dx_result;
2228 dy_result += tf_dy_result;
2241 if(plugin->config.mode2 == MotionCVConfig::SAVE)
2243 char string[BCTEXTLEN];
2247 plugin->get_source_position());
2248 FILE *output = fopen(string, "w");
2259 perror("MotionCVScan::scan_frame SAVE 1");
2264 printf("MotionCVScan::scan_frame 10 dx=%.2f dy=%.2f\n",
2265 (float)this->dx_result / OVERSAMPLE,
2266 (float)this->dy_result / OVERSAMPLE);
2286 int64_t MotionCVScan::get_cache(int x, int y)
2288 int64_t result = -1;
2289 cache_lock->lock("MotionCVScan::get_cache");
2290 for(int i = 0; i < cache.total; i++)
2292 MotionCVScanCache *ptr = cache.values[i];
2293 if(ptr->x == x && ptr->y == y)
2295 result = ptr->difference;
2299 cache_lock->unlock();
2303 void MotionCVScan::put_cache(int x, int y, int64_t difference)
2305 MotionCVScanCache *ptr = new MotionCVScanCache(x, y, difference);
2306 cache_lock->lock("MotionCVScan::put_cache");
2308 cache_lock->unlock();
2315 MotionCVScanCache::MotionCVScanCache(int x, int y, int64_t difference)
2319 this->difference = difference;
2335 RotateCVScanPackage::RotateCVScanPackage()
2340 RotateCVScanUnit::RotateCVScanUnit(RotateCVScan *server, MotionCVMain *plugin)
2341 : LoadClient(server)
2343 this->server = server;
2344 this->plugin = plugin;
2349 RotateCVScanUnit::~RotateCVScanUnit()
2355 void RotateCVScanUnit::process_package(LoadPackage *package)
2357 if(server->skip) return;
2358 RotateCVScanPackage *pkg = (RotateCVScanPackage*)package;
2360 if((pkg->difference = server->get_cache(pkg->angle)) < 0)
2362 //printf("RotateCVScanUnit::process_package 1\n");
2363 int color_model = server->previous_frame->get_color_model();
2364 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
2365 int row_bytes = server->previous_frame->get_bytes_per_line();
2368 rotater = new AffineEngine(1, 1);
2369 if(!temp) temp = new VFrame(
2370 server->previous_frame->get_w(),
2371 server->previous_frame->get_h(),
2375 // RotateCV original block size
2376 rotater->set_viewport(server->block_x1,
2378 server->block_x2 - server->block_x1,
2379 server->block_y2 - server->block_y1);
2380 rotater->set_pivot(server->block_x, server->block_y);
2382 rotater->rotate(temp,
2383 server->previous_frame,
2385 // Clamp coordinates
2386 int x1 = server->scan_x;
2387 int y1 = server->scan_y;
2388 int x2 = x1 + server->scan_w;
2389 int y2 = y1 + server->scan_h;
2390 x2 = MIN(temp->get_w(), x2);
2391 y2 = MIN(temp->get_h(), y2);
2392 x2 = MIN(server->current_frame->get_w(), x2);
2393 y2 = MIN(server->current_frame->get_h(), y2);
2397 if(x2 > x1 && y2 > y1)
2399 pkg->difference = plugin->abs_diff(
2400 temp->get_rows()[y1] + x1 * pixel_size,
2401 server->current_frame->get_rows()[y1] + x1 * pixel_size,
2406 //printf("RotateCVScanUnit::process_package %d\n", __LINE__);
2407 server->put_cache(pkg->angle, pkg->difference);
2410 // printf("RotateCVScanUnit::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",
2411 // server->block_x1,
2412 // server->block_y1,
2413 // server->block_x2 - server->block_x1,
2414 // server->block_y2 - server->block_y1,
2420 // pkg->difference);
2445 RotateCVScan::RotateCVScan(MotionCVMain *plugin,
2450 total_clients, total_packages
2453 this->plugin = plugin;
2454 cache_lock = new Mutex("RotateCVScan::cache_lock");
2458 RotateCVScan::~RotateCVScan()
2463 void RotateCVScan::init_packages()
2465 for(int i = 0; i < get_total_packages(); i++)
2467 RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i);
2469 (scan_angle2 - scan_angle1) /
2475 LoadClient* RotateCVScan::new_client()
2477 return new RotateCVScanUnit(this, plugin);
2480 LoadPackage* RotateCVScan::new_package()
2482 return new RotateCVScanPackage;
2486 float RotateCVScan::scan_frame(VFrame *previous_frame,
2487 VFrame *current_frame,
2492 this->block_x = block_x;
2493 this->block_y = block_y;
2495 switch(plugin->config.mode2)
2497 case MotionCVConfig::NO_CALCULATE:
2502 case MotionCVConfig::LOAD:
2504 char string[BCTEXTLEN];
2505 sprintf(string, "%s%06jd", ROTATION_FILE, plugin->get_source_position());
2506 FILE *input = fopen(string, "r");
2509 fscanf(input, "%f", &result);
2515 perror("RotateCVScan::scan_frame LOAD");
2528 this->previous_frame = previous_frame;
2529 this->current_frame = current_frame;
2530 int w = current_frame->get_w();
2531 int h = current_frame->get_h();
2532 int block_w = w * plugin->config.rotation_block_w / 100;
2533 int block_h = h * plugin->config.rotation_block_h / 100;
2535 if(this->block_x - block_w / 2 < 0) block_w = this->block_x * 2;
2536 if(this->block_y - block_h / 2 < 0) block_h = this->block_y * 2;
2537 if(this->block_x + block_w / 2 > w) block_w = (w - this->block_x) * 2;
2538 if(this->block_y + block_h / 2 > h) block_h = (h - this->block_y) * 2;
2540 block_x1 = this->block_x - block_w / 2;
2541 block_x2 = this->block_x + block_w / 2;
2542 block_y1 = this->block_y - block_h / 2;
2543 block_y2 = this->block_y + block_h / 2;
2546 // Calculate the maximum area available to scan after rotation.
2547 // Must be calculated from the starting range because of cache.
2548 // Get coords of rectangle after rotation.
2549 double center_x = this->block_x;
2550 double center_y = this->block_y;
2551 double max_angle = plugin->config.rotation_range;
2552 double base_angle1 = atan((float)block_h / block_w);
2553 double base_angle2 = atan((float)block_w / block_h);
2554 double target_angle1 = base_angle1 + max_angle * 2 * M_PI / 360;
2555 double target_angle2 = base_angle2 + max_angle * 2 * M_PI / 360;
2556 double radius = sqrt(block_w * block_w + block_h * block_h) / 2;
2557 double x1 = center_x - cos(target_angle1) * radius;
2558 double y1 = center_y - sin(target_angle1) * radius;
2559 double x2 = center_x + sin(target_angle2) * radius;
2560 double y2 = center_y - cos(target_angle2) * radius;
2561 double x3 = center_x - sin(target_angle2) * radius;
2562 double y3 = center_y + cos(target_angle2) * radius;
2564 // Track top edge to find greatest area.
2565 double max_area1 = 0;
2566 //double max_x1 = 0;
2568 for(double x = x1; x < x2; x++)
2570 double y = y1 + (y2 - y1) * (x - x1) / (x2 - x1);
2571 if(x >= center_x && x < block_x2 && y >= block_y1 && y < center_y)
2573 double area = fabs(x - center_x) * fabs(y - center_y);
2574 if(area > max_area1)
2583 // Track left edge to find greatest area.
2584 double max_area2 = 0;
2586 //double max_y2 = 0;
2587 for(double y = y1; y < y3; y++)
2589 double x = x1 + (x3 - x1) * (y - y1) / (y3 - y1);
2590 if(x >= block_x1 && x < center_x && y >= block_y1 && y < center_y)
2592 double area = fabs(x - center_x) * fabs(y - center_y);
2593 if(area > max_area2)
2602 double max_x, max_y;
2606 // Get reduced scan coords
2607 scan_w = (int)(fabs(max_x - center_x) * 2);
2608 scan_h = (int)(fabs(max_y - center_y) * 2);
2609 scan_x = (int)(center_x - scan_w / 2);
2610 scan_y = (int)(center_y - scan_h / 2);
2611 // printf("RotateCVScan::scan_frame center=%d,%d scan=%d,%d %dx%d\n",
2612 // this->block_x, this->block_y, scan_x, scan_y, scan_w, scan_h);
2613 // printf(" angle_range=%f block= %d,%d,%d,%d\n", max_angle, block_x1, block_y1, block_x2, block_y2);
2615 // Determine min angle from size of block
2616 double angle1 = atan((double)block_h / block_w);
2617 double angle2 = atan((double)(block_h - 1) / (block_w + 1));
2618 double min_angle = fabs(angle2 - angle1) / OVERSAMPLE;
2619 min_angle = MAX(min_angle, MIN_ANGLE);
2622 printf("RotateCVScan::scan_frame min_angle=%f\n", min_angle * 360 / 2 / M_PI);
2625 cache.remove_all_objects();
2628 // Initial search range
2629 float angle_range = (float)plugin->config.rotation_range;
2631 total_steps = plugin->config.rotate_positions;
2634 while(angle_range >= min_angle * total_steps)
2636 scan_angle1 = result - angle_range;
2637 scan_angle2 = result + angle_range;
2640 set_package_count(total_steps);
2641 //set_package_count(1);
2644 int64_t min_difference = -1;
2645 for(int i = 0; i < get_total_packages(); i++)
2647 RotateCVScanPackage *pkg = (RotateCVScanPackage*)get_package(i);
2648 if(pkg->difference < min_difference || min_difference == -1)
2650 min_difference = pkg->difference;
2651 result = pkg->angle;
2663 if(!skip && plugin->config.mode2 == MotionCVConfig::SAVE)
2665 char string[BCTEXTLEN];
2669 plugin->get_source_position());
2670 FILE *output = fopen(string, "w");
2673 fprintf(output, "%f\n", result);
2678 perror("RotateCVScan::scan_frame SAVE");
2683 printf("RotateCVScan::scan_frame 10 angle=%f\n", result);
2691 int64_t RotateCVScan::get_cache(float angle)
2693 int64_t result = -1;
2694 cache_lock->lock("RotateCVScan::get_cache");
2695 for(int i = 0; i < cache.total; i++)
2697 RotateCVScanCache *ptr = cache.values[i];
2698 if(fabs(ptr->angle - angle) <= MIN_ANGLE)
2700 result = ptr->difference;
2704 cache_lock->unlock();
2708 void RotateCVScan::put_cache(float angle, int64_t difference)
2710 RotateCVScanCache *ptr = new RotateCVScanCache(angle, difference);
2711 cache_lock->lock("RotateCVScan::put_cache");
2713 cache_lock->unlock();
2724 RotateCVScanCache::RotateCVScanCache(float angle, int64_t difference)
2726 this->angle = angle;
2727 this->difference = difference;