4 * Copyright (C) 2016 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 "bcsignals.h"
25 #include "motioncache-hv.h"
26 #include "motionscan-hv.h"
35 // The module which does the actual scanning
37 // starting level of detail
38 #define STARTING_DOWNSAMPLE 16
39 // minimum size in each level of detail
40 #define MIN_DOWNSAMPLED_SIZE 16
42 #define MIN_DOWNSAMPLED_SCAN 4
43 // scan range for subpixel mode
44 #define SUBPIXEL_RANGE 4
46 MotionHVScanPackage::MotionHVScanPackage()
57 MotionHVScanUnit::MotionHVScanUnit(MotionHVScan *server)
60 this->server = server;
63 MotionHVScanUnit::~MotionHVScanUnit()
68 void MotionHVScanUnit::single_pixel(MotionHVScanPackage *pkg)
70 //int w = server->current_frame->get_w();
71 //int h = server->current_frame->get_h();
72 int color_model = server->current_frame->get_color_model();
73 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
74 int row_bytes = server->current_frame->get_bytes_per_line();
76 // printf("MotionHVScanUnit::process_package %d search_x=%d search_y=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n",
87 // Pointers to first pixel in each block
88 unsigned char *prev_ptr = server->previous_frame->get_rows()[
90 pkg->search_x * pixel_size;
91 unsigned char *current_ptr = 0;
95 current_ptr = server->rotated_current[pkg->angle_step]->get_rows()[
97 pkg->block_x1 * pixel_size;
101 current_ptr = server->current_frame->get_rows()[
103 pkg->block_x1 * pixel_size;
107 pkg->difference1 = MotionHVScan::abs_diff(prev_ptr,
110 pkg->block_x2 - pkg->block_x1,
111 pkg->block_y2 - pkg->block_y1,
114 // printf("MotionHVScanUnit::process_package %d angle_step=%d diff=%d\n",
117 // pkg->difference1);
118 // printf("MotionHVScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n",
119 // __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1);
122 void MotionHVScanUnit::subpixel(MotionHVScanPackage *pkg)
125 //int w = server->current_frame->get_w();
126 //int h = server->current_frame->get_h();
127 int color_model = server->current_frame->get_color_model();
128 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
129 int row_bytes = server->current_frame->get_bytes_per_line();
130 unsigned char *prev_ptr = server->previous_frame->get_rows()[
132 pkg->search_x * pixel_size;
134 unsigned char *current_ptr = server->current_frame->get_rows()[
136 pkg->block_x1 * pixel_size;
138 // With subpixel, there are two ways to compare each position, one by shifting
139 // the previous frame and two by shifting the current frame.
140 pkg->difference1 = MotionHVScan::abs_diff_sub(prev_ptr,
143 pkg->block_x2 - pkg->block_x1,
144 pkg->block_y2 - pkg->block_y1,
148 pkg->difference2 = MotionHVScan::abs_diff_sub(current_ptr,
151 pkg->block_x2 - pkg->block_x1,
152 pkg->block_y2 - pkg->block_y1,
156 // printf("MotionHVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
162 // pkg->difference2);
165 void MotionHVScanUnit::process_package(LoadPackage *package)
167 MotionHVScanPackage *pkg = (MotionHVScanPackage*)package;
171 if(!server->subpixel)
203 MotionHVScan::MotionHVScan(int total_clients,
208 total_clients, total_packages
214 downsample_cache = 0;
215 shared_downsample = 0;
218 MotionHVScan::~MotionHVScan()
220 if(downsample_cache && !shared_downsample) {
221 delete downsample_cache;
222 downsample_cache = 0;
223 shared_downsample = 0;
226 if(rotated_current) {
227 for(int i = 0; i < total_rotated; i++) {
228 delete rotated_current[i];
230 delete [] rotated_current;
236 void MotionHVScan::init_packages()
238 // Set package coords
239 // Total range of positions to scan with downsampling
240 int downsampled_scan_x1 = scan_x1 / current_downsample;
241 //int downsampled_scan_x2 = scan_x2 / current_downsample;
242 int downsampled_scan_y1 = scan_y1 / current_downsample;
243 //int downsampled_scan_y2 = scan_y2 / current_downsample;
244 int downsampled_block_x1 = block_x1 / current_downsample;
245 int downsampled_block_x2 = block_x2 / current_downsample;
246 int downsampled_block_y1 = block_y1 / current_downsample;
247 int downsampled_block_y2 = block_y2 / current_downsample;
250 //printf("MotionHVScan::init_packages %d %d\n", __LINE__, get_total_packages());
251 // printf("MotionHVScan::init_packages %d current_downsample=%d scan_x1=%d scan_x2=%d block_x1=%d block_x2=%d\n",
253 // current_downsample,
254 // downsampled_scan_x1,
255 // downsampled_scan_x2,
256 // downsampled_block_x1,
257 // downsampled_block_x2);
258 // if(current_downsample == 8 && downsampled_scan_x1 == 47)
260 // downsampled_previous->write_png("/tmp/previous");
261 // downsampled_current->write_png("/tmp/current");
264 for(int i = 0; i < get_total_packages(); i++)
266 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
268 pkg->block_x1 = downsampled_block_x1;
269 pkg->block_x2 = downsampled_block_x2;
270 pkg->block_y1 = downsampled_block_y1;
271 pkg->block_y2 = downsampled_block_y2;
272 pkg->difference1 = 0;
273 pkg->difference2 = 0;
283 pkg->search_x = scan_x1 / current_downsample;
284 pkg->search_y = scan_y1 / current_downsample;
290 int current_x_step = (i % x_steps);
291 int current_y_step = (i / x_steps);
293 //printf("MotionHVScan::init_packages %d i=%d x_step=%d y_step=%d angle_step=%d\n",
294 //__LINE__, i, current_x_step, current_y_step, current_angle_step);
295 pkg->search_x = downsampled_scan_x1 + current_x_step *
296 (scan_x2 - scan_x1) / current_downsample / x_steps;
297 pkg->search_y = downsampled_scan_y1 + current_y_step *
298 (scan_y2 - scan_y1) / current_downsample / y_steps;
302 pkg->angle_step = angle_steps / 2;
315 pkg->sub_x = i % (OVERSAMPLE * SUBPIXEL_RANGE);
316 pkg->sub_y = i / (OVERSAMPLE * SUBPIXEL_RANGE);
318 // if(horizontal_only)
328 pkg->search_x = scan_x1 + pkg->sub_x / OVERSAMPLE + 1;
329 pkg->search_y = scan_y1 + pkg->sub_y / OVERSAMPLE + 1;
330 pkg->sub_x %= OVERSAMPLE;
331 pkg->sub_y %= OVERSAMPLE;
335 // printf("MotionHVScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n",
344 // printf("MotionHVScan::init_packages %d %d,%d %d,%d %d,%d\n",
355 LoadClient* MotionHVScan::new_client()
357 return new MotionHVScanUnit(this);
360 LoadPackage* MotionHVScan::new_package()
362 return new MotionHVScanPackage;
366 void MotionHVScan::set_test_match(int value)
368 this->test_match = value;
371 void MotionHVScan::set_cache(MotionHVCache *cache)
373 this->downsample_cache = cache;
374 shared_downsample = 1;
378 double MotionHVScan::step_to_angle(int step, double center)
380 if(step < angle_steps / 2)
382 return center - angle_step * (angle_steps / 2 - step);
385 if(step > angle_steps / 2)
387 return center + angle_step * (step - angle_steps / 2);
396 static int compare(const void *p1, const void *p2)
398 double value1 = *(double*)p1;
399 double value2 = *(double*)p2;
401 //printf("compare %d value1=%f value2=%f\n", __LINE__, value1, value2);
402 return value1 > value2;
406 // reject vectors based on content. It's the reason Goog can't stabilize timelapses.
407 //#define STDDEV_TEST
409 // pixel accurate motion search
410 void MotionHVScan::pixel_search(int &x_result, int &y_result, double &r_result)
412 // reduce level of detail until enough steps
413 while(current_downsample > 1 &&
414 ((block_x2 - block_x1) / current_downsample < MIN_DOWNSAMPLED_SIZE ||
415 (block_y2 - block_y1) / current_downsample < MIN_DOWNSAMPLED_SIZE
417 (scan_x2 - scan_x1) / current_downsample < MIN_DOWNSAMPLED_SCAN ||
418 (scan_y2 - scan_y1) / current_downsample < MIN_DOWNSAMPLED_SCAN
421 current_downsample /= 2;
426 // create downsampled images.
427 // Need to keep entire frame to search for rotation.
428 int downsampled_prev_w = previous_frame_arg->get_w() / current_downsample;
429 int downsampled_prev_h = previous_frame_arg->get_h() / current_downsample;
430 int downsampled_current_w = current_frame_arg->get_w() / current_downsample;
431 int downsampled_current_h = current_frame_arg->get_h() / current_downsample;
433 // printf("MotionHVScan::pixel_search %d current_downsample=%d current_frame_arg->get_w()=%d downsampled_current_w=%d\n",
435 // current_downsample,
436 // current_frame_arg->get_w(),
437 // downsampled_current_w);
439 x_steps = (scan_x2 - scan_x1) / current_downsample;
440 y_steps = (scan_y2 - scan_y1) / current_downsample;
443 double test_angle1 = atan2((double)downsampled_current_h / 2 - 1, (double)downsampled_current_w / 2);
444 double test_angle2 = atan2((double)downsampled_current_h / 2, (double)downsampled_current_w / 2 - 1);
447 angle_step = 360.0f * fabs(test_angle1 - test_angle2) / 2 / M_PI;
449 // printf("MotionHVScan::pixel_search %d test_angle1=%f test_angle2=%f angle_step=%f\n",
451 // 360.0f * test_angle1 / 2 / M_PI,
452 // 360.0f * test_angle2 / 2 / M_PI,
456 if(do_rotate && angle_step < rotation_range)
458 angle_steps = 1 + (int)((scan_angle2 - scan_angle1) / angle_step + 0.5);
466 if(current_downsample > 1)
468 if(!downsample_cache)
470 downsample_cache = new MotionHVCache();
471 shared_downsample = 0;
475 if(!shared_downsample)
477 downsample_cache->clear();
480 previous_frame = downsample_cache->get_image(current_downsample,
485 current_frame = downsample_cache->get_image(current_downsample,
487 downsampled_current_w,
488 downsampled_current_h,
495 previous_frame = previous_frame_arg;
496 current_frame = current_frame_arg;
501 // printf("MotionHVScan::pixel_search %d x_steps=%d y_steps=%d angle_steps=%d total_steps=%d\n",
510 // test variance of constant macroblock
511 int color_model = current_frame->get_color_model();
512 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
513 int row_bytes = current_frame->get_bytes_per_line();
514 int block_w = block_x2 - block_x1;
515 int block_h = block_y2 - block_y1;
517 unsigned char *current_ptr =
518 current_frame->get_rows()[block_y1 / current_downsample] +
519 (block_x1 / current_downsample) * pixel_size;
520 unsigned char *previous_ptr =
521 previous_frame->get_rows()[scan_y1 / current_downsample] +
522 (scan_x1 / current_downsample) * pixel_size;
526 // test detail in prev & current frame
527 double range1 = calculate_range(current_ptr,
529 block_w / current_downsample,
530 block_h / current_downsample,
535 printf("MotionHVScan::pixel_search %d range fail range1=%f\n", __LINE__, range1);
540 double range2 = calculate_range(previous_ptr,
542 block_w / current_downsample,
543 block_h / current_downsample,
548 printf("MotionHVScan::pixel_search %d range fail range2=%f\n", __LINE__, range2);
554 // create rotated images
555 if(rotated_current &&
556 (total_rotated != angle_steps ||
557 rotated_current[0]->get_w() != downsampled_current_w ||
558 rotated_current[0]->get_h() != downsampled_current_h))
560 for(int i = 0; i < total_rotated; i++)
562 delete rotated_current[i];
565 delete [] rotated_current;
572 total_rotated = angle_steps;
577 rotated_current = new VFrame*[total_rotated];
578 bzero(rotated_current, sizeof(VFrame*) * total_rotated);
581 // printf("MotionHVScan::pixel_search %d total_rotated=%d w=%d h=%d block_w=%d block_h=%d\n",
584 // downsampled_current_w,
585 // downsampled_current_h,
586 // (block_x2 - block_x1) / current_downsample,
587 // (block_y2 - block_y1) / current_downsample);
588 for(int i = 0; i < angle_steps; i++)
591 // printf("MotionHVScan::pixel_search %d w=%d h=%d x=%d y=%d angle=%f\n",
593 // downsampled_current_w,
594 // downsampled_current_h,
595 // (block_x1 + block_x2) / 2 / current_downsample,
596 // (block_y1 + block_y2) / 2 / current_downsample,
597 // step_to_angle(i, r_result));
599 // printf("MotionHVScan::pixel_search %d i=%d rotated_current[i]=%p\n",
602 // rotated_current[i]);
603 if(!rotated_current[i])
606 new VFrame(downsampled_current_w+1, downsampled_current_h+1,
607 current_frame_arg->get_color_model(), 0);
608 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
614 rotater = new AffineEngine(get_total_clients(),
615 get_total_clients());
618 // get smallest viewport size required for the angle
619 double diag = hypot((block_x2 - block_x1) / current_downsample,
620 (block_y2 - block_y1) / current_downsample);
621 double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) +
622 TO_RAD(step_to_angle(i, r_result));
623 double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) +
624 TO_RAD(step_to_angle(i, r_result));
625 double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2)));
626 double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2)));
627 int center_x = (block_x1 + block_x2) / 2 / current_downsample;
628 int center_y = (block_y1 + block_y2) / 2 / current_downsample;
629 int x1 = center_x - max_horiz / 2;
630 int y1 = center_y - max_vert / 2;
631 int x2 = x1 + max_horiz;
632 int y2 = y1 + max_vert;
633 CLAMP(x1, 0, downsampled_current_w - 1);
634 CLAMP(y1, 0, downsampled_current_h - 1);
635 CLAMP(x2, 0, downsampled_current_w - 1);
636 CLAMP(y2, 0, downsampled_current_h - 1);
638 //printf("MotionHVScan::pixel_search %d %f %f %d %d\n",
639 //__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert);
640 rotater->set_in_viewport(x1,
644 rotater->set_out_viewport(x1,
649 // rotater->set_in_viewport(0,
651 // downsampled_current_w,
652 // downsampled_current_h);
653 // rotater->set_out_viewport(0,
655 // downsampled_current_w,
656 // downsampled_current_h);
658 rotater->set_in_pivot(center_x, center_y);
659 rotater->set_out_pivot(center_x, center_y);
661 rotater->rotate(rotated_current[i],
663 step_to_angle(i, r_result));
665 // rotated_current[i]->draw_rect(block_x1 / current_downsample,
666 // block_y1 / current_downsample,
667 // block_x2 / current_downsample,
668 // block_y2 / current_downsample);
669 // char string[BCTEXTLEN];
670 // sprintf(string, "/tmp/rotated%d", i);
671 // rotated_current[i]->write_png(string);
672 //downsampled_previous->write_png("/tmp/previous");
673 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
682 // printf("MotionHVScan::pixel_search %d block x=%d y=%d w=%d h=%d\n",
684 // block_x1 / current_downsample,
685 // block_y1 / current_downsample,
686 // block_w / current_downsample,
687 // block_h / current_downsample);
696 // Test only translation of the middle rotated frame
698 total_steps = x_steps * y_steps;
699 set_package_count(total_steps);
707 // Get least difference
708 int64_t min_difference = -1;
710 double stddev_table[get_total_packages()];
712 for(int i = 0; i < get_total_packages(); i++)
714 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
717 double stddev = sqrt(pkg->difference1) /
718 (block_w / current_downsample) /
719 (block_h / current_downsample) /
721 // printf("MotionHVScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n",
723 // current_downsample,
726 // sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */);
728 // printf("MotionHVScan::pixel_search %d range1=%f stddev=%f\n",
733 stddev_table[i] = stddev;
734 #endif // STDDEV_TEST
736 if(pkg->difference1 < min_difference || i == 0)
738 min_difference = pkg->difference1;
739 x_result = pkg->search_x * current_downsample * OVERSAMPLE;
740 y_result = pkg->search_y * current_downsample * OVERSAMPLE;
742 // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
744 // block_x1 * OVERSAMPLE - x_result,
745 // block_y1 * OVERSAMPLE - y_result,
747 // pkg->difference1);
754 qsort(stddev_table, get_total_packages(), sizeof(double), compare);
757 // reject motion vector if not similar enough
758 // if(stddev_table[0] > 0.2)
762 // printf("MotionHVScan::pixel_search %d stddev fail min_stddev=%f\n",
772 printf("MotionHVScan::pixel_search %d\n", __LINE__);
773 for(int i = 0; i < get_total_packages(); i++)
775 printf("%f\n", stddev_table[i]);
779 // reject motion vector if not a sigmoid curve
780 // TODO: use linear interpolation
782 int step = get_total_packages() / steps;
784 for(int i = 0; i < steps; i++)
786 int start = get_total_packages() * i / steps;
787 int end = get_total_packages() * (i + 1) / steps;
788 end = MIN(end, get_total_packages() - 1);
789 curve[i] = stddev_table[end] - stddev_table[start];
793 // if(curve[0] < (curve[1] * 1.01) ||
794 // curve[2] < (curve[1] * 1.01) ||
795 // curve[0] < (curve[2] * 0.75))
796 // if(curve[0] < curve[1])
800 // printf("MotionHVScan::pixel_search %d curve fail %f %f\n",
811 printf("MotionHVScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n",
819 #endif // STDDEV_TEST
828 total_steps = angle_steps;
829 scan_x1 = x_result / OVERSAMPLE;
830 scan_y1 = y_result / OVERSAMPLE;
831 set_package_count(total_steps);
837 double prev_r_result = r_result;
838 for(int i = 0; i < get_total_packages(); i++)
840 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
842 // printf("MotionHVScan::pixel_search %d search_x=%d search_y=%d angle_step=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
846 // pkg->search_angle_step,
850 // pkg->difference2);
851 if(pkg->difference1 < min_difference || i == 0)
853 min_difference = pkg->difference1;
854 r_result = step_to_angle(i, prev_r_result);
856 // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
858 // block_x1 * OVERSAMPLE - x_result,
859 // block_y1 * OVERSAMPLE - y_result,
861 // pkg->difference1);
867 // printf("MotionHVScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n",
869 // current_downsample,
870 // (float)x_result / OVERSAMPLE,
871 // (float)y_result / OVERSAMPLE,
877 // subpixel motion search
878 void MotionHVScan::subpixel_search(int &x_result, int &y_result)
881 previous_frame = previous_frame_arg;
882 current_frame = current_frame_arg;
884 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
885 // Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square
886 total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE);
888 // These aren't used in subpixel
889 x_steps = OVERSAMPLE * SUBPIXEL_RANGE;
890 y_steps = OVERSAMPLE * SUBPIXEL_RANGE;
893 set_package_count(this->total_steps);
896 // Get least difference
897 int64_t min_difference = -1;
898 for(int i = 0; i < get_total_packages(); i++)
900 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
901 //printf("MotionHVScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
902 //__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
903 if(pkg->difference1 < min_difference || min_difference == -1)
905 min_difference = pkg->difference1;
907 // The sub coords are 1 pixel up & left of the block coords
908 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
909 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
913 dx_result = block_x1 * OVERSAMPLE - x_result;
914 dy_result = block_y1 * OVERSAMPLE - y_result;
915 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
916 //__LINE__, dx_result, dy_result, min_difference);
919 if(pkg->difference2 < min_difference)
921 min_difference = pkg->difference2;
923 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
924 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
926 dx_result = block_x1 * OVERSAMPLE - x_result;
927 dy_result = block_y1 * OVERSAMPLE - y_result;
928 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
929 //__LINE__, dx_result, dy_result, min_difference);
935 void MotionHVScan::scan_frame(VFrame *previous_frame,
936 VFrame *current_frame,
955 double rotation_center,
956 double rotation_range)
958 this->previous_frame_arg = previous_frame;
959 this->current_frame_arg = current_frame;
960 this->horizontal_only = horizontal_only;
961 this->vertical_only = vertical_only;
962 this->previous_frame = previous_frame_arg;
963 this->current_frame = current_frame_arg;
964 this->global_origin_x = global_origin_x;
965 this->global_origin_y = global_origin_y;
966 this->action_type = action_type;
967 this->do_motion = do_motion;
968 this->do_rotate = do_rotate;
969 this->rotation_center = rotation_center;
970 this->rotation_range = rotation_range;
972 //printf("MotionHVScan::scan_frame %d\n", __LINE__);
979 // starting level of detail
980 // TODO: base it on a table of resolutions
981 current_downsample = STARTING_DOWNSAMPLE;
985 int w = current_frame->get_w();
986 int h = current_frame->get_h();
988 // Initial search parameters
989 scan_w = global_range_w;
990 scan_h = global_range_h;
992 int block_w = global_block_w;
993 int block_h = global_block_h;
995 // printf("MotionHVScan::scan_frame %d %d %d %d %d %d %d %d %d\n",
1006 // Location of block in previous frame
1007 block_x1 = (int)(block_x - block_w / 2);
1008 block_y1 = (int)(block_y - block_h / 2);
1009 block_x2 = (int)(block_x + block_w / 2);
1010 block_y2 = (int)(block_y + block_h / 2);
1012 // Offset to location of previous block. This offset needn't be very accurate
1013 // since it's the offset of the previous image and current image we want.
1014 if(frame_type == MotionHVScan::TRACK_PREVIOUS)
1016 block_x1 += total_dx / OVERSAMPLE;
1017 block_y1 += total_dy / OVERSAMPLE;
1018 block_x2 += total_dx / OVERSAMPLE;
1019 block_y2 += total_dy / OVERSAMPLE;
1024 switch(tracking_type)
1027 case MotionHVScan::NO_CALCULATE:
1030 dr_result = rotation_center;
1034 case MotionHVScan::LOAD:
1036 // Load result from disk
1037 char string[BCTEXTLEN];
1042 sprintf(string, "%s%06d",
1045 //printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1046 FILE *input = fopen(string, "r");
1049 int temp = fscanf(input,
1054 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1058 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
1073 FILE *input = fopen(string, "r");
1076 int temp = fscanf(input, "%f", &dr_result);
1078 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1080 //dr_result += 0.25;
1091 // Scan from scratch
1099 if(!skip && test_match)
1101 if(previous_frame->data_matches(current_frame))
1103 printf("MotionHVScan::scan_frame: data matches. skipping.\n");
1106 dr_result = rotation_center;
1115 // Location of block in current frame
1116 int origin_offset_x = this->global_origin_x;
1117 int origin_offset_y = this->global_origin_y;
1118 int x_result = block_x1 + origin_offset_x;
1119 int y_result = block_y1 + origin_offset_y;
1120 double r_result = rotation_center;
1122 // printf("MotionHVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
1123 // block_x1 + block_w / 2,
1124 // block_y1 + block_h / 2,
1134 scan_x1 = x_result - scan_w / 2;
1135 scan_y1 = y_result - scan_h / 2;
1136 scan_x2 = x_result + scan_w / 2;
1137 scan_y2 = y_result + scan_h / 2;
1138 scan_angle1 = r_result - rotation_range;
1139 scan_angle2 = r_result + rotation_range;
1143 // Zero out requested values
1144 // if(horizontal_only)
1146 // scan_y1 = block_y1;
1147 // scan_y2 = block_y1 + 1;
1149 // if(vertical_only)
1151 // scan_x1 = block_x1;
1152 // scan_x2 = block_x1 + 1;
1155 // printf("MotionHVScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n",
1167 // Clamp the block coords before the scan so we get useful scan coords.
1181 // printf("MotionHVScan::scan_frame %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_result=%d y_result=%d\n",
1193 //if(y_result == 88) exit(0);
1196 // Give up if invalid coords.
1197 if(scan_y2 <= scan_y1 ||
1198 scan_x2 <= scan_x1 ||
1199 block_x2 <= block_x1 ||
1200 block_y2 <= block_y1)
1205 // For subpixel, the top row and left column are skipped
1209 subpixel_search(x_result, y_result);
1210 // printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n",
1212 // x_result / OVERSAMPLE,
1213 // y_result / OVERSAMPLE);
1220 pixel_search(x_result, y_result, r_result);
1221 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE);
1230 if(current_downsample <= 1)
1232 // Single pixel accuracy reached. Now do exhaustive subpixel search.
1233 if(action_type == MotionHVScan::STABILIZE ||
1234 action_type == MotionHVScan::TRACK ||
1235 action_type == MotionHVScan::NOTHING)
1237 x_result /= OVERSAMPLE;
1238 y_result /= OVERSAMPLE;
1239 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result);
1240 scan_w = SUBPIXEL_RANGE;
1241 scan_h = SUBPIXEL_RANGE;
1243 dr_result = rotation_center - r_result;
1248 // Fill in results and quit
1249 dx_result = block_x1 * OVERSAMPLE - x_result;
1250 dy_result = block_y1 * OVERSAMPLE - y_result;
1251 dr_result = rotation_center - r_result;
1256 // Reduce scan area and try again
1258 // scan_w = (scan_x2 - scan_x1) / 2;
1259 // scan_h = (scan_y2 - scan_y1) / 2;
1260 // need slightly more than 2x downsampling factor
1262 if(current_downsample * 3 < scan_w &&
1263 current_downsample * 3 < scan_h)
1265 scan_w = current_downsample * 3;
1266 scan_h = current_downsample * 3;
1269 if(angle_step * 1.5 < rotation_range)
1271 rotation_range = angle_step * 1.5;
1273 //printf("MotionHVScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range);
1275 current_downsample /= 2;
1277 // convert back to pixels
1278 x_result /= OVERSAMPLE;
1279 y_result /= OVERSAMPLE;
1291 // printf("MotionHVScan::scan_frame %d dx=%f dy=%f dr=%f\n",
1293 // (float)dx_result / OVERSAMPLE,
1294 // (float)dy_result / OVERSAMPLE,
1301 if(!skip && tracking_type == MotionHVScan::SAVE)
1303 char string[BCTEXTLEN];
1312 FILE *output = fopen(string, "w");
1323 printf("MotionHVScan::scan_frame %d: save motion failed\n", __LINE__);
1333 FILE *output = fopen(string, "w");
1336 fprintf(output, "%f\n", dr_result);
1341 printf("MotionHVScan::scan_frame %d save rotation failed\n", __LINE__);
1347 if(vertical_only) dx_result = 0;
1348 if(horizontal_only) dy_result = 0;
1350 // printf("MotionHVScan::scan_frame %d dx=%d dy=%d\n",
1353 // this->dy_result);
1374 #define ABS_DIFF(type, temp_type, multiplier, components) \
1376 temp_type result_temp = 0; \
1377 for(int i = 0; i < h; i++) \
1379 type *prev_row = (type*)prev_ptr; \
1380 type *current_row = (type*)current_ptr; \
1381 for(int j = 0; j < w; j++) \
1383 for(int k = 0; k < 3; k++) \
1385 temp_type difference; \
1386 difference = *prev_row++ - *current_row++; \
1387 difference *= difference; \
1388 result_temp += difference; \
1390 if(components == 4) \
1396 prev_ptr += row_bytes; \
1397 current_ptr += row_bytes; \
1399 result = (int64_t)(result_temp * multiplier); \
1402 int64_t MotionHVScan::abs_diff(unsigned char *prev_ptr,
1403 unsigned char *current_ptr,
1413 ABS_DIFF(unsigned char, int64_t, 1, 3)
1416 ABS_DIFF(unsigned char, int64_t, 1, 4)
1419 ABS_DIFF(float, double, 0x10000, 3)
1422 ABS_DIFF(float, double, 0x10000, 4)
1425 ABS_DIFF(unsigned char, int64_t, 1, 3)
1428 ABS_DIFF(unsigned char, int64_t, 1, 4)
1436 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1438 temp_type result_temp = 0; \
1439 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1440 temp_type y1_fraction = 0x100 - y2_fraction; \
1441 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1442 temp_type x1_fraction = 0x100 - x2_fraction; \
1443 for(int i = 0; i < h_sub; i++) \
1445 type *prev_row1 = (type*)prev_ptr; \
1446 type *prev_row2 = (type*)prev_ptr + components; \
1447 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1448 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1449 type *current_row = (type*)current_ptr; \
1450 for(int j = 0; j < w_sub; j++) \
1452 /* Scan each component */ \
1453 for(int k = 0; k < 3; k++) \
1455 temp_type difference; \
1456 temp_type prev_value = \
1457 (*prev_row1++ * x1_fraction * y1_fraction + \
1458 *prev_row2++ * x2_fraction * y1_fraction + \
1459 *prev_row3++ * x1_fraction * y2_fraction + \
1460 *prev_row4++ * x2_fraction * y2_fraction) / \
1462 temp_type current_value = *current_row++; \
1463 difference = prev_value - current_value; \
1464 difference *= difference; \
1465 result_temp += difference; \
1469 if(components == 4) \
1478 prev_ptr += row_bytes; \
1479 current_ptr += row_bytes; \
1481 result = (int64_t)(result_temp * multiplier); \
1487 int64_t MotionHVScan::abs_diff_sub(unsigned char *prev_ptr,
1488 unsigned char *current_ptr,
1503 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1506 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1509 ABS_DIFF_SUB(float, double, 0x10000, 3)
1512 ABS_DIFF_SUB(float, double, 0x10000, 4)
1515 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1518 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1526 #define VARIANCE(type, temp_type, multiplier, components) \
1528 temp_type average[3] = { 0 }; \
1529 temp_type variance[3] = { 0 }; \
1531 for(int i = 0; i < h; i++) \
1533 type *row = (type*)current_ptr + i * row_bytes; \
1534 for(int j = 0; j < w; j++) \
1536 for(int k = 0; k < 3; k++) \
1538 average[k] += row[k]; \
1540 row += components; \
1543 for(int k = 0; k < 3; k++) \
1545 average[k] /= w * h; \
1548 for(int i = 0; i < h; i++) \
1550 type *row = (type*)current_ptr + i * row_bytes; \
1551 for(int j = 0; j < w; j++) \
1553 for(int k = 0; k < 3; k++) \
1555 variance[k] += SQR(row[k] - average[k]); \
1557 row += components; \
1560 result = (double)multiplier * \
1561 sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \
1564 double MotionHVScan::calculate_variance(unsigned char *current_ptr,
1575 VARIANCE(unsigned char, int, 1, 3)
1578 VARIANCE(unsigned char, int, 1, 4)
1581 VARIANCE(float, double, 255, 3)
1584 VARIANCE(float, double, 255, 4)
1587 VARIANCE(unsigned char, int, 1, 3)
1590 VARIANCE(unsigned char, int, 1, 4)
1602 #define RANGE(type, temp_type, multiplier, components) \
1613 for(int i = 0; i < h; i++) \
1615 type *row = (type*)current_ptr + i * row_bytes; \
1616 for(int j = 0; j < w; j++) \
1618 for(int k = 0; k < 3; k++) \
1620 if(row[k] > max[k]) max[k] = row[k]; \
1621 if(row[k] < min[k]) min[k] = row[k]; \
1623 row += components; \
1627 for(int k = 0; k < 3; k++) \
1629 /* printf("MotionHVScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \
1630 if(max[k] - min[k] > result) result = max[k] - min[k]; \
1635 double MotionHVScan::calculate_range(unsigned char *current_ptr,
1646 RANGE(unsigned char, int, 1, 3)
1649 RANGE(unsigned char, int, 1, 4)
1652 RANGE(float, float, 255, 3)
1655 RANGE(float, float, 255, 4)
1658 RANGE(unsigned char, int, 1, 3)
1661 RANGE(unsigned char, int, 1, 4)
1670 //#define CLAMP_BLOCK
1672 // this truncates the scan area but not the macroblock unless the macro is defined
1673 void MotionHVScan::clamp_scan(int w,
1685 // printf("MotionHVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1700 // Limit size of scan area
1701 // Used for drawing vectors
1702 // scan is always out of range before block.
1706 int difference = -*scan_x1;
1707 *block_x1 += difference;
1715 int difference = -*scan_y1;
1716 *block_y1 += difference;
1723 int difference = *scan_x2 - w;
1725 *block_x2 -= difference;
1727 *scan_x2 -= difference;
1732 int difference = *scan_y2 - h;
1734 *block_y2 -= difference;
1736 *scan_y2 -= difference;
1739 CLAMP(*scan_x1, 0, w);
1740 CLAMP(*scan_y1, 0, h);
1741 CLAMP(*scan_x2, 0, w);
1742 CLAMP(*scan_y2, 0, h);
1746 // Limit range of upper left block coordinates
1747 // Used for motion tracking
1750 int difference = -*scan_x1;
1752 *block_x1 += difference;
1754 *scan_x2 += difference;
1760 int difference = -*scan_y1;
1762 *block_y1 += difference;
1764 *scan_y2 += difference;
1768 if(*scan_x2 - *block_x1 + *block_x2 > w)
1770 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1771 *scan_x2 -= difference;
1773 *block_x2 -= difference;
1777 if(*scan_y2 - *block_y1 + *block_y2 > h)
1779 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1780 *scan_y2 -= difference;
1782 *block_y2 -= difference;
1786 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1787 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1788 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1789 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1792 // Sanity checks which break the calculation but should never happen if the
1793 // center of the block is inside the frame.
1794 CLAMP(*block_x1, 0, w);
1795 CLAMP(*block_x2, 0, w);
1796 CLAMP(*block_y1, 0, h);
1797 CLAMP(*block_y2, 0, h);
1799 // printf("MotionHVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",