apply sge motion plugin mods
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / motion / motionscan.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2012 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "clip.h"
23 //#include "../downsample/downsampleengine.h"
24 #include "motion.h"
25 #include "motionscan.h"
26 #include "mutex.h"
27 #include "vframe.h"
28
29 #include <math.h>
30
31 // The module which does the actual scanning
32
33 MotionScanPackage::MotionScanPackage()
34  : LoadPackage()
35 {
36         valid = 1;
37 }
38
39 MotionScanUnit::MotionScanUnit(MotionScan *server)
40  : LoadClient(server)
41 {
42         this->server = server;
43         cache_lock = new Mutex("MotionScanUnit::cache_lock");
44 }
45
46 MotionScanUnit::~MotionScanUnit()
47 {
48         delete cache_lock;
49 }
50
51 void MotionScanUnit::process_package(LoadPackage *package)
52 {
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();
59
60 // Single pixel
61         if( !server->subpixel ) {
62 // Try cache
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;
76 // Scan block
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,
79                                 color_model);
80
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);
84                 }
85         }
86 // Sub pixel
87         else {
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;
94
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);
100                 pkg->difference2 =
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);
106         }
107 }
108
109 int64_t MotionScanUnit::get_cache(int x, int y)
110 {
111         int64_t result = -1;
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;
117                         break;
118                 }
119         }
120         cache_lock->unlock();
121         return result;
122 }
123
124 void MotionScanUnit::put_cache(int x, int y, int64_t difference)
125 {
126         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
127         cache_lock->lock("MotionScanUnit::put_cache");
128         cache.append(ptr);
129         cache_lock->unlock();
130 }
131
132 MotionScan::MotionScan(MotionMain *plugin, int total_clients, int total_packages)
133  : LoadServer( //1, 1
134                 total_clients, total_packages)
135 {
136         this->plugin = plugin;
137         test_match = 1;
138         cache_lock = new Mutex("MotionScan::cache_lock");
139         downsampled_previous = 0;
140         downsampled_current = 0;
141 //      downsample = 0;
142 }
143
144 MotionScan::~MotionScan()
145 {
146         delete cache_lock;
147         delete downsampled_previous;
148         delete downsampled_current;
149 //      delete downsample;
150 }
151
152 void MotionScan::init_packages()
153 {
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);
158
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;
166
167                 if( !subpixel ) {
168                         pkg->sub_x = pkg->sub_y = 0;
169                         int ipkg = pkg->step;
170                         if (ipkg == 0)
171                         {
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;
179                         }
180                         else
181                         {
182                                 ipkg --;
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;
187                         }
188                 }
189                 else {
190                         pkg->sub_x = pkg->step % x_steps;
191                         pkg->sub_y = pkg->step / x_steps;
192
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;
196
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;
201
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);
204                 }
205
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);
208         }
209 }
210
211 LoadClient* MotionScan::new_client()
212 {
213         return new MotionScanUnit(this);
214 }
215
216 LoadPackage* MotionScan::new_package()
217 {
218         return new MotionScanPackage;
219 }
220
221 void MotionScan::set_test_match(int value)
222 {
223         this->test_match = value;
224 }
225
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)
235 {
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;
244         subpixel = 0;
245
246         cache.remove_all_objects();
247
248 // Single macroblock
249         int w = current_frame->get_w();
250         int h = current_frame->get_h();
251
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;
257
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
262         int dx_saved = 0;
263         int dy_saved = 0;
264         if (passno == 2)
265         {
266                 dx_saved = dx_result;
267                 dy_saved = dy_result;
268         }
269
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;
277
278 // Attention, process_buffer feeds previous_frame and current_frame interchanged
279
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
289                 if (passno == 2)
290                 {
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;
295                 }
296         }
297         block_x1 = lrint (tmp_x1);
298         block_y1 = lrint (tmp_y1);
299         block_x2 = lrint (tmp_x2);
300         block_y2 = lrint (tmp_y2);
301
302 // Preinitialize sane translation results
303         dx_result = 0;
304         dy_result = 0;
305         skip = 0;
306
307 //printf("MotionScan::scan_frame %d frame=%ld passno=%d\n", __LINE__, plugin->get_source_position(), passno);
308         switch( tracking_type ) {
309 // Don't calculate
310         case MotionScan::NO_CALCULATE:
311                 dx_result = dy_result = 0;
312                 skip = 1;
313                 break;
314
315         case MotionScan::LOAD:
316         case MotionScan::SAVE:
317                 if( load_ok ) {
318                         dx_result = load_dx;
319                         dy_result = load_dy;
320                         if (passno == 2) dx_result = dy_result = 0;
321                         skip = 1;
322                 }
323                 break;
324
325 // Scan from scratch with sane translation results
326         default:
327                 dx_result = 0;
328                 dy_result = 0;
329                 skip = 0;
330                 break;
331         }
332
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;
337                         skip = 1;
338                 }
339         }
340 // Perform scan
341         if( !skip ) {
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;
348                 dx_result = 0;
349                 dy_result = 0;
350
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);
354
355                 if (passno == 2)
356                 {
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;
363                 }
364
365                 while(1) {
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;
376
377 // Zero out requested values
378                         if( horizontal_only ) {
379                                 scan_y1 = block_y1;
380                                 scan_y2 = block_y1 + 1;
381                         }
382                         if( vertical_only ) {
383                                 scan_x1 = block_x1;
384                                 scan_x2 = block_x1 + 1;
385                         }
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,
391                                 &scan_y2, 0);
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);
396
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 )
400                                 break;
401
402 // For subpixel, the top row and left column are skipped
403                         if( subpixel ) {
404
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;
413
414                                 total_pixels = x_steps * y_steps;
415                                 this->total_steps = total_pixels;
416
417                                 set_package_count(this->total_steps);
418                                 process_packages();
419
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;
428
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;
432 // Fill in results
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);
437                                         }
438                                         if(pkg->difference1 > max_difference || max_difference == -1)
439                                         {
440                                                 max_difference = pkg->difference1;
441                                         }
442
443                                         if( pkg->difference2 < min_difference ) {
444                                                 min_difference = pkg->difference2;
445
446                                                 x_result = pkg->search_x * OVERSAMPLE - pkg->sub_x;
447                                                 y_result = pkg->search_y * OVERSAMPLE - pkg->sub_y;
448
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);
453                                         }
454                                         if(pkg->difference2 > max_difference)
455                                         {
456                                                 max_difference = pkg->difference2;
457                                         }
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);
460                                 }
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++)
466                                 {
467                                         MotionScanPackage *pkg = (MotionScanPackage*)get_package(i);
468                                         if(pkg->difference1 <= noiselev)
469                                         {
470 // Below noise level - check subpixel translation radius
471                                                 radius = hypot(
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))
477                                                 {
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);
486                                                 }
487                                         }
488                                         if(pkg->difference2 <= noiselev)
489                                         {
490 // Below noise level - check subpixel translation radius
491                                                 radius = hypot(
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))
497                                                 {
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);
506                                                 }
507                                         }
508                                 }
509
510                                 break;
511                         }
512 // Single pixel
513                         else {
514                                 total_pixels =
515                                     (scan_x2 - scan_x1) * (scan_y2 - scan_y1);
516                                 this->total_steps =
517                                     MIN(total_steps, total_pixels);
518
519                                 if( this->total_steps == total_pixels ) {
520                                         x_steps = scan_x2 - scan_x1;
521                                         y_steps = scan_y2 - scan_y1;
522                                 }
523                                 else {
524                                         x_steps = (int)sqrt(this->total_steps);
525                                         y_steps = (int)sqrt(this->total_steps);
526                                 }
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;
532
533 // Use downsampled images
534 //                              if( scan_x2 - scan_x1 > x_steps * 4 ||
535 //                                      scan_y2 - scan_y1 > y_steps * 4 )
536 //                              {
537 // printf("MotionScan::scan_frame %d total_pixels=%d total_steps=%d x_steps=%d y_steps=%d x y steps=%d\n",
538 // __LINE__,
539 // total_pixels,
540 // total_steps,
541 // x_steps,
542 // y_steps,
543 // x_steps * y_steps);
544 //
545 //                                      if( !downsampled_previous ||
546 //                                              !downsampled_previous->equivalent(previous_frame_arg) )
547 //                                      {
548 //                                              delete downsampled_previous;
549 //                                              downsampled_previous = new VFrame(*previous_frame_arg);
550 //                                      }
551 //
552 //                                      if( !downsampled_current ||
553 //                                              !downsampled_current->equivalent(current_frame_arg) )
554 //                                      {
555 //                                              delete downsampled_current;
556 //                                              downsampled_current = new VFrame(*current_frame_arg);
557 //                                      }
558 //
559 //
560 //                                      if( !downsample )
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,
567 //                                              0, 0);
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,
572 //                                              0, 0);
573 //                                      this->previous_frame = downsampled_previous;
574 //                                      this->current_frame = downsampled_current;
575 //                              }
576
577 // printf("MotionScan::scan_frame %d this->total_steps=%d\n",
578 // __LINE__, this->total_steps);
579
580 // One extra step for the position with zero translation
581                                 set_package_count(this->total_steps+1);
582                                 process_packages();
583
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;
594                                         }
595                                         if(pkg->difference1 > max_difference || max_difference == -1)
596                                         {
597                                                 max_difference = pkg->difference1;
598                                         }
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);
601                                 }
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++)
607                                 {
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)
619                                         {
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);
625                                                 continue;
626                                         }
627 // Equal translations, memorize search with min difference
628                                         if(pkg->difference1 < min_difference)
629                                         {
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);
635                                                 continue;
636                                         }
637                                 }
638                                 int rad_x = 0, rad_y = 0;
639                                 for(int i = 0; i < get_total_packages(); i++)
640                                 {
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))
646                                         {
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);
650                                         }
651                                         if(rad_y < abs(y_result-pkg->search_y))
652                                         {
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);
656                                         }
657                                 }
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);
662
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) &&
670                                            passno != 1)
671                                         {
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
674                                                 scan_w = 2;
675                                                 scan_h = 2;
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;
681                                                 subpixel = 1;
682                                         }
683 // Fill in results and quit
684                                         else {
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);
688                                                 break;
689                                         }
690                                 }
691 // Reduce scan area and try again
692                                 else {
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;
710                                 }
711                         }
712                 }
713 // Negate results as previous_frame and current_frame have been interchanged
714                 dx_result = -dx_result;
715                 dy_result = -dy_result;
716         }
717 //printf("MotionScan::scan_frame %d\n", __LINE__);
718
719         if( vertical_only ) dx_result = 0;
720         if( horizontal_only ) dy_result = 0;
721
722 // printf("MotionScan::scan_frame %d passno=%d saved dx=%.2f dy=%.2f measured dx=%.2f dy=%.2f twopass dx=%.2f dy=%.2f\n", 
723 // __LINE__, passno,
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);
730 }
731
732 int64_t MotionScan::get_cache(int x, int y)
733 {
734         int64_t result = -1;
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;
740                         break;
741                 }
742         }
743         cache_lock->unlock();
744         return result;
745 }
746
747 void MotionScan::put_cache(int x, int y, int64_t difference)
748 {
749         MotionScanCache *ptr = new MotionScanCache(x, y, difference);
750         cache_lock->lock("MotionScan::put_cache");
751         cache.append(ptr);
752         cache_lock->unlock();
753 }
754
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; \
766                                 else \
767                                         result_temp += difference; \
768                         } \
769                         if( components == 4 ) { \
770                                 prev_row++; \
771                                 current_row++; \
772                         } \
773                 } \
774                 prev_ptr += row_bytes; \
775                 current_ptr += row_bytes; \
776         } \
777         result = (int64_t)(result_temp * multiplier); \
778 } break
779
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)
783 {
784         int64_t result = 0;
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);
794         }
795         return result;
796 }
797
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) / \
819                                         0x100 / 0x100; \
820                                 temp_type current_value = *current_row++; \
821                                 difference = prev_value - current_value; \
822                                 if( difference < 0 ) \
823                                         result_temp -= difference; \
824                                 else \
825                                         result_temp += difference; \
826                         } \
827  \
828 /* skip alpha */ \
829                         if( components == 4 ) { \
830                                 prev_row1++; \
831                                 prev_row2++; \
832                                 prev_row3++; \
833                                 prev_row4++; \
834                                 current_row++; \
835                         } \
836                 } \
837                 prev_ptr += row_bytes; \
838                 current_ptr += row_bytes; \
839         } \
840         result = (int64_t)(result_temp * multiplier); \
841 } break
842
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,
846                 int sub_y)
847 {
848         int h_sub = h - 1;
849         int w_sub = w - 1;
850         int64_t result = 0;
851
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);
861         }
862         return result;
863 }
864
865 MotionScanCache::MotionScanCache(int x, int y, int64_t difference)
866 {
867         this->x = x;
868         this->y = y;
869         this->difference = difference;
870 }
871
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)
876 {
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,
879 // use_absolute);
880
881         if( use_absolute ) {
882 // Limit size of scan area
883 // Used for drawing vectors
884 // scan is always out of range before block.
885                 if( *scan_x1 < 0 ) {
886 //                      int difference = -*scan_x1;
887 //                      *block_x1 += difference;
888                         *scan_x1 = 0;
889                 }
890
891                 if( *scan_y1 < 0 ) {
892 //                      int difference = -*scan_y1;
893 //                      *block_y1 += difference;
894                         *scan_y1 = 0;
895                 }
896
897                 if( *scan_x2 > w ) {
898                         int difference = *scan_x2 - w;
899 //                      *block_x2 -= difference;
900                         *scan_x2 -= difference;
901                 }
902
903                 if( *scan_y2 > h ) {
904                         int difference = *scan_y2 - h;
905 //                      *block_y2 -= difference;
906                         *scan_y2 -= difference;
907                 }
908
909                 CLAMP(*scan_x1, 0, w);
910                 CLAMP(*scan_y1, 0, h);
911                 CLAMP(*scan_x2, 0, w);
912                 CLAMP(*scan_y2, 0, h);
913         }
914         else {
915 // Limit range of upper left block coordinates
916 // Used for motion tracking
917                 if( *scan_x1 < 0 ) {
918                         int difference = -*scan_x1;
919 //                      *block_x1 += difference;
920                         *scan_x2 += difference;
921                         *scan_x1 = 0;
922                 }
923
924                 if( *scan_y1 < 0 ) {
925                         int difference = -*scan_y1;
926 //                      *block_y1 += difference;
927                         *scan_y2 += difference;
928                         *scan_y1 = 0;
929                 }
930
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;
935                 }
936
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;
941                 }
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));
946         }
947
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);
954
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,
957 // use_absolute);
958 }