1 /* alg.c
2 *
3 * Detect changes in a video stream.
4 * Copyright 2001 by Jeroen Vreeken (pe1rxq@amsat.org)
5 * This software is distributed under the GNU public license version 2
6 * See also the file 'COPYING'.
7 *
8 */
9 #include "motion.h"
10 #include "alg.h"
11
12 #ifdef __MMX__
13 #define HAVE_MMX
14 #include "mmx.h"
15 #endif
16
17 #define MAX2(x, y) ((x) > (y) ? (x) : (y))
18 #define MAX3(x, y, z) ((x) > (y) ? ((x) > (z) ? (x) : (z)) : ((y) > (z) ? (y) : (z)))
19
20 /**
21 * alg_locate_center_size
22 * Locates the center and size of the movement.
23 */
alg_locate_center_size(struct images * imgs,int width,int height,struct coord * cent)24 void alg_locate_center_size(struct images *imgs, int width, int height, struct coord *cent)
25 {
26 unsigned char *out = imgs->img_motion.image_norm;
27 int *labels = imgs->labels;
28 int x, y, centc = 0, xdist = 0, ydist = 0;
29
30 cent->x = 0;
31 cent->y = 0;
32 cent->maxx = 0;
33 cent->maxy = 0;
34 cent->minx = width;
35 cent->miny = height;
36
37 /* If Labeling enabled - locate center of largest labelgroup. */
38 if (imgs->labelsize_max) {
39 /* Locate largest labelgroup */
40 for (y = 0; y < height; y++) {
41 for (x = 0; x < width; x++) {
42 if (*(labels++) & 32768) {
43 cent->x += x;
44 cent->y += y;
45 centc++;
46 }
47 }
48 }
49
50 } else {
51 /* Locate movement */
52 for (y = 0; y < height; y++) {
53 for (x = 0; x < width; x++) {
54 if (*(out++)) {
55 cent->x += x;
56 cent->y += y;
57 centc++;
58 }
59 }
60 }
61
62 }
63
64 if (centc) {
65 cent->x = cent->x / centc;
66 cent->y = cent->y / centc;
67 }
68
69 /* Now we find the size of the Motion. */
70
71 /* First reset pointers back to initial value. */
72 centc = 0;
73 labels = imgs->labels;
74 out = imgs->img_motion.image_norm;
75
76 /* If Labeling then we find the area around largest labelgroup instead. */
77 if (imgs->labelsize_max) {
78 for (y = 0; y < height; y++) {
79 for (x = 0; x < width; x++) {
80 if (*(labels++) & 32768) {
81 if (x > cent->x)
82 xdist += x - cent->x;
83 else if (x < cent->x)
84 xdist += cent->x - x;
85
86 if (y > cent->y)
87 ydist += y - cent->y;
88 else if (y < cent->y)
89 ydist += cent->y - y;
90
91 centc++;
92 }
93 }
94 }
95
96 } else {
97 for (y = 0; y < height; y++) {
98 for (x = 0; x < width; x++) {
99 if (*(out++)) {
100 if (x > cent->x)
101 xdist += x - cent->x;
102 else if (x < cent->x)
103 xdist += cent->x - x;
104
105 if (y > cent->y)
106 ydist += y - cent->y;
107 else if (y < cent->y)
108 ydist += cent->y - y;
109
110 centc++;
111 }
112 }
113 }
114
115 }
116
117 if (centc) {
118 cent->minx = cent->x - xdist / centc * 2;
119 cent->maxx = cent->x + xdist / centc * 2;
120 /*
121 * Make the box a little bigger in y direction to make sure the
122 * heads fit in so we multiply by 3 instead of 2 which seems to
123 * to work well in practical.
124 */
125 cent->miny = cent->y - ydist / centc * 3;
126 cent->maxy = cent->y + ydist / centc * 2;
127 }
128
129 if (cent->maxx > width - 1)
130 cent->maxx = width - 1;
131 else if (cent->maxx < 0)
132 cent->maxx = 0;
133
134 if (cent->maxy > height - 1)
135 cent->maxy = height - 1;
136 else if (cent->maxy < 0)
137 cent->maxy = 0;
138
139 if (cent->minx > width - 1)
140 cent->minx = width - 1;
141 else if (cent->minx < 0)
142 cent->minx = 0;
143
144 if (cent->miny > height - 1)
145 cent->miny = height - 1;
146 else if (cent->miny < 0)
147 cent->miny = 0;
148
149 /* Align for better locate box handling */
150 cent->minx += cent->minx % 2;
151 cent->miny += cent->miny % 2;
152 cent->maxx -= cent->maxx % 2;
153 cent->maxy -= cent->maxy % 2;
154
155 cent->width = cent->maxx - cent->minx;
156 cent->height = cent->maxy - cent->miny;
157
158 /*
159 * We want to center Y coordinate to be the center of the action.
160 * The head of a person is important so we correct the cent.y coordinate
161 * to match the correction to include a persons head that we just did above.
162 */
163 cent->y = (cent->miny + cent->maxy) / 2;
164
165 }
166
167
168 /**
169 * alg_draw_location
170 * Draws a box around the movement.
171 */
alg_draw_location(struct coord * cent,struct images * imgs,int width,unsigned char * new,int style,int mode,int process_thisframe)172 void alg_draw_location(struct coord *cent, struct images *imgs, int width, unsigned char *new,
173 int style, int mode, int process_thisframe)
174 {
175 unsigned char *out = imgs->img_motion.image_norm;
176 int x, y;
177
178 out = imgs->img_motion.image_norm;
179
180 /* Debug image always gets a 'normal' box. */
181 if ((mode == LOCATE_BOTH) && process_thisframe) {
182 int width_miny = width * cent->miny;
183 int width_maxy = width * cent->maxy;
184
185 for (x = cent->minx; x <= cent->maxx; x++) {
186 int width_miny_x = x + width_miny;
187 int width_maxy_x = x + width_maxy;
188
189 out[width_miny_x] =~out[width_miny_x];
190 out[width_maxy_x] =~out[width_maxy_x];
191 }
192
193 for (y = cent->miny; y <= cent->maxy; y++) {
194 int width_minx_y = cent->minx + y * width;
195 int width_maxx_y = cent->maxx + y * width;
196
197 out[width_minx_y] =~out[width_minx_y];
198 out[width_maxx_y] =~out[width_maxx_y];
199 }
200 }
201 if (style == LOCATE_BOX) { /* Draw a box on normal images. */
202 int width_miny = width * cent->miny;
203 int width_maxy = width * cent->maxy;
204
205 for (x = cent->minx; x <= cent->maxx; x++) {
206 int width_miny_x = x + width_miny;
207 int width_maxy_x = x + width_maxy;
208
209 new[width_miny_x] =~new[width_miny_x];
210 new[width_maxy_x] =~new[width_maxy_x];
211 }
212
213 for (y = cent->miny; y <= cent->maxy; y++) {
214 int width_minx_y = cent->minx + y * width;
215 int width_maxx_y = cent->maxx + y * width;
216
217 new[width_minx_y] =~new[width_minx_y];
218 new[width_maxx_y] =~new[width_maxx_y];
219 }
220 } else if (style == LOCATE_CROSS) { /* Draw a cross on normal images. */
221 int centy = cent->y * width;
222
223 for (x = cent->x - 10; x <= cent->x + 10; x++) {
224 new[centy + x] =~new[centy + x];
225 out[centy + x] =~out[centy + x];
226 }
227
228 for (y = cent->y - 10; y <= cent->y + 10; y++) {
229 new[cent->x + y * width] =~new[cent->x + y * width];
230 out[cent->x + y * width] =~out[cent->x + y * width];
231 }
232 }
233 }
234
235
236 /**
237 * alg_draw_red_location
238 * Draws a RED box around the movement.
239 */
alg_draw_red_location(struct coord * cent,struct images * imgs,int width,unsigned char * new,int style,int mode,int process_thisframe)240 void alg_draw_red_location(struct coord *cent, struct images *imgs, int width, unsigned char *new,
241 int style, int mode, int process_thisframe)
242 {
243 unsigned char *out = imgs->img_motion.image_norm;
244 unsigned char *new_u, *new_v;
245 int x, y, v, cwidth, cblock;
246
247 cwidth = width / 2;
248 cblock = imgs->motionsize / 4;
249 x = imgs->motionsize;
250 v = x + cblock;
251 out = imgs->img_motion.image_norm;
252 new_u = new + x;
253 new_v = new + v;
254
255 /* Debug image always gets a 'normal' box. */
256 if ((mode == LOCATE_BOTH) && process_thisframe) {
257 int width_miny = width * cent->miny;
258 int width_maxy = width * cent->maxy;
259
260 for (x = cent->minx; x <= cent->maxx; x++) {
261 int width_miny_x = x + width_miny;
262 int width_maxy_x = x + width_maxy;
263
264 out[width_miny_x] =~out[width_miny_x];
265 out[width_maxy_x] =~out[width_maxy_x];
266 }
267
268 for (y = cent->miny; y <= cent->maxy; y++) {
269 int width_minx_y = cent->minx + y * width;
270 int width_maxx_y = cent->maxx + y * width;
271
272 out[width_minx_y] =~out[width_minx_y];
273 out[width_maxx_y] =~out[width_maxx_y];
274 }
275 }
276
277 if (style == LOCATE_REDBOX) { /* Draw a red box on normal images. */
278 int width_miny = width * cent->miny;
279 int width_maxy = width * cent->maxy;
280 int cwidth_miny = cwidth * (cent->miny / 2);
281 int cwidth_maxy = cwidth * (cent->maxy / 2);
282
283 for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) {
284 int width_miny_x = x + width_miny;
285 int width_maxy_x = x + width_maxy;
286 int cwidth_miny_x = x / 2 + cwidth_miny;
287 int cwidth_maxy_x = x / 2 + cwidth_maxy;
288
289 new_u[cwidth_miny_x] = 128;
290 new_u[cwidth_maxy_x] = 128;
291 new_v[cwidth_miny_x] = 255;
292 new_v[cwidth_maxy_x] = 255;
293
294 new[width_miny_x] = 128;
295 new[width_maxy_x] = 128;
296
297 new[width_miny_x + 1] = 128;
298 new[width_maxy_x + 1] = 128;
299
300 new[width_miny_x + width] = 128;
301 new[width_maxy_x + width] = 128;
302
303 new[width_miny_x + 1 + width] = 128;
304 new[width_maxy_x + 1 + width] = 128;
305 }
306
307 for (y = cent->miny; y <= cent->maxy; y += 2) {
308 int width_minx_y = cent->minx + y * width;
309 int width_maxx_y = cent->maxx + y * width;
310 int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth;
311 int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth;
312
313 new_u[cwidth_minx_y] = 128;
314 new_u[cwidth_maxx_y] = 128;
315 new_v[cwidth_minx_y] = 255;
316 new_v[cwidth_maxx_y] = 255;
317
318 new[width_minx_y] = 128;
319 new[width_maxx_y] = 128;
320
321 new[width_minx_y + width] = 128;
322 new[width_maxx_y + width] = 128;
323
324 new[width_minx_y + 1] = 128;
325 new[width_maxx_y + 1] = 128;
326
327 new[width_minx_y + width + 1] = 128;
328 new[width_maxx_y + width + 1] = 128;
329 }
330 } else if (style == LOCATE_REDCROSS) { /* Draw a red cross on normal images. */
331 int cwidth_maxy = cwidth * (cent->y / 2);
332
333 for (x = cent->x - 10; x <= cent->x + 10; x += 2) {
334 int cwidth_maxy_x = x / 2 + cwidth_maxy;
335
336 new_u[cwidth_maxy_x] = 128;
337 new_v[cwidth_maxy_x] = 255;
338 }
339
340 for (y = cent->y - 10; y <= cent->y + 10; y += 2) {
341 int cwidth_minx_y = (cent->x / 2) + (y / 2) * cwidth;
342
343 new_u[cwidth_minx_y] = 128;
344 new_v[cwidth_minx_y] = 255;
345 }
346 }
347 }
348
349
350 #define NORM 100
351 #define ABS(x) ((x) < 0 ? -(x) : (x))
352 #define DIFF(x, y) (ABS((x)-(y)))
353 #define NDIFF(x, y) (ABS(x) * NORM / (ABS(x) + 2 * DIFF(x, y)))
354
355 /**
356 * alg_noise_tune
357 *
358 */
alg_noise_tune(struct context * cnt,unsigned char * new)359 void alg_noise_tune(struct context *cnt, unsigned char *new)
360 {
361 struct images *imgs = &cnt->imgs;
362 int i;
363 unsigned char *ref = imgs->ref;
364 int diff, sum = 0, count = 0;
365 unsigned char *mask = imgs->mask;
366 unsigned char *smartmask = imgs->smartmask_final;
367
368 i = imgs->motionsize;
369
370 for (; i > 0; i--) {
371 diff = ABS(*ref - *new);
372
373 if (mask)
374 diff = ((diff * *mask++) / 255);
375
376 if (*smartmask) {
377 sum += diff + 1;
378 count++;
379 }
380
381 ref++;
382 new++;
383 smartmask++;
384 }
385
386 if (count > 3) /* Avoid divide by zero. */
387 sum /= count / 3;
388
389 /* 5: safe, 4: regular, 3: more sensitive */
390 cnt->noise = 4 + (cnt->noise + sum) / 2;
391 }
392
393 /**
394 * alg_threshold_tune
395 *
396 */
alg_threshold_tune(struct context * cnt,int diffs,int motion)397 void alg_threshold_tune(struct context *cnt, int diffs, int motion)
398 {
399 int i;
400 int sum = 0, top = diffs;
401
402 if (!diffs)
403 return;
404
405 if (motion)
406 diffs = cnt->threshold / 4;
407
408 for (i = 0; i < THRESHOLD_TUNE_LENGTH - 1; i++) {
409 sum += cnt->diffs_last[i];
410
411 if (cnt->diffs_last[i + 1] && !motion)
412 cnt->diffs_last[i] = cnt->diffs_last[i + 1];
413 else
414 cnt->diffs_last[i] = cnt->threshold / 4;
415
416 if (cnt->diffs_last[i] > top)
417 top = cnt->diffs_last[i];
418 }
419
420 sum += cnt->diffs_last[i];
421 cnt->diffs_last[i] = diffs;
422
423 sum /= THRESHOLD_TUNE_LENGTH / 4;
424
425 if (sum < top * 2)
426 sum = top * 2;
427
428 if (sum < cnt->conf.threshold)
429 cnt->threshold = (cnt->threshold + sum) / 2;
430 }
431
432 /*
433 * Labeling by Joerg Weber. Based on an idea from Hubert Mara.
434 * Floodfill enhanced by Ian McConnel based on code from
435 * http://www.acm.org/pubs/tog/GraphicsGems/
436 * http://www.codeproject.com/gdi/QuickFill.asp
437
438 * Filled horizontal segment of scanline y for xl <= x <= xr.
439 * Parent segment was on line y - dy. dy = 1 or -1
440 */
441
442 #define MAXS 10000 /* max depth of stack */
443
444 #define PUSH(Y, XL, XR, DY) /* push new segment on stack */ \
445 if (sp<stack+MAXS && Y+(DY) >= 0 && Y+(DY) < height) \
446 {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;}
447
448 #define POP(Y, XL, XR, DY) /* pop segment off stack */ \
449 {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;}
450
451 typedef struct {
452 short y, xl, xr, dy;
453 } Segment;
454
455 /**
456 * iflood
457 *
458 */
iflood(int x,int y,int width,int height,unsigned char * out,int * labels,int newvalue,int oldvalue)459 static int iflood(int x, int y, int width, int height,
460 unsigned char *out, int *labels, int newvalue, int oldvalue)
461 {
462 int l, x1, x2, dy;
463 Segment stack[MAXS], *sp = stack; /* Stack of filled segments. */
464 int count = 0;
465
466 if (x < 0 || x >= width || y < 0 || y >= height)
467 return 0;
468
469 PUSH(y, x, x, 1); /* Needed in some cases. */
470 PUSH(y+1, x, x, -1); /* Seed segment (popped 1st). */
471
472 while (sp > stack) {
473 /* Pop segment off stack and fill a neighboring scan line. */
474 POP(y, x1, x2, dy);
475 /*
476 * Segment of scan line y-dy for x1<=x<=x2 was previously filled,
477 * now explore adjacent pixels in scan line y
478 */
479 for (x = x1; x >= 0 && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x--) {
480 labels[y * width + x] = newvalue;
481 count++;
482 }
483
484 if (x >= x1)
485 goto skip;
486
487 l = x + 1;
488
489 if (l < x1)
490 PUSH(y, l, x1 - 1, -dy); /* Leak on left? */
491
492 x = x1 + 1;
493
494 do {
495 for (; x < width && out[y * width + x] != 0 && labels[y * width + x] == oldvalue; x++) {
496 labels[y * width + x] = newvalue;
497 count++;
498 }
499
500 PUSH(y, l, x - 1, dy);
501
502 if (x > x2 + 1)
503 PUSH(y, x2 + 1, x - 1, -dy); /* Leak on right? */
504
505 skip:
506
507 for (x++; x <= x2 && !(out[y * width + x] != 0 && labels[y * width + x] == oldvalue); x++);
508
509 l = x;
510 } while (x <= x2);
511 }
512 return count;
513 }
514
515 /**
516 * alg_labeling
517 *
518 */
alg_labeling(struct context * cnt)519 static int alg_labeling(struct context *cnt)
520 {
521 struct images *imgs = &cnt->imgs;
522 unsigned char *out = imgs->img_motion.image_norm;
523 int *labels = imgs->labels;
524 int ix, iy, pixelpos;
525 int width = imgs->width;
526 int height = imgs->height;
527 int labelsize = 0;
528 int current_label = 2;
529 /* Keep track of the area just under the threshold. */
530 int max_under = 0;
531
532 cnt->current_image->total_labels = 0;
533 imgs->labelsize_max = 0;
534 /* ALL labels above threshold are counted as labelgroup. */
535 imgs->labelgroup_max = 0;
536 imgs->labels_above = 0;
537
538 /* Init: 0 means no label set / not checked. */
539 memset(labels, 0, width * height * sizeof(*labels));
540 pixelpos = 0;
541
542 for (iy = 0; iy < height - 1; iy++) {
543 for (ix = 0; ix < width - 1; ix++, pixelpos++) {
544 /* No motion - no label */
545 if (out[pixelpos] == 0) {
546 labels[pixelpos] = 1;
547 continue;
548 }
549
550 /* Already visited by iflood */
551 if (labels[pixelpos] > 0)
552 continue;
553
554 labelsize = iflood(ix, iy, width, height, out, labels, current_label, 0);
555
556 if (labelsize > 0) {
557 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "Label: %i (%i) Size: %i (%i,%i)",
558 // current_label, cnt->current_image->total_labels,
559 // labelsize, ix, iy);
560
561 /* Label above threshold? Mark it again (add 32768 to labelnumber). */
562 if (labelsize > cnt->threshold) {
563 labelsize = iflood(ix, iy, width, height, out, labels, current_label + 32768, current_label);
564 imgs->labelgroup_max += labelsize;
565 imgs->labels_above++;
566 } else if(max_under < labelsize)
567 max_under = labelsize;
568
569 if (imgs->labelsize_max < labelsize) {
570 imgs->labelsize_max = labelsize;
571 imgs->largest_label = current_label;
572 }
573
574 cnt->current_image->total_labels++;
575 current_label++;
576 }
577 }
578 pixelpos++; /* Compensate for ix < width - 1 */
579 }
580
581 //MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%i Labels found. Largest connected Area: %i Pixel(s). "
582 // "Largest Label: %i", imgs->largest_label, imgs->labelsize_max,
583 // cnt->current_image->total_labels);
584
585 /* Return group of significant labels or if that's none, the next largest
586 * group (which is under the threshold, but especially for setup gives an
587 * idea how close it was).
588 */
589 return imgs->labelgroup_max ? imgs->labelgroup_max : max_under;
590 }
591
592 /**
593 * dilate9
594 * Dilates a 3x3 box.
595 */
dilate9(unsigned char * img,int width,int height,void * buffer)596 static int dilate9(unsigned char *img, int width, int height, void *buffer)
597 {
598 /*
599 * - row1, row2 and row3 represent lines in the temporary buffer.
600 * - Window is a sliding window containing max values of the columns
601 * in the 3x3 matrix.
602 * - width is an index into the sliding window (this is faster than
603 * doing modulo 3 on i).
604 * - blob keeps the current max value.
605 */
606 int y, i, sum = 0, widx;
607 unsigned char *row1, *row2, *row3, *rowTemp,*yp;
608 unsigned char window[3], blob, latest;
609
610 /* Set up row pointers in the temporary buffer. */
611 row1 = buffer;
612 row2 = row1 + width;
613 row3 = row2 + width;
614
615 /* Init rows 2 and 3. */
616 memset(row2, 0, width);
617 memcpy(row3, img, width);
618
619 /* Pointer to the current row in img. */
620 yp = img;
621
622 for (y = 0; y < height; y++) {
623 /* Move down one step; row 1 becomes the previous row 2 and so on. */
624 rowTemp = row1;
625 row1 = row2;
626 row2 = row3;
627 row3 = rowTemp;
628
629 /* If we're at the last row, fill with zeros, otherwise copy from img. */
630 if (y == height - 1)
631 memset(row3, 0, width);
632 else
633 memcpy(row3, yp+width, width);
634
635 /* Init slots 0 and 1 in the moving window. */
636 window[0] = MAX3(row1[0], row2[0], row3[0]);
637 window[1] = MAX3(row1[1], row2[1], row3[1]);
638
639 /* Init blob to the current max, and set window index. */
640 blob = MAX2(window[0], window[1]);
641 widx = 2;
642
643 /*
644 * Iterate over the current row; index i is off by one to eliminate
645 * a lot of +1es in the loop.
646 */
647 for (i = 2; i <= width - 1; i++) {
648 /* Get the max value of the next column in the 3x3 matrix. */
649 latest = window[widx] = MAX3(row1[i], row2[i], row3[i]);
650
651 /*
652 * If the value is larger than the current max, use it. Otherwise,
653 * calculate a new max (because the new value may not be the max.
654 */
655 if (latest >= blob)
656 blob = latest;
657 else
658 blob = MAX3(window[0], window[1], window[2]);
659
660 /* Write the max value (blob) to the image. */
661 if (blob != 0) {
662 *(yp + i - 1) = blob;
663 sum++;
664 }
665
666 /* Wrap around the window index if necessary. */
667 if (++widx == 3)
668 widx = 0;
669 }
670
671 /* Store zeros in the vertical sides. */
672 *yp = *(yp + width - 1) = 0;
673 yp += width;
674 }
675
676 return sum;
677 }
678
679 /**
680 * dilate5
681 * Dilates a + shape.
682 */
dilate5(unsigned char * img,int width,int height,void * buffer)683 static int dilate5(unsigned char *img, int width, int height, void *buffer)
684 {
685 /*
686 * - row1, row2 and row3 represent lines in the temporary buffer.
687 * - mem holds the max value of the overlapping part of two + shapes.
688 */
689 int y, i, sum = 0;
690 unsigned char *row1, *row2, *row3, *rowTemp, *yp;
691 unsigned char blob, mem, latest;
692
693 /* Set up row pointers in the temporary buffer. */
694 row1 = buffer;
695 row2 = row1 + width;
696 row3 = row2 + width;
697
698 /* Init rows 2 and 3. */
699 memset(row2, 0, width);
700 memcpy(row3, img, width);
701
702 /* Pointer to the current row in img. */
703 yp = img;
704
705 for (y = 0; y < height; y++) {
706 /* Move down one step; row 1 becomes the previous row 2 and so on. */
707 rowTemp = row1;
708 row1 = row2;
709 row2 = row3;
710 row3 = rowTemp;
711
712 /* If we're at the last row, fill with zeros, otherwise copy from img. */
713 if (y == height - 1)
714 memset(row3, 0, width);
715 else
716 memcpy(row3, yp + width, width);
717
718 /* Init mem and set blob to force an evaluation of the entire + shape. */
719 mem = MAX2(row2[0], row2[1]);
720 blob = 1; /* dummy value, must be > 0 */
721
722 for (i = 1; i < width - 1; i++) {
723 /* Get the max value of the "right edge" of the + shape. */
724 latest = MAX3(row1[i], row2[i + 1], row3[i]);
725
726 if (blob == 0) {
727 /* In case the last blob is zero, only latest matters. */
728 blob = latest;
729 mem = row2[i + 1];
730 } else {
731 /* Otherwise, we have to check both latest and mem. */
732 blob = MAX2(mem, latest);
733 mem = MAX2(row2[i], row2[i + 1]);
734 }
735
736 /* Write the max value (blob) to the image. */
737 if (blob != 0) {
738 *(yp + i) = blob;
739 sum++;
740 }
741 }
742
743 /* Store zeros in the vertical sides. */
744 *yp = *(yp + width - 1) = 0;
745 yp += width;
746 }
747 return sum;
748 }
749
750 /**
751 * erode9
752 * Erodes a 3x3 box.
753 */
erode9(unsigned char * img,int width,int height,void * buffer,unsigned char flag)754 static int erode9(unsigned char *img, int width, int height, void *buffer, unsigned char flag)
755 {
756 int y, i, sum = 0;
757 char *Row1,*Row2,*Row3;
758
759 Row1 = buffer;
760 Row2 = Row1 + width;
761 Row3 = Row1 + 2 * width;
762 memset(Row2, flag, width);
763 memcpy(Row3, img, width);
764
765 for (y = 0; y < height; y++) {
766 memcpy(Row1, Row2, width);
767 memcpy(Row2, Row3, width);
768
769 if (y == height-1)
770 memset(Row3, flag, width);
771 else
772 memcpy(Row3, img + (y+1) * width, width);
773
774 for (i = width - 2; i >= 1; i--) {
775 if (Row1[i - 1] == 0 ||
776 Row1[i] == 0 ||
777 Row1[i + 1] == 0 ||
778 Row2[i - 1] == 0 ||
779 Row2[i] == 0 ||
780 Row2[i + 1] == 0 ||
781 Row3[i - 1] == 0 ||
782 Row3[i] == 0 ||
783 Row3[i + 1] == 0)
784 img[y * width + i] = 0;
785 else
786 sum++;
787 }
788
789 img[y * width] = img[y * width + width - 1] = flag;
790 }
791 return sum;
792 }
793
794 /**
795 * erode5
796 * Erodes in a + shape.
797 */
erode5(unsigned char * img,int width,int height,void * buffer,unsigned char flag)798 static int erode5(unsigned char *img, int width, int height, void *buffer, unsigned char flag)
799 {
800 int y, i, sum = 0;
801 char *Row1,*Row2,*Row3;
802
803 Row1 = buffer;
804 Row2 = Row1 + width;
805 Row3 = Row1 + 2 * width;
806 memset(Row2, flag, width);
807 memcpy(Row3, img, width);
808
809 for (y = 0; y < height; y++) {
810 memcpy(Row1, Row2, width);
811 memcpy(Row2, Row3, width);
812
813 if (y == height-1)
814 memset(Row3, flag, width);
815 else
816 memcpy(Row3, img + (y + 1) * width, width);
817
818 for (i = width - 2; i >= 1; i--) {
819 if (Row1[i] == 0 ||
820 Row2[i - 1] == 0 ||
821 Row2[i] == 0 ||
822 Row2[i + 1] == 0 ||
823 Row3[i] == 0)
824 img[y * width + i] = 0;
825 else
826 sum++;
827 }
828
829 img[y * width] = img[y * width + width - 1] = flag;
830 }
831 return sum;
832 }
833
834 /**
835 * alg_despeckle
836 * Despeckling routine to remove noisy detections.
837 */
alg_despeckle(struct context * cnt,int olddiffs)838 int alg_despeckle(struct context *cnt, int olddiffs)
839 {
840 int diffs = 0;
841 unsigned char *out = cnt->imgs.img_motion.image_norm;
842 int width = cnt->imgs.width;
843 int height = cnt->imgs.height;
844 int done = 0, i, len = strlen(cnt->conf.despeckle_filter);
845 unsigned char *common_buffer = cnt->imgs.common_buffer;
846
847 for (i = 0; i < len; i++) {
848 switch (cnt->conf.despeckle_filter[i]) {
849 case 'E':
850 if ((diffs = erode9(out, width, height, common_buffer, 0)) == 0)
851 i = len;
852 done = 1;
853 break;
854 case 'e':
855 if ((diffs = erode5(out, width, height, common_buffer, 0)) == 0)
856 i = len;
857 done = 1;
858 break;
859 case 'D':
860 diffs = dilate9(out, width, height, common_buffer);
861 done = 1;
862 break;
863 case 'd':
864 diffs = dilate5(out, width, height, common_buffer);
865 done = 1;
866 break;
867 /* No further despeckle after labeling! */
868 case 'l':
869 diffs = alg_labeling(cnt);
870 i = len;
871 done = 2;
872 break;
873 }
874 }
875
876 /* If conf.despeckle_filter contains any valid action EeDdl */
877 if (done) {
878 if (done != 2)
879 cnt->imgs.labelsize_max = 0; // Disable Labeling
880 return diffs;
881 } else {
882 cnt->imgs.labelsize_max = 0; // Disable Labeling
883 }
884
885 return olddiffs;
886 }
887
888 /**
889 * alg_tune_smartmask
890 * Generates actual smartmask. Calculate sensitivity based on motion.
891 */
alg_tune_smartmask(struct context * cnt)892 void alg_tune_smartmask(struct context *cnt)
893 {
894 int i, diff;
895 int motionsize = cnt->imgs.motionsize;
896 unsigned char *smartmask = cnt->imgs.smartmask;
897 unsigned char *smartmask_final = cnt->imgs.smartmask_final;
898 int *smartmask_buffer = cnt->imgs.smartmask_buffer;
899 int sensitivity = cnt->lastrate * (11 - cnt->smartmask_speed);
900
901 for (i = 0; i < motionsize; i++) {
902 /* Decrease smart_mask sensitivity every 5*speed seconds only. */
903 if (smartmask[i] > 0)
904 smartmask[i]--;
905 /* Increase smart_mask sensitivity based on the buffered values. */
906 diff = smartmask_buffer[i]/sensitivity;
907
908 if (diff) {
909 if (smartmask[i] <= diff + 80)
910 smartmask[i] += diff;
911 else
912 smartmask[i] = 80;
913 smartmask_buffer[i] %= sensitivity;
914 }
915 /* Transfer raw mask to the final stage when above trigger value. */
916 if (smartmask[i] > 20)
917 smartmask_final[i] = 0;
918 else
919 smartmask_final[i] = 255;
920 }
921 /* Further expansion (here:erode due to inverted logic!) of the mask. */
922 diff = erode9(smartmask_final, cnt->imgs.width, cnt->imgs.height,
923 cnt->imgs.common_buffer, 255);
924 diff = erode5(smartmask_final, cnt->imgs.width, cnt->imgs.height,
925 cnt->imgs.common_buffer, 255);
926 }
927
928 /* Increment for *smartmask_buffer in alg_diff_standard. */
929 #define SMARTMASK_SENSITIVITY_INCR 5
930
931 /**
932 * alg_diff_standard
933 *
934 */
alg_diff_standard(struct context * cnt,unsigned char * new)935 int alg_diff_standard(struct context *cnt, unsigned char *new)
936 {
937 struct images *imgs = &cnt->imgs;
938 int i, diffs = 0;
939 int noise = cnt->noise;
940 int smartmask_speed = cnt->smartmask_speed;
941 unsigned char *ref = imgs->ref;
942 unsigned char *out = imgs->img_motion.image_norm;
943 unsigned char *mask = imgs->mask;
944 unsigned char *smartmask_final = imgs->smartmask_final;
945 int *smartmask_buffer = imgs->smartmask_buffer;
946 #ifdef HAVE_MMX
947 mmx_t mmtemp; /* Used for transferring to/from memory. */
948 int unload; /* Counter for unloading diff counts. */
949 #endif
950
951 i = imgs->motionsize;
952 memset(out + i, 128, i / 2); /* Motion pictures are now b/w i.o. green */
953 /*
954 * Keeping this memset in the MMX case when zeroes are necessarily
955 * written anyway seems to be beneficial in terms of speed. Perhaps a
956 * cache thing?
957 */
958 memset(out, 0, i);
959
960 #ifdef HAVE_MMX
961 /*
962 * NOTE: The Pentium has two instruction pipes: U and V. I have grouped MMX
963 * instructions in pairs according to how I think they will be scheduled in
964 * the U and V pipes. Due to pairing constraints, the V pipe will sometimes
965 * be empty (for example, memory access always goes into the U pipe).
966 *
967 * The following MMX registers are kept throughout the loop:
968 * mm5 - 8 separate diff counters (unloaded periodically)
969 * mm6 - mask: 00ff 00ff 00ff 00ff
970 * mm7 - noise level as 8 packed bytes
971 *
972 * -- Per Jonsson
973 */
974
975 /*
976 * To avoid a div, we work with differences multiplied by 255 in the
977 * default case and *mask otherwise. Thus, the limit to compare with is
978 * 255 * (noise + 1) - 1).
979 */
980 mmtemp.uw[0] = mmtemp.uw[1] = mmtemp.uw[2] = mmtemp.uw[3] =
981 (unsigned short)(noise * 255 + 254);
982
983 /*
984 * Reset mm5 to zero, set the mm6 mask, and store the multiplied noise
985 * level as four words in mm7.
986 */
987 movq_m2r(mmtemp, mm7); /* U */
988 pcmpeqb_r2r(mm6, mm6); /* V */
989
990 pxor_r2r(mm5, mm5); /* U */
991 psrlw_i2r(8, mm6); /* V */
992
993 /*
994 * We must unload mm5 every 255th round, because the diffs accumulate
995 * in each packed byte, which can hold at most 255 diffs before it
996 * gets saturated.
997 */
998 unload = 255;
999
1000 for (; i > 7; i -= 8) {
1001 /* Calculate abs(*ref-*new) for 8 pixels in parallel. */
1002 movq_m2r(*ref, mm0); /* U: mm0 = r7 r6 r5 r4 r3 r2 r1 r0 */
1003 pxor_r2r(mm4, mm4); /* V: mm4 = 0 */
1004
1005 movq_m2r(*new, mm1); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */
1006 movq_r2r(mm0, mm2); /* V: mm2 = r7 r6 r5 r4 r3 r2 r1 r0 */
1007
1008 /* These subtractions are saturated, i.e. won't go below 0. */
1009 psubusb_r2r(mm1, mm0); /* U: mm0 = (r7-n7) ... (r0-n0) */
1010 psubusb_r2r(mm2, mm1); /* V: mm1 = (n7-r7) ... (n0-r0) */
1011
1012 /* Each byte dX in mm0 is abs(nX-rX). */
1013 por_r2r(mm1, mm0); /* U: mm0 = d7 d6 d5 d4 d3 d2 d1 d0 */
1014
1015 /* Expand the absolute differences to words in mm0 and mm1. */
1016 movq_r2r(mm0, mm1); /* U: mm1 = d7 d6 d5 d4 d3 d2 d1 d0 */
1017 punpcklbw_r2r(mm4, mm0); /* V: mm0 = d3 d2 d1 d0 */
1018
1019 punpckhbw_r2r(mm4, mm1); /* U: mm1 = d7 d6 d5 d4 */
1020
1021 if (mask) {
1022 /*
1023 * Load and expand 8 mask bytes to words in mm2 and mm3. Then
1024 * multiply by mm0 and mm1, respectively.
1025 */
1026 movq_m2r(*mask, mm2); /* U: mm2 = m7 m6 m5 m4 m3 m2 m1 m0 */
1027
1028 movq_r2r(mm2, mm3); /* U: mm3 = m7 m6 m5 m4 m3 m2 m1 m0 */
1029 punpcklbw_r2r(mm4, mm2); /* v: mm2 = m3 m2 m1 m0 */
1030
1031 punpckhbw_r2r(mm4, mm3); /* U: mm3 = m7 m6 m5 m4 */
1032 pmullw_r2r(mm2, mm0); /* V: mm0 = (d3*m3) ... (d0*m0) */
1033
1034 pmullw_r2r(mm3, mm1); /* U: mm1 = (d7*m7) ... (d4*m4) */
1035
1036 mask += 8;
1037 } else {
1038 /*
1039 * Not using mask - multiply the absolute differences by 255. We
1040 * do this by left-shifting 8 places and then subtracting dX.
1041 */
1042 movq_r2r(mm0, mm2); /* U: mm2 = d3 d2 d1 d0 */
1043 psllw_i2r(8, mm0); /* V: mm2 = (256*d3) ... (256*d0) */
1044
1045 movq_r2r(mm1, mm3); /* U: mm3 = d7 d6 d5 d4 */
1046 psllw_i2r(8, mm1); /* V: mm3 = (256*d7) ... (256*d4) */
1047
1048 psubusw_r2r(mm2, mm0); /* U */
1049 psubusw_r2r(mm3, mm1); /* V */
1050 }
1051
1052 /*
1053 * Next, compare the multiplied absolute differences with the multiplied
1054 * noise level (repeated as 4 words in mm7), resulting in a "motion flag"
1055 * for each pixel.
1056 *
1057 * Since pcmpgtw performs signed comparisons, we have to subtract noise,
1058 * test for equality to 0 and then invert the result.
1059 *
1060 * Note that it is safe to generate the "motion flags" before the
1061 * smartmask code, as all that can happen is that individual flags get
1062 * reset to 0 because of the smartmask.
1063 */
1064 psubusw_r2r(mm7, mm0); /* U: subtract by (multiplied) noise */
1065 psubusw_r2r(mm7, mm1); /* V */
1066
1067 pcmpeqw_r2r(mm4, mm0); /* U: test for equality with 0 */
1068 pcmpeqw_r2r(mm4, mm1); /* V */
1069
1070 pand_r2r(mm6, mm0); /* U: convert 0xffff -> 0x00ff */
1071 pand_r2r(mm6, mm1); /* V */
1072
1073 pxor_r2r(mm6, mm0); /* U: invert the result */
1074 pxor_r2r(mm6, mm1); /* V */
1075
1076 /* Each fX is the "motion flag" = 0 for no motion, 0xff for motion. */
1077 packuswb_r2r(mm1, mm0); /* U: mm0 = f7 f6 f5 f4 f3 f2 f1 f0 */
1078
1079 if (smartmask_speed) {
1080 /*
1081 * Apply the smartmask. Basically, if *smartmask_final is 0, the
1082 * corresponding "motion flag" in mm0 will be reset.
1083 */
1084 movq_m2r(*smartmask_final, mm3); /* U: mm3 = s7 s6 s5 s4 s3 s2 s1 s0 */
1085
1086 /*
1087 * ...but move the "motion flags" to memory before, in order to
1088 * increment *smartmask_buffer properly below.
1089 */
1090 movq_r2m(mm0, mmtemp); /* U */
1091 pcmpeqb_r2r(mm4, mm3); /* V: mm3 = 0xff where sX==0 */
1092
1093 /* AND negates the target before anding. */
1094 pandn_r2r(mm0, mm3); /* U: mm3 = 0xff where dX>noise && sX>0 */
1095
1096 movq_r2r(mm3, mm0); /* U */
1097
1098 /* Add to *smartmask_buffer. This is probably the fastest way to do it. */
1099 if (cnt->event_nr != cnt->prev_event) {
1100 if (mmtemp.ub[0]) smartmask_buffer[0] += SMARTMASK_SENSITIVITY_INCR;
1101 if (mmtemp.ub[1]) smartmask_buffer[1] += SMARTMASK_SENSITIVITY_INCR;
1102 if (mmtemp.ub[2]) smartmask_buffer[2] += SMARTMASK_SENSITIVITY_INCR;
1103 if (mmtemp.ub[3]) smartmask_buffer[3] += SMARTMASK_SENSITIVITY_INCR;
1104 if (mmtemp.ub[4]) smartmask_buffer[4] += SMARTMASK_SENSITIVITY_INCR;
1105 if (mmtemp.ub[5]) smartmask_buffer[5] += SMARTMASK_SENSITIVITY_INCR;
1106 if (mmtemp.ub[6]) smartmask_buffer[6] += SMARTMASK_SENSITIVITY_INCR;
1107 if (mmtemp.ub[7]) smartmask_buffer[7] += SMARTMASK_SENSITIVITY_INCR;
1108 }
1109
1110 smartmask_buffer += 8;
1111 smartmask_final += 8;
1112 }
1113
1114 movq_m2r(*new, mm2); /* U: mm1 = n7 n6 n5 n4 n3 n2 n1 n0 */
1115
1116 /*
1117 * Cancel out pixels in *new according to the "motion flags" in mm0.
1118 * Each NX is either 0 or nX as from *new.
1119 */
1120 pand_r2r(mm0, mm2); /* U: mm1 = N7 N6 N5 N4 N3 N2 N1 N0 */
1121 psubb_r2r(mm0, mm4); /* V: mm4 = 0x01 where dX>noise */
1122
1123 /*
1124 * mm5 holds 8 separate counts - each one is increased according to
1125 * the contents of mm4 (where each byte is either 0x00 or 0x01).
1126 */
1127 movq_r2m(mm2, *out); /* U: this will stall */
1128 paddusb_r2r(mm4, mm5); /* V: add counts to mm5 */
1129
1130 /*
1131 * Every 255th turn, we need to unload mm5 into the diffs variable,
1132 * because otherwise the packed bytes will get saturated.
1133 */
1134 if (--unload == 0) {
1135 /* Unload mm5 to memory and reset it. */
1136 movq_r2m(mm5, mmtemp); /* U */
1137 pxor_r2r(mm5, mm5); /* V: mm5 = 0 */
1138
1139 diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] +
1140 mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7];
1141 unload = 255;
1142 }
1143
1144 out += 8;
1145 ref += 8;
1146 new += 8;
1147 }
1148
1149 /*
1150 * Check if there are diffs left in mm5 that need to be copied to the
1151 * diffs variable.
1152 */
1153 if (unload < 255) {
1154 movq_r2m(mm5, mmtemp);
1155 diffs += mmtemp.ub[0] + mmtemp.ub[1] + mmtemp.ub[2] + mmtemp.ub[3] +
1156 mmtemp.ub[4] + mmtemp.ub[5] + mmtemp.ub[6] + mmtemp.ub[7];
1157 }
1158
1159 emms();
1160
1161 #endif
1162 /*
1163 * Note that the non-MMX code is present even if the MMX code is present.
1164 * This is necessary if the resolution is not a multiple of 8, in which
1165 * case the non-MMX code needs to take care of the remaining pixels.
1166 */
1167
1168 for (; i > 0; i--) {
1169 register unsigned char curdiff = (int)(abs(*ref - *new)); /* Using a temp variable is 12% faster. */
1170 /* Apply fixed mask */
1171 if (mask)
1172 curdiff = ((int)(curdiff * *mask++) / 255);
1173
1174 if (smartmask_speed) {
1175 if (curdiff > noise) {
1176 /*
1177 * Increase smart_mask sensitivity every frame when motion
1178 * is detected. (with speed=5, mask is increased by 1 every
1179 * second. To be able to increase by 5 every second (with
1180 * speed=10) we add 5 here. NOT related to the 5 at ratio-
1181 * calculation.
1182 */
1183 if (cnt->event_nr != cnt->prev_event)
1184 (*smartmask_buffer) += SMARTMASK_SENSITIVITY_INCR;
1185 /* Apply smart_mask */
1186 if (!*smartmask_final)
1187 curdiff = 0;
1188 }
1189 smartmask_final++;
1190 smartmask_buffer++;
1191 }
1192 /* Pixel still in motion after all the masks? */
1193 if (curdiff > noise) {
1194 *out = *new;
1195 diffs++;
1196 }
1197 out++;
1198 ref++;
1199 new++;
1200 }
1201 return diffs;
1202 }
1203
1204 /**
1205 * alg_diff_fast
1206 * Very fast diff function, does not apply mask overlaying.
1207 */
alg_diff_fast(struct context * cnt,int max_n_changes,unsigned char * new)1208 static char alg_diff_fast(struct context *cnt, int max_n_changes, unsigned char *new)
1209 {
1210 struct images *imgs = &cnt->imgs;
1211 int i, diffs = 0, step = imgs->motionsize/10000;
1212 int noise = cnt->noise;
1213 unsigned char *ref = imgs->ref;
1214
1215 if (!step % 2)
1216 step++;
1217 /* We're checking only 1 of several pixels. */
1218 max_n_changes /= step;
1219
1220 i = imgs->motionsize;
1221
1222 for (; i > 0; i -= step) {
1223 register unsigned char curdiff = (int)(abs((char)(*ref - *new))); /* Using a temp variable is 12% faster. */
1224 if (curdiff > noise) {
1225 diffs++;
1226 if (diffs > max_n_changes)
1227 return 1;
1228 }
1229 ref += step;
1230 new += step;
1231 }
1232
1233 return 0;
1234 }
1235
1236 /**
1237 * alg_diff
1238 * Uses diff_fast to quickly decide if there is anything worth
1239 * sending to diff_standard.
1240 */
alg_diff(struct context * cnt,unsigned char * new)1241 int alg_diff(struct context *cnt, unsigned char *new)
1242 {
1243 int diffs = 0;
1244
1245 if (alg_diff_fast(cnt, cnt->conf.threshold / 2, new))
1246 diffs = alg_diff_standard(cnt, new);
1247
1248 return diffs;
1249 }
1250
1251 /**
1252 * alg_lightswitch
1253 * Detects a sudden massive change in the picture.
1254 * It is assumed to be the light being switched on or a camera displacement.
1255 * In any way the user doesn't think it is worth capturing.
1256 */
alg_lightswitch(struct context * cnt,int diffs)1257 int alg_lightswitch(struct context *cnt, int diffs)
1258 {
1259 struct images *imgs = &cnt->imgs;
1260
1261 if (cnt->conf.lightswitch_percent < 0)
1262 cnt->conf.lightswitch_percent = 0;
1263 if (cnt->conf.lightswitch_percent > 100)
1264 cnt->conf.lightswitch_percent = 100;
1265
1266 /* Is lightswitch percent of the image changed? */
1267 if (diffs > (imgs->motionsize * cnt->conf.lightswitch_percent / 100))
1268 return 1;
1269
1270 return 0;
1271 }
1272
1273 /**
1274 * alg_switchfilter
1275 *
1276 */
alg_switchfilter(struct context * cnt,int diffs,unsigned char * newimg)1277 int alg_switchfilter(struct context *cnt, int diffs, unsigned char *newimg)
1278 {
1279 int linediff = diffs / cnt->imgs.height;
1280 unsigned char *out = cnt->imgs.img_motion.image_norm;
1281 int y, x, line;
1282 int lines = 0, vertlines = 0;
1283
1284 for (y = 0; y < cnt->imgs.height; y++) {
1285 line = 0;
1286 for (x = 0; x < cnt->imgs.width; x++) {
1287 if (*(out++))
1288 line++;
1289 }
1290
1291 if (line > cnt->imgs.width / 18)
1292 vertlines++;
1293
1294 if (line > linediff * 2)
1295 lines++;
1296 }
1297
1298 if (vertlines > cnt->imgs.height / 10 && lines < vertlines / 3 &&
1299 (vertlines > cnt->imgs.height / 4 || lines - vertlines > lines / 2)) {
1300 if (cnt->conf.text_changes) {
1301 char tmp[80];
1302 sprintf(tmp, "%d %d", lines, vertlines);
1303 draw_text(newimg, cnt->imgs.width, cnt->imgs.height, cnt->imgs.width - 10, 20, tmp, cnt->conf.text_scale);
1304 }
1305 return diffs;
1306 }
1307 return 0;
1308 }
1309
1310 /**
1311 * alg_update_reference_frame
1312 *
1313 * Called from 'motion_loop' to calculate the reference frame
1314 * Moving objects are excluded from the reference frame for a certain
1315 * amount of time to improve detection.
1316 *
1317 * Parameters:
1318 *
1319 * cnt - current thread's context struct
1320 * action - UPDATE_REF_FRAME or RESET_REF_FRAME
1321 *
1322 */
1323 #define ACCEPT_STATIC_OBJECT_TIME 10 /* Seconds */
1324 #define EXCLUDE_LEVEL_PERCENT 20
alg_update_reference_frame(struct context * cnt,int action)1325 void alg_update_reference_frame(struct context *cnt, int action)
1326 {
1327 int accept_timer = cnt->lastrate * ACCEPT_STATIC_OBJECT_TIME;
1328 int i, threshold_ref;
1329 int *ref_dyn = cnt->imgs.ref_dyn;
1330 unsigned char *image_virgin = cnt->imgs.image_vprvcy.image_norm;
1331 unsigned char *ref = cnt->imgs.ref;
1332 unsigned char *smartmask = cnt->imgs.smartmask_final;
1333 unsigned char *out = cnt->imgs.img_motion.image_norm;
1334
1335 if (cnt->lastrate > 5) /* Match rate limit */
1336 accept_timer /= (cnt->lastrate / 3);
1337
1338 if (action == UPDATE_REF_FRAME) { /* Black&white only for better performance. */
1339 threshold_ref = cnt->noise * EXCLUDE_LEVEL_PERCENT / 100;
1340
1341 for (i = cnt->imgs.motionsize; i > 0; i--) {
1342 /* Exclude pixels from ref frame well below noise level. */
1343 if (((int)(abs(*ref - *image_virgin)) > threshold_ref) && (*smartmask)) {
1344 if (*ref_dyn == 0) { /* Always give new pixels a chance. */
1345 *ref_dyn = 1;
1346 } else if (*ref_dyn > accept_timer) { /* Include static Object after some time. */
1347 *ref_dyn = 0;
1348 *ref = *image_virgin;
1349 } else if (*out) {
1350 (*ref_dyn)++; /* Motionpixel? Keep excluding from ref frame. */
1351 } else {
1352 *ref_dyn = 0; /* Nothing special - release pixel. */
1353 *ref = (*ref + *image_virgin) / 2;
1354 }
1355
1356 } else { /* No motion: copy to ref frame. */
1357 *ref_dyn = 0; /* Reset pixel */
1358 *ref = *image_virgin;
1359 }
1360
1361 ref++;
1362 image_virgin++;
1363 smartmask++;
1364 ref_dyn++;
1365 out++;
1366 } /* end for i */
1367
1368 } else { /* action == RESET_REF_FRAME - also used to initialize the frame at startup. */
1369 /* Copy fresh image */
1370 memcpy(cnt->imgs.ref, cnt->imgs.image_vprvcy.image_norm, cnt->imgs.size_norm);
1371 /* Reset static objects */
1372 memset(cnt->imgs.ref_dyn, 0, cnt->imgs.motionsize * sizeof(*cnt->imgs.ref_dyn));
1373 }
1374 }
1375