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
37 MotionScanPackage::MotionScanPackage()
48 MotionScanUnit::MotionScanUnit(MotionScan *server)
51 this->server = server;
52 cache_lock = new Mutex("MotionScanUnit::cache_lock");
55 MotionScanUnit::~MotionScanUnit()
62 void MotionScanUnit::process_package(LoadPackage *package)
64 MotionScanPackage *pkg = (MotionScanPackage*)package;
65 //int w = server->current_frame->get_w();
66 //int h = server->current_frame->get_h();
67 int color_model = server->current_frame->get_color_model();
68 int pixel_size = BC_CModels::calculate_pixelsize(color_model);
69 int row_bytes = server->current_frame->get_bytes_per_line();
86 pkg->difference1 = server->get_cache(pkg->search_x, pkg->search_y);
87 if(pkg->difference1 < 0)
89 //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",
90 //pkg->search_x, pkg->search_y, pkg->scan_x1, pkg->scan_y1, pkg->scan_x2, pkg->scan_y2, server->x_steps, server->y_steps);
91 // Pointers to first pixel in each block
92 unsigned char *prev_ptr = server->previous_frame->get_rows()[
94 pkg->search_x * pixel_size;
95 unsigned char *current_ptr = server->current_frame->get_rows()[
97 pkg->block_x1 * pixel_size;
100 pkg->difference1 = MotionScan::abs_diff(prev_ptr,
103 pkg->block_x2 - pkg->block_x1,
104 pkg->block_y2 - pkg->block_y1,
107 // printf("MotionScanUnit::process_package %d search_x=%d search_y=%d diff=%lld\n",
108 // __LINE__, server->block_x1 - pkg->search_x, server->block_y1 - pkg->search_y, pkg->difference1);
109 server->put_cache(pkg->search_x, pkg->search_y, pkg->difference1);
130 unsigned char *prev_ptr = server->previous_frame->get_rows()[
132 pkg->search_x * pixel_size;
133 unsigned char *current_ptr = server->current_frame->get_rows()[
135 pkg->block_x1 * pixel_size;
137 // With subpixel, there are two ways to compare each position, one by shifting
138 // the previous frame and two by shifting the current frame.
139 pkg->difference1 = MotionScan::abs_diff_sub(prev_ptr,
142 pkg->block_x2 - pkg->block_x1,
143 pkg->block_y2 - pkg->block_y1,
147 pkg->difference2 = MotionScan::abs_diff_sub(current_ptr,
150 pkg->block_x2 - pkg->block_x1,
151 pkg->block_y2 - pkg->block_y1,
155 // printf("MotionScanUnit::process_package sub_x=%d sub_y=%d search_x=%d search_y=%d diff1=%lld diff2=%lld\n",
161 // pkg->difference2);
178 int64_t MotionScanUnit::get_cache(int x, int y)
181 cache_lock->lock("MotionScanUnit::get_cache");
182 for(int i = 0; i < cache.total; i++)
184 MotionScanCache *ptr = cache.values[i];
185 if(ptr->x == x && ptr->y == y)
187 result = ptr->difference;
191 cache_lock->unlock();
195 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
197 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
198 cache_lock->lock("MotionScanUnit::put_cache");
200 cache_lock->unlock();
213 MotionScan::MotionScan(int total_clients,
217 total_clients, total_packages
221 cache_lock = new Mutex("MotionScan::cache_lock");
222 downsampled_previous = 0;
223 downsampled_current = 0;
227 MotionScan::~MotionScan()
230 delete downsampled_previous;
231 delete downsampled_current;
232 // delete downsample;
236 void MotionScan::init_packages()
238 // Set package coords
239 //printf("MotionScan::init_packages %d %d\n", __LINE__, get_total_packages());
240 for(int i = 0; i < get_total_packages(); i++)
242 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
244 pkg->block_x1 = block_x1;
245 pkg->block_x2 = block_x2;
246 pkg->block_y1 = block_y1;
247 pkg->block_y2 = block_y2;
248 pkg->scan_x1 = scan_x1;
249 pkg->scan_x2 = scan_x2;
250 pkg->scan_y1 = scan_y1;
251 pkg->scan_y2 = scan_y2;
253 pkg->difference1 = 0;
254 pkg->difference2 = 0;
261 pkg->search_x = pkg->scan_x1 + (pkg->step % x_steps) *
262 (scan_x2 - scan_x1) / x_steps;
263 pkg->search_y = pkg->scan_y1 + (pkg->step / x_steps) *
264 (scan_y2 - scan_y1) / y_steps;
270 pkg->sub_x = pkg->step % (OVERSAMPLE * 2);
271 pkg->sub_y = pkg->step / (OVERSAMPLE * 2);
283 pkg->search_x = pkg->scan_x1 + pkg->sub_x / OVERSAMPLE + 1;
284 pkg->search_y = pkg->scan_y1 + pkg->sub_y / OVERSAMPLE + 1;
285 pkg->sub_x %= OVERSAMPLE;
286 pkg->sub_y %= OVERSAMPLE;
290 // printf("MotionScan::init_packages %d i=%d search_x=%d search_y=%d sub_x=%d sub_y=%d\n",
299 // printf("MotionScan::init_packages %d %d,%d %d,%d %d,%d\n",
310 LoadClient* MotionScan::new_client()
312 return new MotionScanUnit(this);
315 LoadPackage* MotionScan::new_package()
317 return new MotionScanPackage;
321 void MotionScan::set_test_match(int value)
323 this->test_match = value;
326 void MotionScan::scan_frame(VFrame *previous_frame,
327 VFrame *current_frame,
346 this->previous_frame_arg = previous_frame;
347 this->current_frame_arg = current_frame;
348 this->horizontal_only = horizontal_only;
349 this->vertical_only = vertical_only;
350 this->previous_frame = previous_frame_arg;
351 this->current_frame = current_frame_arg;
352 this->global_origin_x = global_origin_x;
353 this->global_origin_y = global_origin_y;
356 cache.remove_all_objects();
359 int w = current_frame->get_w();
360 int h = current_frame->get_h();
362 // Initial search parameters
363 int scan_w = w * global_range_w / 100;
364 int scan_h = h * global_range_h / 100;
365 int block_w = w * global_block_w / 100;
366 int block_h = h * global_block_h / 100;
368 // Location of block in previous frame
369 block_x1 = (int)(w * block_x / 100 - block_w / 2);
370 block_y1 = (int)(h * block_y / 100 - block_h / 2);
371 block_x2 = (int)(w * block_x / 100 + block_w / 2);
372 block_y2 = (int)(h * block_y / 100 + block_h / 2);
374 // Offset to location of previous block. This offset needn't be very accurate
375 // since it's the offset of the previous image and current image we want.
376 if(frame_type == MotionScan::TRACK_PREVIOUS)
378 block_x1 += total_dx / OVERSAMPLE;
379 block_y1 += total_dy / OVERSAMPLE;
380 block_x2 += total_dx / OVERSAMPLE;
381 block_y2 += total_dy / OVERSAMPLE;
386 switch(tracking_type)
389 case MotionScan::NO_CALCULATE:
395 case MotionScan::LOAD:
397 // Load result from disk
398 char string[BCTEXTLEN];
399 sprintf(string, "%s%06d",
402 //printf("MotionScan::scan_frame %d %s\n", __LINE__, string);
403 FILE *input = fopen(string, "r");
406 (void)fscanf(input, "%d %d",
407 &dx_result, &dy_result);
411 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
424 if(!skip && test_match)
426 if(previous_frame->data_matches(current_frame))
428 printf("MotionScan::scan_frame: data matches. skipping.\n");
438 //printf("MotionScan::scan_frame %d\n", __LINE__);
439 // Location of block in current frame
440 int origin_offset_x = this->global_origin_x * w / 100;
441 int origin_offset_y = this->global_origin_y * h / 100;
442 int x_result = block_x1 + origin_offset_x;
443 int y_result = block_y1 + origin_offset_y;
445 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
446 // block_x1 + block_w / 2,
447 // block_y1 + block_h / 2,
457 // Cache needs to be cleared if downsampling is used because the sums of
458 // different downsamplings can't be compared.
459 // Subpixel never uses the cache.
460 // cache.remove_all_objects();
461 scan_x1 = x_result - scan_w / 2;
462 scan_y1 = y_result - scan_h / 2;
463 scan_x2 = x_result + scan_w / 2;
464 scan_y2 = y_result + scan_h / 2;
468 // Zero out requested values
472 scan_y2 = block_y1 + 1;
477 scan_x2 = block_x1 + 1;
480 // printf("MotionScan::scan_frame 1 %d %d %d %d %d %d %d %d\n",
489 // Clamp the block coords before the scan so we get useful scan coords.
501 // 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",
515 // Give up if invalid coords.
516 if(scan_y2 <= scan_y1 ||
517 scan_x2 <= scan_x1 ||
518 block_x2 <= block_x1 ||
519 block_y2 <= block_y1)
522 // For subpixel, the top row and left column are skipped
526 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
527 // Scan every subpixel in a 2 pixel * 2 pixel square
528 total_pixels = (2 * OVERSAMPLE) * (2 * OVERSAMPLE);
530 this->total_steps = total_pixels;
531 // These aren't used in subpixel
532 this->x_steps = OVERSAMPLE * 2;
533 this->y_steps = OVERSAMPLE * 2;
535 set_package_count(this->total_steps);
538 // Get least difference
539 int64_t min_difference = -1;
540 for(int i = 0; i < get_total_packages(); i++)
542 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
543 //printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
544 //__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
545 if(pkg->difference1 < min_difference || min_difference == -1)
547 min_difference = pkg->difference1;
549 // The sub coords are 1 pixel up & left of the block coords
550 x_result = pkg->search_x * OVERSAMPLE + pkg->sub_x;
551 y_result = pkg->search_y * OVERSAMPLE + pkg->sub_y;
555 dx_result = block_x1 * OVERSAMPLE - x_result;
556 dy_result = block_y1 * OVERSAMPLE - y_result;
557 //printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
558 //__LINE__, dx_result, dy_result, min_difference);
561 if(pkg->difference2 < min_difference)
563 min_difference = pkg->difference2;
565 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
566 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
568 dx_result = block_x1 * OVERSAMPLE - x_result;
569 dy_result = block_y1 * OVERSAMPLE - y_result;
570 //printf("MotionScan::scan_frame %d dx_result=%d dy_result=%d diff=%lld\n",
571 //__LINE__, dx_result, dy_result, min_difference);
580 total_pixels = (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
581 this->total_steps = MIN(total_steps, total_pixels);
583 if(this->total_steps == total_pixels)
585 x_steps = scan_x2 - scan_x1;
586 y_steps = scan_y2 - scan_y1;
590 x_steps = (int)sqrt(this->total_steps);
591 y_steps = (int)sqrt(this->total_steps);
594 // Use downsampled images
595 // if(scan_x2 - scan_x1 > x_steps * 4 ||
596 // scan_y2 - scan_y1 > y_steps * 4)
598 // printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n",
604 // x_steps * y_steps);
606 // if(!downsampled_previous ||
607 // !downsampled_previous->equivalent(previous_frame_arg))
609 // delete downsampled_previous;
610 // downsampled_previous = new VFrame(*previous_frame_arg);
613 // if(!downsampled_current ||
614 // !downsampled_current->equivalent(current_frame_arg))
616 // delete downsampled_current;
617 // downsampled_current = new VFrame(*current_frame_arg);
622 // downsample = new DownSampleServer(get_total_clients(),
623 // get_total_clients());
624 // downsample->process_frame(downsampled_previous,
625 // previous_frame_arg,
630 // (scan_y2 - scan_y1) / y_steps,
631 // (scan_x2 - scan_x1) / x_steps,
634 // downsample->process_frame(downsampled_current,
635 // current_frame_arg,
640 // (scan_y2 - scan_y1) / y_steps,
641 // (scan_x2 - scan_x1) / x_steps,
644 // this->previous_frame = downsampled_previous;
645 // this->current_frame = downsampled_current;
652 // printf("MotionScan::scan_frame %d this->total_steps=%d\n",
654 // this->total_steps);
657 set_package_count(this->total_steps);
660 // Get least difference
661 int64_t min_difference = -1;
662 for(int i = 0; i < get_total_packages(); i++)
664 MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
665 //printf("MotionScan::scan_frame %d search_x=%d search_y=%d sub_x=%d sub_y=%d diff1=%lld diff2=%lld\n",
666 //__LINE__, pkg->search_x, pkg->search_y, pkg->sub_x, pkg->sub_y, pkg->difference1, pkg->difference2);
667 if(pkg->difference1 < min_difference || min_difference == -1)
669 min_difference = pkg->difference1;
670 x_result = pkg->search_x;
671 y_result = pkg->search_y;
672 x_result *= OVERSAMPLE;
673 y_result *= OVERSAMPLE;
674 //printf("MotionScan::scan_frame %d x_result=%d y_result=%d diff=%lld\n",
675 //__LINE__, block_x1 * OVERSAMPLE - x_result, block_y1 * OVERSAMPLE - y_result, pkg->difference1);
680 // If a new search is required, rescale results back to pixels.
681 if(this->total_steps >= total_pixels)
683 // Single pixel accuracy reached. Now do exhaustive subpixel search.
684 if(action_type == MotionScan::STABILIZE ||
685 action_type == MotionScan::TRACK ||
686 action_type == MotionScan::NOTHING)
688 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, x_result, y_result);
689 x_result /= OVERSAMPLE;
690 y_result /= OVERSAMPLE;
697 // Fill in results and quit
698 dx_result = block_x1 * OVERSAMPLE - x_result;
699 dy_result = block_y1 * OVERSAMPLE - y_result;
700 //printf("MotionScan::scan_frame %d %d %d\n", __LINE__, dx_result, dy_result);
705 // Reduce scan area and try again
707 scan_w = (scan_x2 - scan_x1) / 2;
708 scan_h = (scan_y2 - scan_y1) / 2;
709 x_result /= OVERSAMPLE;
710 y_result /= OVERSAMPLE;
718 //printf("MotionScan::scan_frame %d\n", __LINE__);
721 if(vertical_only) dx_result = 0;
722 if(horizontal_only) dy_result = 0;
727 if(tracking_type == MotionScan::SAVE)
729 char string[BCTEXTLEN];
734 FILE *output = fopen(string, "w");
745 printf("MotionScan::scan_frame %d: save coordinate failed", __LINE__);
749 // printf("MotionScan::scan_frame %d dx=%.2f dy=%.2f\n",
751 // (float)this->dx_result / OVERSAMPLE,
752 // (float)this->dy_result / OVERSAMPLE);
771 int64_t MotionScan::get_cache(int x, int y)
774 cache_lock->lock("MotionScan::get_cache");
775 for(int i = 0; i < cache.total; i++)
777 MotionScanCache *ptr = cache.values[i];
778 if(ptr->x == x && ptr->y == y)
780 result = ptr->difference;
784 cache_lock->unlock();
788 void MotionScan::put_cache(int x, int y, int64_t difference)
790 MotionScanCache *ptr = new MotionScanCache(x, y, difference);
791 cache_lock->lock("MotionScan::put_cache");
793 cache_lock->unlock();
798 #define ABS_DIFF(type, temp_type, multiplier, components) \
800 temp_type result_temp = 0; \
801 for(int i = 0; i < h; i++) \
803 type *prev_row = (type*)prev_ptr; \
804 type *current_row = (type*)current_ptr; \
805 for(int j = 0; j < w; j++) \
807 for(int k = 0; k < 3; k++) \
809 temp_type difference; \
810 difference = *prev_row++ - *current_row++; \
812 result_temp -= difference; \
814 result_temp += difference; \
816 if(components == 4) \
822 prev_ptr += row_bytes; \
823 current_ptr += row_bytes; \
825 result = (int64_t)(result_temp * multiplier); \
828 int64_t MotionScan::abs_diff(unsigned char *prev_ptr,
829 unsigned char *current_ptr,
839 ABS_DIFF(unsigned char, int64_t, 1, 3)
842 ABS_DIFF(unsigned char, int64_t, 1, 4)
845 ABS_DIFF(float, double, 0x10000, 3)
848 ABS_DIFF(float, double, 0x10000, 4)
851 ABS_DIFF(unsigned char, int64_t, 1, 3)
854 ABS_DIFF(unsigned char, int64_t, 1, 4)
857 ABS_DIFF(uint16_t, int64_t, 1, 3)
859 case BC_YUVA16161616:
860 ABS_DIFF(uint16_t, int64_t, 1, 4)
868 #define ABS_DIFF_SUB(type, temp_type, multiplier, components) \
870 temp_type result_temp = 0; \
871 temp_type y2_fraction = sub_y * 0x100 / OVERSAMPLE; \
872 temp_type y1_fraction = 0x100 - y2_fraction; \
873 temp_type x2_fraction = sub_x * 0x100 / OVERSAMPLE; \
874 temp_type x1_fraction = 0x100 - x2_fraction; \
875 for(int i = 0; i < h_sub; i++) \
877 type *prev_row1 = (type*)prev_ptr; \
878 type *prev_row2 = (type*)prev_ptr + components; \
879 type *prev_row3 = (type*)(prev_ptr + row_bytes); \
880 type *prev_row4 = (type*)(prev_ptr + row_bytes) + components; \
881 type *current_row = (type*)current_ptr; \
882 for(int j = 0; j < w_sub; j++) \
884 /* Scan each component */ \
885 for(int k = 0; k < 3; k++) \
887 temp_type difference; \
888 temp_type prev_value = \
889 (*prev_row1++ * x1_fraction * y1_fraction + \
890 *prev_row2++ * x2_fraction * y1_fraction + \
891 *prev_row3++ * x1_fraction * y2_fraction + \
892 *prev_row4++ * x2_fraction * y2_fraction) / \
894 temp_type current_value = *current_row++; \
895 difference = prev_value - current_value; \
897 result_temp -= difference; \
899 result_temp += difference; \
903 if(components == 4) \
912 prev_ptr += row_bytes; \
913 current_ptr += row_bytes; \
915 result = (int64_t)(result_temp * multiplier); \
921 int64_t MotionScan::abs_diff_sub(unsigned char *prev_ptr,
922 unsigned char *current_ptr,
937 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
940 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
943 ABS_DIFF_SUB(float, double, 0x10000, 3)
946 ABS_DIFF_SUB(float, double, 0x10000, 4)
949 ABS_DIFF_SUB(unsigned char, int64_t, 1, 3)
952 ABS_DIFF_SUB(unsigned char, int64_t, 1, 4)
955 ABS_DIFF_SUB(uint16_t, int64_t, 1, 3)
957 case BC_YUVA16161616:
958 ABS_DIFF_SUB(uint16_t, int64_t, 1, 4)
968 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
972 this->difference = difference;
977 void MotionScan::clamp_scan(int w,
989 // printf("MotionMain::clamp_scan 1 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",
1004 // Limit size of scan area
1005 // Used for drawing vectors
1006 // scan is always out of range before block.
1009 // int difference = -*scan_x1;
1010 // *block_x1 += difference;
1016 // int difference = -*scan_y1;
1017 // *block_y1 += difference;
1023 int difference = *scan_x2 - w;
1024 // *block_x2 -= difference;
1025 *scan_x2 -= difference;
1030 int difference = *scan_y2 - h;
1031 // *block_y2 -= difference;
1032 *scan_y2 -= difference;
1035 CLAMP(*scan_x1, 0, w);
1036 CLAMP(*scan_y1, 0, h);
1037 CLAMP(*scan_x2, 0, w);
1038 CLAMP(*scan_y2, 0, h);
1042 // Limit range of upper left block coordinates
1043 // Used for motion tracking
1046 int difference = -*scan_x1;
1047 // *block_x1 += difference;
1048 *scan_x2 += difference;
1054 int difference = -*scan_y1;
1055 // *block_y1 += difference;
1056 *scan_y2 += difference;
1060 if(*scan_x2 - *block_x1 + *block_x2 > w)
1062 int difference = *scan_x2 - *block_x1 + *block_x2 - w;
1063 *scan_x2 -= difference;
1064 // *block_x2 -= difference;
1067 if(*scan_y2 - *block_y1 + *block_y2 > h)
1069 int difference = *scan_y2 - *block_y1 + *block_y2 - h;
1070 *scan_y2 -= difference;
1071 // *block_y2 -= difference;
1074 // CLAMP(*scan_x1, 0, w - (*block_x2 - *block_x1));
1075 // CLAMP(*scan_y1, 0, h - (*block_y2 - *block_y1));
1076 // CLAMP(*scan_x2, 0, w - (*block_x2 - *block_x1));
1077 // CLAMP(*scan_y2, 0, h - (*block_y2 - *block_y1));
1080 // Sanity checks which break the calculation but should never happen if the
1081 // center of the block is inside the frame.
1082 CLAMP(*block_x1, 0, w);
1083 CLAMP(*block_x2, 0, w);
1084 CLAMP(*block_y1, 0, h);
1085 CLAMP(*block_y2, 0, h);
1087 // printf("MotionMain::clamp_scan 2 w=%d h=%d block=%d %d %d %d scan=%d %d %d %d absolute=%d\n",