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])
605 rotated_current[i] = new VFrame();
606 rotated_current[i]->set_use_shm(0);
607 rotated_current[i]->reallocate(0,
612 downsampled_current_w + 1,
613 downsampled_current_h + 1,
614 current_frame_arg->get_color_model(),
616 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
622 rotater = new AffineEngine(get_total_clients(),
623 get_total_clients());
626 // get smallest viewport size required for the angle
627 double diag = hypot((block_x2 - block_x1) / current_downsample,
628 (block_y2 - block_y1) / current_downsample);
629 double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) +
630 TO_RAD(step_to_angle(i, r_result));
631 double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) +
632 TO_RAD(step_to_angle(i, r_result));
633 double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2)));
634 double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2)));
635 int center_x = (block_x1 + block_x2) / 2 / current_downsample;
636 int center_y = (block_y1 + block_y2) / 2 / current_downsample;
637 int x1 = center_x - max_horiz / 2;
638 int y1 = center_y - max_vert / 2;
639 int x2 = x1 + max_horiz;
640 int y2 = y1 + max_vert;
641 CLAMP(x1, 0, downsampled_current_w - 1);
642 CLAMP(y1, 0, downsampled_current_h - 1);
643 CLAMP(x2, 0, downsampled_current_w - 1);
644 CLAMP(y2, 0, downsampled_current_h - 1);
646 //printf("MotionHVScan::pixel_search %d %f %f %d %d\n",
647 //__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert);
648 rotater->set_in_viewport(x1,
652 rotater->set_out_viewport(x1,
657 // rotater->set_in_viewport(0,
659 // downsampled_current_w,
660 // downsampled_current_h);
661 // rotater->set_out_viewport(0,
663 // downsampled_current_w,
664 // downsampled_current_h);
666 rotater->set_in_pivot(center_x, center_y);
667 rotater->set_out_pivot(center_x, center_y);
669 rotater->rotate(rotated_current[i],
671 step_to_angle(i, r_result));
673 // rotated_current[i]->draw_rect(block_x1 / current_downsample,
674 // block_y1 / current_downsample,
675 // block_x2 / current_downsample,
676 // block_y2 / current_downsample);
677 // char string[BCTEXTLEN];
678 // sprintf(string, "/tmp/rotated%d", i);
679 // rotated_current[i]->write_png(string);
680 //downsampled_previous->write_png("/tmp/previous");
681 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
690 // printf("MotionHVScan::pixel_search %d block x=%d y=%d w=%d h=%d\n",
692 // block_x1 / current_downsample,
693 // block_y1 / current_downsample,
694 // block_w / current_downsample,
695 // block_h / current_downsample);
704 // Test only translation of the middle rotated frame
706 total_steps = x_steps * y_steps;
707 set_package_count(total_steps);
715 // Get least difference
716 int64_t min_difference = -1;
718 double stddev_table[get_total_packages()];
720 for(int i = 0; i < get_total_packages(); i++)
722 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
725 double stddev = sqrt(pkg->difference1) /
726 (block_w / current_downsample) /
727 (block_h / current_downsample) /
729 // printf("MotionHVScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n",
731 // current_downsample,
734 // sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */);
736 // printf("MotionHVScan::pixel_search %d range1=%f stddev=%f\n",
741 stddev_table[i] = stddev;
742 #endif // STDDEV_TEST
744 if(pkg->difference1 < min_difference || i == 0)
746 min_difference = pkg->difference1;
747 x_result = pkg->search_x * current_downsample * OVERSAMPLE;
748 y_result = pkg->search_y * current_downsample * OVERSAMPLE;
750 // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
752 // block_x1 * OVERSAMPLE - x_result,
753 // block_y1 * OVERSAMPLE - y_result,
755 // pkg->difference1);
762 qsort(stddev_table, get_total_packages(), sizeof(double), compare);
765 // reject motion vector if not similar enough
766 // if(stddev_table[0] > 0.2)
770 // printf("MotionHVScan::pixel_search %d stddev fail min_stddev=%f\n",
780 printf("MotionHVScan::pixel_search %d\n", __LINE__);
781 for(int i = 0; i < get_total_packages(); i++)
783 printf("%f\n", stddev_table[i]);
787 // reject motion vector if not a sigmoid curve
788 // TODO: use linear interpolation
790 int step = get_total_packages() / steps;
792 for(int i = 0; i < steps; i++)
794 int start = get_total_packages() * i / steps;
795 int end = get_total_packages() * (i + 1) / steps;
796 end = MIN(end, get_total_packages() - 1);
797 curve[i] = stddev_table[end] - stddev_table[start];
801 // if(curve[0] < (curve[1] * 1.01) ||
802 // curve[2] < (curve[1] * 1.01) ||
803 // curve[0] < (curve[2] * 0.75))
804 // if(curve[0] < curve[1])
808 // printf("MotionHVScan::pixel_search %d curve fail %f %f\n",
819 printf("MotionHVScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n",
827 #endif // STDDEV_TEST
836 total_steps = angle_steps;
837 scan_x1 = x_result / OVERSAMPLE;
838 scan_y1 = y_result / OVERSAMPLE;
839 set_package_count(total_steps);
845 double prev_r_result = r_result;
846 for(int i = 0; i < get_total_packages(); i++)
848 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
850 // 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",
854 // pkg->search_angle_step,
858 // pkg->difference2);
859 if(pkg->difference1 < min_difference || i == 0)
861 min_difference = pkg->difference1;
862 r_result = step_to_angle(i, prev_r_result);
864 // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
866 // block_x1 * OVERSAMPLE - x_result,
867 // block_y1 * OVERSAMPLE - y_result,
869 // pkg->difference1);
875 // printf("MotionHVScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n",
877 // current_downsample,
878 // (float)x_result / OVERSAMPLE,
879 // (float)y_result / OVERSAMPLE,
885 // subpixel motion search
886 void MotionHVScan::subpixel_search(int &x_result, int &y_result)
889 previous_frame = previous_frame_arg;
890 current_frame = current_frame_arg;
892 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
893 // Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square
894 total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE);
896 // These aren't used in subpixel
897 x_steps = OVERSAMPLE * SUBPIXEL_RANGE;
898 y_steps = OVERSAMPLE * SUBPIXEL_RANGE;
901 set_package_count(this->total_steps);
904 // Get least difference
905 int64_t min_difference = -1;
906 for(int i = 0; i < get_total_packages(); i++)
908 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
909 //printf("MotionHVScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
910 //__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
911 if(pkg->difference1 < min_difference || min_difference == -1)
913 min_difference = pkg->difference1;
915 // The sub coords are 1 pixel up & left of the block coords
916 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
917 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
921 dx_result = block_x1 * OVERSAMPLE - x_result;
922 dy_result = block_y1 * OVERSAMPLE - y_result;
923 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
924 //__LINE__, dx_result, dy_result, min_difference);
927 if(pkg->difference2 < min_difference)
929 min_difference = pkg->difference2;
931 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
932 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
934 dx_result = block_x1 * OVERSAMPLE - x_result;
935 dy_result = block_y1 * OVERSAMPLE - y_result;
936 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
937 //__LINE__, dx_result, dy_result, min_difference);
943 void MotionHVScan::scan_frame(VFrame *previous_frame,
944 VFrame *current_frame,
963 double rotation_center,
964 double rotation_range)
966 this->previous_frame_arg = previous_frame;
967 this->current_frame_arg = current_frame;
968 this->horizontal_only = horizontal_only;
969 this->vertical_only = vertical_only;
970 this->previous_frame = previous_frame_arg;
971 this->current_frame = current_frame_arg;
972 this->global_origin_x = global_origin_x;
973 this->global_origin_y = global_origin_y;
974 this->action_type = action_type;
975 this->do_motion = do_motion;
976 this->do_rotate = do_rotate;
977 this->rotation_center = rotation_center;
978 this->rotation_range = rotation_range;
980 //printf("MotionHVScan::scan_frame %d\n", __LINE__);
987 // starting level of detail
988 // TODO: base it on a table of resolutions
989 current_downsample = STARTING_DOWNSAMPLE;
993 int w = current_frame->get_w();
994 int h = current_frame->get_h();
996 // Initial search parameters
997 scan_w = global_range_w;
998 scan_h = global_range_h;
1000 int block_w = global_block_w;
1001 int block_h = global_block_h;
1003 // printf("MotionHVScan::scan_frame %d %d %d %d %d %d %d %d %d\n",
1014 // Location of block in previous frame
1015 block_x1 = (int)(block_x - block_w / 2);
1016 block_y1 = (int)(block_y - block_h / 2);
1017 block_x2 = (int)(block_x + block_w / 2);
1018 block_y2 = (int)(block_y + block_h / 2);
1020 // Offset to location of previous block. This offset needn't be very accurate
1021 // since it's the offset of the previous image and current image we want.
1022 if(frame_type == MotionHVScan::TRACK_PREVIOUS)
1024 block_x1 += total_dx / OVERSAMPLE;
1025 block_y1 += total_dy / OVERSAMPLE;
1026 block_x2 += total_dx / OVERSAMPLE;
1027 block_y2 += total_dy / OVERSAMPLE;
1032 switch(tracking_type)
1035 case MotionHVScan::NO_CALCULATE:
1038 dr_result = rotation_center;
1042 case MotionHVScan::LOAD:
1044 // Load result from disk
1045 char string[BCTEXTLEN];
1050 sprintf(string, "%s%06d",
1053 //printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1054 FILE *input = fopen(string, "r");
1057 int temp = fscanf(input,
1062 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1066 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
1081 FILE *input = fopen(string, "r");
1084 int temp = fscanf(input, "%f", &dr_result);
1086 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1088 //dr_result += 0.25;
1099 // Scan from scratch
1107 if(!skip && test_match)
1109 if(previous_frame->data_matches(current_frame))
1111 printf("MotionHVScan::scan_frame: data matches. skipping.\n");
1114 dr_result = rotation_center;
1123 // Location of block in current frame
1124 int origin_offset_x = this->global_origin_x;
1125 int origin_offset_y = this->global_origin_y;
1126 int x_result = block_x1 + origin_offset_x;
1127 int y_result = block_y1 + origin_offset_y;
1128 double r_result = rotation_center;
1130 // printf("MotionHVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
1131 // block_x1 + block_w / 2,
1132 // block_y1 + block_h / 2,
1142 scan_x1 = x_result - scan_w / 2;
1143 scan_y1 = y_result - scan_h / 2;
1144 scan_x2 = x_result + scan_w / 2;
1145 scan_y2 = y_result + scan_h / 2;
1146 scan_angle1 = r_result - rotation_range;
1147 scan_angle2 = r_result + rotation_range;
1151 // Zero out requested values
1152 // if(horizontal_only)
1154 // scan_y1 = block_y1;
1155 // scan_y2 = block_y1 + 1;
1157 // if(vertical_only)
1159 // scan_x1 = block_x1;
1160 // scan_x2 = block_x1 + 1;
1163 // 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",
1175 // Clamp the block coords before the scan so we get useful scan coords.
1189 // 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",
1201 //if(y_result == 88) exit(0);
1204 // Give up if invalid coords.
1205 if(scan_y2 <= scan_y1 ||
1206 scan_x2 <= scan_x1 ||
1207 block_x2 <= block_x1 ||
1208 block_y2 <= block_y1)
1213 // For subpixel, the top row and left column are skipped
1217 subpixel_search(x_result, y_result);
1218 // printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n",
1220 // x_result / OVERSAMPLE,
1221 // y_result / OVERSAMPLE);
1228 pixel_search(x_result, y_result, r_result);
1229 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE);
1238 if(current_downsample <= 1)
1240 // Single pixel accuracy reached. Now do exhaustive subpixel search.
1241 if(action_type == MotionHVScan::STABILIZE ||
1242 action_type == MotionHVScan::TRACK ||
1243 action_type == MotionHVScan::NOTHING)
1245 x_result /= OVERSAMPLE;
1246 y_result /= OVERSAMPLE;
1247 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result);
1248 scan_w = SUBPIXEL_RANGE;
1249 scan_h = SUBPIXEL_RANGE;
1251 dr_result = rotation_center - r_result;
1256 // Fill in results and quit
1257 dx_result = block_x1 * OVERSAMPLE - x_result;
1258 dy_result = block_y1 * OVERSAMPLE - y_result;
1259 dr_result = rotation_center - r_result;
1264 // Reduce scan area and try again
1266 // scan_w = (scan_x2 - scan_x1) / 2;
1267 // scan_h = (scan_y2 - scan_y1) / 2;
1268 // need slightly more than 2x downsampling factor
1270 if(current_downsample * 3 < scan_w &&
1271 current_downsample * 3 < scan_h)
1273 scan_w = current_downsample * 3;
1274 scan_h = current_downsample * 3;
1277 if(angle_step * 1.5 < rotation_range)
1279 rotation_range = angle_step * 1.5;
1281 //printf("MotionHVScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range);
1283 current_downsample /= 2;
1285 // convert back to pixels
1286 x_result /= OVERSAMPLE;
1287 y_result /= OVERSAMPLE;
1299 // printf("MotionHVScan::scan_frame %d dx=%f dy=%f dr=%f\n",
1301 // (float)dx_result / OVERSAMPLE,
1302 // (float)dy_result / OVERSAMPLE,
1309 if(!skip && tracking_type == MotionHVScan::SAVE)
1311 char string[BCTEXTLEN];
1320 FILE *output = fopen(string, "w");
1331 printf("MotionHVScan::scan_frame %d: save motion failed\n", __LINE__);
1341 FILE *output = fopen(string, "w");
1344 fprintf(output, "%f\n", dr_result);
1349 printf("MotionHVScan::scan_frame %d save rotation failed\n", __LINE__);
1355 if(vertical_only) dx_result = 0;
1356 if(horizontal_only) dy_result = 0;
1358 // printf("MotionHVScan::scan_frame %d dx=%d dy=%d\n",
1361 // this->dy_result);
1382 #define ABS_DIFF(type, temp_type, multiplier, components) \
1384 temp_type result_temp = 0; \
1385 for(int i = 0; i < h; i++) \
1387 type *prev_row = (type*)prev_ptr; \
1388 type *current_row = (type*)current_ptr; \
1389 for(int j = 0; j < w; j++) \
1391 for(int k = 0; k < 3; k++) \
1393 temp_type difference; \
1394 difference = *prev_row++ - *current_row++; \
1395 difference *= difference; \
1396 result_temp += difference; \
1398 if(components == 4) \
1404 prev_ptr += row_bytes; \
1405 current_ptr += row_bytes; \
1407 result = (int64_t)(result_temp * multiplier); \
1410 int64_t MotionHVScan::abs_diff(unsigned char *prev_ptr,
1411 unsigned char *current_ptr,
1421 ABS_DIFF(unsigned char, int64_t, 1, 3)
1424 ABS_DIFF(unsigned char, int64_t, 1, 4)
1427 ABS_DIFF(float, double, 0x10000, 3)
1430 ABS_DIFF(float, double, 0x10000, 4)
1433 ABS_DIFF(unsigned char, int64_t, 1, 3)
1436 ABS_DIFF(unsigned char, int64_t, 1, 4)
1444 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1446 temp_type result_temp = 0; \
1447 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1448 temp_type y1_fraction = 0x100 - y2_fraction; \
1449 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1450 temp_type x1_fraction = 0x100 - x2_fraction; \
1451 for(int i = 0; i < h_sub; i++) \
1453 type *prev_row1 = (type*)prev_ptr; \
1454 type *prev_row2 = (type*)prev_ptr + components; \
1455 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1456 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1457 type *current_row = (type*)current_ptr; \
1458 for(int j = 0; j < w_sub; j++) \
1460 /* Scan each component */ \
1461 for(int k = 0; k < 3; k++) \
1463 temp_type difference; \
1464 temp_type prev_value = \
1465 (*prev_row1++ * x1_fraction * y1_fraction + \
1466 *prev_row2++ * x2_fraction * y1_fraction + \
1467 *prev_row3++ * x1_fraction * y2_fraction + \
1468 *prev_row4++ * x2_fraction * y2_fraction) / \
1470 temp_type current_value = *current_row++; \
1471 difference = prev_value - current_value; \
1472 difference *= difference; \
1473 result_temp += difference; \
1477 if(components == 4) \
1486 prev_ptr += row_bytes; \
1487 current_ptr += row_bytes; \
1489 result = (int64_t)(result_temp * multiplier); \
1495 int64_t MotionHVScan::abs_diff_sub(unsigned char *prev_ptr,
1496 unsigned char *current_ptr,
1511 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1514 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1517 ABS_DIFF_SUB(float, double, 0x10000, 3)
1520 ABS_DIFF_SUB(float, double, 0x10000, 4)
1523 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1526 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1534 #define VARIANCE(type, temp_type, multiplier, components) \
1536 temp_type average[3] = { 0 }; \
1537 temp_type variance[3] = { 0 }; \
1539 for(int i = 0; i < h; i++) \
1541 type *row = (type*)current_ptr + i * row_bytes; \
1542 for(int j = 0; j < w; j++) \
1544 for(int k = 0; k < 3; k++) \
1546 average[k] += row[k]; \
1548 row += components; \
1551 for(int k = 0; k < 3; k++) \
1553 average[k] /= w * h; \
1556 for(int i = 0; i < h; i++) \
1558 type *row = (type*)current_ptr + i * row_bytes; \
1559 for(int j = 0; j < w; j++) \
1561 for(int k = 0; k < 3; k++) \
1563 variance[k] += SQR(row[k] - average[k]); \
1565 row += components; \
1568 result = (double)multiplier * \
1569 sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \
1572 double MotionHVScan::calculate_variance(unsigned char *current_ptr,
1583 VARIANCE(unsigned char, int, 1, 3)
1586 VARIANCE(unsigned char, int, 1, 4)
1589 VARIANCE(float, double, 255, 3)
1592 VARIANCE(float, double, 255, 4)
1595 VARIANCE(unsigned char, int, 1, 3)
1598 VARIANCE(unsigned char, int, 1, 4)
1610 #define RANGE(type, temp_type, multiplier, components) \
1621 for(int i = 0; i < h; i++) \
1623 type *row = (type*)current_ptr + i * row_bytes; \
1624 for(int j = 0; j < w; j++) \
1626 for(int k = 0; k < 3; k++) \
1628 if(row[k] > max[k]) max[k] = row[k]; \
1629 if(row[k] < min[k]) min[k] = row[k]; \
1631 row += components; \
1635 for(int k = 0; k < 3; k++) \
1637 /* printf("MotionHVScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \
1638 if(max[k] - min[k] > result) result = max[k] - min[k]; \
1643 double MotionHVScan::calculate_range(unsigned char *current_ptr,
1654 RANGE(unsigned char, int, 1, 3)
1657 RANGE(unsigned char, int, 1, 4)
1660 RANGE(float, float, 255, 3)
1663 RANGE(float, float, 255, 4)
1666 RANGE(unsigned char, int, 1, 3)
1669 RANGE(unsigned char, int, 1, 4)
1678 //#define CLAMP_BLOCK
1680 // this truncates the scan area but not the macroblock unless the macro is defined
1681 void MotionHVScan::clamp_scan(int w,
1693 // printf("MotionHVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1708 // Limit size of scan area
1709 // Used for drawing vectors
1710 // scan is always out of range before block.
1714 int difference = -*scan_x1;
1715 *block_x1 += difference;
1723 int difference = -*scan_y1;
1724 *block_y1 += difference;
1731 int difference = *scan_x2 - w;
1733 *block_x2 -= difference;
1735 *scan_x2 -= difference;
1740 int difference = *scan_y2 - h;
1742 *block_y2 -= difference;
1744 *scan_y2 -= difference;
1747 CLAMP(*scan_x1, 0, w);
1748 CLAMP(*scan_y1, 0, h);
1749 CLAMP(*scan_x2, 0, w);
1750 CLAMP(*scan_y2, 0, h);
1754 // Limit range of upper left block coordinates
1755 // Used for motion tracking
1758 int difference = -*scan_x1;
1760 *block_x1 += difference;
1762 *scan_x2 += difference;
1768 int difference = -*scan_y1;
1770 *block_y1 += difference;
1772 *scan_y2 += difference;
1776 if(*scan_x2 - *block_x1 + *block_x2 > w)
1778 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1779 *scan_x2 -= difference;
1781 *block_x2 -= difference;
1785 if(*scan_y2 - *block_y1 + *block_y2 > h)
1787 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1788 *scan_y2 -= difference;
1790 *block_y2 -= difference;
1794 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1795 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1796 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1797 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1800 // Sanity checks which break the calculation but should never happen if the
1801 // center of the block is inside the frame.
1802 CLAMP(*block_x1, 0, w);
1803 CLAMP(*block_x2, 0, w);
1804 CLAMP(*block_y1, 0, h);
1805 CLAMP(*block_y2, 0, h);
1807 // printf("MotionHVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",