X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fmotion%2Fmotionscan.C;fp=cinelerra-5.1%2Fplugins%2Fmotion%2Fmotionscan.C;h=4ec92da4498fbe6e2753ae1071fc2006e9373443;hb=5a1b2bb96f2bd6b7ef4f8031763683726c02219d;hp=b1a533ef850477a9c7412d39e92c45d5d60c2a09;hpb=d8506a82d13c3179b5a30c985e6e2045304e95cf;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/motion/motionscan.C b/cinelerra-5.1/plugins/motion/motionscan.C index b1a533ef..4ec92da4 100644 --- a/cinelerra-5.1/plugins/motion/motionscan.C +++ b/cinelerra-5.1/plugins/motion/motionscan.C @@ -1,7 +1,7 @@ /* * CINELERRA - * Copyright (C) 2012 Adam Williams + * Copyright (C) 2016 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,20 +19,28 @@ * */ +#include "affine.h" +#include "bcsignals.h" #include "clip.h" -//#include "../downsample/downsampleengine.h" -//#include "motion.h" #include "motionscan.h" #include "mutex.h" #include "vframe.h" + #include +#include +#include // The module which does the actual scanning - - - +// starting level of detail +#define STARTING_DOWNSAMPLE 16 +// minimum size in each level of detail +#define MIN_DOWNSAMPLED_SIZE 16 +// minimum scan range +#define MIN_DOWNSAMPLED_SCAN 4 +// scan range for subpixel mode +#define SUBPIXEL_RANGE 4 MotionScanPackage::MotionScanPackage() : LoadPackage() @@ -49,156 +57,137 @@ MotionScanUnit::MotionScanUnit(MotionScan *server) : LoadClient(server) { this->server = server; - cache_lock = new Mutex("MotionScanUnit::cache_lock"); } MotionScanUnit::~MotionScanUnit() { - delete cache_lock; } - -void MotionScanUnit::process_package(LoadPackage *package) +void MotionScanUnit::single_pixel(MotionScanPackage *pkg) { - MotionScanPackage *pkg = (MotionScanPackage*)package; //int w = server->current_frame->get_w(); //int h = server->current_frame->get_h(); int color_model = server->current_frame->get_color_model(); int pixel_size = BC_CModels::calculate_pixelsize(color_model); int row_bytes = server->current_frame->get_bytes_per_line(); +// printf("MotionScanUnit::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", +// __LINE__, +// pkg->search_x, +// pkg->search_y, +// pkg->scan_x1, +// pkg->scan_y1, +// pkg->scan_x2, +// pkg->scan_y2, +// server->x_steps, +// server->y_steps); - - - - - - - - - - -// Single pixel - if(!server->subpixel) - { -// Try cache - pkg->difference1 = server->get_cache(pkg->search_x, pkg->search_y); - if(pkg->difference1 < 0) - { -//printf("MotionScanUnit::process_package 1 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", -//pkg->search_x, pkg->search_y, pkg->scan_x1, pkg->scan_y1, pkg->scan_x2, pkg->scan_y2, server->x_steps, server->y_steps); // Pointers to first pixel in each block - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; - unsigned char *current_ptr = server->current_frame->get_rows()[ - pkg->block_y1] + - pkg->block_x1 * pixel_size; - -// Scan block - pkg->difference1 = MotionScan::abs_diff(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model); + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; + unsigned char *current_ptr = 0; -// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", -// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); - server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1); - } + if(server->do_rotate) + { + current_ptr = server->rotated_current[pkg->angle_step]->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; } - - - - - - - else - - - - - - - - -// Sub pixel { - unsigned char *prev_ptr = server->previous_frame->get_rows()[ - pkg->search_y] + - pkg->search_x * pixel_size; - unsigned char *current_ptr = server->current_frame->get_rows()[ + current_ptr = server->current_frame->get_rows()[ pkg->block_y1] + pkg->block_x1 * pixel_size; + } + +// Scan block + pkg->difference1 = MotionScan::abs_diff(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model); + +// printf("MotionScanUnit::process_package %d angle_step=%d diff=%d\n", +// __LINE__, +// pkg->angle_step, +// pkg->difference1); +// printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n", +// __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1); +} + +void MotionScanUnit::subpixel(MotionScanPackage *pkg) +{ +//PRINT_TRACE + //int w = server->current_frame->get_w(); + //int h = server->current_frame->get_h(); + int color_model = server->current_frame->get_color_model(); + int pixel_size = BC_CModels::calculate_pixelsize(color_model); + int row_bytes = server->current_frame->get_bytes_per_line(); + unsigned char *prev_ptr = server->previous_frame->get_rows()[ + pkg->search_y] + + pkg->search_x * pixel_size; +// neglect rotation + unsigned char *current_ptr = server->current_frame->get_rows()[ + pkg->block_y1] + + pkg->block_x1 * pixel_size; // With subpixel, there are two ways to compare each position, one by shifting // the previous frame and two by shifting the current frame. - pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, - current_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); - pkg->difference2 = MotionScan::abs_diff_sub(current_ptr, - prev_ptr, - row_bytes, - pkg->block_x2 - pkg->block_x1, - pkg->block_y2 - pkg->block_y1, - color_model, - pkg->sub_x, - pkg->sub_y); + pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, + current_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); + pkg->difference2 = MotionScan::abs_diff_sub(current_ptr, + prev_ptr, + row_bytes, + pkg->block_x2 - pkg->block_x1, + pkg->block_y2 - pkg->block_y1, + color_model, + pkg->sub_x, + pkg->sub_y); // printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n", -// sub_x, -// sub_y, -// search_x, -// search_y, +// pkg->sub_x, +// pkg->sub_y, +// pkg->search_x, +// pkg->search_y, // pkg->difference1, // pkg->difference2); - } - +} +void MotionScanUnit::process_package(LoadPackage *package) +{ + MotionScanPackage *pkg = (MotionScanPackage*)package; -} +// Single pixel + if(!server->subpixel) + { + single_pixel(pkg); + } + else +// Sub pixel + { + subpixel(pkg); + } +} -int64_t MotionScanUnit::get_cache(int x, int y) -{ - int64_t result = -1; - cache_lock->lock("MotionScanUnit::get_cache"); - for(int i = 0; i < cache.total; i++) - { - MotionScanCache *ptr = cache.values[i]; - if(ptr->x == x && ptr->y == y) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} -void MotionScanUnit::put_cache(int x, int y, int64_t difference) -{ - MotionScanCache *ptr = new MotionScanCache(x, y, difference); - cache_lock->lock("MotionScanUnit::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} @@ -213,75 +202,129 @@ void MotionScanUnit::put_cache(int x, int y, int64_t difference) MotionScan::MotionScan(int total_clients, int total_packages) : LoadServer( +// DEBUG //1, 1 total_clients, total_packages ) { test_match = 1; - cache_lock = new Mutex("MotionScan::cache_lock"); downsampled_previous = 0; downsampled_current = 0; -// downsample = 0; + rotated_current = 0; + rotater = 0; } MotionScan::~MotionScan() { - delete cache_lock; delete downsampled_previous; delete downsampled_current; -// delete downsample; + if(rotated_current) + { + for(int i = 0; i < total_rotated; i++) + { + delete rotated_current[i]; + } + + delete [] rotated_current; + } + delete rotater; } void MotionScan::init_packages() { // Set package coords +// Total range of positions to scan with downsampling + int downsampled_scan_x1 = scan_x1 / current_downsample; + //int downsampled_scan_x2 = scan_x2 / current_downsample; + int downsampled_scan_y1 = scan_y1 / current_downsample; + //int downsampled_scan_y2 = scan_y2 / current_downsample; + int downsampled_block_x1 = block_x1 / current_downsample; + int downsampled_block_x2 = block_x2 / current_downsample; + int downsampled_block_y1 = block_y1 / current_downsample; + int downsampled_block_y2 = block_y2 / current_downsample; + + //printf("MotionScan::init_packages %d %d\n", __LINE__, get_total_packages()); +// printf("MotionScan::init_packages %d current_downsample=%d scan_x1=%d scan_x2=%d block_x1=%d block_x2=%d\n", +// __LINE__, +// current_downsample, +// downsampled_scan_x1, +// downsampled_scan_x2, +// downsampled_block_x1, +// downsampled_block_x2); +// if(current_downsample == 8 && downsampled_scan_x1 == 47) +// { +// downsampled_previous->write_png("/tmp/previous"); +// downsampled_current->write_png("/tmp/current"); +// } + for(int i = 0; i < get_total_packages(); i++) { MotionScanPackage *pkg = (MotionScanPackage*)get_package(i); - pkg->block_x1 = block_x1; - pkg->block_x2 = block_x2; - pkg->block_y1 = block_y1; - pkg->block_y2 = block_y2; - pkg->scan_x1 = scan_x1; - pkg->scan_x2 = scan_x2; - pkg->scan_y1 = scan_y1; - pkg->scan_y2 = scan_y2; - pkg->step = i; + pkg->block_x1 = downsampled_block_x1; + pkg->block_x2 = downsampled_block_x2; + pkg->block_y1 = downsampled_block_y1; + pkg->block_y2 = downsampled_block_y2; pkg->difference1 = 0; pkg->difference2 = 0; pkg->dx = 0; pkg->dy = 0; pkg->valid = 1; + pkg->angle_step = 0; if(!subpixel) { - pkg->search_x = pkg->scan_x1 + (pkg->step % x_steps) * - (scan_x2 - scan_x1) / x_steps; - pkg->search_y = pkg->scan_y1 + (pkg->step / x_steps) * - (scan_y2 - scan_y1) / y_steps; + if(rotation_pass) + { + pkg->search_x = scan_x1 / current_downsample; + pkg->search_y = scan_y1 / current_downsample; + pkg->angle_step = i; + } + else + { + + int current_x_step = (i % x_steps); + int current_y_step = (i / x_steps); + + //printf("MotionScan::init_packages %d i=%d x_step=%d y_step=%d angle_step=%d\n", + //__LINE__, i, current_x_step, current_y_step, current_angle_step); + pkg->search_x = downsampled_scan_x1 + current_x_step * + (scan_x2 - scan_x1) / current_downsample / x_steps; + pkg->search_y = downsampled_scan_y1 + current_y_step * + (scan_y2 - scan_y1) / current_downsample / y_steps; + + if(do_rotate) + { + pkg->angle_step = angle_steps / 2; + } + else + { + pkg->angle_step = 0; + } + } + pkg->sub_x = 0; pkg->sub_y = 0; } else { - pkg->sub_x = pkg->step % (OVERSAMPLE * 2); - pkg->sub_y = pkg->step / (OVERSAMPLE * 2); + pkg->sub_x = i % (OVERSAMPLE * SUBPIXEL_RANGE); + pkg->sub_y = i / (OVERSAMPLE * SUBPIXEL_RANGE); - if(horizontal_only) - { - pkg->sub_y = 0; - } - - if(vertical_only) - { - pkg->sub_x = 0; - } +// if(horizontal_only) +// { +// pkg->sub_y = 0; +// } +// +// if(vertical_only) +// { +// pkg->sub_x = 0; +// } - pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE + 1; - pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE + 1; + pkg->search_x = scan_x1 + pkg->sub_x / OVERSAMPLE + 1; + pkg->search_y = scan_y1 + pkg->sub_y / OVERSAMPLE + 1; pkg->sub_x %= OVERSAMPLE; pkg->sub_y %= OVERSAMPLE; @@ -312,36 +355,727 @@ LoadClient* MotionScan::new_client() return new MotionScanUnit(this); } -LoadPackage* MotionScan::new_package() -{ - return new MotionScanPackage; -} +LoadPackage* MotionScan::new_package() +{ + return new MotionScanPackage; +} + + +void MotionScan::set_test_match(int value) +{ + this->test_match = value; +} + + + + +#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; -void MotionScan::set_test_match(int value) -{ - this->test_match = value; + 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, - double block_x, - double block_y, + 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_steps, int total_dx, int total_dy, int global_origin_x, - int global_origin_y) + int global_origin_y, + int do_motion, + int do_rotate, + double rotation_center, + double rotation_range) { this->previous_frame_arg = previous_frame; this->current_frame_arg = current_frame; @@ -351,25 +1085,51 @@ void MotionScan::scan_frame(VFrame *previous_frame, this->current_frame = current_frame_arg; this->global_origin_x = global_origin_x; this->global_origin_y = global_origin_y; - subpixel = 0; + this->action_type = action_type; + this->do_motion = do_motion; + this->do_rotate = do_rotate; + this->rotation_center = rotation_center; + this->rotation_range = rotation_range; - cache.remove_all_objects(); +//printf("MotionScan::scan_frame %d\n", __LINE__); + dx_result = 0; + dy_result = 0; + dr_result = 0; + failed = 0; + + subpixel = 0; +// starting level of detail +// TODO: base it on a table of resolutions + current_downsample = STARTING_DOWNSAMPLE; + angle_step = 0; // Single macroblock int w = current_frame->get_w(); int h = current_frame->get_h(); // Initial search parameters - int scan_w = w * global_range_w / 100; - int scan_h = h * global_range_h / 100; - int block_w = w * global_block_w / 100; - int block_h = h * global_block_h / 100; + scan_w = global_range_w; + scan_h = global_range_h; + + int block_w = global_block_w; + int block_h = global_block_h; + +// printf("MotionScan::scan_frame %d %d %d %d %d %d %d %d %d\n", +// __LINE__, +// global_range_w, +// global_range_h, +// global_block_w, +// global_block_h, +// scan_w, +// scan_h, +// block_w, +// block_h); // Location of block in previous frame - block_x1 = (int)(w * block_x / 100 - block_w / 2); - block_y1 = (int)(h * block_y / 100 - block_h / 2); - block_x2 = (int)(w * block_x / 100 + block_w / 2); - block_y2 = (int)(h * block_y / 100 + block_h / 2); + block_x1 = (int)(block_x - block_w / 2); + block_y1 = (int)(block_y - block_h / 2); + block_x2 = (int)(block_x + block_w / 2); + block_y2 = (int)(block_y + block_h / 2); // Offset to location of previous block. This offset needn't be very accurate // since it's the offset of the previous image and current image we want. @@ -389,6 +1149,7 @@ void MotionScan::scan_frame(VFrame *previous_frame, case MotionScan::NO_CALCULATE: dx_result = 0; dy_result = 0; + dr_result = rotation_center; skip = 1; break; @@ -396,21 +1157,55 @@ void MotionScan::scan_frame(VFrame *previous_frame, { // Load result from disk char string[BCTEXTLEN]; - sprintf(string, "%s%06d", - MOTION_FILE, - source_position); -//printf("MotionScan::scan_frame %d %s\n", __LINE__, string); - FILE *input = fopen(string, "r"); - if(input) + + skip = 1; + if(do_motion) { - (void)fscanf(input, "%d %d", - &dx_result, &dy_result); + sprintf(string, "%s%06d", + MOTION_FILE, + source_position); +//printf("MotionScan::scan_frame %d %s\n", __LINE__, string); + FILE *input = fopen(string, "r"); + if(input) + { + int temp = fscanf(input, + "%d %d", + &dx_result, + &dy_result); + if( temp != 2 ) + printf("MotionScan::scan_frame %d %s\n", __LINE__, string); // HACK //dx_result *= 2; //dy_result *= 2; //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); - fclose(input); - skip = 1; + fclose(input); + } + else + { + skip = 0; + } + } + + if(do_rotate) + { + sprintf(string, + "%s%06d", + ROTATION_FILE, + source_position); + FILE *input = fopen(string, "r"); + if(input) + { + int temp = fscanf(input, "%f", &dr_result); + if( temp != 1 ) + printf("MotionScan::scan_frame %d %s\n", __LINE__, string); +// DEBUG +//dr_result += 0.25; + fclose(input); + } + else + { + skip = 0; + } } break; } @@ -421,6 +1216,8 @@ void MotionScan::scan_frame(VFrame *previous_frame, break; } + + if(!skip && test_match) { if(previous_frame->data_matches(current_frame)) @@ -428,19 +1225,21 @@ void MotionScan::scan_frame(VFrame *previous_frame, printf("MotionScan::scan_frame: data matches. skipping.\n"); dx_result = 0; dy_result = 0; + dr_result = rotation_center; skip = 1; } } + // Perform scan if(!skip) { -//printf("MotionScan::scan_frame %d\n", __LINE__); // Location of block in current frame - int origin_offset_x = this->global_origin_x * w / 100; - int origin_offset_y = this->global_origin_y * h / 100; + int origin_offset_x = this->global_origin_x; + int origin_offset_y = this->global_origin_y; int x_result = block_x1 + origin_offset_x; int y_result = block_y1 + origin_offset_y; + double r_result = rotation_center; // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", // block_x1 + block_w / 2, @@ -452,32 +1251,31 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // block_x2, // block_y2); - while(1) + while(!failed) { -// Cache needs to be cleared if downsampling is used because the sums of -// different downsamplings can't be compared. -// Subpixel never uses the cache. -// cache.remove_all_objects(); scan_x1 = x_result - scan_w / 2; scan_y1 = y_result - scan_h / 2; scan_x2 = x_result + scan_w / 2; scan_y2 = y_result + scan_h / 2; - + scan_angle1 = r_result - rotation_range; + scan_angle2 = r_result + rotation_range; + // Zero out requested values - if(horizontal_only) - { - scan_y1 = block_y1; - scan_y2 = block_y1 + 1; - } - if(vertical_only) - { - scan_x1 = block_x1; - scan_x2 = block_x1 + 1; - } - -// printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n", +// if(horizontal_only) +// { +// scan_y1 = block_y1; +// scan_y2 = block_y1 + 1; +// } +// if(vertical_only) +// { +// scan_x1 = block_x1; +// scan_x2 = block_x1 + 1; +// } + +// printf("MotionScan::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", +// __LINE__, // block_x1, // block_y1, // block_x2, @@ -486,6 +1284,8 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // scan_y1, // scan_x2, // scan_y2); + + // Clamp the block coords before the scan so we get useful scan coords. clamp_scan(w, h, @@ -498,7 +1298,9 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); &scan_x2, &scan_y2, 0); -// printf("MotionScan::scan_frame 1 %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n x_result=%d y_result=%d\n", + + +// printf("MotionScan::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", // __LINE__, // block_x1, // block_y1, @@ -510,6 +1312,7 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // scan_y2, // x_result, // y_result); +//if(y_result == 88) exit(0); // Give up if invalid coords. @@ -517,179 +1320,49 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); scan_x2 <= scan_x1 || block_x2 <= block_x1 || block_y2 <= block_y1) + { break; + } // For subpixel, the top row and left column are skipped if(subpixel) { -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); -// Scan every subpixel in a 2 pixel * 2 pixel square - total_pixels = (2 * OVERSAMPLE) * (2 * OVERSAMPLE); - - this->total_steps = total_pixels; -// These aren't used in subpixel - this->x_steps = OVERSAMPLE * 2; - this->y_steps = OVERSAMPLE * 2; - - 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); - } - } + subpixel_search(x_result, y_result); +// printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", +// __LINE__, +// x_result / OVERSAMPLE, +// y_result / OVERSAMPLE); break; } else // Single pixel { - total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1); - this->total_steps = MIN(total_steps, total_pixels); - - if(this->total_steps == total_pixels) + pixel_search(x_result, y_result, r_result); +//printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result / OVERSAMPLE, y_result / OVERSAMPLE); + + if(failed) { - x_steps = scan_x2 - scan_x1; - y_steps = scan_y2 - scan_y1; + dr_result = 0; + dx_result = 0; + dy_result = 0; } else + if(current_downsample <= 1) { - x_steps = (int)sqrt(this->total_steps); - y_steps = (int)sqrt(this->total_steps); - } - -// Use downsampled images -// if(scan_x2 - scan_x1 > x_steps * 4 || -// scan_y2 - scan_y1 > y_steps * 4) -// { -// printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n", -// __LINE__, -// total_pixels, -// total_steps, -// x_steps, -// y_steps, -// x_steps * y_steps); -// -// if(!downsampled_previous || -// !downsampled_previous->equivalent(previous_frame_arg)) -// { -// delete downsampled_previous; -// downsampled_previous = new VFrame(*previous_frame_arg); -// } -// -// if(!downsampled_current || -// !downsampled_current->equivalent(current_frame_arg)) -// { -// delete downsampled_current; -// downsampled_current = new VFrame(*current_frame_arg); -// } -// -// -// if(!downsample) -// downsample = new DownSampleServer(get_total_clients(), -// get_total_clients()); -// downsample->process_frame(downsampled_previous, -// previous_frame_arg, -// 1, -// 1, -// 1, -// 1, -// (scan_y2 - scan_y1) / y_steps, -// (scan_x2 - scan_x1) / x_steps, -// 0, -// 0); -// downsample->process_frame(downsampled_current, -// current_frame_arg, -// 1, -// 1, -// 1, -// 1, -// (scan_y2 - scan_y1) / y_steps, -// (scan_x2 - scan_x1) / x_steps, -// 0, -// 0); -// this->previous_frame = downsampled_previous; -// this->current_frame = downsampled_current; -// } - - - - - -// printf("MotionScan::scan_frame %d this->total_steps=%d\n", -// __LINE__, -// this->total_steps); - - - 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; - x_result = pkg->search_x; - y_result = pkg->search_y; - x_result *= OVERSAMPLE; - y_result *= OVERSAMPLE; -//printf("MotionScan::scan_frame %d x_result=%d y_result=%d diff=%lld\n", -//__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, pkg->difference1); - } - } - - -// If a new search is required, rescale results back to pixels. - if(this->total_steps >= total_pixels) - { -// Single pixel accuracy reached. Now do exhaustive subpixel search. + // Single pixel accuracy reached. Now do exhaustive subpixel search. if(action_type == MotionScan::STABILIZE || action_type == MotionScan::TRACK || action_type == MotionScan::NOTHING) { -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result); x_result /= OVERSAMPLE; y_result /= OVERSAMPLE; - scan_w = 2; - scan_h = 2; +//printf("MotionScan::scan_frame %d x_result=%d y_result=%d\n", __LINE__, x_result, y_result); + scan_w = SUBPIXEL_RANGE; + scan_h = SUBPIXEL_RANGE; +// Final R result + dr_result = rotation_center - r_result; subpixel = 1; } else @@ -697,62 +1370,110 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); // Fill in results and quit dx_result = block_x1 * OVERSAMPLE - x_result; dy_result = block_y1 * OVERSAMPLE - y_result; -//printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result); + dr_result = rotation_center - r_result; break; } } else // Reduce scan area and try again { - scan_w = (scan_x2 - scan_x1) / 2; - scan_h = (scan_y2 - scan_y1) / 2; +// scan_w = (scan_x2 - scan_x1) / 2; +// scan_h = (scan_y2 - scan_y1) / 2; +// need slightly more than 2x downsampling factor + + if(current_downsample * 3 < scan_w && + current_downsample * 3 < scan_h) + { + scan_w = current_downsample * 3; + scan_h = current_downsample * 3; + } + + if(angle_step * 1.5 < rotation_range) + { + rotation_range = angle_step * 1.5; + } +//printf("MotionScan::scan_frame %d %f %f\n", __LINE__, angle_step, rotation_range); + + current_downsample /= 2; + +// convert back to pixels x_result /= OVERSAMPLE; y_result /= OVERSAMPLE; +// debug +//exit(1); } + } } dx_result *= -1; dy_result *= -1; + dr_result *= -1; } -//printf("MotionScan::scan_frame %d\n", __LINE__); - +// printf("MotionScan::scan_frame %d dx=%f dy=%f dr=%f\n", +// __LINE__, +// (float)dx_result / OVERSAMPLE, +// (float)dy_result / OVERSAMPLE, +// dr_result); - if(vertical_only) dx_result = 0; - if(horizontal_only) dy_result = 0; // Write results - if(tracking_type == MotionScan::SAVE) + if(!skip && tracking_type == MotionScan::SAVE) { char string[BCTEXTLEN]; - sprintf(string, - "%s%06d", - MOTION_FILE, - source_position); - FILE *output = fopen(string, "w"); - if(output) + + + if(do_motion) { - fprintf(output, - "%d %d\n", - dx_result, - dy_result); - fclose(output); + sprintf(string, + "%s%06d", + MOTION_FILE, + source_position); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, + "%d %d\n", + dx_result, + dy_result); + fclose(output); + } + else + { + printf("MotionScan::scan_frame %d: save motion failed\n", __LINE__); + } } - else + + if(do_rotate) { - printf("MotionScan::scan_frame %d: save coordinate failed", __LINE__); + sprintf(string, + "%s%06d", + ROTATION_FILE, + source_position); + FILE *output = fopen(string, "w"); + if(output) + { + fprintf(output, "%f\n", dr_result); + fclose(output); + } + else + { + printf("MotionScan::scan_frame %d save rotation failed\n", __LINE__); + } } } -// printf("MotionScan::scan_frame %d dx=%.2f dy=%.2f\n", -// __LINE__, -// (float)this->dx_result / OVERSAMPLE, -// (float)this->dy_result / OVERSAMPLE); -} + if(vertical_only) dx_result = 0; + if(horizontal_only) dy_result = 0; +// printf("MotionScan::scan_frame %d dx=%d dy=%d\n", +// __LINE__, +// this->dx_result, +// this->dy_result); +} @@ -768,30 +1489,7 @@ printf("MotionScan::scan_frame: data matches. skipping.\n"); -int64_t MotionScan::get_cache(int x, int y) -{ - int64_t result = -1; - cache_lock->lock("MotionScan::get_cache"); - for(int i = 0; i < cache.total; i++) - { - MotionScanCache *ptr = cache.values[i]; - if(ptr->x == x && ptr->y == y) - { - result = ptr->difference; - break; - } - } - cache_lock->unlock(); - return result; -} -void MotionScan::put_cache(int x, int y, int64_t difference) -{ - MotionScanCache *ptr = new MotionScanCache(x, y, difference); - cache_lock->lock("MotionScan::put_cache"); - cache.append(ptr); - cache_lock->unlock(); -} @@ -808,10 +1506,8 @@ void MotionScan::put_cache(int x, int y, int64_t difference) { \ temp_type difference; \ difference = *prev_row++ - *current_row++; \ - if(difference < 0) \ - result_temp -= difference; \ - else \ - result_temp += difference; \ + difference *= difference; \ + result_temp += difference; \ } \ if(components == 4) \ { \ @@ -853,12 +1549,6 @@ int64_t MotionScan::abs_diff(unsigned char *prev_ptr, case BC_YUVA8888: ABS_DIFF(unsigned char, int64_t, 1, 4) break; - case BC_YUV161616: - ABS_DIFF(uint16_t, int64_t, 1, 3) - break; - case BC_YUVA16161616: - ABS_DIFF(uint16_t, int64_t, 1, 4) - break; } return result; } @@ -893,10 +1583,8 @@ int64_t MotionScan::abs_diff(unsigned char *prev_ptr, 0x100 / 0x100; \ temp_type current_value = *current_row++; \ difference = prev_value - current_value; \ - if(difference < 0) \ - result_temp -= difference; \ - else \ - result_temp += difference; \ + difference *= difference; \ + result_temp += difference; \ } \ \ /* skip alpha */ \ @@ -951,29 +1639,159 @@ int64_t MotionScan::abs_diff_sub(unsigned char *prev_ptr, case BC_YUVA8888: ABS_DIFF_SUB(unsigned char, int64_t, 1, 4) break; - case BC_YUV161616: - ABS_DIFF_SUB(uint16_t, int64_t, 1, 3) + } + return result; +} + + +#if 0 +#define VARIANCE(type, temp_type, multiplier, components) \ +{ \ + temp_type average[3] = { 0 }; \ + temp_type variance[3] = { 0 }; \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + average[k] += row[k]; \ + } \ + row += components; \ + } \ + } \ + for(int k = 0; k < 3; k++) \ + { \ + average[k] /= w * h; \ + } \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + variance[k] += SQR(row[k] - average[k]); \ + } \ + row += components; \ + } \ + } \ + result = (double)multiplier * \ + sqrt((variance[0] + variance[1] + variance[2]) / w / h / 3); \ +} + +double MotionScan::calculate_variance(unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) +{ + double result = 0; + + switch(color_model) + { + case BC_RGB888: + VARIANCE(unsigned char, int, 1, 3) + break; + case BC_RGBA8888: + VARIANCE(unsigned char, int, 1, 4) + break; + case BC_RGB_FLOAT: + VARIANCE(float, double, 255, 3) + break; + case BC_RGBA_FLOAT: + VARIANCE(float, double, 255, 4) + break; + case BC_YUV888: + VARIANCE(unsigned char, int, 1, 3) break; - case BC_YUVA16161616: - ABS_DIFF_SUB(uint16_t, int64_t, 1, 4) + case BC_YUVA8888: + VARIANCE(unsigned char, int, 1, 4) break; } + + return result; } +#endif // 0 +#define RANGE(type, temp_type, multiplier, components) \ +{ \ + temp_type min[3]; \ + temp_type max[3]; \ + min[0] = 0x7fff; \ + min[1] = 0x7fff; \ + min[2] = 0x7fff; \ + max[0] = 0; \ + max[1] = 0; \ + max[2] = 0; \ + \ + for(int i = 0; i < h; i++) \ + { \ + type *row = (type*)current_ptr + i * row_bytes; \ + for(int j = 0; j < w; j++) \ + { \ + for(int k = 0; k < 3; k++) \ + { \ + if(row[k] > max[k]) max[k] = row[k]; \ + if(row[k] < min[k]) min[k] = row[k]; \ + } \ + row += components; \ + } \ + } \ + \ + for(int k = 0; k < 3; k++) \ + { \ + /* printf("MotionScan::calculate_range %d k=%d max=%d min=%d\n", __LINE__, k, max[k], min[k]); */ \ + if(max[k] - min[k] > result) result = max[k] - min[k]; \ + } \ + \ +} -MotionScanCache::MotionScanCache(int x, int y, int64_t difference) +double MotionScan::calculate_range(unsigned char *current_ptr, + int row_bytes, + int w, + int h, + int color_model) { - this->x = x; - this->y = y; - this->difference = difference; + double result = 0; + + switch(color_model) + { + case BC_RGB888: + RANGE(unsigned char, int, 1, 3) + break; + case BC_RGBA8888: + RANGE(unsigned char, int, 1, 4) + break; + case BC_RGB_FLOAT: + RANGE(float, float, 255, 3) + break; + case BC_RGBA_FLOAT: + RANGE(float, float, 255, 4) + break; + case BC_YUV888: + RANGE(unsigned char, int, 1, 3) + break; + case BC_YUVA8888: + RANGE(unsigned char, int, 1, 4) + break; + } + + + return result; } +//#define CLAMP_BLOCK +// this truncates the scan area but not the macroblock unless the macro is defined void MotionScan::clamp_scan(int w, int h, int *block_x1, @@ -1006,29 +1824,37 @@ void MotionScan::clamp_scan(int w, // scan is always out of range before block. if(*scan_x1 < 0) { -// int difference = -*scan_x1; -// *block_x1 += difference; +#ifdef CLAMP_BLOCK + int difference = -*scan_x1; + *block_x1 += difference; +#endif *scan_x1 = 0; } if(*scan_y1 < 0) { -// int difference = -*scan_y1; -// *block_y1 += difference; +#ifdef CLAMP_BLOCK + int difference = -*scan_y1; + *block_y1 += difference; +#endif *scan_y1 = 0; } if(*scan_x2 > w) { int difference = *scan_x2 - w; -// *block_x2 -= difference; +#ifdef CLAMP_BLOCK + *block_x2 -= difference; +#endif *scan_x2 -= difference; } if(*scan_y2 > h) { int difference = *scan_y2 - h; -// *block_y2 -= difference; +#ifdef CLAMP_BLOCK + *block_y2 -= difference; +#endif *scan_y2 -= difference; } @@ -1044,7 +1870,9 @@ void MotionScan::clamp_scan(int w, if(*scan_x1 < 0) { int difference = -*scan_x1; -// *block_x1 += difference; +#ifdef CLAMP_BLOCK + *block_x1 += difference; +#endif *scan_x2 += difference; *scan_x1 = 0; } @@ -1052,7 +1880,9 @@ void MotionScan::clamp_scan(int w, if(*scan_y1 < 0) { int difference = -*scan_y1; -// *block_y1 += difference; +#ifdef CLAMP_BLOCK + *block_y1 += difference; +#endif *scan_y2 += difference; *scan_y1 = 0; } @@ -1061,14 +1891,18 @@ void MotionScan::clamp_scan(int w, { int difference = *scan_x2 - *block_x1 + *block_x2 - w; *scan_x2 -= difference; -// *block_x2 -= difference; +#ifdef CLAMP_BLOCK + *block_x2 -= difference; +#endif } if(*scan_y2 - *block_y1 + *block_y2 > h) { int difference = *scan_y2 - *block_y1 + *block_y2 - h; *scan_y2 -= difference; -// *block_y2 -= difference; +#ifdef CLAMP_BLOCK + *block_y2 -= difference; +#endif } // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));