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)
10 * This file is part of vid.stab video stabilization library
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.
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.
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.
27 #include "motiondetect.h"
28 #include "motiondetect_internal.h"
29 #include "motiondetect_opt.h"
41 #include "vidstabdefines.h"
42 #include "localmotion2transform.h"
43 #include "transformtype_operations.h"
44 #include "transformtype_operations.h"
46 #define USE_SPIRAL_FIELD_CALC
49 /* internal data structures */
51 // structure that contains the contrast and the index of a field
52 typedef struct _contrast_idx
{
58 VSMotionDetectConfig
vsMotionDetectGetDefaultConfig(const char* modName
){
59 VSMotionDetectConfig conf
;
63 conf
.virtualTripod
= 0;
64 conf
.contrastThreshold
= 0.25;
66 conf
.modName
= modName
;
70 void vsMotionDetectGetConfig(VSMotionDetectConfig
* conf
, const VSMotionDetect
* md
){
75 const VSFrameInfo
* vsMotionDetectGetFrameInfo(const VSMotionDetect
* md
){
80 int vsMotionDetectInit(VSMotionDetect
* md
, const VSMotionDetectConfig
* conf
, const VSFrameInfo
* fi
){
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",
91 vsFrameAllocate(&md
->prev
, &md
->fi
);
92 if (vsFrameIsNull(&md
->prev
)) {
93 vs_log_error(md
->conf
.modName
, "malloc failed");
97 vsFrameNull(&md
->curr
);
98 vsFrameNull(&md
->currorig
);
99 vsFrameNull(&md
->currtmp
);
100 md
->hasSeenOneFrame
= 0;
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;
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
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));
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;
129 if (!initFields(md
, &md
->fieldscoarse
, fieldSize
, maxShift
, md
->conf
.stepSize
,
130 1, 0, md
->conf
.contrastThreshold
)) {
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)) {
139 vsFrameAllocate(&md
->curr
,&md
->fi
);
140 vsFrameAllocate(&md
->currtmp
, &md
->fi
);
146 void vsMotionDetectionCleanup(VSMotionDetect
* md
) {
147 if(md
->fieldscoarse
.fields
) {
148 vs_free(md
->fieldscoarse
.fields
);
149 md
->fieldscoarse
.fields
=0;
151 if(md
->fieldsfine
.fields
) {
152 vs_free(md
->fieldsfine
.fields
);
153 md
->fieldsfine
.fields
=0;
155 vsFrameFree(&md
->prev
);
156 vsFrameFree(&md
->curr
);
157 vsFrameFree(&md
->currtmp
);
162 // returns true if match of local motion is better than threshold
163 short lm_match_better(void* thresh
, void* lm
){
164 if(((LocalMotion
*)lm
)->match
<= *((double*)thresh
))
170 int vsMotionDetection(VSMotionDetect
* md
, LocalMotions
* motions
, VSFrame
*frame
) {
171 assert(md
->initialized
==2);
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
);
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*/,
184 // two times yields tent-kernel smoothing, which may be better, but I don't
186 //boxblurPlanar(md->curr, md->curr, md->currtmp, &md->fi, md->stepSize*1,
190 if (md
->hasSeenOneFrame
) {
191 LocalMotions motionscoarse
;
192 LocalMotions motionsfine
;
193 vs_vector_init(&motionsfine
,0);
195 if (md
->fi
.pFormat
> PF_PACKED
) {
196 motionscoarse
= calcTransFields(md
, &md
->fieldscoarse
,
197 calcFieldTransPacked
, contrastSubImgPacked
);
199 motionscoarse
= calcTransFields(md
, &md
->fieldscoarse
,
200 calcFieldTransPlanar
, contrastSubImgPlanar
);
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
);
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
);
216 motions2
= calcTransFields(md
, &md
->fieldsfine
,
217 calcFieldTransPlanar
, contrastSubImgPlanar
);
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
);
224 printf("\nMatches: mean: %f | ", meanMatch
);
225 vs_array_print(matchQualities1
, stdout
);
227 VSArray matchQualities2
= localmotionsGetMatch(&motions2
);
228 vs_array_print(matchQualities2
, stdout
);
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
);
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);
248 *motions
= vs_vector_concat(&motionscoarse
,&motionsfine
);
249 //*motions = motionscoarse;
250 //*motions = motionsfine;
252 vs_vector_init(motions
,1); // dummy vector
253 md
->hasSeenOneFrame
= 1;
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
);
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
271 int 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
;
278 fs
->contrastThreshold
= contrastThreshold
;
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
;
286 if (!(fs
->fields
= (Field
*) vs_malloc(sizeof(Field
) * fs
->fieldNum
))) {
287 vs_log_error(md
->conf
.modName
, "malloc failed!\n");
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)
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
;
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
);
317 /** \see contrastSubImg*/
318 double contrastSubImgPlanar(VSMotionDetect
* md
, const Field
* field
) {
320 return contrastSubImg1_SSE(md
->curr
.data
[0], field
, md
->curr
.linesize
[0],md
->fi
.height
);
322 return contrastSubImg(md
->curr
.data
[0],field
,md
->curr
.linesize
[0],md
->fi
.height
,1);
328 \see contrastSubImg_Michelson three times called with bytesPerPixel=3
331 double 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;
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
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
349 double contrastSubImg(unsigned char* const I
, const Field
* field
, int width
,
350 int height
, int bytesPerPixel
) {
352 unsigned char* p
= NULL
;
353 int s2
= field
->size
/ 2;
354 unsigned char mini
= 255;
355 unsigned char maxi
= 0;
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
;
364 p
+= (width
- field
->size
) * bytesPerPixel
;
366 return (maxi
- mini
) / (maxi
+ mini
+ 0.1); // +0.1 to avoid division by 0
369 /* calculates the optimal transformation for one field in Planar frames
372 LocalMotion
calcFieldTransPlanar(VSMotionDetect
* md
, VSMotionDetectFields
* fs
,
373 const Field
* field
, int fieldnum
) {
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
380 int stepSize
= fs
->stepSize
;
381 int maxShift
= fs
->maxShift
;
382 Vec offset
= { 0, 0};
383 LocalMotion lm
= null_localmotion();
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
)){
401 // printf("%i %i %f\n", md->frameNum, fieldnum, contr);
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
);
409 #ifdef USE_SPIRAL_FIELD_CALC
410 unsigned int minerror
= UINT_MAX
;
412 // check all positions by outgoing spiral
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
,
422 if (error
< minerror
) {
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
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
) {
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
) {
482 fprintf(f
, "%i %i %f\n", i
, j
, error
);
489 while(stepSize
> 1) {// make fine grain check around the best match
490 int txc
= tx
; // save the shifts
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
);
501 fprintf(f
, "%i %i %f\n", i
, j
, error
);
503 if (error
< minerror
) {
514 vs_log_msg(md
->modName
, "Minerror: %f\n", minerror
);
517 if (unlikely(fabs(tx
) >= maxShift
+ stepSize
- 1 ||
518 fabs(ty
) >= maxShift
+ stepSize
)) {
520 vs_log_msg(md
->modName
, "maximal shift ");
522 lm
.match
=-1.0; // to be kicked out
526 lm
.v
.x
= tx
+ offset
.x
;
527 lm
.v
.y
= ty
+ offset
.y
;
528 lm
.match
= ((double) minerror
)/(field
->size
*field
->size
);
532 /* calculates the optimal transformation for one field in Packed
533 * slower than the Planar version because it uses all three color channels
535 LocalMotion
calcFieldTransPacked(VSMotionDetect
* md
, VSMotionDetectFields
* fs
,
536 const Field
* field
, int fieldnum
) {
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
543 int stepSize
= fs
->stepSize
;
544 int maxShift
= fs
->maxShift
;
546 Vec offset
= { 0, 0};
547 LocalMotion lm
= null_localmotion();
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
)){
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
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
) {
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
) {
578 if (stepSize
> 1) { // make fine grain check around the best match
579 int txc
= tx
; // save the shifts
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
) {
597 if (fabs(tx
) >= maxShift
+ stepSize
- 1 || fabs(ty
) >= maxShift
+ stepSize
- 1) {
599 vs_log_msg(md
->modName
, "maximal shift ");
605 lm
.v
.x
= tx
+ offset
.x
;
606 lm
.v
.y
= ty
+ offset
.y
;
607 lm
.match
= ((double)minerror
)/(field
->size
*field
->size
);
611 /* compares contrast_idx structures respect to the contrast
614 int 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);
620 /* select only the best 'maxfields' fields
621 first calc contrasts then select from each part of the
623 We may simplify here by using random. People want high quality, so typically we use all.
625 VSVector
selectfields(VSMotionDetect
* md
, VSMotionDetectFields
* fs
,
626 contrastSubImgFunc contrastfunc
) {
630 (contrast_idx
*) vs_malloc(sizeof(contrast_idx
) * fs
->fieldNum
);
631 vs_vector_init(&goodflds
, fs
->fieldNum
);
633 // we split all fields into row+1 segments and take from each segment
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
);
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
]);
646 if (ci
[i
].contrast
< fs
->contrastThreshold
)
648 // else printf("%i %lf\n", ci[i].index, ci[i].contrast);
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);
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
)
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;
676 // check whether enough fields are selected
677 // printf("Phase2: %i\n", vs_list_size(goodflds));
678 remaining
= fs
->maxFields
- vs_vector_size(&goodflds
);
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
));
688 // printf("Ende: %i\n", vs_list_size(goodflds));
694 /* tries to register current frame onto previous frame.
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
701 LocalMotions
calcTransFields(VSMotionDetect
* md
,
702 VSMotionDetectFields
* fields
,
703 calcFieldTransFunc fieldfunc
,
704 contrastSubImgFunc contrastfunc
) {
705 LocalMotions localmotions
;
706 vs_vector_init(&localmotions
,fields
->maxFields
);
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
);
716 VSVector goodflds
= selectfields(md
, fields
, contrastfunc
);
717 // use all "good" fields and calculate optimal match to previous frame
719 #pragma omp parallel for shared(goodflds, md, localmotions, fs) // does not bring speedup
721 for(int index
=0; index
< vs_vector_size(&goodflds
); index
++){
722 int i
= ((contrast_idx
*)vs_vector_get(&goodflds
,index
))->index
;
724 m
= fieldfunc(md
, fields
, &fields
->fields
[i
], i
); // e.g. calcFieldTransPlanar
726 m
.contrast
= ((contrast_idx
*)vs_vector_get(&goodflds
,index
))->contrast
;
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
);
731 vs_vector_append_dup(&localmotions
, &m
, sizeof(LocalMotion
));
734 vs_vector_del(&goodflds
);
746 /** draws the field scanning area */
747 void drawFieldScanArea(VSMotionDetect
* md
, const LocalMotion
* lm
, int maxShift
) {
748 if (md
->fi
.pFormat
> PF_PACKED
)
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);
754 /** draws the field */
755 void drawField(VSMotionDetect
* md
, const LocalMotion
* lm
, short box
) {
756 if (md
->fi
.pFormat
> PF_PACKED
)
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);
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);
766 /** draws the transform data of this field */
767 void drawFieldTrans(VSMotionDetect
* md
, const LocalMotion
* lm
, int color
) {
768 if (md
->fi
.pFormat
> PF_PACKED
)
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
);
781 * draws a box at the given position x,y (center) in the given color
782 (the same for all channels)
784 void drawBox(unsigned char* I
, int width
, int height
, int bytesPerPixel
, int x
,
785 int y
, int sizex
, int sizey
, unsigned char color
) {
787 unsigned char* p
= NULL
;
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
++) {
795 p
+= (width
- sizex
) * bytesPerPixel
;
800 * draws a rectangle (not filled) at the given position x,y (center) in the given color
803 void drawRectangle(unsigned char* I
, int width
, int height
, int bytesPerPixel
, int x
,
804 int y
, int sizex
, int sizey
, unsigned char color
) {
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
819 * draws a line from a to b with given thickness(not filled) at the given position x,y (center) in the given color
822 void drawLine(unsigned char* I
, int width
, int height
, int bytesPerPixel
,
823 Vec
* a
, Vec
* b
, int thickness
, unsigned char color
) {
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
; }
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
; }
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
; }
853 // void addTrans(VSMotionDetect* md, VSTransform sl) {
854 // if (!md->transs) {
855 // md->transs = vs_list_new(0);
857 // vs_list_append_dup(md->transs, &sl, sizeof(sl));
860 // VSTransform getLastVSTransform(VSMotionDetect* md){
861 // if (!md->transs || !md->transs->head) {
862 // return null_transform();
864 // return *((VSTransform*)md->transs->tail);
869 /// plain C implementation of compareSubImg (without ORC)
870 unsigned 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
) {
875 unsigned char* p1
= NULL
;
876 unsigned char* p2
= NULL
;
877 int s2
= field
->size
/ 2;
878 unsigned int sum
= 0;
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
)
883 for (j
= 0; j
< field
->size
; j
++) {
884 for (k
= 0; k
< field
->size
* bytesPerPixel
; k
++) {
885 sum
+= abs((int) *p1
- (int) *p2
);
889 if( sum
> threshold
) // no need to calculate any longer: worse than the best match
891 p1
+= (width1
- field
->size
) * bytesPerPixel
;
892 p2
+= (width2
- field
->size
) * bytesPerPixel
;
900 * c-file-style: "stroustrup"
901 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
902 * indent-tabs-mode: nil
904 * c-basic-offset: 2 t