Algorithm C code snippet
From ZoneMinder
- The following snippit of code was edited to remove some diagnostics code. The vars are easily related to the config settings, and if you know C, you can easily see the algorithm.
for ( int y = lo_y; y <= hi_y; y++ )
{
pdiff = diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{
if ( (*pdiff > min_pixel_threshold) && (!max_pixel_threshold || (*pdiff < max_pixel_threshold)) )
{
*pdiff = WHITE;
alarm_pixels++;
}
else
{
*pdiff = BLACK;
}
}
}
if ( !alarm_pixels ) return( false );
if ( min_alarm_pixels && alarm_pixels < min_alarm_pixels ) return( false );
if ( max_alarm_pixels && alarm_pixels > max_alarm_pixels ) return( false );
score = (100*alarm_pixels)/(limits.Size().X()*limits.Size().Y());
if ( check_method >= FILTERED_PIXELS )
{
int bx = filter_box.X();
int by = filter_box.Y();
int bx1 = bx-1;
int by1 = by-1;
if ( bx > 1 || by > 1 )
{
// Now remove any pixels smaller than our filter size
unsigned char *pdiff;
unsigned char *cpdiff;
int ldx, hdx, ldy, hdy;
bool block;
for ( int y = lo_y; y <= hi_y; y++ )
{
pdiff = diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{
if ( *pdiff == WHITE )
{
// Check participation in an X block
ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x;
hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1);
ldy = (y>=(lo_y+by1))?-by1:lo_y-y;
hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1);
block = false;
for ( int dy = ldy; !block && dy <= hdy; dy++ )
{
for ( int dx = ldx; !block && dx <= hdx; dx++ )
{
block = true;
for ( int dy2 = 0; block && dy2 < by; dy2++ )
{
for ( int dx2 = 0; block && dx2 < bx; dx2++ )
{
cpdiff = diff_image->Buffer( x+dx+dx2, y+dy+dy2 );
if ( !*cpdiff )
{
block = false;
}
}
}
}
}
if ( !block )
{
*pdiff = BLACK;
continue;
}
alarm_filter_pixels++;
}
}
}
}
else
{
alarm_filter_pixels = alarm_pixels;
}
if ( !alarm_filter_pixels ) return( false );
if ( min_filter_pixels && alarm_filter_pixels < min_filter_pixels ) return( false );
if ( max_filter_pixels && alarm_filter_pixels > max_filter_pixels ) return( false );
score = (100*alarm_filter_pixels)/(limits.Size().X()*limits.Size().Y());
if ( check_method >= BLOBS )
{
typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats;
BlobStats blob_stats[256];
memset( blob_stats, 0, sizeof(BlobStats)*256 );
unsigned char *pdiff, *spdiff;
int lx, ly;
BlobStats *bsx, *bsy;
BlobStats *bsm, *bss;
for ( int y = lo_y; y <= hi_y; y++ )
{
pdiff = diff_image->Buffer( lo_x, y );
for ( int x = lo_x; x <= hi_x; x++, pdiff++ )
{
if ( *pdiff == WHITE )
{
//printf( "Got white pixel at %d,%d (%x)\n", x, y, pdiff );
lx = x>lo_x?*(pdiff-1):0;
ly = y>lo_y?*(pdiff-diff_image->Width()):0;
if ( lx )
{
//printf( "Left neighbour is %d\n", lx );
bsx = &blob_stats[lx];
if ( ly )
{
//printf( "Top neighbour is %d\n", ly );
bsy = &blob_stats[ly];
if ( lx == ly )
{
//printf( "Matching neighbours, setting to %d\n", lx );
// Add to the blob from the x side (either side really)
*pdiff = lx;
bsx->count++;
if ( x > bsx->hi_x ) bsx->hi_x = x;
if ( y > bsx->hi_y ) bsx->hi_y = y;
}
else
{
// Aggregate blobs
bsm = bsx->count>=bsy->count?bsx:bsy;
bss = bsm==bsx?bsy:bsx;
//printf( "Different neighbours, setting pixels of %d to %d\n", bss->tag, bsm->tag );
// Now change all those pixels to the other setting
for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++ )
{
spdiff = diff_image->Buffer( bss->lo_x, sy );
for ( int sx = bss->lo_x; sx <= bss->hi_x; sx++, spdiff++ )
{
//printf( "Pixel at %d,%d (%x) is %d", sx, sy, spdiff, *spdiff );
if ( *spdiff == bss->tag )
{
//printf( ", setting" );
*spdiff = bsm->tag;
}
//printf( "\n" );
}
}
*pdiff = bsm->tag;
// Merge the slave blob into the master
bsm->count += bss->count+1;
if ( x > bsm->hi_x ) bsm->hi_x = x;
if ( y > bsm->hi_y ) bsm->hi_y = y;
if ( bss->lo_x < bsm->lo_x ) bsm->lo_x = bss->lo_x;
if ( bss->lo_y < bsm->lo_y ) bsm->lo_y = bss->lo_y;
if ( bss->hi_x > bsm->hi_x ) bsm->hi_x = bss->hi_x;
if ( bss->hi_y > bsm->hi_y ) bsm->hi_y = bss->hi_y;
alarm_blobs--;
Debug( 2, ( "Merging blob %d with %d, %d current blobs", bss->tag, bsm->tag, alarm_blobs ));
// Clear out the old blob
bss->tag = 0;
bss->count = 0;
bss->lo_x = 0;
bss->lo_y = 0;
bss->hi_x = 0;
bss->hi_y = 0;
}
}
else
{
//printf( "Setting to left neighbour %d\n", lx );
// Add to the blob from the x side
*pdiff = lx;
bsx->count++;
if ( x > bsx->hi_x ) bsx->hi_x = x;
if ( y > bsx->hi_y ) bsx->hi_y = y;
}
}
else
{
if ( ly )
{
//printf( "Setting to top neighbour %d\n", ly );
// Add to the blob from the y side
BlobStats *bsy = &blob_stats[ly];
*pdiff = ly;
bsy->count++;
if ( x > bsy->hi_x ) bsy->hi_x = x;
if ( y > bsy->hi_y ) bsy->hi_y = y;
}
else
{
// Create a new blob
//for ( int i = 1; i < WHITE; i++ )
for ( int i = (WHITE-1); i > 0; i-- )
{
BlobStats *bs = &blob_stats[i];
if ( !bs->count )
{
//printf( "Creating new blob %d\n", i );
*pdiff = i;
bs->tag = i;
bs->count++;
bs->lo_x = bs->hi_x = x;
bs->lo_y = bs->hi_y = y;
alarm_blobs++;
Debug( 2, ( "Created blob %d, %d current blobs", bs->tag, alarm_blobs ));
break;
}
}
}
}
}
}
}
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 );
}
diff_image->WriteJpeg( diag_path );
}
if ( !alarm_blobs ) return( false );
alarm_blob_pixels = alarm_filter_pixels;
// Now eliminate blobs under the threshold
for ( int i = 1; i < WHITE; i++ )
{
BlobStats *bs = &blob_stats[i];
if ( bs->count && ((min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels)) )
{
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ )
{
unsigned char *spdiff = diff_image->Buffer( bs->lo_x, sy );
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ )
{
if ( *spdiff == bs->tag )
{
*spdiff = BLACK;
}
}
}
alarm_blobs--;
alarm_blob_pixels -= bs->count;
Debug( 2, ( "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ));
bs->tag = 0;
bs->count = 0;
bs->lo_x = 0;
bs->lo_y = 0;
bs->hi_x = 0;
bs->hi_y = 0;
}
else
{
if ( bs->count )
{
if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count;
if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count;
}
}
}
if ( config.record_diag_images )
{
static char diag_path[PATH_MAX] = "";
if ( !diag_path[0] )
{
snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 );
}
diff_image->WriteJpeg( diag_path );
}
if ( !alarm_blobs ) return( false );
if ( min_blobs && alarm_blobs < min_blobs ) return( false );
if ( max_blobs && alarm_blobs > max_blobs ) return( false );
alarm_lo_x = hi_x+1;
alarm_hi_x = lo_x-1;
alarm_lo_y = hi_y+1;
alarm_hi_y = lo_y-1;
for ( int i = 1; i < WHITE; i++ )
{
BlobStats *bs = &blob_stats[i];
if ( bs->count )
{
if ( bs->count == max_blob_size )
{
//if ( monitor->followMotion() )
if ( true )
{
unsigned long x_total = 0;
unsigned long y_total = 0;
for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ )
{
unsigned char *spdiff = diff_image->Buffer( bs->lo_x, sy );
for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ )
{
if ( *spdiff == bs->tag )
{
x_total += sx;
y_total += sy;
}
}
}
alarm_mid_x = int(round(x_total/bs->count));
alarm_mid_y = int(round(y_total/bs->count));
}
}
if ( alarm_lo_x > bs->lo_x ) alarm_lo_x = bs->lo_x;
if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y;
if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x;
if ( alarm_hi_y < bs->hi_y ) alarm_hi_y = bs->hi_y;
}
}
score = ((100*alarm_blob_pixels)/int(sqrt((double)alarm_blobs)))/(limits.Size().X()*limits.Size().Y());
}
}
if ( type == INCLUSIVE )
{
score /= 2;
}
else if ( type == EXCLUSIVE )
{
score *= 2;
}


