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