Imported Debian version 1.0~trusty
[deb_vid.stab.git] / src / motiondetect.c
CommitLineData
80f575fc
DM
1/*
2 * motiondetect.c
3 *
4 * Copyright (C) Georg Martius - February 1007-2011
5 * georg dot martius at web dot de
6 * Copyright (C) Alexey Osipov - Jule 2011
7 * simba at lerlan dot ru
8 * speed optimizations (threshold, spiral, SSE, asm)
9 *
10 * This file is part of vid.stab video stabilization library
11 *
12 * vid.stab is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License,
14 * as published by the Free Software Foundation; either version 2, or
15 * (at your option) any later version.
16 *
17 * vid.stab is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with GNU Make; see the file COPYING. If not, write to
24 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27#include "motiondetect.h"
28#include "motiondetect_internal.h"
29#include "motiondetect_opt.h"
30#include <math.h>
31#include <limits.h>
32#include <stdint.h>
33#include <string.h>
34#include <assert.h>
35
36#ifdef USE_OMP
37#include <omp.h>
38#endif
39
40#include "boxblur.h"
41#include "vidstabdefines.h"
42#include "localmotion2transform.h"
43#include "transformtype_operations.h"
44#include "transformtype_operations.h"
45
46#define USE_SPIRAL_FIELD_CALC
47
48
49/* internal data structures */
50
51// structure that contains the contrast and the index of a field
52typedef struct _contrast_idx {
53 double contrast;
54 int index;
55} contrast_idx;
56
57
58VSMotionDetectConfig vsMotionDetectGetDefaultConfig(const char* modName){
59 VSMotionDetectConfig conf;
60 conf.stepSize = 6;
61 conf.accuracy = 15;
62 conf.shakiness = 5;
63 conf.virtualTripod = 0;
64 conf.contrastThreshold = 0.25;
65 conf.show = 0;
66 conf.modName = modName;
67 return conf;
68}
69
70void vsMotionDetectGetConfig(VSMotionDetectConfig* conf, const VSMotionDetect* md){
71 if(md && conf)
72 *conf = md->conf;
73}
74
75const VSFrameInfo* vsMotionDetectGetFrameInfo(const VSMotionDetect* md){
76 return &md->fi;
77}
78
79
80int vsMotionDetectInit(VSMotionDetect* md, const VSMotionDetectConfig* conf, const VSFrameInfo* fi){
81 assert(md && fi);
82 md->conf = *conf;
83 md->fi = *fi;
84
85 if(fi->pFormat<=PF_NONE || fi->pFormat==PF_PACKED || fi->pFormat>=PF_NUMBER) {
86 vs_log_warn(md->conf.modName, "unsupported Pixel Format (%i)\n",
87 md->fi.pFormat);
88 return VS_ERROR;
89 }
90
91 vsFrameAllocate(&md->prev, &md->fi);
92 if (vsFrameIsNull(&md->prev)) {
93 vs_log_error(md->conf.modName, "malloc failed");
94 return VS_ERROR;
95 }
96
97 vsFrameNull(&md->curr);
98 vsFrameNull(&md->currorig);
99 vsFrameNull(&md->currtmp);
100 md->hasSeenOneFrame = 0;
101 md->frameNum = 0;
102
103 // TODO: get rid of shakiness parameter in the long run
104 md->conf.shakiness = VS_MIN(10,VS_MAX(1,md->conf.shakiness));
105 md->conf.accuracy = VS_MIN(15,VS_MAX(1,md->conf.accuracy));
106 if (md->conf.accuracy < md->conf.shakiness / 2) {
107 vs_log_info(md->conf.modName, "Accuracy should not be lower than shakiness/2 -- fixed");
108 md->conf.accuracy = md->conf.shakiness / 2;
109 }
110 if (md->conf.accuracy > 9 && md->conf.stepSize > 6) {
111 vs_log_info(md->conf.modName, "For high accuracy use lower stepsize -- set to 6 now");
112 md->conf.stepSize = 6; // maybe 4
113 }
114
115 int minDimension = VS_MIN(md->fi.width, md->fi.height);
116// shift: shakiness 1: height/40; 10: height/4
117// md->maxShift = VS_MAX(4,(minDimension*md->conf.shakiness)/40);
118// size: shakiness 1: height/40; 10: height/6 (clipped)
119// md->fieldSize = VS_MAX(4,VS_MIN(minDimension/6, (minDimension*md->conf.shakiness)/40));
120
121 // fixed size and shift now
122 int maxShift = VS_MAX(16, minDimension/7);
123 int fieldSize = VS_MAX(16, minDimension/10);
124 int fieldSizeFine = VS_MAX(6, minDimension/60);
125#if defined(USE_SSE2) || defined(USE_SSE2_ASM)
126 fieldSize = (fieldSize / 16 + 1) * 16;
127 fieldSizeFine = (fieldSizeFine / 16 + 1) * 16;
128#endif
129 if (!initFields(md, &md->fieldscoarse, fieldSize, maxShift, md->conf.stepSize,
130 1, 0, md->conf.contrastThreshold)) {
131 return VS_ERROR;
132 }
133 // for the fine check we use a smaller size and smaller maximal shift (=size)
134 if (!initFields(md, &md->fieldsfine, fieldSizeFine, fieldSizeFine,
135 2, 1, fieldSizeFine, md->conf.contrastThreshold/2)) {
136 return VS_ERROR;
137 }
138
139 vsFrameAllocate(&md->curr,&md->fi);
140 vsFrameAllocate(&md->currtmp, &md->fi);
141
142 md->initialized = 2;
143 return VS_OK;
144}
145
146void vsMotionDetectionCleanup(VSMotionDetect* md) {
147 if(md->fieldscoarse.fields) {
148 vs_free(md->fieldscoarse.fields);
149 md->fieldscoarse.fields=0;
150 }
151 if(md->fieldsfine.fields) {
152 vs_free(md->fieldsfine.fields);
153 md->fieldsfine.fields=0;
154 }
155 vsFrameFree(&md->prev);
156 vsFrameFree(&md->curr);
157 vsFrameFree(&md->currtmp);
158
159 md->initialized = 0;
160}
161
162// returns true if match of local motion is better than threshold
163short lm_match_better(void* thresh, void* lm){
164 if(((LocalMotion*)lm)->match <= *((double*)thresh))
165 return 1;
166 else
167 return 0;
168}
169
170int vsMotionDetection(VSMotionDetect* md, LocalMotions* motions, VSFrame *frame) {
171 assert(md->initialized==2);
172
173 md->currorig = *frame;
174 // smoothen image to do better motion detection
175 // (larger stepsize or eventually gradient descent (need higher resolution))
176 if (md->fi.pFormat > PF_PACKED) {
177 // we could calculate a grayscale version and use the PLANAR stuff afterwards
178 // so far smoothing is only implemented for PLANAR
179 vsFrameCopy(&md->curr, frame, &md->fi);
180 } else {
181 // box-kernel smoothing (plain average of pixels), which is fine for us
182 boxblurPlanar(&md->curr, frame, &md->currtmp, &md->fi, md->conf.stepSize*1/*1.4*/,
183 BoxBlurNoColor);
184 // two times yields tent-kernel smoothing, which may be better, but I don't
185 // think we need it
186 //boxblurPlanar(md->curr, md->curr, md->currtmp, &md->fi, md->stepSize*1,
187 // BoxBlurNoColor);
188 }
189
190 if (md->hasSeenOneFrame) {
191 LocalMotions motionscoarse;
192 LocalMotions motionsfine;
193 vs_vector_init(&motionsfine,0);
194 // md->curr = frame;
195 if (md->fi.pFormat > PF_PACKED) {
196 motionscoarse = calcTransFields(md, &md->fieldscoarse,
197 calcFieldTransPacked, contrastSubImgPacked);
198 } else { // PLANAR
199 motionscoarse = calcTransFields(md, &md->fieldscoarse,
200 calcFieldTransPlanar, contrastSubImgPlanar);
201 }
202 int num_motions = vs_vector_size(&motionscoarse);
203 if (num_motions < 1) {
204 vs_log_warn(md->conf.modName, "too low contrast. \
205(no translations are detected in frame %i)\n", md->frameNum);
206 }else{
207 // calc transformation and perform another scan with small fields
208 VSTransform t = vsSimpleMotionsToTransform(md->fi, md->conf.modName, &motionscoarse);
209 md->fieldsfine.offset = t;
210 md->fieldsfine.useOffset = 1;
211 LocalMotions motions2;
212 if (md->fi.pFormat > PF_PACKED) {
213 motions2 = calcTransFields(md, &md->fieldsfine,
214 calcFieldTransPacked, contrastSubImgPacked);
215 } else { // PLANAR
216 motions2 = calcTransFields(md, &md->fieldsfine,
217 calcFieldTransPlanar, contrastSubImgPlanar);
218 }
219 // through out those with bad match (worse than mean of coarse scan)
220 VSArray matchQualities1 = localmotionsGetMatch(&motionscoarse);
221 double meanMatch = cleanmean(matchQualities1.dat, matchQualities1.len, NULL, NULL);
222 motionsfine = vs_vector_filter(&motions2, lm_match_better, &meanMatch);
223 if(0){
224 printf("\nMatches: mean: %f | ", meanMatch);
225 vs_array_print(matchQualities1, stdout);
226 printf("\n fine: ");
227 VSArray matchQualities2 = localmotionsGetMatch(&motions2);
228 vs_array_print(matchQualities2, stdout);
229 printf("\n");
230 }
231 }
232 if (md->conf.show) { // draw fields and transforms into frame.
233 int num_motions_fine = vs_vector_size(&motionsfine);
234 // this has to be done one after another to handle possible overlap
235 if (md->conf.show > 1) {
236 for (int i = 0; i < num_motions; i++)
237 drawFieldScanArea(md, LMGet(&motionscoarse,i), md->fieldscoarse.maxShift);
238 }
239 for (int i = 0; i < num_motions; i++)
240 drawField(md, LMGet(&motionscoarse,i), 1);
241 for (int i = 0; i < num_motions_fine; i++)
242 drawField(md, LMGet(&motionsfine,i), 0);
243 for (int i = 0; i < num_motions; i++)
244 drawFieldTrans(md, LMGet(&motionscoarse,i),180);
245 for (int i = 0; i < num_motions_fine; i++)
246 drawFieldTrans(md, LMGet(&motionsfine,i), 64);
247 }
248 *motions = vs_vector_concat(&motionscoarse,&motionsfine);
249 //*motions = motionscoarse;
250 //*motions = motionsfine;
251 } else {
252 vs_vector_init(motions,1); // dummy vector
253 md->hasSeenOneFrame = 1;
254 }
255
256 // for tripod we keep a certain reference frame
257 if(md->conf.virtualTripod < 1 || md->frameNum < md->conf.virtualTripod)
258 // copy current frame (smoothed) to prev for next frame comparison
259 vsFrameCopy(&md->prev, &md->curr, &md->fi);
260 md->frameNum++;
261 return VS_OK;
262}
263
264
265/** initialise measurement fields on the frame.
266 The size of the fields and the maxshift is used to
267 calculate an optimal distribution in the frame.
268 if border is set then they are placed savely away from the border for maxShift
269*/
270
271int initFields(VSMotionDetect* md, VSMotionDetectFields* fs,
272 int size, int maxShift, int stepSize,
273 short keepBorder, int spacing, double contrastThreshold) {
274 fs->fieldSize = size;
275 fs->maxShift = maxShift;
276 fs->stepSize = stepSize;
277 fs->useOffset = 0;
278 fs->contrastThreshold = contrastThreshold;
279
280 int rows = VS_MAX(3,(md->fi.height - fs->maxShift*2)/(size+spacing)-1);
281 int cols = VS_MAX(3,(md->fi.width - fs->maxShift*2)/(size+spacing)-1);
282 // make sure that the remaining rows have the same length
283 fs->fieldNum = rows * cols;
284 fs->fieldRows = rows;
285
286 if (!(fs->fields = (Field*) vs_malloc(sizeof(Field) * fs->fieldNum))) {
287 vs_log_error(md->conf.modName, "malloc failed!\n");
288 return 0;
289 } else {
290 int i, j;
291 int border=fs->stepSize;
292 // the border is the amount by which the field centers
293 // have to be away from the image boundary
294 // (stepsize is added in case shift is increased through stepsize)
295 if(keepBorder)
296 border = size / 2 + fs->maxShift + fs->stepSize;
297 int step_x = (md->fi.width - 2 * border) / VS_MAX(cols-1,1);
298 int step_y = (md->fi.height - 2 * border) / VS_MAX(rows-1,1);
299 for (j = 0; j < rows; j++) {
300 for (i = 0; i < cols; i++) {
301 int idx = j * cols + i;
302 fs->fields[idx].x = border + i * step_x;
303 fs->fields[idx].y = border + j * step_y;
304 fs->fields[idx].size = size;
305 }
306 }
307 }
308 fs->maxFields = (md->conf.accuracy) * fs->fieldNum / 15;
309 vs_log_info(md->conf.modName, "Fieldsize: %i, Maximal translation: %i pixel\n",
310 fs->fieldSize, fs->maxShift);
311 vs_log_info(md->conf.modName, "Number of used measurement fields: %i out of %i\n",
312 fs->maxFields, fs->fieldNum);
313
314 return 1;
315}
316
317/** \see contrastSubImg*/
318double contrastSubImgPlanar(VSMotionDetect* md, const Field* field) {
319#ifdef USE_SSE2
320 return contrastSubImg1_SSE(md->curr.data[0], field, md->curr.linesize[0],md->fi.height);
321#else
322 return contrastSubImg(md->curr.data[0],field,md->curr.linesize[0],md->fi.height,1);
323#endif
324
325}
326
327/**
328 \see contrastSubImg_Michelson three times called with bytesPerPixel=3
329 for all channels
330*/
331double contrastSubImgPacked(VSMotionDetect* md, const Field* field) {
332 unsigned char* const I = md->curr.data[0];
333 int linesize2 = md->curr.linesize[0]/3; // linesize in pixels
334 return (contrastSubImg(I, field, linesize2, md->fi.height, 3)
335 + contrastSubImg(I + 1, field, linesize2, md->fi.height, 3)
336 + contrastSubImg(I + 2, field, linesize2, md->fi.height, 3)) / 3;
337}
338
339/**
340 calculates Michelson-contrast in the given small part of the given image
341 to be more compatible with the absolute difference formula this is scaled by 0.1
342
343 \param I pointer to framebuffer
344 \param field Field specifies position(center) and size of subimage
345 \param width width of frame (linesize in pixels)
346 \param height height of frame
347 \param bytesPerPixel calc contrast for only for first channel
348*/
349double contrastSubImg(unsigned char* const I, const Field* field, int width,
350 int height, int bytesPerPixel) {
351 int k, j;
352 unsigned char* p = NULL;
353 int s2 = field->size / 2;
354 unsigned char mini = 255;
355 unsigned char maxi = 0;
356
357 p = I + ((field->x - s2) + (field->y - s2) * width) * bytesPerPixel;
358 for (j = 0; j < field->size; j++) {
359 for (k = 0; k < field->size; k++) {
360 mini = (mini < *p) ? mini : *p;
361 maxi = (maxi > *p) ? maxi : *p;
362 p += bytesPerPixel;
363 }
364 p += (width - field->size) * bytesPerPixel;
365 }
366 return (maxi - mini) / (maxi + mini + 0.1); // +0.1 to avoid division by 0
367}
368
369/* calculates the optimal transformation for one field in Planar frames
370 * (only luminance)
371 */
372LocalMotion calcFieldTransPlanar(VSMotionDetect* md, VSMotionDetectFields* fs,
373 const Field* field, int fieldnum) {
374 int tx = 0;
375 int ty = 0;
376 uint8_t *Y_c = md->curr.data[0], *Y_p = md->prev.data[0];
377 int linesize_c = md->curr.linesize[0], linesize_p = md->prev.linesize[0];
378 // we only use the luminance part of the image
379 int i, j;
380 int stepSize = fs->stepSize;
381 int maxShift = fs->maxShift;
382 Vec offset = { 0, 0};
383 LocalMotion lm = null_localmotion();
384 if(fs->useOffset){
385 // Todo: we could put the preparedtransform into fs
386 PreparedTransform pt = prepare_transform(&fs->offset, &md->fi);
387 Vec fieldpos = {field->x, field->y};
388 offset = sub_vec(transform_vec(&pt, &fieldpos), fieldpos);
389 // is the field still in the frame
390 int s2 = field->size/2;
391 if(unlikely(fieldpos.x+offset.x-s2-maxShift-stepSize < 0 ||
392 fieldpos.x+offset.x+s2+maxShift+stepSize >= md->fi.width ||
393 fieldpos.y+offset.y-s2-maxShift-stepSize < 0 ||
394 fieldpos.y+offset.y+s2+maxShift+stepSize >= md->fi.height)){
395 lm.match=-1;
396 return lm;
397 }
398 }
399
400#ifdef STABVERBOSE
401 // printf("%i %i %f\n", md->frameNum, fieldnum, contr);
402 FILE *f = NULL;
403 char buffer[32];
404 vs_snprintf(buffer, sizeof(buffer), "f%04i_%02i.dat", md->frameNum, fieldnum);
405 f = fopen(buffer, "w");
406 fprintf(f, "# splot \"%s\"\n", buffer);
407#endif
408
409#ifdef USE_SPIRAL_FIELD_CALC
410 unsigned int minerror = UINT_MAX;
411
412 // check all positions by outgoing spiral
413 i = 0; j = 0;
414 int limit = 1;
415 int step = 0;
416 int dir = 0;
417 while (j >= -maxShift && j <= maxShift && i >= -maxShift && i <= maxShift) {
418 unsigned int error = compareSubImg(Y_c, Y_p, field, linesize_c, linesize_p,
419 md->fi.height, 1, i + offset.x, j + offset.y,
420 minerror);
421
422 if (error < minerror) {
423 minerror = error;
424 tx = i;
425 ty = j;
426 }
427
428 //spiral indexing...
429 step++;
430 switch (dir) {
431 case 0:
432 i += stepSize;
433 if (step == limit) {
434 dir = 1;
435 step = 0;
436 }
437 break;
438 case 1:
439 j += stepSize;
440 if (step == limit) {
441 dir = 2;
442 step = 0;
443 limit++;
444 }
445 break;
446 case 2:
447 i -= stepSize;
448 if (step == limit) {
449 dir = 3;
450 step = 0;
451 }
452 break;
453 case 3:
454 j -= stepSize;
455 if (step == limit) {
456 dir = 0;
457 step = 0;
458 limit++;
459 }
460 break;
461 }
462 }
463#else
464 /* Here we improve speed by checking first the most probable position
465 then the search paths are most effectively cut. (0,0) is a simple start
466 */
467 unsigned int minerror = compareSubImg(Y_c, Y_p, field, linesize_c, linesize_p,
468 md->fi.height, 1, 0, 0, UINT_MAX);
469 // check all positions...
470 for (i = -maxShift; i <= maxShift; i += stepSize) {
471 for (j = -maxShift; j <= maxShift; j += stepSize) {
472 if( i==0 && j==0 )
473 continue; //no need to check this since already done
474 unsigned int error = compareSubImg(Y_c, Y_p, field, linesize_c, linesize_p,
475 md->fi.height, 1, i+offset.x, j+offset.y, minerror);
476 if (error < minerror) {
477 minerror = error;
478 tx = i;
479 ty = j;
480 }
481#ifdef STABVERBOSE
482 fprintf(f, "%i %i %f\n", i, j, error);
483#endif
484 }
485 }
486
487#endif
488
489 while(stepSize > 1) {// make fine grain check around the best match
490 int txc = tx; // save the shifts
491 int tyc = ty;
492 int newStepSize = stepSize/2;
493 int r = stepSize - newStepSize;
494 for (i = txc - r; i <= txc + r; i += newStepSize) {
495 for (j = tyc - r; j <= tyc + r; j += newStepSize) {
496 if (i == txc && j == tyc)
497 continue; //no need to check this since already done
498 unsigned int error = compareSubImg(Y_c, Y_p, field, linesize_c, linesize_p,
499 md->fi.height, 1, i+offset.x, j+offset.y, minerror);
500#ifdef STABVERBOSE
501 fprintf(f, "%i %i %f\n", i, j, error);
502#endif
503 if (error < minerror) {
504 minerror = error;
505 tx = i;
506 ty = j;
507 }
508 }
509 }
510 stepSize /= 2;
511 }
512#ifdef STABVERBOSE
513 fclose(f);
514 vs_log_msg(md->modName, "Minerror: %f\n", minerror);
515#endif
516
517 if (unlikely(fabs(tx) >= maxShift + stepSize - 1 ||
518 fabs(ty) >= maxShift + stepSize)) {
519#ifdef STABVERBOSE
520 vs_log_msg(md->modName, "maximal shift ");
521#endif
522 lm.match =-1.0; // to be kicked out
523 return lm;
524 }
525 lm.f = *field;
526 lm.v.x = tx + offset.x;
527 lm.v.y = ty + offset.y;
528 lm.match = ((double) minerror)/(field->size*field->size);
529 return lm;
530}
531
532/* calculates the optimal transformation for one field in Packed
533 * slower than the Planar version because it uses all three color channels
534 */
535LocalMotion calcFieldTransPacked(VSMotionDetect* md, VSMotionDetectFields* fs,
536 const Field* field, int fieldnum) {
537 int tx = 0;
538 int ty = 0;
539 uint8_t *I_c = md->curr.data[0], *I_p = md->prev.data[0];
540 int width1 = md->curr.linesize[0]/3; // linesize in pixels
541 int width2 = md->prev.linesize[0]/3; // linesize in pixels
542 int i, j;
543 int stepSize = fs->stepSize;
544 int maxShift = fs->maxShift;
545
546 Vec offset = { 0, 0};
547 LocalMotion lm = null_localmotion();
548 if(fs->useOffset){
549 PreparedTransform pt = prepare_transform(&fs->offset, &md->fi);
550 offset = transform_vec(&pt, (Vec*)field);
551 // is the field still in the frame
552 if(unlikely(offset.x-maxShift-stepSize < 0 || offset.x+maxShift+stepSize >= md->fi.width ||
553 offset.y-maxShift-stepSize < 0 || offset.y+maxShift+stepSize >= md->fi.height)){
554 lm.match=-1;
555 return lm;
556 }
557 }
558
559 /* Here we improve speed by checking first the most probable position
560 then the search paths are most effectively cut. (0,0) is a simple start
561 */
562 unsigned int minerror = compareSubImg(I_c, I_p, field, width1, width2, md->fi.height,
563 3, offset.x, offset.y, UINT_MAX);
564 // check all positions...
565 for (i = -maxShift; i <= maxShift; i += stepSize) {
566 for (j = -maxShift; j <= maxShift; j += stepSize) {
567 if( i==0 && j==0 )
568 continue; //no need to check this since already done
569 unsigned int error = compareSubImg(I_c, I_p, field, width1, width2,
570 md->fi.height, 3, i + offset.x, j + offset.y, minerror);
571 if (error < minerror) {
572 minerror = error;
573 tx = i;
574 ty = j;
575 }
576 }
577 }
578 if (stepSize > 1) { // make fine grain check around the best match
579 int txc = tx; // save the shifts
580 int tyc = ty;
581 int r = stepSize - 1;
582 for (i = txc - r; i <= txc + r; i += 1) {
583 for (j = tyc - r; j <= tyc + r; j += 1) {
584 if (i == txc && j == tyc)
585 continue; //no need to check this since already done
586 unsigned int error = compareSubImg(I_c, I_p, field, width1, width2,
587 md->fi.height, 3, i + offset.x, j + offset.y, minerror);
588 if (error < minerror) {
589 minerror = error;
590 tx = i;
591 ty = j;
592 }
593 }
594 }
595 }
596
597 if (fabs(tx) >= maxShift + stepSize - 1 || fabs(ty) >= maxShift + stepSize - 1) {
598#ifdef STABVERBOSE
599 vs_log_msg(md->modName, "maximal shift ");
600#endif
601 lm.match = -1;
602 return lm;
603 }
604 lm.f = *field;
605 lm.v.x = tx + offset.x;
606 lm.v.y = ty + offset.y;
607 lm.match = ((double)minerror)/(field->size*field->size);
608 return lm;
609}
610
611/* compares contrast_idx structures respect to the contrast
612 (for sort function)
613*/
614int cmp_contrast_idx(const void *ci1, const void* ci2) {
615 double a = ((contrast_idx*) ci1)->contrast;
616 double b = ((contrast_idx*) ci2)->contrast;
617 return a < b ? 1 : (a > b ? -1 : 0);
618}
619
620/* select only the best 'maxfields' fields
621 first calc contrasts then select from each part of the
622 frame some fields
623 We may simplify here by using random. People want high quality, so typically we use all.
624*/
625VSVector selectfields(VSMotionDetect* md, VSMotionDetectFields* fs,
626 contrastSubImgFunc contrastfunc) {
627 int i, j;
628 VSVector goodflds;
629 contrast_idx *ci =
630 (contrast_idx*) vs_malloc(sizeof(contrast_idx) * fs->fieldNum);
631 vs_vector_init(&goodflds, fs->fieldNum);
632
633 // we split all fields into row+1 segments and take from each segment
634 // the best fields
635 int numsegms = (fs->fieldRows + 1);
636 int segmlen = fs->fieldNum / (fs->fieldRows + 1) + 1;
637 // split the frame list into rows+1 segments
638 contrast_idx *ci_segms =
639 (contrast_idx*) vs_malloc(sizeof(contrast_idx) * fs->fieldNum);
640 int remaining = 0;
641 // calculate contrast for each field
642 // #pragma omp parallel for shared(ci,md) no speedup because to short
643 for (i = 0; i < fs->fieldNum; i++) {
644 ci[i].contrast = contrastfunc(md, &fs->fields[i]);
645 ci[i].index = i;
646 if (ci[i].contrast < fs->contrastThreshold)
647 ci[i].contrast = 0;
648 // else printf("%i %lf\n", ci[i].index, ci[i].contrast);
649 }
650
651 memcpy(ci_segms, ci, sizeof(contrast_idx) * fs->fieldNum);
652 // get best fields from each segment
653 for (i = 0; i < numsegms; i++) {
654 int startindex = segmlen * i;
655 int endindex = segmlen * (i + 1);
656 endindex = endindex > fs->fieldNum ? fs->fieldNum : endindex;
657 //printf("Segment: %i: %i-%i\n", i, startindex, endindex);
658
659 // sort within segment
660 qsort(ci_segms + startindex, endindex - startindex,
661 sizeof(contrast_idx), cmp_contrast_idx);
662 // take maxfields/numsegms
663 for (j = 0; j < fs->maxFields / numsegms; j++) {
664 if (startindex + j >= endindex)
665 continue;
666 // printf("%i %lf\n", ci_segms[startindex+j].index,
667 // ci_segms[startindex+j].contrast);
668 if (ci_segms[startindex + j].contrast > 0) {
669 vs_vector_append_dup(&goodflds, &ci[ci_segms[startindex+j].index],
670 sizeof(contrast_idx));
671 // don't consider them in the later selection process
672 ci_segms[startindex + j].contrast = 0;
673 }
674 }
675 }
676 // check whether enough fields are selected
677 // printf("Phase2: %i\n", vs_list_size(goodflds));
678 remaining = fs->maxFields - vs_vector_size(&goodflds);
679 if (remaining > 0) {
680 // take the remaining from the leftovers
681 qsort(ci_segms, fs->fieldNum, sizeof(contrast_idx), cmp_contrast_idx);
682 for (j = 0; j < remaining; j++) {
683 if (ci_segms[j].contrast > 0) {
684 vs_vector_append_dup(&goodflds, &ci_segms[j], sizeof(contrast_idx));
685 }
686 }
687 }
688 // printf("Ende: %i\n", vs_list_size(goodflds));
689 vs_free(ci);
690 vs_free(ci_segms);
691 return goodflds;
692}
693
694/* tries to register current frame onto previous frame.
695 * Algorithm:
696 * discards fields with low contrast
697 * select maxfields fields according to their contrast
698 * check theses fields for vertical and horizontal transformation
699 * use minimal difference of all possible positions
700 */
701LocalMotions calcTransFields(VSMotionDetect* md,
702 VSMotionDetectFields* fields,
703 calcFieldTransFunc fieldfunc,
704 contrastSubImgFunc contrastfunc) {
705 LocalMotions localmotions;
706 vs_vector_init(&localmotions,fields->maxFields);
707
708#ifdef STABVERBOSE
709 FILE *file = NULL;
710 char buffer[32];
711 vs_snprintf(buffer, sizeof(buffer), "k%04i.dat", md->frameNum);
712 file = fopen(buffer, "w");
713 fprintf(file, "# plot \"%s\" w l, \"\" every 2:1:0\n", buffer);
714#endif
715
716 VSVector goodflds = selectfields(md, fields, contrastfunc);
717 // use all "good" fields and calculate optimal match to previous frame
718#ifdef USE_OMP
719#pragma omp parallel for shared(goodflds, md, localmotions, fs) // does not bring speedup
720#endif
721 for(int index=0; index < vs_vector_size(&goodflds); index++){
722 int i = ((contrast_idx*)vs_vector_get(&goodflds,index))->index;
723 LocalMotion m;
724 m = fieldfunc(md, fields, &fields->fields[i], i); // e.g. calcFieldTransPlanar
725 if(m.match >= 0){
726 m.contrast = ((contrast_idx*)vs_vector_get(&goodflds,index))->contrast;
727#ifdef STABVERBOSE
728 fprintf(file, "%i %i\n%f %f %f %f\n \n\n", m.f.x, m.f.y,
729 m.f.x + m.v.x, m.f.y + m.v.y, m.match, m.contrast);
730#endif
731 vs_vector_append_dup(&localmotions, &m, sizeof(LocalMotion));
732 }
733 }
734 vs_vector_del(&goodflds);
735
736#ifdef STABVERBOSE
737 fclose(file);
738#endif
739 return localmotions;
740}
741
742
743
744
745
746/** draws the field scanning area */
747void drawFieldScanArea(VSMotionDetect* md, const LocalMotion* lm, int maxShift) {
748 if (md->fi.pFormat > PF_PACKED)
749 return;
750 drawRectangle(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1, lm->f.x, lm->f.y,
751 lm->f.size + 2 * maxShift, lm->f.size + 2 * maxShift, 80);
752}
753
754/** draws the field */
755void drawField(VSMotionDetect* md, const LocalMotion* lm, short box) {
756 if (md->fi.pFormat > PF_PACKED)
757 return;
758 if(box)
759 drawBox(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1,
760 lm->f.x, lm->f.y, lm->f.size, lm->f.size, /*lm->match >100 ? 100 :*/ 40);
761 else
762 drawRectangle(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1,
763 lm->f.x, lm->f.y, lm->f.size, lm->f.size, /*lm->match >100 ? 100 :*/ 40);
764}
765
766/** draws the transform data of this field */
767void drawFieldTrans(VSMotionDetect* md, const LocalMotion* lm, int color) {
768 if (md->fi.pFormat > PF_PACKED)
769 return;
770 Vec end = add_vec(field_to_vec(lm->f),lm->v);
771 drawBox(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1,
772 lm->f.x, lm->f.y, 5, 5, 0); // draw center
773 drawBox(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1,
774 lm->f.x + lm->v.x, lm->f.y + lm->v.y, 5, 5, 250); // draw translation
775 drawLine(md->currorig.data[0], md->currorig.linesize[0], md->fi.height, 1,
776 (Vec*)&lm->f, &end, 3, color);
777
778}
779
780/**
781 * draws a box at the given position x,y (center) in the given color
782 (the same for all channels)
783*/
784void drawBox(unsigned char* I, int width, int height, int bytesPerPixel, int x,
785 int y, int sizex, int sizey, unsigned char color) {
786
787 unsigned char* p = NULL;
788 int j, k;
789 p = I + ((x - sizex / 2) + (y - sizey / 2) * width) * bytesPerPixel;
790 for (j = 0; j < sizey; j++) {
791 for (k = 0; k < sizex * bytesPerPixel; k++) {
792 *p = color;
793 p++;
794 }
795 p += (width - sizex) * bytesPerPixel;
796 }
797}
798
799/**
800 * draws a rectangle (not filled) at the given position x,y (center) in the given color
801 at the first channel
802*/
803void drawRectangle(unsigned char* I, int width, int height, int bytesPerPixel, int x,
804 int y, int sizex, int sizey, unsigned char color) {
805
806 unsigned char* p;
807 int k;
808 p = I + ((x - sizex / 2) + (y - sizey / 2) * width) * bytesPerPixel;
809 for (k = 0; k < sizex; k++) { *p = color; p+= bytesPerPixel; } // upper line
810 p = I + ((x - sizex / 2) + (y + sizey / 2) * width) * bytesPerPixel;
811 for (k = 0; k < sizex; k++) { *p = color; p+= bytesPerPixel; } // lower line
812 p = I + ((x - sizex / 2) + (y - sizey / 2) * width) * bytesPerPixel;
813 for (k = 0; k < sizey; k++) { *p = color; p+= width * bytesPerPixel; } // left line
814 p = I + ((x + sizex / 2) + (y - sizey / 2) * width) * bytesPerPixel;
815 for (k = 0; k < sizey; k++) { *p = color; p+= width * bytesPerPixel; } // right line
816}
817
818/**
819 * draws a line from a to b with given thickness(not filled) at the given position x,y (center) in the given color
820 at the first channel
821*/
822void drawLine(unsigned char* I, int width, int height, int bytesPerPixel,
823 Vec* a, Vec* b, int thickness, unsigned char color) {
824 unsigned char* p;
825 Vec div = sub_vec(*b,*a);
826 if(div.y==0){ // horizontal line
827 if(div.x<0) {*a=*b; div.x*=-1;}
828 for(int r=-thickness/2; r<=thickness/2; r++){
829 p = I + ((a->x) + (a->y+r) * width) * bytesPerPixel;
830 for (int k = 0; k <= div.x; k++) { *p = color; p+= bytesPerPixel; }
831 }
832 }else{
833 if(div.x==0){ // vertical line
834 if(div.y<0) {*a=*b; div.y*=-1;}
835 for(int r=-thickness/2; r<=thickness/2; r++){
836 p = I + ((a->x+r) + (a->y) * width) * bytesPerPixel;
837 for (int k = 0; k <= div.y; k++) { *p = color; p+= width * bytesPerPixel; }
838 }
839 }else{
840 double m = (double)div.x/(double)div.y;
841 int horlen = thickness + fabs(m);
842 for( int c=0; c<= abs(div.y); c++){
843 int dy = div.y<0 ? -c : c;
844 int x = a->x + m*dy - horlen/2;
845 p = I + (x + (a->y+dy) * width) * bytesPerPixel;
846 for( int k=0; k<= horlen; k++){ *p = color; p+= bytesPerPixel; }
847 }
848 }
849 }
850}
851
852
853// void addTrans(VSMotionDetect* md, VSTransform sl) {
854// if (!md->transs) {
855// md->transs = vs_list_new(0);
856// }
857// vs_list_append_dup(md->transs, &sl, sizeof(sl));
858// }
859
860// VSTransform getLastVSTransform(VSMotionDetect* md){
861// if (!md->transs || !md->transs->head) {
862// return null_transform();
863// }
864// return *((VSTransform*)md->transs->tail);
865// }
866
867
868//#ifdef TESTING
869/// plain C implementation of compareSubImg (without ORC)
870unsigned int compareSubImg_thr(unsigned char* const I1, unsigned char* const I2,
871 const Field* field, int width1, int width2, int height,
872 int bytesPerPixel, int d_x, int d_y,
873 unsigned int threshold) {
874 int k, j;
875 unsigned char* p1 = NULL;
876 unsigned char* p2 = NULL;
877 int s2 = field->size / 2;
878 unsigned int sum = 0;
879
880 p1 = I1 + ((field->x - s2) + (field->y - s2) * width1) * bytesPerPixel;
881 p2 = I2 + ((field->x - s2 + d_x) + (field->y - s2 + d_y) * width2)
882 * bytesPerPixel;
883 for (j = 0; j < field->size; j++) {
884 for (k = 0; k < field->size * bytesPerPixel; k++) {
885 sum += abs((int) *p1 - (int) *p2);
886 p1++;
887 p2++;
888 }
889 if( sum > threshold) // no need to calculate any longer: worse than the best match
890 break;
891 p1 += (width1 - field->size) * bytesPerPixel;
892 p2 += (width2 - field->size) * bytesPerPixel;
893 }
894 return sum;
895}
896
897
898/*
899 * Local variables:
900 * c-file-style: "stroustrup"
901 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
902 * indent-tabs-mode: nil
903 * tab-width: 2
904 * c-basic-offset: 2 t
905 * End:
906 *
907 */