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 "motionscan-hv.h"
34 // The module which does the actual scanning
36 // starting level of detail
37 #define STARTING_DOWNSAMPLE 16
38 // minimum size in each level of detail
39 #define MIN_DOWNSAMPLED_SIZE 16
41 #define MIN_DOWNSAMPLED_SCAN 4
42 // scan range for subpixel mode
43 #define SUBPIXEL_RANGE 4
45 MotionHVScanPackage::MotionHVScanPackage()
56 MotionHVScanUnit::MotionHVScanUnit(MotionHVScan *server)
59 this->server = server;
62 MotionHVScanUnit::~MotionHVScanUnit()
67 void MotionHVScanUnit::single_pixel(MotionHVScanPackage *pkg)
69 //int w = server->current_frame->get_w();
70 //int h = server->current_frame->get_h();
71 int color_model = server->current_frame->get_color_model();
72 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
73 int row_bytes = server->current_frame->get_bytes_per_line();
75 // 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",
86 // Pointers to first pixel in each block
87 unsigned char *prev_ptr = server->previous_frame->get_rows()[
89 pkg->search_x * pixel_size;
90 unsigned char *current_ptr = 0;
94 current_ptr = server->rotated_current[pkg->angle_step]->get_rows()[
96 pkg->block_x1 * pixel_size;
100 current_ptr = server->current_frame->get_rows()[
102 pkg->block_x1 * pixel_size;
106 pkg->difference1 = MotionHVScan::abs_diff(prev_ptr,
109 pkg->block_x2 - pkg->block_x1,
110 pkg->block_y2 - pkg->block_y1,
113 // printf("MotionHVScanUnit::process_package %d angle_step=%d diff=%d\n",
116 // pkg->difference1);
117 // printf("MotionHVScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n",
118 // __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1);
121 void MotionHVScanUnit::subpixel(MotionHVScanPackage *pkg)
124 //int w = server->current_frame->get_w();
125 //int h = server->current_frame->get_h();
126 int color_model = server->current_frame->get_color_model();
127 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
128 int row_bytes = server->current_frame->get_bytes_per_line();
129 unsigned char *prev_ptr = server->previous_frame->get_rows()[
131 pkg->search_x * pixel_size;
133 unsigned char *current_ptr = server->current_frame->get_rows()[
135 pkg->block_x1 * pixel_size;
137 // With subpixel, there are two ways to compare each position, one by shifting
138 // the previous frame and two by shifting the current frame.
139 pkg->difference1 = MotionHVScan::abs_diff_sub(prev_ptr,
142 pkg->block_x2 - pkg->block_x1,
143 pkg->block_y2 - pkg->block_y1,
147 pkg->difference2 = MotionHVScan::abs_diff_sub(current_ptr,
150 pkg->block_x2 - pkg->block_x1,
151 pkg->block_y2 - pkg->block_y1,
155 // printf("MotionHVScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
161 // pkg->difference2);
164 void MotionHVScanUnit::process_package(LoadPackage *package)
166 MotionHVScanPackage *pkg = (MotionHVScanPackage*)package;
170 if(!server->subpixel)
202 MotionHVScan::MotionHVScan(int total_clients,
207 total_clients, total_packages
211 downsampled_previous = 0;
212 downsampled_current = 0;
217 MotionHVScan::~MotionHVScan()
219 delete downsampled_previous;
220 delete downsampled_current;
223 for(int i = 0; i < total_rotated; i++)
225 delete rotated_current[i];
228 delete [] rotated_current;
234 void MotionHVScan::init_packages()
236 // Set package coords
237 // Total range of positions to scan with downsampling
238 int downsampled_scan_x1 = scan_x1 / current_downsample;
239 //int downsampled_scan_x2 = scan_x2 / current_downsample;
240 int downsampled_scan_y1 = scan_y1 / current_downsample;
241 //int downsampled_scan_y2 = scan_y2 / current_downsample;
242 int downsampled_block_x1 = block_x1 / current_downsample;
243 int downsampled_block_x2 = block_x2 / current_downsample;
244 int downsampled_block_y1 = block_y1 / current_downsample;
245 int downsampled_block_y2 = block_y2 / current_downsample;
248 //printf("MotionHVScan::init_packages %d %d\n", __LINE__, get_total_packages());
249 // printf("MotionHVScan::init_packages %d current_downsample=%d scan_x1=%d scan_x2=%d block_x1=%d block_x2=%d\n",
251 // current_downsample,
252 // downsampled_scan_x1,
253 // downsampled_scan_x2,
254 // downsampled_block_x1,
255 // downsampled_block_x2);
256 // if(current_downsample == 8 && downsampled_scan_x1 == 47)
258 // downsampled_previous->write_png("/tmp/previous");
259 // downsampled_current->write_png("/tmp/current");
262 for(int i = 0; i < get_total_packages(); i++)
264 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
266 pkg->block_x1 = downsampled_block_x1;
267 pkg->block_x2 = downsampled_block_x2;
268 pkg->block_y1 = downsampled_block_y1;
269 pkg->block_y2 = downsampled_block_y2;
270 pkg->difference1 = 0;
271 pkg->difference2 = 0;
281 pkg->search_x = scan_x1 / current_downsample;
282 pkg->search_y = scan_y1 / current_downsample;
288 int current_x_step = (i % x_steps);
289 int current_y_step = (i / x_steps);
291 //printf("MotionHVScan::init_packages %d i=%d x_step=%d y_step=%d angle_step=%d\n",
292 //__LINE__, i, current_x_step, current_y_step, current_angle_step);
293 pkg->search_x = downsampled_scan_x1 + current_x_step *
294 (scan_x2 - scan_x1) / current_downsample / x_steps;
295 pkg->search_y = downsampled_scan_y1 + current_y_step *
296 (scan_y2 - scan_y1) / current_downsample / y_steps;
300 pkg->angle_step = angle_steps / 2;
313 pkg->sub_x = i % (OVERSAMPLE * SUBPIXEL_RANGE);
314 pkg->sub_y = i / (OVERSAMPLE * SUBPIXEL_RANGE);
316 // if(horizontal_only)
326 pkg->search_x = scan_x1 + pkg->sub_x / OVERSAMPLE + 1;
327 pkg->search_y = scan_y1 + pkg->sub_y / OVERSAMPLE + 1;
328 pkg->sub_x %= OVERSAMPLE;
329 pkg->sub_y %= OVERSAMPLE;
333 // printf("MotionHVScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n",
342 // printf("MotionHVScan::init_packages %d %d,%d %d,%d %d,%d\n",
353 LoadClient* MotionHVScan::new_client()
355 return new MotionHVScanUnit(this);
358 LoadPackage* MotionHVScan::new_package()
360 return new MotionHVScanPackage;
364 void MotionHVScan::set_test_match(int value)
366 this->test_match = value;
372 #define DOWNSAMPLE(type, temp_type, components, max) \
378 type **in_rows = (type**)src->get_rows(); \
379 type **out_rows = (type**)dst->get_rows(); \
381 for(int i = 0; i < h; i += downsample) \
383 int y1 = MAX(i, 0); \
384 int y2 = MIN(i + downsample, h); \
391 int x1 = MAX(j, 0); \
392 int x2 = MIN(j + downsample, w); \
394 temp_type scale = (x2 - x1) * (y2 - y1); \
395 if(x2 > x1 && y2 > y1) \
398 /* Read in values */ \
402 if(components == 4) a = 0; \
404 for(int k = y1; k < y2; k++) \
406 type *row = in_rows[k] + x1 * components; \
407 for(int l = x1; l < x2; l++) \
412 if(components == 4) a += *row++; \
416 /* Write average */ \
420 if(components == 4) a /= scale; \
422 type *row = out_rows[y1 / downsample] + \
423 x1 / downsample * components; \
427 if(components == 4) *row++ = a; \
430 /*printf("DOWNSAMPLE 3 %d\n", i);*/ \
437 void MotionHVScan::downsample_frame(VFrame *dst,
441 int h = src->get_h();
442 int w = src->get_w();
445 //printf("downsample=%d w=%d h=%d dst=%d %d\n", downsample, w, h, dst->get_w(), dst->get_h());
446 switch(src->get_color_model())
449 DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
452 DOWNSAMPLE(float, float, 3, 1.0)
455 DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
458 DOWNSAMPLE(float, float, 4, 1.0)
461 DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
464 DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
470 double MotionHVScan::step_to_angle(int step, double center)
472 if(step < angle_steps / 2)
474 return center - angle_step * (angle_steps / 2 - step);
477 if(step > angle_steps / 2)
479 return center + angle_step * (step - angle_steps / 2);
488 static int compare(const void *p1, const void *p2)
490 double value1 = *(double*)p1;
491 double value2 = *(double*)p2;
493 //printf("compare %d value1=%f value2=%f\n", __LINE__, value1, value2);
494 return value1 > value2;
498 // reject vectors based on content. It's the reason Goog can't stabilize timelapses.
499 //#define STDDEV_TEST
501 // pixel accurate motion search
502 void MotionHVScan::pixel_search(int &x_result, int &y_result, double &r_result)
504 // reduce level of detail until enough steps
505 while(current_downsample > 1 &&
506 ((block_x2 - block_x1) / current_downsample < MIN_DOWNSAMPLED_SIZE ||
507 (block_y2 - block_y1) / current_downsample < MIN_DOWNSAMPLED_SIZE
509 (scan_x2 - scan_x1) / current_downsample < MIN_DOWNSAMPLED_SCAN ||
510 (scan_y2 - scan_y1) / current_downsample < MIN_DOWNSAMPLED_SCAN
513 current_downsample /= 2;
518 // create downsampled images.
519 // Need to keep entire frame to search for rotation.
520 int downsampled_prev_w = previous_frame_arg->get_w() / current_downsample;
521 int downsampled_prev_h = previous_frame_arg->get_h() / current_downsample;
522 int downsampled_current_w = current_frame_arg->get_w() / current_downsample;
523 int downsampled_current_h = current_frame_arg->get_h() / current_downsample;
525 // printf("MotionHVScan::pixel_search %d current_downsample=%d current_frame_arg->get_w()=%d downsampled_current_w=%d\n",
527 // current_downsample,
528 // current_frame_arg->get_w(),
529 // downsampled_current_w);
531 x_steps = (scan_x2 - scan_x1) / current_downsample;
532 y_steps = (scan_y2 - scan_y1) / current_downsample;
535 double test_angle1 = atan2((double)downsampled_current_h / 2 - 1, (double)downsampled_current_w / 2);
536 double test_angle2 = atan2((double)downsampled_current_h / 2, (double)downsampled_current_w / 2 - 1);
539 angle_step = 360.0f * fabs(test_angle1 - test_angle2) / 2 / M_PI;
541 // printf("MotionHVScan::pixel_search %d test_angle1=%f test_angle2=%f angle_step=%f\n",
543 // 360.0f * test_angle1 / 2 / M_PI,
544 // 360.0f * test_angle2 / 2 / M_PI,
548 if(do_rotate && angle_step < rotation_range)
550 angle_steps = 1 + (int)((scan_angle2 - scan_angle1) / angle_step + 0.5);
558 if(current_downsample > 1)
560 if(!downsampled_previous ||
561 downsampled_previous->get_w() != downsampled_prev_w ||
562 downsampled_previous->get_h() != downsampled_prev_h)
564 delete downsampled_previous;
565 downsampled_previous = new VFrame();
566 downsampled_previous->set_use_shm(0);
567 downsampled_previous->reallocate(0,
572 downsampled_prev_w + 1,
573 downsampled_prev_h + 1,
574 previous_frame_arg->get_color_model(),
578 if(!downsampled_current ||
579 downsampled_current->get_w() != downsampled_current_w ||
580 downsampled_current->get_h() != downsampled_current_h)
582 delete downsampled_current;
583 downsampled_current = new VFrame();
584 downsampled_current->set_use_shm(0);
585 downsampled_current->reallocate(0,
590 downsampled_current_w + 1,
591 downsampled_current_h + 1,
592 current_frame_arg->get_color_model(),
597 downsample_frame(downsampled_previous,
600 downsample_frame(downsampled_current,
603 previous_frame = downsampled_previous;
604 current_frame = downsampled_current;
609 previous_frame = previous_frame_arg;
610 current_frame = current_frame_arg;
615 // printf("MotionHVScan::pixel_search %d x_steps=%d y_steps=%d angle_steps=%d total_steps=%d\n",
624 // test variance of constant macroblock
625 int color_model = current_frame->get_color_model();
626 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
627 int row_bytes = current_frame->get_bytes_per_line();
628 int block_w = block_x2 - block_x1;
629 int block_h = block_y2 - block_y1;
631 unsigned char *current_ptr =
632 current_frame->get_rows()[block_y1 / current_downsample] +
633 (block_x1 / current_downsample) * pixel_size;
634 unsigned char *previous_ptr =
635 previous_frame->get_rows()[scan_y1 / current_downsample] +
636 (scan_x1 / current_downsample) * pixel_size;
640 // test detail in prev & current frame
641 double range1 = calculate_range(current_ptr,
643 block_w / current_downsample,
644 block_h / current_downsample,
649 printf("MotionHVScan::pixel_search %d range fail range1=%f\n", __LINE__, range1);
654 double range2 = calculate_range(previous_ptr,
656 block_w / current_downsample,
657 block_h / current_downsample,
662 printf("MotionHVScan::pixel_search %d range fail range2=%f\n", __LINE__, range2);
668 // create rotated images
669 if(rotated_current &&
670 (total_rotated != angle_steps ||
671 rotated_current[0]->get_w() != downsampled_current_w ||
672 rotated_current[0]->get_h() != downsampled_current_h))
674 for(int i = 0; i < total_rotated; i++)
676 delete rotated_current[i];
679 delete [] rotated_current;
686 total_rotated = angle_steps;
691 rotated_current = new VFrame*[total_rotated];
692 bzero(rotated_current, sizeof(VFrame*) * total_rotated);
695 // printf("MotionHVScan::pixel_search %d total_rotated=%d w=%d h=%d block_w=%d block_h=%d\n",
698 // downsampled_current_w,
699 // downsampled_current_h,
700 // (block_x2 - block_x1) / current_downsample,
701 // (block_y2 - block_y1) / current_downsample);
702 for(int i = 0; i < angle_steps; i++)
705 // printf("MotionHVScan::pixel_search %d w=%d h=%d x=%d y=%d angle=%f\n",
707 // downsampled_current_w,
708 // downsampled_current_h,
709 // (block_x1 + block_x2) / 2 / current_downsample,
710 // (block_y1 + block_y2) / 2 / current_downsample,
711 // step_to_angle(i, r_result));
713 // printf("MotionHVScan::pixel_search %d i=%d rotated_current[i]=%p\n",
716 // rotated_current[i]);
717 if(!rotated_current[i])
719 rotated_current[i] = new VFrame();
720 rotated_current[i]->set_use_shm(0);
721 rotated_current[i]->reallocate(0,
726 downsampled_current_w + 1,
727 downsampled_current_h + 1,
728 current_frame_arg->get_color_model(),
730 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
736 rotater = new AffineEngine(get_total_clients(),
737 get_total_clients());
740 // get smallest viewport size required for the angle
741 double diag = hypot((block_x2 - block_x1) / current_downsample,
742 (block_y2 - block_y1) / current_downsample);
743 double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) +
744 TO_RAD(step_to_angle(i, r_result));
745 double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) +
746 TO_RAD(step_to_angle(i, r_result));
747 double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2)));
748 double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2)));
749 int center_x = (block_x1 + block_x2) / 2 / current_downsample;
750 int center_y = (block_y1 + block_y2) / 2 / current_downsample;
751 int x1 = center_x - max_horiz / 2;
752 int y1 = center_y - max_vert / 2;
753 int x2 = x1 + max_horiz;
754 int y2 = y1 + max_vert;
755 CLAMP(x1, 0, downsampled_current_w - 1);
756 CLAMP(y1, 0, downsampled_current_h - 1);
757 CLAMP(x2, 0, downsampled_current_w - 1);
758 CLAMP(y2, 0, downsampled_current_h - 1);
760 //printf("MotionHVScan::pixel_search %d %f %f %d %d\n",
761 //__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert);
762 rotater->set_in_viewport(x1,
766 rotater->set_out_viewport(x1,
771 // rotater->set_in_viewport(0,
773 // downsampled_current_w,
774 // downsampled_current_h);
775 // rotater->set_out_viewport(0,
777 // downsampled_current_w,
778 // downsampled_current_h);
780 rotater->set_in_pivot(center_x, center_y);
781 rotater->set_out_pivot(center_x, center_y);
783 rotater->rotate(rotated_current[i],
785 step_to_angle(i, r_result));
787 // rotated_current[i]->draw_rect(block_x1 / current_downsample,
788 // block_y1 / current_downsample,
789 // block_x2 / current_downsample,
790 // block_y2 / current_downsample);
791 // char string[BCTEXTLEN];
792 // sprintf(string, "/tmp/rotated%d", i);
793 // rotated_current[i]->write_png(string);
794 //downsampled_previous->write_png("/tmp/previous");
795 //printf("MotionHVScan::pixel_search %d\n", __LINE__);
804 // printf("MotionHVScan::pixel_search %d block x=%d y=%d w=%d h=%d\n",
806 // block_x1 / current_downsample,
807 // block_y1 / current_downsample,
808 // block_w / current_downsample,
809 // block_h / current_downsample);
818 // Test only translation of the middle rotated frame
820 total_steps = x_steps * y_steps;
821 set_package_count(total_steps);
829 // Get least difference
830 int64_t min_difference = -1;
832 double stddev_table[get_total_packages()];
834 for(int i = 0; i < get_total_packages(); i++)
836 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
839 double stddev = sqrt(pkg->difference1) /
840 (block_w / current_downsample) /
841 (block_h / current_downsample) /
843 // printf("MotionHVScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n",
845 // current_downsample,
848 // sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */);
850 // printf("MotionHVScan::pixel_search %d range1=%f stddev=%f\n",
855 stddev_table[i] = stddev;
856 #endif // STDDEV_TEST
858 if(pkg->difference1 < min_difference || i == 0)
860 min_difference = pkg->difference1;
861 x_result = pkg->search_x * current_downsample * OVERSAMPLE;
862 y_result = pkg->search_y * current_downsample * OVERSAMPLE;
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);
876 qsort(stddev_table, get_total_packages(), sizeof(double), compare);
879 // reject motion vector if not similar enough
880 // if(stddev_table[0] > 0.2)
884 // printf("MotionHVScan::pixel_search %d stddev fail min_stddev=%f\n",
894 printf("MotionHVScan::pixel_search %d\n", __LINE__);
895 for(int i = 0; i < get_total_packages(); i++)
897 printf("%f\n", stddev_table[i]);
901 // reject motion vector if not a sigmoid curve
902 // TODO: use linear interpolation
904 int step = get_total_packages() / steps;
906 for(int i = 0; i < steps; i++)
908 int start = get_total_packages() * i / steps;
909 int end = get_total_packages() * (i + 1) / steps;
910 end = MIN(end, get_total_packages() - 1);
911 curve[i] = stddev_table[end] - stddev_table[start];
915 // if(curve[0] < (curve[1] * 1.01) ||
916 // curve[2] < (curve[1] * 1.01) ||
917 // curve[0] < (curve[2] * 0.75))
918 // if(curve[0] < curve[1])
922 // printf("MotionHVScan::pixel_search %d curve fail %f %f\n",
933 printf("MotionHVScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n",
941 #endif // STDDEV_TEST
950 total_steps = angle_steps;
951 scan_x1 = x_result / OVERSAMPLE;
952 scan_y1 = y_result / OVERSAMPLE;
953 set_package_count(total_steps);
959 double prev_r_result = r_result;
960 for(int i = 0; i < get_total_packages(); i++)
962 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
964 // 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",
968 // pkg->search_angle_step,
972 // pkg->difference2);
973 if(pkg->difference1 < min_difference || i == 0)
975 min_difference = pkg->difference1;
976 r_result = step_to_angle(i, prev_r_result);
978 // printf("MotionHVScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
980 // block_x1 * OVERSAMPLE - x_result,
981 // block_y1 * OVERSAMPLE - y_result,
983 // pkg->difference1);
989 // printf("MotionHVScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n",
991 // current_downsample,
992 // (float)x_result / OVERSAMPLE,
993 // (float)y_result / OVERSAMPLE,
999 // subpixel motion search
1000 void MotionHVScan::subpixel_search(int &x_result, int &y_result)
1003 previous_frame = previous_frame_arg;
1004 current_frame = current_frame_arg;
1006 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
1007 // Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square
1008 total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE);
1010 // These aren't used in subpixel
1011 x_steps = OVERSAMPLE * SUBPIXEL_RANGE;
1012 y_steps = OVERSAMPLE * SUBPIXEL_RANGE;
1015 set_package_count(this->total_steps);
1018 // Get least difference
1019 int64_t min_difference = -1;
1020 for(int i = 0; i < get_total_packages(); i++)
1022 MotionHVScanPackage *pkg = (MotionHVScanPackage*)get_package(i);
1023 //printf("MotionHVScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
1024 //__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
1025 if(pkg->difference1 < min_difference || min_difference == -1)
1027 min_difference = pkg->difference1;
1029 // The sub coords are 1 pixel up & left of the block coords
1030 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
1031 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
1035 dx_result = block_x1 * OVERSAMPLE - x_result;
1036 dy_result = block_y1 * OVERSAMPLE - y_result;
1037 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
1038 //__LINE__, dx_result, dy_result, min_difference);
1041 if(pkg->difference2 < min_difference)
1043 min_difference = pkg->difference2;
1045 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
1046 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
1048 dx_result = block_x1 * OVERSAMPLE - x_result;
1049 dy_result = block_y1 * OVERSAMPLE - y_result;
1050 //printf("MotionHVScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
1051 //__LINE__, dx_result, dy_result, min_difference);
1057 void MotionHVScan::scan_frame(VFrame *previous_frame,
1058 VFrame *current_frame,
1068 int horizontal_only,
1070 int source_position,
1073 int global_origin_x,
1074 int global_origin_y,
1077 double rotation_center,
1078 double rotation_range)
1080 this->previous_frame_arg = previous_frame;
1081 this->current_frame_arg = current_frame;
1082 this->horizontal_only = horizontal_only;
1083 this->vertical_only = vertical_only;
1084 this->previous_frame = previous_frame_arg;
1085 this->current_frame = current_frame_arg;
1086 this->global_origin_x = global_origin_x;
1087 this->global_origin_y = global_origin_y;
1088 this->action_type = action_type;
1089 this->do_motion = do_motion;
1090 this->do_rotate = do_rotate;
1091 this->rotation_center = rotation_center;
1092 this->rotation_range = rotation_range;
1094 //printf("MotionHVScan::scan_frame %d\n", __LINE__);
1101 // starting level of detail
1102 // TODO: base it on a table of resolutions
1103 current_downsample = STARTING_DOWNSAMPLE;
1106 // Single macroblock
1107 int w = current_frame->get_w();
1108 int h = current_frame->get_h();
1110 // Initial search parameters
1111 scan_w = global_range_w;
1112 scan_h = global_range_h;
1114 int block_w = global_block_w;
1115 int block_h = global_block_h;
1117 // printf("MotionHVScan::scan_frame %d %d %d %d %d %d %d %d %d\n",
1128 // Location of block in previous frame
1129 block_x1 = (int)(block_x - block_w / 2);
1130 block_y1 = (int)(block_y - block_h / 2);
1131 block_x2 = (int)(block_x + block_w / 2);
1132 block_y2 = (int)(block_y + block_h / 2);
1134 // Offset to location of previous block. This offset needn't be very accurate
1135 // since it's the offset of the previous image and current image we want.
1136 if(frame_type == MotionHVScan::TRACK_PREVIOUS)
1138 block_x1 += total_dx / OVERSAMPLE;
1139 block_y1 += total_dy / OVERSAMPLE;
1140 block_x2 += total_dx / OVERSAMPLE;
1141 block_y2 += total_dy / OVERSAMPLE;
1146 switch(tracking_type)
1149 case MotionHVScan::NO_CALCULATE:
1152 dr_result = rotation_center;
1156 case MotionHVScan::LOAD:
1158 // Load result from disk
1159 char string[BCTEXTLEN];
1164 sprintf(string, "%s%06d",
1167 //printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1168 FILE *input = fopen(string, "r");
1171 int temp = fscanf(input,
1176 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1180 //printf("MotionHVScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
1195 FILE *input = fopen(string, "r");
1198 int temp = fscanf(input, "%f", &dr_result);
1200 printf("MotionHVScan::scan_frame %d %s\n", __LINE__, string);
1202 //dr_result += 0.25;
1213 // Scan from scratch
1221 if(!skip && test_match)
1223 if(previous_frame->data_matches(current_frame))
1225 printf("MotionHVScan::scan_frame: data matches. skipping.\n");
1228 dr_result = rotation_center;
1237 // Location of block in current frame
1238 int origin_offset_x = this->global_origin_x;
1239 int origin_offset_y = this->global_origin_y;
1240 int x_result = block_x1 + origin_offset_x;
1241 int y_result = block_y1 + origin_offset_y;
1242 double r_result = rotation_center;
1244 // printf("MotionHVScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
1245 // block_x1 + block_w / 2,
1246 // block_y1 + block_h / 2,
1256 scan_x1 = x_result - scan_w / 2;
1257 scan_y1 = y_result - scan_h / 2;
1258 scan_x2 = x_result + scan_w / 2;
1259 scan_y2 = y_result + scan_h / 2;
1260 scan_angle1 = r_result - rotation_range;
1261 scan_angle2 = r_result + rotation_range;
1265 // Zero out requested values
1266 // if(horizontal_only)
1268 // scan_y1 = block_y1;
1269 // scan_y2 = block_y1 + 1;
1271 // if(vertical_only)
1273 // scan_x1 = block_x1;
1274 // scan_x2 = block_x1 + 1;
1277 // 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",
1289 // Clamp the block coords before the scan so we get useful scan coords.
1303 // 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",
1315 //if(y_result == 88) exit(0);
1318 // Give up if invalid coords.
1319 if(scan_y2 <= scan_y1 ||
1320 scan_x2 <= scan_x1 ||
1321 block_x2 <= block_x1 ||
1322 block_y2 <= block_y1)
1327 // For subpixel, the top row and left column are skipped
1331 subpixel_search(x_result, y_result);
1332 // printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n",
1334 // x_result / OVERSAMPLE,
1335 // y_result / OVERSAMPLE);
1342 pixel_search(x_result, y_result, r_result);
1343 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE);
1352 if(current_downsample <= 1)
1354 // Single pixel accuracy reached. Now do exhaustive subpixel search.
1355 if(action_type == MotionHVScan::STABILIZE ||
1356 action_type == MotionHVScan::TRACK ||
1357 action_type == MotionHVScan::NOTHING)
1359 x_result /= OVERSAMPLE;
1360 y_result /= OVERSAMPLE;
1361 //printf("MotionHVScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result);
1362 scan_w = SUBPIXEL_RANGE;
1363 scan_h = SUBPIXEL_RANGE;
1365 dr_result = rotation_center - r_result;
1370 // Fill in results and quit
1371 dx_result = block_x1 * OVERSAMPLE - x_result;
1372 dy_result = block_y1 * OVERSAMPLE - y_result;
1373 dr_result = rotation_center - r_result;
1378 // Reduce scan area and try again
1380 // scan_w = (scan_x2 - scan_x1) / 2;
1381 // scan_h = (scan_y2 - scan_y1) / 2;
1382 // need slightly more than 2x downsampling factor
1384 if(current_downsample * 3 < scan_w &&
1385 current_downsample * 3 < scan_h)
1387 scan_w = current_downsample * 3;
1388 scan_h = current_downsample * 3;
1391 if(angle_step * 1.5 < rotation_range)
1393 rotation_range = angle_step * 1.5;
1395 //printf("MotionHVScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range);
1397 current_downsample /= 2;
1399 // convert back to pixels
1400 x_result /= OVERSAMPLE;
1401 y_result /= OVERSAMPLE;
1413 // printf("MotionHVScan::scan_frame %d dx=%f dy=%f dr=%f\n",
1415 // (float)dx_result / OVERSAMPLE,
1416 // (float)dy_result / OVERSAMPLE,
1423 if(!skip && tracking_type == MotionHVScan::SAVE)
1425 char string[BCTEXTLEN];
1434 FILE *output = fopen(string, "w");
1445 printf("MotionHVScan::scan_frame %d: save motion failed\n", __LINE__);
1455 FILE *output = fopen(string, "w");
1458 fprintf(output, "%f\n", dr_result);
1463 printf("MotionHVScan::scan_frame %d save rotation failed\n", __LINE__);
1469 if(vertical_only) dx_result = 0;
1470 if(horizontal_only) dy_result = 0;
1472 // printf("MotionHVScan::scan_frame %d dx=%d dy=%d\n",
1475 // this->dy_result);
1496 #define ABS_DIFF(type, temp_type, multiplier, components) \
1498 temp_type result_temp = 0; \
1499 for(int i = 0; i < h; i++) \
1501 type *prev_row = (type*)prev_ptr; \
1502 type *current_row = (type*)current_ptr; \
1503 for(int j = 0; j < w; j++) \
1505 for(int k = 0; k < 3; k++) \
1507 temp_type difference; \
1508 difference = *prev_row++ - *current_row++; \
1509 difference *= difference; \
1510 result_temp += difference; \
1512 if(components == 4) \
1518 prev_ptr += row_bytes; \
1519 current_ptr += row_bytes; \
1521 result = (int64_t)(result_temp * multiplier); \
1524 int64_t MotionHVScan::abs_diff(unsigned char *prev_ptr,
1525 unsigned char *current_ptr,
1535 ABS_DIFF(unsigned char, int64_t, 1, 3)
1538 ABS_DIFF(unsigned char, int64_t, 1, 4)
1541 ABS_DIFF(float, double, 0x10000, 3)
1544 ABS_DIFF(float, double, 0x10000, 4)
1547 ABS_DIFF(unsigned char, int64_t, 1, 3)
1550 ABS_DIFF(unsigned char, int64_t, 1, 4)
1558 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
1560 temp_type result_temp = 0; \
1561 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
1562 temp_type y1_fraction = 0x100 - y2_fraction; \
1563 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
1564 temp_type x1_fraction = 0x100 - x2_fraction; \
1565 for(int i = 0; i < h_sub; i++) \
1567 type *prev_row1 = (type*)prev_ptr; \
1568 type *prev_row2 = (type*)prev_ptr + components; \
1569 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
1570 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
1571 type *current_row = (type*)current_ptr; \
1572 for(int j = 0; j < w_sub; j++) \
1574 /* Scan each component */ \
1575 for(int k = 0; k < 3; k++) \
1577 temp_type difference; \
1578 temp_type prev_value = \
1579 (*prev_row1++ * x1_fraction * y1_fraction + \
1580 *prev_row2++ * x2_fraction * y1_fraction + \
1581 *prev_row3++ * x1_fraction * y2_fraction + \
1582 *prev_row4++ * x2_fraction * y2_fraction) / \
1584 temp_type current_value = *current_row++; \
1585 difference = prev_value - current_value; \
1586 difference *= difference; \
1587 result_temp += difference; \
1591 if(components == 4) \
1600 prev_ptr += row_bytes; \
1601 current_ptr += row_bytes; \
1603 result = (int64_t)(result_temp * multiplier); \
1609 int64_t MotionHVScan::abs_diff_sub(unsigned char *prev_ptr,
1610 unsigned char *current_ptr,
1625 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1628 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1631 ABS_DIFF_SUB(float, double, 0x10000, 3)
1634 ABS_DIFF_SUB(float, double, 0x10000, 4)
1637 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
1640 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
1648 #define VARIANCE(type, temp_type, multiplier, components) \
1650 temp_type average[3] = { 0 }; \
1651 temp_type variance[3] = { 0 }; \
1653 for(int i = 0; i < h; i++) \
1655 type *row = (type*)current_ptr + i * row_bytes; \
1656 for(int j = 0; j < w; j++) \
1658 for(int k = 0; k < 3; k++) \
1660 average[k] += row[k]; \
1662 row += components; \
1665 for(int k = 0; k < 3; k++) \
1667 average[k] /= w * h; \
1670 for(int i = 0; i < h; i++) \
1672 type *row = (type*)current_ptr + i * row_bytes; \
1673 for(int j = 0; j < w; j++) \
1675 for(int k = 0; k < 3; k++) \
1677 variance[k] += SQR(row[k] - average[k]); \
1679 row += components; \
1682 result = (double)multiplier * \
1683 sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \
1686 double MotionHVScan::calculate_variance(unsigned char *current_ptr,
1697 VARIANCE(unsigned char, int, 1, 3)
1700 VARIANCE(unsigned char, int, 1, 4)
1703 VARIANCE(float, double, 255, 3)
1706 VARIANCE(float, double, 255, 4)
1709 VARIANCE(unsigned char, int, 1, 3)
1712 VARIANCE(unsigned char, int, 1, 4)
1724 #define RANGE(type, temp_type, multiplier, components) \
1735 for(int i = 0; i < h; i++) \
1737 type *row = (type*)current_ptr + i * row_bytes; \
1738 for(int j = 0; j < w; j++) \
1740 for(int k = 0; k < 3; k++) \
1742 if(row[k] > max[k]) max[k] = row[k]; \
1743 if(row[k] < min[k]) min[k] = row[k]; \
1745 row += components; \
1749 for(int k = 0; k < 3; k++) \
1751 /* printf("MotionHVScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \
1752 if(max[k] - min[k] > result) result = max[k] - min[k]; \
1757 double MotionHVScan::calculate_range(unsigned char *current_ptr,
1768 RANGE(unsigned char, int, 1, 3)
1771 RANGE(unsigned char, int, 1, 4)
1774 RANGE(float, float, 255, 3)
1777 RANGE(float, float, 255, 4)
1780 RANGE(unsigned char, int, 1, 3)
1783 RANGE(unsigned char, int, 1, 4)
1792 //#define CLAMP_BLOCK
1794 // this truncates the scan area but not the macroblock unless the macro is defined
1795 void MotionHVScan::clamp_scan(int w,
1807 // printf("MotionHVMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1822 // Limit size of scan area
1823 // Used for drawing vectors
1824 // scan is always out of range before block.
1828 int difference = -*scan_x1;
1829 *block_x1 += difference;
1837 int difference = -*scan_y1;
1838 *block_y1 += difference;
1845 int difference = *scan_x2 - w;
1847 *block_x2 -= difference;
1849 *scan_x2 -= difference;
1854 int difference = *scan_y2 - h;
1856 *block_y2 -= difference;
1858 *scan_y2 -= difference;
1861 CLAMP(*scan_x1, 0, w);
1862 CLAMP(*scan_y1, 0, h);
1863 CLAMP(*scan_x2, 0, w);
1864 CLAMP(*scan_y2, 0, h);
1868 // Limit range of upper left block coordinates
1869 // Used for motion tracking
1872 int difference = -*scan_x1;
1874 *block_x1 += difference;
1876 *scan_x2 += difference;
1882 int difference = -*scan_y1;
1884 *block_y1 += difference;
1886 *scan_y2 += difference;
1890 if(*scan_x2 - *block_x1 + *block_x2 > w)
1892 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1893 *scan_x2 -= difference;
1895 *block_x2 -= difference;
1899 if(*scan_y2 - *block_y1 + *block_y2 > h)
1901 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1902 *scan_y2 -= difference;
1904 *block_y2 -= difference;
1908 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1909 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1910 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1911 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1914 // Sanity checks which break the calculation but should never happen if the
1915 // center of the block is inside the frame.
1916 CLAMP(*block_x1, 0, w);
1917 CLAMP(*block_x2, 0, w);
1918 CLAMP(*block_y1, 0, h);
1919 CLAMP(*block_y2, 0, h);
1921 // printf("MotionHVMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",