"Fossies" - the Fresh Open Source Software Archive

Member "motion-Release-4.3.0/src/alg.c" (14 Jan 2020, 43420 Bytes) of package /linux/misc/motion-Release-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "alg.c" see the Fossies "Dox" file reference documentation.

    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  */
   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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
  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  */
 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  */
 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  */
 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  */
 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
 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 }