4 * Copyright (C) 2012 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 "../downsample/downsampleengine.h"
25 #include "motionscan.h"
31 // The module which does the actual scanning
33 MotionScanPackage::MotionScanPackage()
39 MotionScanUnit::MotionScanUnit(MotionScan *server)
42 this->server = server;
43 cache_lock = new Mutex("MotionScanUnit::cache_lock");
46 MotionScanUnit::~MotionScanUnit()
51 void MotionScanUnit::process_package(LoadPackage *package)
53 MotionScanPackage *pkg = (MotionScanPackage*)package;
54 //int w = server->current_frame->get_w();
55 //int h = server->current_frame->get_h();
56 int color_model = server->current_frame->get_color_model();
57 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
58 int row_bytes = server->current_frame->get_bytes_per_line();
61 if( !server->subpixel ) {
63 pkg->difference1 = server->get_cache(pkg->search_x, pkg->search_y);
64 if( pkg->difference1 < 0 ) {
65 //printf("MotionScanUnit::process_package 1 search_x=%d search_y=%d"
66 // " scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d x_steps=%d y_steps=%d\n",
67 // pkg->search_x, pkg->search_y, pkg->scan_x1, pkg->scan_y1, pkg->scan_x2, pkg->scan_y2,
68 // server->x_steps, server->y_steps);
69 // Pointers to first pixel in each block
70 unsigned char *prev_ptr =
71 server->previous_frame->get_rows()[pkg->search_y] +
72 pkg->search_x * pixel_size;
73 unsigned char *current_ptr =
74 server->current_frame->get_rows()[pkg->block_y1] +
75 pkg->block_x1 * pixel_size;
77 pkg->difference1 = MotionScan::abs_diff(prev_ptr, current_ptr, row_bytes,
78 pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1,
81 // printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%ld\n",
82 // __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1);
83 server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1);
88 unsigned char *prev_ptr =
89 server->previous_frame->get_rows()[pkg->search_y] +
90 pkg->search_x * pixel_size;
91 unsigned char *current_ptr =
92 server->current_frame->get_rows()[pkg->block_y1] +
93 pkg->block_x1 * pixel_size;
95 // With subpixel, there are two ways to compare each position, one by shifting
96 // the previous frame and two by shifting the current frame.
97 pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr, current_ptr, row_bytes,
98 pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1,
99 color_model, pkg->sub_x, pkg->sub_y);
101 MotionScan::abs_diff_sub(current_ptr, prev_ptr, row_bytes,
102 pkg->block_x2 - pkg->block_x1, pkg->block_y2 - pkg->block_y1,
103 color_model, pkg->sub_x, pkg->sub_y);
104 //printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
105 // sub_x, sub_y, search_x, search_y, pkg->difference1, pkg->difference2);
109 int64_t MotionScanUnit::get_cache(int x, int y)
112 cache_lock->lock("MotionScanUnit::get_cache");
113 for( int i = 0; i < cache.total; i++ ) {
114 MotionScanCache *ptr = cache.values[i];
115 if( ptr->x == x && ptr->y == y ) {
116 result = ptr->difference;
120 cache_lock->unlock();
124 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
126 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
127 cache_lock->lock("MotionScanUnit::put_cache");
129 cache_lock->unlock();
132 MotionScan::MotionScan(MotionMain *plugin, int total_clients, int total_packages)
134 total_clients, total_packages)
136 this->plugin = plugin;
138 cache_lock = new Mutex("MotionScan::cache_lock");
139 downsampled_previous = 0;
140 downsampled_current = 0;
144 MotionScan::~MotionScan()
147 delete downsampled_previous;
148 delete downsampled_current;
149 // delete downsample;
152 void MotionScan::init_packages()
154 // Set package coords
155 //printf("MotionScan::init_packages %d %d\n", __LINE__, get_total_packages());
156 for( int i = 0; i < get_total_packages(); i++ ) {
157 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
159 pkg->block_x1 = block_x1; pkg->block_x2 = block_x2;
160 pkg->block_y1 = block_y1; pkg->block_y2 = block_y2;
161 pkg->scan_x1 = scan_x1; pkg->scan_x2 = scan_x2;
162 pkg->scan_y1 = scan_y1; pkg->scan_y2 = scan_y2;
163 pkg->difference1 = 0; pkg->difference2 = 0;
164 pkg->step = i; pkg->valid = 1;
165 pkg->dx = pkg->dy = 0;
168 pkg->sub_x = pkg->sub_y = 0;
169 int ipkg = pkg->step;
172 // First package additionally checks position nearest to the best found so far
173 pkg->search_x = x_result;
174 pkg->search_y = y_result;
175 if (pkg->search_x < pkg->scan_x1) pkg->search_x = pkg->scan_x1;
176 if (pkg->search_x >= pkg->scan_x2) pkg->search_x = pkg->scan_x2-1;
177 if (pkg->search_y < pkg->scan_y1) pkg->search_y = pkg->scan_y1;
178 if (pkg->search_y >= pkg->scan_y2) pkg->search_y = pkg->scan_y2-1;
183 pkg->search_x = pkg->scan_x1 + (ipkg % x_steps) *
184 (scan_x2 - scan_x1) / x_steps;
185 pkg->search_y = pkg->scan_y1 + (ipkg / x_steps) *
186 (scan_y2 - scan_y1) / y_steps;
190 pkg->sub_x = pkg->step % x_steps;
191 pkg->sub_y = pkg->step / x_steps;
193 // Unidirectional cases already taken into account in x_steps and y_steps
194 // if( horizontal_only ) pkg->sub_y = 0;
195 // if( vertical_only ) pkg->sub_x = 0;
197 pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE;
198 pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE;
199 pkg->sub_x %= OVERSAMPLE;
200 pkg->sub_y %= OVERSAMPLE;
202 // printf("MotionScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n",
203 // __LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y);
206 // printf("MotionScan::init_packages %d %d,%d %d,%d %d,%d\n",
207 // __LINE__, scan_x1, scan_x2, scan_y1, scan_y2, pkg->search_x, pkg->search_y);
211 LoadClient* MotionScan::new_client()
213 return new MotionScanUnit(this);
216 LoadPackage* MotionScan::new_package()
218 return new MotionScanPackage;
221 void MotionScan::set_test_match(int value)
223 this->test_match = value;
226 void MotionScan::scan_frame(VFrame *previous_frame, VFrame *current_frame,
227 int global_range_w, int global_range_h,
228 int global_block_w, int global_block_h,
229 double block_x, double block_y, int frame_type,
230 int tracking_type, int action_type,
231 int horizontal_only, int vertical_only,
232 int source_position, int total_steps, int total_dx,
233 int total_dy, int global_origin_x, int global_origin_y,
234 int passno, int load_ok, int load_dx, int load_dy)
236 this->previous_frame_arg = previous_frame;
237 this->current_frame_arg = current_frame;
238 this->horizontal_only = horizontal_only;
239 this->vertical_only = vertical_only;
240 this->previous_frame = previous_frame_arg;
241 this->current_frame = current_frame_arg;
242 this->global_origin_x = global_origin_x;
243 this->global_origin_y = global_origin_y;
246 cache.remove_all_objects();
249 int w = current_frame->get_w();
250 int h = current_frame->get_h();
252 // Initial search parameters
253 int scan_w = w * global_range_w / 100;
254 int scan_h = h * global_range_h / 100;
255 int block_w = w * global_block_w / 100;
256 int block_h = h * global_block_h / 100;
258 // passno == 0: single pass tracking
259 // passno == 1: 1st pass of two-pass tracking (skip subpixel)
260 // passno == 2: 2nd pass of two-pass tracking (reduce search area)
261 // Save may be needed for 2nd pass
266 dx_saved = dx_result;
267 dy_saved = dy_result;
270 // Location of block in previous frame
271 double tmp_x1, tmp_y1;
272 double tmp_x2, tmp_y2;
273 tmp_x1 = w * block_x / 100 - block_w / 2;
274 tmp_y1 = h * block_y / 100 - block_h / 2;
275 tmp_x2 = w * block_x / 100 + block_w / 2;
276 tmp_y2 = h * block_y / 100 + block_h / 2;
278 // Attention, process_buffer feeds previous_frame and current_frame interchanged
280 // Offset to location of previous block. This offset needn't be very accurate
281 // since it's the offset on the previous image while current image is moved.
282 // Nevertheless compute it in floating point and round to int afterwards.
283 if( frame_type == MotionScan::TRACK_PREVIOUS ) {
284 tmp_x1 += (double)total_dx / OVERSAMPLE;
285 tmp_y1 += (double)total_dy / OVERSAMPLE;
286 tmp_x2 += (double)total_dx / OVERSAMPLE;
287 tmp_y2 += (double)total_dy / OVERSAMPLE;
288 // Compensate displacement computed in the 1st pass
291 tmp_x1 -= (double)dx_saved / OVERSAMPLE;
292 tmp_y1 -= (double)dy_saved / OVERSAMPLE;
293 tmp_x2 -= (double)dx_saved / OVERSAMPLE;
294 tmp_y2 -= (double)dy_saved / OVERSAMPLE;
297 block_x1 = lrint (tmp_x1);
298 block_y1 = lrint (tmp_y1);
299 block_x2 = lrint (tmp_x2);
300 block_y2 = lrint (tmp_y2);
302 // Preinitialize sane translation results
307 //printf("MotionScan::scan_frame %d frame=%ld passno=%d\n", __LINE__, plugin->get_source_position(), passno);
308 switch( tracking_type ) {
310 case MotionScan::NO_CALCULATE:
311 dx_result = dy_result = 0;
315 case MotionScan::LOAD:
316 case MotionScan::SAVE:
320 if (passno == 2) dx_result = dy_result = 0;
325 // Scan from scratch with sane translation results
333 if( !skip && test_match ) {
334 if( previous_frame->data_matches(current_frame) ) {
335 // printf("MotionScan::scan_frame: data matches. skipping.\n");
336 dx_result = dy_result = 0;
342 //printf("MotionScan::scan_frame %d\n", __LINE__);
343 // Location of block in current frame
344 int origin_offset_x = this->global_origin_x * w / 100;
345 int origin_offset_y = this->global_origin_y * h / 100;
346 x_result = block_x1 + origin_offset_x;
347 y_result = block_y1 + origin_offset_y;
351 //printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
352 // block_x1 + block_w / 2, block_y1 + block_h / 2,
353 // block_w, block_h, block_x1, block_y1, block_x2, block_y2);
357 // Evtl reduce scan area for refinement pass to gain speed
358 // For 1st pass subpixel search will be skipped
359 if (scan_w > 64) scan_w /= 4;
360 else if (scan_w > 16) scan_w = 16;
361 if (scan_h > 64) scan_h /= 4;
362 else if (scan_h > 16) scan_h = 16;
366 // Cache needs to be cleared if downsampling is used because the sums of
367 // different downsamplings can't be compared.
368 // Subpixel never uses the cache.
369 // cache.remove_all_objects();
370 scan_x1 = x_result - scan_w / 2;
371 scan_y1 = y_result - scan_h / 2;
372 scan_x2 = x_result + scan_w / 2;
373 scan_y2 = y_result + scan_h / 2;
374 if (scan_x2 <= scan_x1) scan_x2 = scan_x1 + 1;
375 if (scan_y2 <= scan_y1) scan_y2 = scan_y1 + 1;
377 // Zero out requested values
378 if( horizontal_only ) {
380 scan_y2 = block_y1 + 1;
382 if( vertical_only ) {
384 scan_x2 = block_x1 + 1;
386 //printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
387 // block_x1, block_y1, block_x2, block_y2, scan_x1, scan_y1, scan_x2, scan_y2);
388 // Clamp the block coords before the scan so we get useful scan coords.
389 clamp_scan(w, h, &block_x1, &block_y1, &block_x2,
390 &block_y2, &scan_x1, &scan_y1, &scan_x2,
392 // printf("MotionScan::scan_frame 1 %d block_x1=%d block_y1=%d block_x2=%d block_y2=%d\n"
393 // " scan_x1=%d scan_y1=%d scan_x2=%d scan_y2=%d\n"
394 // " x_result=%d y_result=%d\n", __LINE__, block_x1, block_y1, block_x2, block_y2,
395 // scan_x1, scan_y1, scan_x2, scan_y2, x_result, y_result);
397 // Give up if invalid coords.
398 if (scan_y2 <= scan_y1 || scan_x2 <= scan_x1 ||
399 block_x2 <= block_x1 || block_y2 <= block_y1 )
402 // For subpixel, the top row and left column are skipped
405 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
406 // Scan every subpixel in a scan_w * scan_h pixel square
407 x_steps = (scan_x2 - scan_x1) * OVERSAMPLE;
408 y_steps = (scan_y2 - scan_y1) * OVERSAMPLE;
409 if(horizontal_only) y_steps = 1;
410 if(vertical_only) x_steps = 1;
411 if (x_steps < 1) x_steps = 1;
412 if (y_steps < 1) y_steps = 1;
414 total_pixels = x_steps * y_steps;
415 this->total_steps = total_pixels;
417 set_package_count(this->total_steps);
420 // Get least difference
421 int64_t min_difference = -1, max_difference = -1, noiselev = -1;
422 double radius, best_radius;
423 for( int i = 0; i < get_total_packages(); i++ ) {
424 MotionScanPackage *pkg = (MotionScanPackage *)get_package(i);
425 if( pkg->difference1 < min_difference ||
426 min_difference == -1 ) {
427 min_difference = pkg->difference1;
429 // The sub coords are 1 pixel up & left of the block coords
430 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
431 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
433 dx_result = block_x1 * OVERSAMPLE - x_result;
434 dy_result = block_y1 * OVERSAMPLE - y_result;
435 //printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff1=%ld\n",
436 //__LINE__, dx_result, dy_result, min_difference);
438 if(pkg->difference1 > max_difference || max_difference == -1)
440 max_difference = pkg->difference1;
443 if( pkg->difference2 < min_difference ) {
444 min_difference = pkg->difference2;
446 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
447 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
449 dx_result = block_x1 * OVERSAMPLE - x_result;
450 dy_result = block_y1 * OVERSAMPLE - y_result;
451 //printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff2=%ld\n",
452 //__LINE__, dx_result, dy_result, min_difference);
454 if(pkg->difference2 > max_difference)
456 max_difference = pkg->difference2;
458 //printf("MotionScan::scan_frame %d pkg=%d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%ld diff2=%ld min_diff=%ld max_diff=%ld\n",
459 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2, min_difference, max_difference);
461 // Determine noise level (not active on pass 2)
462 noiselev = min_difference+(max_difference-min_difference)*plugin->config.noise_level/100;
463 if (passno == 2) noiselev = min_difference;
464 //printf("MotionScan::scan_frame min_diff=%ld max_diff=%ld noiselev=%ld\n", min_difference, max_difference, noiselev);
465 for(int i = 0; i < get_total_packages(); i++)
467 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
468 if(pkg->difference1 <= noiselev)
470 // Below noise level - check subpixel translation radius
472 (double)((block_x1-pkg->search_x)*OVERSAMPLE-pkg->sub_x),
473 (double)((block_y1-pkg->search_y)*OVERSAMPLE-pkg->sub_y));
474 best_radius = hypot((double)dx_result,(double)dy_result);
475 if(radius < best_radius ||
476 (radius == best_radius && pkg->difference1 < min_difference))
478 // Below noise level and smaller translation, memorize
479 min_difference = pkg->difference1;
480 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
481 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
482 dx_result = block_x1 * OVERSAMPLE - x_result;
483 dy_result = block_y1 * OVERSAMPLE - y_result;
484 //printf("MotionScan::scan_frame %d diff1 override=%d search_x=%d search_y=%d sub_x=%d sub_y=%d dx_result=%d dy_result=%d diff1=%ld min_diff=%ld\n",
485 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, dx_result, dy_result, pkg->difference1, min_difference);
488 if(pkg->difference2 <= noiselev)
490 // Below noise level - check subpixel translation radius
492 (double)((block_x1-pkg->search_x)*OVERSAMPLE+pkg->sub_x),
493 (double)((block_y1-pkg->search_y)*OVERSAMPLE+pkg->sub_y));
494 best_radius = hypot((double)dx_result,(double)dy_result);
495 if(radius < best_radius ||
496 (radius == best_radius && pkg->difference2 < min_difference))
498 // Below noise level and smaller translation, memorize
499 min_difference = pkg->difference2;
500 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
501 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
502 dx_result = block_x1 * OVERSAMPLE - x_result;
503 dy_result = block_y1 * OVERSAMPLE - y_result;
504 //printf("MotionScan::scan_frame %d diff2 override=%d search_x=%d search_y=%d sub_x=%d sub_y=%d dx_result=%d dy_result=%d diff2=%ld min_diff=%ld\n",
505 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, dx_result, dy_result, pkg->difference2, min_difference);
515 (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
517 MIN(total_steps, total_pixels);
519 if( this->total_steps == total_pixels ) {
520 x_steps = scan_x2 - scan_x1;
521 y_steps = scan_y2 - scan_y1;
524 x_steps = (int)sqrt(this->total_steps);
525 y_steps = (int)sqrt(this->total_steps);
527 if(horizontal_only) y_steps = 1;
528 if(vertical_only) x_steps = 1;
529 if (x_steps < 1) x_steps = 1;
530 if (y_steps < 1) y_steps = 1;
531 this->total_steps = x_steps * y_steps;
533 // Use downsampled images
534 // if( scan_x2 - scan_x1 > x_steps * 4 ||
535 // scan_y2 - scan_y1 > y_steps * 4 )
537 // printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n",
543 // x_steps * y_steps);
545 // if( !downsampled_previous ||
546 // !downsampled_previous->equivalent(previous_frame_arg) )
548 // delete downsampled_previous;
549 // downsampled_previous = new VFrame(*previous_frame_arg);
552 // if( !downsampled_current ||
553 // !downsampled_current->equivalent(current_frame_arg) )
555 // delete downsampled_current;
556 // downsampled_current = new VFrame(*current_frame_arg);
561 // downsample = new DownSampleServer(get_total_clients(),
562 // get_total_clients());
563 // downsample->process_frame(downsampled_previous,
564 // previous_frame_arg, 1, 1, 1, 1,
565 // (scan_y2 - scan_y1) / y_steps,
566 // (scan_x2 - scan_x1) / x_steps,
568 // downsample->process_frame(downsampled_current,
569 // current_frame_arg, 1, 1, 1, 1,
570 // (scan_y2 - scan_y1) / y_steps,
571 // (scan_x2 - scan_x1) / x_steps,
573 // this->previous_frame = downsampled_previous;
574 // this->current_frame = downsampled_current;
577 // printf("MotionScan::scan_frame %d this->total_steps=%d\n",
578 // __LINE__, this->total_steps);
580 // One extra step for the position with zero translation
581 set_package_count(this->total_steps+1);
584 // Get least difference
585 int64_t min_difference = -1, max_difference = -1, noiselev = -1;
586 double radius, best_radius;
587 for( int i = 0; i < get_total_packages(); i++ ) {
588 MotionScanPackage *pkg = (MotionScanPackage *) get_package(i);
589 if (pkg->difference1 < min_difference
590 || min_difference == -1) {
591 min_difference = pkg->difference1;
592 x_result = pkg->search_x;
593 y_result = pkg->search_y;
595 if(pkg->difference1 > max_difference || max_difference == -1)
597 max_difference = pkg->difference1;
599 //printf("MotionScan::scan_frame %d pkg=%d search_x=%d search_y=%d diff=%ld min_diff=%ld max_diff=%ld\n",
600 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference, max_difference);
602 // Determine noise level (not active on pass 2)
603 noiselev = min_difference+(max_difference-min_difference)*plugin->config.noise_level/100;
604 if (passno == 2) noiselev = min_difference;
605 //printf("MotionScan::scan_frame min_diff=%ld max_diff=%ld noiselev=%ld\n", min_difference, max_difference, noiselev);
606 for(int i = 0; i < get_total_packages(); i++)
608 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
609 // Already found as the best search, not necessary to memorize
610 if(x_result == pkg->search_x && y_result == pkg->search_y) continue;
611 // Above noise level - a definitely bad search, skip
612 if(pkg->difference1 > noiselev) continue;
613 radius = hypot((double)(block_x1-pkg->search_x),(double)(block_y1-pkg->search_y));
614 best_radius = hypot((double)(block_x1-x_result),(double)(block_y1-y_result));
615 // Below noise level but bigger translation, skip
616 if(radius > best_radius) continue;
617 // Below noise level and smaller translation, memorize
618 if(radius < best_radius)
620 min_difference = pkg->difference1;
621 x_result = pkg->search_x;
622 y_result = pkg->search_y;
623 //printf("MotionScan::scan_frame %d search override=%d search_x=%d search_y=%d diff=%ld min_diff=%ld\n",
624 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference);
627 // Equal translations, memorize search with min difference
628 if(pkg->difference1 < min_difference)
630 min_difference = pkg->difference1;
631 x_result = pkg->search_x;
632 y_result = pkg->search_y;
633 //printf("MotionScan::scan_frame %d difference override=%d search_x=%d search_y=%d diff=%ld min_diff=%ld\n",
634 //__LINE__, i, pkg->search_x, pkg->search_y, pkg->difference1, min_difference);
638 int rad_x = 0, rad_y = 0;
639 for(int i = 0; i < get_total_packages(); i++)
641 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
642 // Above noise level - skip this radius
643 if(pkg->difference1 > noiselev) continue;
644 // Below noise level - measure max distance from the best position
645 if(rad_x < abs(x_result-pkg->search_x))
647 rad_x = abs(x_result-pkg->search_x);
648 //printf("MotionScan::scan_frame %d X-radius override=%d search_x=%d radius=%d\n",
649 //__LINE__, i, pkg->search_x, rad_x);
651 if(rad_y < abs(y_result-pkg->search_y))
653 rad_y = abs(y_result-pkg->search_y);
654 //printf("MotionScan::scan_frame %d Y-radius override=%d search_y=%d radius=%d\n",
655 //__LINE__, i, pkg->search_y, rad_y);
658 x_result *= OVERSAMPLE;
659 y_result *= OVERSAMPLE;
660 //printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%ld\n",
661 //__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, min_difference);
663 // If a new search is required, rescale results back to pixels.
664 if( this->total_steps >= total_pixels ) {
665 // Single pixel accuracy reached. Now do exhaustive subpixel search.
666 // Subpixel search skipped on the 1st pass of a two-pass search.
667 if((action_type == MotionScan::STABILIZE ||
668 action_type == MotionScan::TRACK ||
669 action_type == MotionScan::NOTHING) &&
672 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
673 // Subpixel scan area might be +/- one pixel from the best position
676 // Evtl expand scan area to +/- two pixels if some samples below noise
677 if (rad_x > 0) scan_w = 4;
678 if (rad_y > 0) scan_h = 4;
679 x_result /= OVERSAMPLE;
680 y_result /= OVERSAMPLE;
683 // Fill in results and quit
685 dx_result = block_x1 * OVERSAMPLE - x_result;
686 dy_result = block_y1 * OVERSAMPLE - y_result;
687 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
691 // Reduce scan area and try again
693 // Optimum new scan area might be +/- one search step from the best position
694 scan_w = (scan_x2 - scan_x1) * 2 / x_steps;
695 scan_h = (scan_y2 - scan_y1) * 2 / y_steps;
696 // Evtl expand scan area to +/- two search steps if some samples below noise
697 if (rad_x > 0) scan_w = (scan_x2 - scan_x1) * 4 / x_steps;
698 if (rad_y > 0) scan_h = (scan_y2 - scan_y1) * 4 / y_steps;
699 // Evtl expand scan area to include samples below noise level
700 if (scan_w < rad_x * 2) scan_w = rad_x * 2;
701 if (scan_h < rad_y * 2) scan_h = rad_y * 2;
702 // Always reduce scan area at least twice
703 if (scan_w > (scan_x2 - scan_x1) / 2) scan_w = (scan_x2 - scan_x1) / 2;
704 if (scan_h > (scan_y2 - scan_y1) / 2) scan_h = (scan_y2 - scan_y1) / 2;
705 // But retain scan area at least one pixel in size
706 if (scan_w < 1) scan_w = 1;
707 if (scan_h < 1) scan_h = 1;
708 x_result /= OVERSAMPLE;
709 y_result /= OVERSAMPLE;
713 // Negate results as previous_frame and current_frame have been interchanged
714 dx_result = -dx_result;
715 dy_result = -dy_result;
717 //printf("MotionScan::scan_frame %d\n", __LINE__);
719 if( vertical_only ) dx_result = 0;
720 if( horizontal_only ) dy_result = 0;
722 // printf("MotionScan::scan_frame %d passno=%d saved dx=%.2f dy=%.2f measured dx=%.2f dy=%.2f twopass dx=%.2f dy=%.2f\n",
724 // (float)dx_saved / OVERSAMPLE,
725 // (float)dy_saved / OVERSAMPLE,
726 // (float)this->dx_result / OVERSAMPLE,
727 // (float)this->dy_result / OVERSAMPLE,
728 // (float)(this->dx_result + dx_saved) / OVERSAMPLE,
729 // (float)(this->dy_result + dy_saved) / OVERSAMPLE);
732 int64_t MotionScan::get_cache(int x, int y)
735 cache_lock->lock("MotionScan::get_cache");
736 for( int i = 0; i < cache.total; i++ ) {
737 MotionScanCache *ptr = cache.values[i];
738 if( ptr->x == x && ptr->y == y ) {
739 result = ptr->difference;
743 cache_lock->unlock();
747 void MotionScan::put_cache(int x, int y, int64_t difference)
749 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
750 cache_lock->lock("MotionScan::put_cache");
752 cache_lock->unlock();
755 #define ABS_DIFF(model, type, temp_type, multiplier, components) case model: { \
756 temp_type result_temp = 0; \
757 for( int i = 0; i < h; i++ ) { \
758 type *prev_row = (type*)prev_ptr; \
759 type *current_row = (type*)current_ptr; \
760 for( int j = 0; j < w; j++ ) { \
761 for( int k = 0; k < 3; k++ ) { \
762 temp_type difference; \
763 difference = *prev_row++ - *current_row++; \
764 if( difference < 0 ) \
765 result_temp -= difference; \
767 result_temp += difference; \
769 if( components == 4 ) { \
774 prev_ptr += row_bytes; \
775 current_ptr += row_bytes; \
777 result = (int64_t)(result_temp * multiplier); \
780 int64_t MotionScan::abs_diff(unsigned char *prev_ptr,
781 unsigned char *current_ptr, int row_bytes, int w,
782 int h, int color_model)
785 switch( color_model ) {
786 ABS_DIFF(BC_RGB888, unsigned char, int64_t, 1, 3);
787 ABS_DIFF(BC_RGBA8888, unsigned char, int64_t, 1, 4);
788 ABS_DIFF(BC_RGB_FLOAT, float, double, 0x10000, 3);
789 ABS_DIFF(BC_RGBA_FLOAT, float, double, 0x10000, 4);
790 ABS_DIFF(BC_YUV888, unsigned char, int64_t, 1, 3);
791 ABS_DIFF(BC_YUVA8888, unsigned char, int64_t, 1, 4);
792 ABS_DIFF(BC_YUV161616, uint16_t, int64_t, 1, 3);
793 ABS_DIFF(BC_YUVA16161616, uint16_t, int64_t, 1, 4);
798 #define ABS_DIFF_SUB(model, type, temp_type, multiplier, components) case model: { \
799 temp_type result_temp = 0; \
800 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
801 temp_type y1_fraction = 0x100 - y2_fraction; \
802 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
803 temp_type x1_fraction = 0x100 - x2_fraction; \
804 for( int i = 0; i < h_sub; i++ ) { \
805 type *prev_row1 = (type*)prev_ptr; \
806 type *prev_row2 = (type*)prev_ptr + components; \
807 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
808 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
809 type *current_row = (type*)current_ptr; \
810 for( int j = 0; j < w_sub; j++ ) { \
811 /* Scan each component */ \
812 for( int k = 0; k < 3; k++ ) { \
813 temp_type difference; \
814 temp_type prev_value = \
815 (*prev_row1++ * x1_fraction * y1_fraction + \
816 *prev_row2++ * x2_fraction * y1_fraction + \
817 *prev_row3++ * x1_fraction * y2_fraction + \
818 *prev_row4++ * x2_fraction * y2_fraction) / \
820 temp_type current_value = *current_row++; \
821 difference = prev_value - current_value; \
822 if( difference < 0 ) \
823 result_temp -= difference; \
825 result_temp += difference; \
829 if( components == 4 ) { \
837 prev_ptr += row_bytes; \
838 current_ptr += row_bytes; \
840 result = (int64_t)(result_temp * multiplier); \
843 int64_t MotionScan::abs_diff_sub(unsigned char *prev_ptr,
844 unsigned char *current_ptr, int row_bytes,
845 int w, int h, int color_model, int sub_x,
852 switch( color_model ) {
853 ABS_DIFF_SUB(BC_RGB888, unsigned char, int64_t, 1, 3);
854 ABS_DIFF_SUB(BC_RGBA8888, unsigned char, int64_t, 1, 4);
855 ABS_DIFF_SUB(BC_RGB_FLOAT, float, double, 0x10000, 3);
856 ABS_DIFF_SUB(BC_RGBA_FLOAT, float, double, 0x10000, 4);
857 ABS_DIFF_SUB(BC_YUV888, unsigned char, int64_t, 1, 3);
858 ABS_DIFF_SUB(BC_YUVA8888, unsigned char, int64_t, 1, 4);
859 ABS_DIFF_SUB(BC_YUV161616, uint16_t, int64_t, 1, 3);
860 ABS_DIFF_SUB(BC_YUVA16161616, uint16_t, int64_t, 1, 4);
865 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
869 this->difference = difference;
872 void MotionScan::clamp_scan(int w, int h,
873 int *block_x1, int *block_y1, int *block_x2,
874 int *block_y2, int *scan_x1, int *scan_y1,
875 int *scan_x2, int *scan_y2, int use_absolute)
877 //printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
878 // w, h, *block_x1, *block_y1, *block_x2, *block_y2, *scan_x1, *scan_y1, *scan_x2, *scan_y2,
882 // Limit size of scan area
883 // Used for drawing vectors
884 // scan is always out of range before block.
886 // int difference = -*scan_x1;
887 // *block_x1 += difference;
892 // int difference = -*scan_y1;
893 // *block_y1 += difference;
898 int difference = *scan_x2 - w;
899 // *block_x2 -= difference;
900 *scan_x2 -= difference;
904 int difference = *scan_y2 - h;
905 // *block_y2 -= difference;
906 *scan_y2 -= difference;
909 CLAMP(*scan_x1, 0, w);
910 CLAMP(*scan_y1, 0, h);
911 CLAMP(*scan_x2, 0, w);
912 CLAMP(*scan_y2, 0, h);
915 // Limit range of upper left block coordinates
916 // Used for motion tracking
918 int difference = -*scan_x1;
919 // *block_x1 += difference;
920 *scan_x2 += difference;
925 int difference = -*scan_y1;
926 // *block_y1 += difference;
927 *scan_y2 += difference;
931 if( *scan_x2 - *block_x1 + *block_x2 > w ) {
932 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
933 *scan_x2 -= difference;
934 // *block_x2 -= difference;
937 if( *scan_y2 - *block_y1 + *block_y2 > h ) {
938 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
939 *scan_y2 -= difference;
940 // *block_y2 -= difference;
942 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
943 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
944 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
945 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
948 // Sanity checks which break the calculation but should never happen if the
949 // center of the block is inside the frame.
950 CLAMP(*block_x1, 0, w);
951 CLAMP(*block_x2, 0, w);
952 CLAMP(*block_y1, 0, h);
953 CLAMP(*block_y2, 0, h);
955 //printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
956 // w, h, *block_x1, *block_y1, *block_x2, *block_y2, *scan_x1, *scan_y1, *scan_x2, *scan_y2,