-
-
-
-#define DOWNSAMPLE(type, temp_type, components, max) \
-{ \
- temp_type r; \
- temp_type g; \
- temp_type b; \
- temp_type a; \
- type **in_rows = (type**)src->get_rows(); \
- type **out_rows = (type**)dst->get_rows(); \
- \
- for(int i = 0; i < h; i += downsample) \
- { \
- int y1 = MAX(i, 0); \
- int y2 = MIN(i + downsample, h); \
- \
- \
- for(int j = 0; \
- j < w; \
- j += downsample) \
- { \
- int x1 = MAX(j, 0); \
- int x2 = MIN(j + downsample, w); \
- \
- temp_type scale = (x2 - x1) * (y2 - y1); \
- if(x2 > x1 && y2 > y1) \
- { \
- \
-/* Read in values */ \
- r = 0; \
- g = 0; \
- b = 0; \
- if(components == 4) a = 0; \
- \
- for(int k = y1; k < y2; k++) \
- { \
- type *row = in_rows[k] + x1 * components; \
- for(int l = x1; l < x2; l++) \
- { \
- r += *row++; \
- g += *row++; \
- b += *row++; \
- if(components == 4) a += *row++; \
- } \
- } \
- \
-/* Write average */ \
- r /= scale; \
- g /= scale; \
- b /= scale; \
- if(components == 4) a /= scale; \
- \
- type *row = out_rows[y1 / downsample] + \
- x1 / downsample * components; \
- *row++ = r; \
- *row++ = g; \
- *row++ = b; \
- if(components == 4) *row++ = a; \
- } \
- } \
-/*printf("DOWNSAMPLE 3 %d\n", i);*/ \
- } \
-}
-
-
-
-
-void MotionScan::downsample_frame(VFrame *dst,
- VFrame *src,
- int downsample)
-{
- int h = src->get_h();
- int w = src->get_w();
-
-//PRINT_TRACE
-//printf("downsample=%d w=%d h=%d dst=%d %d\n", downsample, w, h, dst->get_w(), dst->get_h());
- switch(src->get_color_model())
- {
- case BC_RGB888:
- DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
- break;
- case BC_RGB_FLOAT:
- DOWNSAMPLE(float, float, 3, 1.0)
- break;
- case BC_RGBA8888:
- DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
- break;
- case BC_RGBA_FLOAT:
- DOWNSAMPLE(float, float, 4, 1.0)
- break;
- case BC_YUV888:
- DOWNSAMPLE(uint8_t, int64_t, 3, 0xff)
- break;
- case BC_YUVA8888:
- DOWNSAMPLE(uint8_t, int64_t, 4, 0xff)
- break;
- }
-//PRINT_TRACE
-}
-
-double MotionScan::step_to_angle(int step, double center)
-{
- if(step < angle_steps / 2)
- {
- return center - angle_step * (angle_steps / 2 - step);
- }
- else
- if(step > angle_steps / 2)
- {
- return center + angle_step * (step - angle_steps / 2);
- }
- else
- {
- return center;
- }
-}
-
-#ifdef STDDEV_TEST
-static int compare(const void *p1, const void *p2)
-{
- double value1 = *(double*)p1;
- double value2 = *(double*)p2;
-
-//printf("compare %d value1=%f value2=%f\n", __LINE__, value1, value2);
- return value1 > value2;
-}
-#endif
-
-// reject vectors based on content. It's the reason Goog can't stabilize timelapses.
-//#define STDDEV_TEST
-
-// pixel accurate motion search
-void MotionScan::pixel_search(int &x_result, int &y_result, double &r_result)
-{
-// reduce level of detail until enough steps
- while(current_downsample > 1 &&
- ((block_x2 - block_x1) / current_downsample < MIN_DOWNSAMPLED_SIZE ||
- (block_y2 - block_y1) / current_downsample < MIN_DOWNSAMPLED_SIZE
- ||
- (scan_x2 - scan_x1) / current_downsample < MIN_DOWNSAMPLED_SCAN ||
- (scan_y2 - scan_y1) / current_downsample < MIN_DOWNSAMPLED_SCAN
- ))
- {
- current_downsample /= 2;
- }
-
-
-
-// create downsampled images.
-// Need to keep entire frame to search for rotation.
- int downsampled_prev_w = previous_frame_arg->get_w() / current_downsample;
- int downsampled_prev_h = previous_frame_arg->get_h() / current_downsample;
- int downsampled_current_w = current_frame_arg->get_w() / current_downsample;
- int downsampled_current_h = current_frame_arg->get_h() / current_downsample;
-
-// printf("MotionScan::pixel_search %d current_downsample=%d current_frame_arg->get_w()=%d downsampled_current_w=%d\n",
-// __LINE__,
-// current_downsample,
-// current_frame_arg->get_w(),
-// downsampled_current_w);
-
- x_steps = (scan_x2 - scan_x1) / current_downsample;
- y_steps = (scan_y2 - scan_y1) / current_downsample;
-
-// in rads
- double test_angle1 = atan2((double)downsampled_current_h / 2 - 1, (double)downsampled_current_w / 2);
- double test_angle2 = atan2((double)downsampled_current_h / 2, (double)downsampled_current_w / 2 - 1);
-
-// in deg
- angle_step = 360.0f * fabs(test_angle1 - test_angle2) / 2 / M_PI;
-
-// printf("MotionScan::pixel_search %d test_angle1=%f test_angle2=%f angle_step=%f\n",
-// __LINE__,
-// 360.0f * test_angle1 / 2 / M_PI,
-// 360.0f * test_angle2 / 2 / M_PI,
-// angle_step);
-
-
- if(do_rotate && angle_step < rotation_range)
- {
- angle_steps = 1 + (int)((scan_angle2 - scan_angle1) / angle_step + 0.5);
- }
- else
- {
- angle_steps = 1;
- }
-
-
- if(current_downsample > 1)
- {
- if(!downsampled_previous ||
- downsampled_previous->get_w() != downsampled_prev_w ||
- downsampled_previous->get_h() != downsampled_prev_h)
- {
- delete downsampled_previous;
- downsampled_previous = new VFrame();
- downsampled_previous->set_use_shm(0);
- downsampled_previous->reallocate(0,
- -1,
- 0,
- 0,
- 0,
- downsampled_prev_w + 1,
- downsampled_prev_h + 1,
- previous_frame_arg->get_color_model(),
- -1);
- }
-
- if(!downsampled_current ||
- downsampled_current->get_w() != downsampled_current_w ||
- downsampled_current->get_h() != downsampled_current_h)
- {
- delete downsampled_current;
- downsampled_current = new VFrame();
- downsampled_current->set_use_shm(0);
- downsampled_current->reallocate(0,
- -1,
- 0,
- 0,
- 0,
- downsampled_current_w + 1,
- downsampled_current_h + 1,
- current_frame_arg->get_color_model(),
- -1);
- }
-
-
- downsample_frame(downsampled_previous,
- previous_frame_arg,
- current_downsample);
- downsample_frame(downsampled_current,
- current_frame_arg,
- current_downsample);
- previous_frame = downsampled_previous;
- current_frame = downsampled_current;
-
- }
- else
- {
- previous_frame = previous_frame_arg;
- current_frame = current_frame_arg;
- }
-
-
-
-// printf("MotionScan::pixel_search %d x_steps=%d y_steps=%d angle_steps=%d total_steps=%d\n",
-// __LINE__,
-// x_steps,
-// y_steps,
-// angle_steps,
-// total_steps);
-
-
-
-// test variance of constant macroblock
- int color_model = current_frame->get_color_model();
- int pixel_size = BC_CModels::calculate_pixelsize(color_model);
- int row_bytes = current_frame->get_bytes_per_line();
- int block_w = block_x2 - block_x1;
- int block_h = block_y2 - block_y1;
-
- unsigned char *current_ptr =
- current_frame->get_rows()[block_y1 / current_downsample] +
- (block_x1 / current_downsample) * pixel_size;
- unsigned char *previous_ptr =
- previous_frame->get_rows()[scan_y1 / current_downsample] +
- (scan_x1 / current_downsample) * pixel_size;
-
-
-
-// test detail in prev & current frame
- double range1 = calculate_range(current_ptr,
- row_bytes,
- block_w / current_downsample,
- block_h / current_downsample,
- color_model);
-
- if(range1 < 1)
- {
-printf("MotionScan::pixel_search %d range fail range1=%f\n", __LINE__, range1);
- failed = 1;
- return;
- }
-
- double range2 = calculate_range(previous_ptr,
- row_bytes,
- block_w / current_downsample,
- block_h / current_downsample,
- color_model);
-
- if(range2 < 1)
- {
-printf("MotionScan::pixel_search %d range fail range2=%f\n", __LINE__, range2);
- failed = 1;
- return;
- }
-
-
-// create rotated images
- if(rotated_current &&
- (total_rotated != angle_steps ||
- rotated_current[0]->get_w() != downsampled_current_w ||
- rotated_current[0]->get_h() != downsampled_current_h))
- {
- for(int i = 0; i < total_rotated; i++)
- {
- delete rotated_current[i];
- }
-
- delete [] rotated_current;
- rotated_current = 0;
- total_rotated = 0;
- }
-
- if(do_rotate)
- {
- total_rotated = angle_steps;
-
-
- if(!rotated_current)
- {
- rotated_current = new VFrame*[total_rotated];
- bzero(rotated_current, sizeof(VFrame*) * total_rotated);
- }
-
-// printf("MotionScan::pixel_search %d total_rotated=%d w=%d h=%d block_w=%d block_h=%d\n",
-// __LINE__,
-// total_rotated,
-// downsampled_current_w,
-// downsampled_current_h,
-// (block_x2 - block_x1) / current_downsample,
-// (block_y2 - block_y1) / current_downsample);
- for(int i = 0; i < angle_steps; i++)
- {
-
-// printf("MotionScan::pixel_search %d w=%d h=%d x=%d y=%d angle=%f\n",
-// __LINE__,
-// downsampled_current_w,
-// downsampled_current_h,
-// (block_x1 + block_x2) / 2 / current_downsample,
-// (block_y1 + block_y2) / 2 / current_downsample,
-// step_to_angle(i, r_result));
-
-// printf("MotionScan::pixel_search %d i=%d rotated_current[i]=%p\n",
-// __LINE__,
-// i,
-// rotated_current[i]);
- if(!rotated_current[i])
- {
- rotated_current[i] = new VFrame();
- rotated_current[i]->set_use_shm(0);
- rotated_current[i]->reallocate(0,
- -1,
- 0,
- 0,
- 0,
- downsampled_current_w + 1,
- downsampled_current_h + 1,
- current_frame_arg->get_color_model(),
- -1);
-//printf("MotionScan::pixel_search %d\n", __LINE__);
- }
-
-
- if(!rotater)
- {
- rotater = new AffineEngine(get_total_clients(),
- get_total_clients());
- }
-
-// get smallest viewport size required for the angle
- double diag = hypot((block_x2 - block_x1) / current_downsample,
- (block_y2 - block_y1) / current_downsample);
- double angle1 = atan2(block_y2 - block_y1, block_x2 - block_x1) +
- TO_RAD(step_to_angle(i, r_result));
- double angle2 = -atan2(block_y2 - block_y1, block_x2 - block_x1) +
- TO_RAD(step_to_angle(i, r_result));
- double max_horiz = MAX(abs(diag * cos(angle1)), abs(diag * cos(angle2)));
- double max_vert = MAX(abs(diag * sin(angle1)), abs(diag * sin(angle2)));
- int center_x = (block_x1 + block_x2) / 2 / current_downsample;
- int center_y = (block_y1 + block_y2) / 2 / current_downsample;
- int x1 = center_x - max_horiz / 2;
- int y1 = center_y - max_vert / 2;
- int x2 = x1 + max_horiz;
- int y2 = y1 + max_vert;
- CLAMP(x1, 0, downsampled_current_w - 1);
- CLAMP(y1, 0, downsampled_current_h - 1);
- CLAMP(x2, 0, downsampled_current_w - 1);
- CLAMP(y2, 0, downsampled_current_h - 1);
-
-//printf("MotionScan::pixel_search %d %f %f %d %d\n",
-//__LINE__, TO_DEG(angle1), TO_DEG(angle2), (int)max_horiz, (int)max_vert);
- rotater->set_in_viewport(x1,
- y1,
- x2 - x1,
- y2 - y1);
- rotater->set_out_viewport(x1,
- y1,
- x2 - x1,
- y2 - y1);
-
-// rotater->set_in_viewport(0,
-// 0,
-// downsampled_current_w,
-// downsampled_current_h);
-// rotater->set_out_viewport(0,
-// 0,
-// downsampled_current_w,
-// downsampled_current_h);
-
- rotater->set_in_pivot(center_x, center_y);
- rotater->set_out_pivot(center_x, center_y);
-
- rotater->rotate(rotated_current[i],
- current_frame,
- step_to_angle(i, r_result));
-
-// rotated_current[i]->draw_rect(block_x1 / current_downsample,
-// block_y1 / current_downsample,
-// block_x2 / current_downsample,
-// block_y2 / current_downsample);
-// char string[BCTEXTLEN];
-// sprintf(string, "/tmp/rotated%d", i);
-// rotated_current[i]->write_png(string);
-//downsampled_previous->write_png("/tmp/previous");
-//printf("MotionScan::pixel_search %d\n", __LINE__);
- }
- }
-
-
-
-
-
-
-// printf("MotionScan::pixel_search %d block x=%d y=%d w=%d h=%d\n",
-// __LINE__,
-// block_x1 / current_downsample,
-// block_y1 / current_downsample,
-// block_w / current_downsample,
-// block_h / current_downsample);
-
-
-
-
-
-
-
-//exit(1);
-// Test only translation of the middle rotated frame
- rotation_pass = 0;
- total_steps = x_steps * y_steps;
- set_package_count(total_steps);
- process_packages();
-
-
-
-
-
-
-// Get least difference
- int64_t min_difference = -1;
-#ifdef STDDEV_TEST
- double stddev_table[get_total_packages()];
-#endif
- for(int i = 0; i < get_total_packages(); i++)
- {
- MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
-
-#ifdef STDDEV_TEST
- double stddev = sqrt(pkg->difference1) /
- (block_w / current_downsample) /
- (block_h / current_downsample) /
- 3;
-// printf("MotionScan::pixel_search %d current_downsample=%d search_x=%d search_y=%d diff1=%f\n",
-// __LINE__,
-// current_downsample,
-// pkg->search_x,
-// pkg->search_y,
-// sqrt(pkg->difference1) / block_w / current_downsample / block_h / 3 /* / variance */);
-
-// printf("MotionScan::pixel_search %d range1=%f stddev=%f\n",
-// __LINE__,
-// range1,
-// stddev);
-
- stddev_table[i] = stddev;
-#endif // STDDEV_TEST
-
- if(pkg->difference1 < min_difference || i == 0)
- {
- min_difference = pkg->difference1;
- x_result = pkg->search_x * current_downsample * OVERSAMPLE;
- y_result = pkg->search_y * current_downsample * OVERSAMPLE;
-
-// printf("MotionScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
-// __LINE__,
-// block_x1 * OVERSAMPLE - x_result,
-// block_y1 * OVERSAMPLE - y_result,
-// pkg->angle_step,
-// pkg->difference1);
-
- }
- }
-
-
-#ifdef STDDEV_TEST
- qsort(stddev_table, get_total_packages(), sizeof(double), compare);
-
-
-// reject motion vector if not similar enough
-// if(stddev_table[0] > 0.2)
-// {
-// if(debug)
-// {
-// printf("MotionScan::pixel_search %d stddev fail min_stddev=%f\n",
-// __LINE__,
-// stddev_table[0]);
-// }
-// failed = 1;
-// return;
-// }
-
-if(debug)
-{
- printf("MotionScan::pixel_search %d\n", __LINE__);
- for(int i = 0; i < get_total_packages(); i++)
- {
- printf("%f\n", stddev_table[i]);
- }
-}
-
-// reject motion vector if not a sigmoid curve
-// TODO: use linear interpolation
- int steps = 2;
- int step = get_total_packages() / steps;
- double curve[steps];
- for(int i = 0; i < steps; i++)
- {
- int start = get_total_packages() * i / steps;
- int end = get_total_packages() * (i + 1) / steps;
- end = MIN(end, get_total_packages() - 1);
- curve[i] = stddev_table[end] - stddev_table[start];
- }
-
-
-// if(curve[0] < (curve[1] * 1.01) ||
-// curve[2] < (curve[1] * 1.01) ||
-// curve[0] < (curve[2] * 0.75))
-// if(curve[0] < curve[1])
-// {
-// if(debug)
-// {
-// printf("MotionScan::pixel_search %d curve fail %f %f\n",
-// __LINE__,
-// curve[0],
-// curve[1]);
-// }
-// failed = 1;
-// return;
-// }
-
-if(debug)
-{
-printf("MotionScan::pixel_search %d curve=%f %f ranges=%f %f min_stddev=%f\n",
-__LINE__,
-curve[0],
-curve[1],
-range1,
-range2,
-stddev_table[0]);
-}
-#endif // STDDEV_TEST
-
-
-
-
-
- if(do_rotate)
- {
- rotation_pass = 1;;
- total_steps = angle_steps;
- scan_x1 = x_result / OVERSAMPLE;
- scan_y1 = y_result / OVERSAMPLE;
- set_package_count(total_steps);
- process_packages();
-
-
-
- min_difference = -1;
- double prev_r_result = r_result;
- for(int i = 0; i < get_total_packages(); i++)
- {
- MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
-
-// printf("MotionScan::pixel_search %d search_x=%d search_y=%d angle_step=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
-// __LINE__,
-// pkg->search_x,
-// pkg->search_y,
-// pkg->search_angle_step,
-// pkg->sub_x,
-// pkg->sub_y,
-// pkg->difference1,
-// pkg->difference2);
- if(pkg->difference1 < min_difference || i == 0)
- {
- min_difference = pkg->difference1;
- r_result = step_to_angle(i, prev_r_result);
-
- // printf("MotionScan::pixel_search %d x_result=%d y_result=%d angle_step=%d diff=%lld\n",
- // __LINE__,
- // block_x1 * OVERSAMPLE - x_result,
- // block_y1 * OVERSAMPLE - y_result,
- // pkg->angle_step,
- // pkg->difference1);
- }
- }
- }
-
-
-// printf("MotionScan::scan_frame %d current_downsample=%d x_result=%f y_result=%f r_result=%f\n",
-// __LINE__,
-// current_downsample,
-// (float)x_result / OVERSAMPLE,
-// (float)y_result / OVERSAMPLE,
-// r_result);
-
-}
-
-
-// subpixel motion search
-void MotionScan::subpixel_search(int &x_result, int &y_result)
-{
- rotation_pass = 0;
- previous_frame = previous_frame_arg;
- current_frame = current_frame_arg;
-
-//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
-// Scan every subpixel in a SUBPIXEL_RANGE * SUBPIXEL_RANGE square
- total_steps = (SUBPIXEL_RANGE * OVERSAMPLE) * (SUBPIXEL_RANGE * OVERSAMPLE);
-
-// These aren't used in subpixel
- x_steps = OVERSAMPLE * SUBPIXEL_RANGE;
- y_steps = OVERSAMPLE * SUBPIXEL_RANGE;
- angle_steps = 1;
-
- set_package_count(this->total_steps);
- process_packages();
-
-// Get least difference
- int64_t min_difference = -1;
- for(int i = 0; i < get_total_packages(); i++)
- {
- MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
-//printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
-//__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
- if(pkg->difference1 < min_difference || min_difference == -1)
- {
- min_difference = pkg->difference1;
-
-// The sub coords are 1 pixel up & left of the block coords
- x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
- y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
-
-
-// Fill in results
- dx_result = block_x1 * OVERSAMPLE - x_result;
- dy_result = block_y1 * OVERSAMPLE - y_result;
-//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
-//__LINE__, dx_result, dy_result, min_difference);
- }
-
- if(pkg->difference2 < min_difference)
- {
- min_difference = pkg->difference2;
-
- x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
- y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
-
- dx_result = block_x1 * OVERSAMPLE - x_result;
- dy_result = block_y1 * OVERSAMPLE - y_result;
-//printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
-//__LINE__, dx_result, dy_result, min_difference);
- }
- }
-}
-
-
-void MotionScan::scan_frame(VFrame *previous_frame,
- VFrame *current_frame,
- int global_range_w,
- int global_range_h,
- int global_block_w,
- int global_block_h,
- int block_x,
- int block_y,
- int frame_type,
- int tracking_type,
- int action_type,
- int horizontal_only,
- int vertical_only,
- int source_position,
- int total_dx,
- int total_dy,
- int global_origin_x,
- int global_origin_y,
- int do_motion,
- int do_rotate,
- double rotation_center,
- double rotation_range)