2002-09-24 06:08:50 +08:00
//
2002-12-10 21:21:41 +08:00
// ZoneMinder Core Implementation, $Date$, $Revision$
2003-01-12 02:22:27 +08:00
// Copyright (C) 2003 Philip Coombes
2002-09-24 06:08:50 +08:00
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
2002-09-16 17:19:24 +08:00
# include "zm.h"
MYSQL dbconn ;
2002-11-07 20:25:48 +08:00
void Zone : : Setup ( Monitor * p_monitor , int p_id , const char * p_label , ZoneType p_type , const Box & p_limits , const Rgb p_alarm_rgb , int p_alarm_threshold , int p_min_alarm_pixels , int p_max_alarm_pixels , const Coord & p_filter_box , int p_min_filter_pixels , int p_max_filter_pixels , int p_min_blob_pixels , int p_max_blob_pixels , int p_min_blobs , int p_max_blobs )
2002-09-16 17:19:24 +08:00
{
2002-11-07 20:25:48 +08:00
monitor = p_monitor ;
2002-09-16 17:19:24 +08:00
id = p_id ;
label = new char [ strlen ( p_label ) + 1 ] ;
strcpy ( label , p_label ) ;
type = p_type ;
limits = p_limits ;
alarm_rgb = p_alarm_rgb ;
alarm_threshold = p_alarm_threshold ;
min_alarm_pixels = p_min_alarm_pixels ;
max_alarm_pixels = p_max_alarm_pixels ;
filter_box = p_filter_box ;
min_filter_pixels = p_min_filter_pixels ;
max_filter_pixels = p_max_filter_pixels ;
min_blob_pixels = p_min_blob_pixels ;
max_blob_pixels = p_max_blob_pixels ;
min_blobs = p_min_blobs ;
max_blobs = p_max_blobs ;
Info ( ( " Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, AT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d \n " , id , label , type , limits . Width ( ) , limits . Height ( ) , alarm_rgb , alarm_threshold , min_alarm_pixels , max_alarm_pixels , filter_box . X ( ) , filter_box . Y ( ) , min_filter_pixels , max_filter_pixels , min_blob_pixels , max_blob_pixels , min_blobs , max_blobs ) ) ;
alarmed = false ;
alarm_pixels = 0 ;
alarm_filter_pixels = 0 ;
2002-11-07 20:25:48 +08:00
alarm_blob_pixels = 0 ;
2002-09-16 17:19:24 +08:00
alarm_blobs = 0 ;
image = 0 ;
score = 0 ;
}
Zone : : ~ Zone ( )
{
delete [ ] label ;
delete image ;
}
2002-11-07 20:25:48 +08:00
void Zone : : RecordStats ( const Event * event )
{
static char sql [ 256 ] ;
sprintf ( sql , " insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d " , monitor - > Id ( ) , id , event - > Id ( ) , event - > Frames ( ) + 1 , alarm_pixels , alarm_filter_pixels , alarm_blob_pixels , alarm_blobs , min_blob_size , max_blob_size , alarm_box . LoX ( ) , alarm_box . LoY ( ) , alarm_box . HiX ( ) , alarm_box . HiY ( ) , score ) ;
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't insert event: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
}
int Zone : : Load ( Monitor * monitor , Zone * * & zones )
2002-09-16 17:19:24 +08:00
{
static char sql [ 256 ] ;
2002-11-07 20:25:48 +08:00
sprintf ( sql , " select Id,Name,Type+0,Units,LoX,LoY,HiX,HiY,AlarmRGB,AlarmThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs from Zones where MonitorId = %d order by Type, Id " , monitor - > Id ( ) ) ;
2002-09-16 17:19:24 +08:00
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't run query: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
if ( ! result )
{
Error ( ( " Can't use query result: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
int n_zones = mysql_num_rows ( result ) ;
2002-11-07 20:25:48 +08:00
Info ( ( " Got %d zones for monitor %s \n " , n_zones , monitor - > Name ( ) ) ) ;
2002-09-16 17:19:24 +08:00
delete [ ] zones ;
zones = new Zone * [ n_zones ] ;
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + )
{
int Id = atoi ( dbrow [ 0 ] ) ;
const char * Name = dbrow [ 1 ] ;
int Type = atoi ( dbrow [ 2 ] ) ;
const char * Units = dbrow [ 3 ] ;
int LoX = atoi ( dbrow [ 4 ] ) ;
int LoY = atoi ( dbrow [ 5 ] ) ;
int HiX = atoi ( dbrow [ 6 ] ) ;
int HiY = atoi ( dbrow [ 7 ] ) ;
int AlarmRGB = dbrow [ 8 ] ? atoi ( dbrow [ 8 ] ) : 0 ;
int AlarmThreshold = dbrow [ 9 ] ? atoi ( dbrow [ 9 ] ) : 0 ;
int MinAlarmPixels = dbrow [ 10 ] ? atoi ( dbrow [ 10 ] ) : 0 ;
int MaxAlarmPixels = dbrow [ 11 ] ? atoi ( dbrow [ 11 ] ) : 0 ;
int FilterX = dbrow [ 12 ] ? atoi ( dbrow [ 12 ] ) : 0 ;
int FilterY = dbrow [ 13 ] ? atoi ( dbrow [ 13 ] ) : 0 ;
int MinFilterPixels = dbrow [ 14 ] ? atoi ( dbrow [ 14 ] ) : 0 ;
int MaxFilterPixels = dbrow [ 15 ] ? atoi ( dbrow [ 15 ] ) : 0 ;
int MinBlobPixels = dbrow [ 16 ] ? atoi ( dbrow [ 16 ] ) : 0 ;
int MaxBlobPixels = dbrow [ 17 ] ? atoi ( dbrow [ 17 ] ) : 0 ;
int MinBlobs = dbrow [ 18 ] ? atoi ( dbrow [ 18 ] ) : 0 ;
int MaxBlobs = dbrow [ 19 ] ? atoi ( dbrow [ 19 ] ) : 0 ;
if ( ! strcmp ( Units , " Percent " ) )
{
2002-11-07 20:25:48 +08:00
LoX = ( LoX * ( monitor - > Width ( ) - 1 ) ) / 100 ;
LoY = ( LoY * ( monitor - > Height ( ) - 1 ) ) / 100 ;
HiX = ( HiX * ( monitor - > Width ( ) - 1 ) ) / 100 ;
HiY = ( HiY * ( monitor - > Height ( ) - 1 ) ) / 100 ;
MinAlarmPixels = ( MinAlarmPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
MaxAlarmPixels = ( MaxAlarmPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
MinFilterPixels = ( MinFilterPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
MaxFilterPixels = ( MaxFilterPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
MinBlobPixels = ( MinBlobPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
MaxBlobPixels = ( MaxBlobPixels * monitor - > Width ( ) * monitor - > Height ( ) ) / 100 ;
2002-09-16 17:19:24 +08:00
}
if ( atoi ( dbrow [ 2 ] ) = = Zone : : INACTIVE )
{
2002-11-07 20:25:48 +08:00
zones [ i ] = new Zone ( monitor , Id , Name , Box ( LoX , LoY , HiX , HiY ) ) ;
2002-09-16 17:19:24 +08:00
}
else
{
2002-11-07 20:25:48 +08:00
zones [ i ] = new Zone ( monitor , Id , Name , ( Zone : : ZoneType ) Type , Box ( LoX , LoY , HiX , HiY ) , AlarmRGB , AlarmThreshold , MinAlarmPixels , MaxAlarmPixels , Coord ( FilterX , FilterY ) , MinFilterPixels , MaxFilterPixels , MinBlobPixels , MaxBlobPixels , MinBlobs , MaxBlobs ) ;
2002-09-16 17:19:24 +08:00
}
}
if ( mysql_errno ( & dbconn ) )
{
Error ( ( " Can't fetch row: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
// Yadda yadda
mysql_free_result ( result ) ;
return ( n_zones ) ;
}
void Image : : ReadJpeg ( const char * filename )
{
struct jpeg_decompress_struct cinfo ;
struct jpeg_error_mgr jerr ;
cinfo . err = jpeg_std_error ( & jerr ) ;
jpeg_create_decompress ( & cinfo ) ;
FILE * infile ;
if ( ( infile = fopen ( filename , " rb " ) ) = = NULL )
{
Error ( ( " Can't open %s: %s \n " , filename , strerror ( errno ) ) ) ;
exit ( 1 ) ;
}
jpeg_stdio_src ( & cinfo , infile ) ;
jpeg_read_header ( & cinfo , TRUE ) ;
width = cinfo . image_width ;
height = cinfo . image_height ;
colours = cinfo . num_components ;
size = width * height * colours ;
assert ( colours = = 1 | | colours = = 3 ) ;
buffer = new JSAMPLE [ size ] ;
jpeg_start_decompress ( & cinfo ) ;
JSAMPROW row_pointer ; /* pointer to a single row */
int row_stride = width * colours ; /* physical row width in buffer */
while ( cinfo . output_scanline < cinfo . output_height )
{
row_pointer = & buffer [ cinfo . output_scanline * row_stride ] ;
jpeg_read_scanlines ( & cinfo , & row_pointer , 1 ) ;
}
jpeg_finish_decompress ( & cinfo ) ;
jpeg_destroy_decompress ( & cinfo ) ;
fclose ( infile ) ;
}
void Image : : WriteJpeg ( const char * filename ) const
{
struct jpeg_compress_struct cinfo ;
struct jpeg_error_mgr jerr ;
cinfo . err = jpeg_std_error ( & jerr ) ;
jpeg_create_compress ( & cinfo ) ;
FILE * outfile ;
if ( ( outfile = fopen ( filename , " wb " ) ) = = NULL )
{
Error ( ( " Can't open %s: %s \n " , filename , strerror ( errno ) ) ) ;
exit ( 1 ) ;
}
jpeg_stdio_dest ( & cinfo , outfile ) ;
cinfo . image_width = width ; /* image width and height, in pixels */
cinfo . image_height = height ;
cinfo . input_components = colours ; /* # of color components per pixel */
if ( colours = = 1 )
{
cinfo . in_color_space = JCS_GRAYSCALE ; /* colorspace of input image */
}
else
{
cinfo . in_color_space = JCS_RGB ; /* colorspace of input image */
}
jpeg_set_defaults ( & cinfo ) ;
cinfo . dct_method = JDCT_FASTEST ;
//jpeg_set_quality(&cinfo, 100, false);
jpeg_start_compress ( & cinfo , TRUE ) ;
JSAMPROW row_pointer ; /* pointer to a single row */
int row_stride = cinfo . image_width * cinfo . input_components ; /* physical row width in buffer */
while ( cinfo . next_scanline < cinfo . image_height )
{
row_pointer = & buffer [ cinfo . next_scanline * row_stride ] ;
jpeg_write_scanlines ( & cinfo , & row_pointer , 1 ) ;
}
jpeg_finish_compress ( & cinfo ) ;
jpeg_destroy_compress ( & cinfo ) ;
fclose ( outfile ) ;
}
void Image : : EncodeJpeg ( JOCTET * outbuffer , int * outbuffer_size ) const
{
struct jpeg_compress_struct cinfo ;
struct jpeg_error_mgr jerr ;
cinfo . err = jpeg_std_error ( & jerr ) ;
jpeg_create_compress ( & cinfo ) ;
jpeg_mem_dest ( & cinfo , outbuffer , outbuffer_size ) ;
cinfo . image_width = width ; /* image width and height, in pixels */
cinfo . image_height = height ;
cinfo . input_components = colours ; /* # of color components per pixel */
if ( colours = = 1 )
{
cinfo . in_color_space = JCS_GRAYSCALE ; /* colorspace of input image */
}
else
{
cinfo . in_color_space = JCS_RGB ; /* colorspace of input image */
}
jpeg_set_defaults ( & cinfo ) ;
cinfo . dct_method = JDCT_FASTEST ;
//jpeg_set_quality(&cinfo, 100, false);
jpeg_start_compress ( & cinfo , TRUE ) ;
JSAMPROW row_pointer ; /* pointer to a single row */
int row_stride = cinfo . image_width * cinfo . input_components ; /* physical row width in buffer */
while ( cinfo . next_scanline < cinfo . image_height )
{
row_pointer = & buffer [ cinfo . next_scanline * row_stride ] ;
jpeg_write_scanlines ( & cinfo , & row_pointer , 1 ) ;
}
jpeg_finish_compress ( & cinfo ) ;
jpeg_destroy_compress ( & cinfo ) ;
}
void Image : : Overlay ( const Image & image )
{
//assert( width == image.width && height == image.height && colours == image.colours );
assert ( width = = image . width & & height = = image . height ) ;
unsigned char * pdest = buffer ;
unsigned char * psrc = image . buffer ;
if ( colours = = 1 )
{
if ( image . colours = = 1 )
{
while ( pdest < ( buffer + size ) )
{
if ( * psrc )
{
* pdest = * psrc ;
}
pdest + + ;
psrc + + ;
}
}
else
{
Colourise ( ) ;
pdest = buffer ;
while ( pdest < ( buffer + size ) )
{
if ( RED ( psrc ) | | GREEN ( psrc ) | | BLUE ( psrc ) )
{
RED ( pdest ) = RED ( psrc ) ;
GREEN ( pdest ) = GREEN ( psrc ) ;
BLUE ( pdest ) = BLUE ( psrc ) ;
}
psrc + = 3 ;
pdest + = 3 ;
}
}
}
else
{
if ( image . colours = = 1 )
{
while ( pdest < ( buffer + size ) )
{
if ( * psrc )
{
RED ( pdest ) = GREEN ( pdest ) = BLUE ( pdest ) = * psrc + + ;
}
pdest + = 3 ;
}
}
else
{
while ( pdest < ( buffer + size ) )
{
if ( RED ( psrc ) | | GREEN ( psrc ) | | BLUE ( psrc ) )
{
RED ( pdest ) = RED ( psrc ) ;
GREEN ( pdest ) = GREEN ( psrc ) ;
BLUE ( pdest ) = BLUE ( psrc ) ;
}
psrc + = 3 ;
pdest + = 3 ;
}
}
}
}
void Image : : Blend ( const Image & image , double transparency ) const
{
assert ( width = = image . width & & height = = image . height & & colours = = image . colours ) ;
JSAMPLE * psrc = image . buffer ;
JSAMPLE * pdest = buffer ;
while ( pdest < ( buffer + size ) )
{
* pdest + + = ( JSAMPLE ) round ( ( * pdest * ( 1.0 - transparency ) ) + ( * psrc + + * transparency ) ) ;
}
}
void Image : : Blend ( const Image & image , int transparency ) const
{
assert ( width = = image . width & & height = = image . height & & colours = = image . colours ) ;
JSAMPLE * psrc = image . buffer ;
JSAMPLE * pdest = buffer ;
while ( pdest < ( buffer + size ) )
{
* pdest + + = ( JSAMPLE ) ( ( ( * pdest * ( 100 - transparency ) ) + ( * psrc + + * transparency ) ) / 100 ) ;
}
}
Image * Image : : Merge ( int n_images , Image * images [ ] )
{
if ( n_images < = 0 ) return ( 0 ) ;
if ( n_images = = 1 ) return ( new Image ( * images [ 0 ] ) ) ;
int width = images [ 0 ] - > width ;
int height = images [ 0 ] - > height ;
int colours = images [ 0 ] - > colours ;
for ( int i = 1 ; i < n_images ; i + + )
{
assert ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ;
}
Image * result = new Image ( width , height , images [ 0 ] - > colours ) ;
int size = result - > size ;
for ( int i = 0 ; i < size ; i + + )
{
int total = 0 ;
JSAMPLE * pdest = result - > buffer ;
for ( int j = 0 ; j < n_images ; j + + )
{
JSAMPLE * psrc = images [ j ] - > buffer ;
total + = * psrc ;
psrc + + ;
}
* pdest = total / n_images ;
pdest + + ;
}
return ( result ) ;
}
Image * Image : : Merge ( int n_images , Image * images [ ] , double weight )
{
if ( n_images < = 0 ) return ( 0 ) ;
if ( n_images = = 1 ) return ( new Image ( * images [ 0 ] ) ) ;
int width = images [ 0 ] - > width ;
int height = images [ 0 ] - > height ;
int colours = images [ 0 ] - > colours ;
for ( int i = 1 ; i < n_images ; i + + )
{
assert ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ;
}
Image * result = new Image ( * images [ 0 ] ) ;
int size = result - > size ;
double factor = 1.0 * weight ;
for ( int i = 1 ; i < n_images ; i + + )
{
JSAMPLE * pdest = result - > buffer ;
JSAMPLE * psrc = images [ i ] - > buffer ;
for ( int j = 0 ; j < size ; j + + )
{
* pdest = ( JSAMPLE ) ( ( ( * pdest ) * ( 1.0 - factor ) ) + ( ( * psrc ) * factor ) ) ;
pdest + + ;
psrc + + ;
}
factor * = weight ;
}
return ( result ) ;
}
Image * Image : : Highlight ( int n_images , Image * images [ ] , const Rgb threshold , const Rgb ref_colour )
{
if ( n_images < = 0 ) return ( 0 ) ;
if ( n_images = = 1 ) return ( new Image ( * images [ 0 ] ) ) ;
int width = images [ 0 ] - > width ;
int height = images [ 0 ] - > height ;
int colours = images [ 0 ] - > colours ;
for ( int i = 1 ; i < n_images ; i + + )
{
assert ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ;
}
const Image * reference = Merge ( n_images , images ) ;
Image * result = new Image ( width , height , images [ 0 ] - > colours ) ;
int size = result - > size ;
for ( int c = 0 ; c < 3 ; c + + )
{
for ( int i = 0 ; i < size ; i + + )
{
int count = 0 ;
JSAMPLE * pdest = result - > buffer + c ;
for ( int j = 0 ; j < n_images ; j + + )
{
JSAMPLE * psrc = images [ j ] - > buffer + c ;
if ( abs ( ( * psrc ) - RGB_VAL ( ref_colour , c ) ) > = RGB_VAL ( threshold , c ) )
{
count + + ;
}
psrc + = 3 ;
}
* pdest = ( count * 255 ) / n_images ;
pdest + = 3 ;
}
}
return ( result ) ;
}
Image * Image : : Delta ( const Image & image , bool absolute ) const
{
assert ( width = = image . width & & height = = image . height & & colours = = image . colours ) ;
Image * result = new Image ( width , height , 1 ) ;
typedef JSAMPLE IMAGE [ width ] [ height ] [ colours ] ;
IMAGE & data = reinterpret_cast < IMAGE & > ( * buffer ) ;
IMAGE & image_data = reinterpret_cast < IMAGE & > ( * image . buffer ) ;
IMAGE & diff_data = reinterpret_cast < IMAGE & > ( * result - > buffer ) ;
unsigned char * psrc = buffer ;
unsigned char * pref = image . buffer ;
unsigned char * pdiff = result - > buffer ;
if ( colours = = 1 )
{
if ( absolute )
{
while ( psrc < ( buffer + size ) )
{
* pdiff + + = abs ( * psrc + + - * pref + + ) ;
}
}
else
{
while ( psrc < ( buffer + size ) )
{
* pdiff + + = * psrc + + - * pref + + ;
}
}
}
else
{
if ( absolute )
{
while ( psrc < ( buffer + size ) )
{
int red = abs ( * psrc + + - * pref + + ) ;
int green = abs ( * psrc + + - * pref + + ) ;
int blue = abs ( * psrc + + - * pref + + ) ;
//*pdiff++ = (JSAMPLE)sqrt((red*red + green*green + blue*blue)/3);
* pdiff + + = ( JSAMPLE ) ( ( red + green + blue ) / 3 ) ;
}
}
else
{
while ( psrc < ( buffer + size ) )
{
int red = * psrc + + - * pref + + ;
int green = * psrc + + - * pref + + ;
int blue = * psrc + + - * pref + + ;
* pdiff + + = 127 + ( ( int ( red + green + blue ) ) / ( 3 * 2 ) ) ;
}
}
}
return ( result ) ;
}
2002-11-07 20:25:48 +08:00
bool Image : : CheckAlarms ( Zone * zone , const Image * delta_image ) const
2002-09-16 17:19:24 +08:00
{
bool alarm = false ;
unsigned int score = 0 ;
2002-11-07 20:25:48 +08:00
zone - > ResetStats ( ) ;
2002-09-16 17:19:24 +08:00
delete zone - > image ;
Image * diff_image = zone - > image = new Image ( * delta_image ) ;
int alarm_pixels = 0 ;
int lo_x = zone - > limits . Lo ( ) . X ( ) ;
int lo_y = zone - > limits . Lo ( ) . Y ( ) ;
int hi_x = zone - > limits . Hi ( ) . X ( ) ;
int hi_y = zone - > limits . Hi ( ) . Y ( ) ;
for ( int y = lo_y ; y < = hi_y ; y + + )
{
unsigned char * pdiff = & diff_image - > buffer [ ( y * diff_image - > width ) + lo_x ] ;
for ( int x = lo_x ; x < = hi_x ; x + + , pdiff + + )
{
if ( * pdiff > zone - > alarm_threshold )
{
* pdiff = WHITE ;
alarm_pixels + + ;
continue ;
}
* pdiff = BLACK ;
}
}
//diff_image->WriteJpeg( "diff1.jpg" );
if ( ! alarm_pixels ) return ( false ) ;
if ( zone - > min_alarm_pixels & & alarm_pixels < zone - > min_alarm_pixels ) return ( false ) ;
if ( zone - > max_alarm_pixels & & alarm_pixels > zone - > max_alarm_pixels ) return ( false ) ;
int filter_pixels = 0 ;
int bx = zone - > filter_box . X ( ) ;
int by = zone - > filter_box . Y ( ) ;
int bx1 = bx - 1 ;
int by1 = by - 1 ;
// Now eliminate all pixels that don't participate in a blob
for ( int y = lo_y ; y < = hi_y ; y + + )
{
unsigned char * pdiff = & diff_image - > buffer [ ( y * diff_image - > width ) + lo_x ] ;
for ( int x = lo_x ; x < = hi_x ; x + + , pdiff + + )
{
if ( * pdiff = = WHITE )
{
// Check participation in an X blob
int ldx = ( x > = ( lo_x + bx1 ) ) ? - bx1 : lo_x - x ;
int hdx = ( x < = ( hi_x - bx1 ) ) ? 0 : ( ( hi_x - x ) - bx1 ) ;
int ldy = ( y > = ( lo_y + by1 ) ) ? - by1 : lo_y - y ;
int hdy = ( y < = ( hi_y - by1 ) ) ? 0 : ( ( hi_y - y ) - by1 ) ;
bool blob = false ;
for ( int dy = ldy ; ! blob & & dy < = hdy ; dy + + )
{
for ( int dx = ldx ; ! blob & & dx < = hdx ; dx + + )
{
blob = true ;
for ( int dy2 = 0 ; blob & & dy2 < by ; dy2 + + )
{
for ( int dx2 = 0 ; blob & & dx2 < bx ; dx2 + + )
{
unsigned char * cpdiff = & diff_image - > buffer [ ( ( y + dy + dy2 ) * diff_image - > width ) + x + dx + dx2 ] ;
if ( ! * cpdiff )
{
blob = false ;
}
}
}
}
}
if ( ! blob )
{
* pdiff = BLACK ;
continue ;
}
filter_pixels + + ;
}
}
}
//diff_image->WriteJpeg( "diff2.jpg" );
if ( ! filter_pixels ) return ( false ) ;
if ( zone - > min_filter_pixels & & filter_pixels < zone - > min_filter_pixels ) return ( false ) ;
if ( zone - > max_filter_pixels & & filter_pixels > zone - > max_filter_pixels ) return ( false ) ;
int blobs = 0 ;
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 ) ;
//printf( "%x\n", diff_image->buffer );
for ( int y = lo_y ; y < = hi_y ; y + + )
{
unsigned char * pdiff = & diff_image - > buffer [ ( y * diff_image - > width ) + lo_x ] ;
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 );
int lx = x > lo_x ? * ( pdiff - 1 ) : 0 ;
int ly = y > lo_y ? * ( pdiff - diff_image - > width ) : 0 ;
if ( lx )
{
//printf( "Left neighbour is %d\n", lx );
BlobStats * bsx = & blob_stats [ lx ] ;
if ( ly )
{
//printf( "Top neighbour is %d\n", ly );
BlobStats * 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->lo_x ) bsx->lo_x = x;
//if ( y < bsx->lo_y ) bsx->lo_y = y;
if ( x > bsx - > hi_x ) bsx - > hi_x = x ;
if ( y > bsx - > hi_y ) bsx - > hi_y = y ;
}
else
{
// Amortise blobs
BlobStats * bsm = bsx - > count > = bsy - > count ? bsx : bsy ;
BlobStats * 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 + + )
{
unsigned char * spdiff = & diff_image - > buffer [ ( sy * diff_image - > width ) + bss - > lo_x ] ;
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 ;
// 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 ;
blobs - - ;
}
}
else
{
//printf( "Setting to left neighbour %d\n", lx );
// Add to the blob from the x side
* pdiff = lx ;
bsx - > count + + ;
//if ( x < bsx->lo_x ) bsx->lo_x = x;
//if ( y < bsx->lo_y ) bsx->lo_y = y;
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->lo_x ) bsy->lo_x = x;
//if ( y < bsy->lo_y ) bsy->lo_y = y;
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 + + )
{
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 ;
blobs + + ;
break ;
}
}
}
}
}
}
}
//diff_image->WriteJpeg( "diff3.jpg" );
if ( ! blobs ) return ( false ) ;
int blob_pixels = filter_pixels ;
2002-11-07 20:25:48 +08:00
int min_blob_size = 0 ;
int max_blob_size = 0 ;
2002-09-16 17:19:24 +08:00
// Now eliminate blobs under the alarm_threshold
for ( int i = 1 ; i < WHITE ; i + + )
{
BlobStats * bs = & blob_stats [ i ] ;
if ( bs - > count & & ( ( zone - > min_blob_pixels & & bs - > count < zone - > min_blob_pixels ) | | ( zone - > max_blob_pixels & & bs - > count > zone - > max_blob_pixels ) ) )
{
//Info(( "Eliminating blob %d, %d pixels (%d,%d - %d,%d)\n", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y ));
for ( int sy = bs - > lo_y ; sy < = bs - > hi_y ; sy + + )
{
unsigned char * spdiff = & diff_image - > buffer [ ( sy * diff_image - > width ) + bs - > lo_x ] ;
for ( int sx = bs - > lo_x ; sx < = bs - > hi_x ; sx + + , spdiff + + )
{
if ( * spdiff = = bs - > tag )
{
* spdiff = BLACK ;
}
}
}
blobs - - ;
blob_pixels - = bs - > count ;
bs - > tag = 0 ;
bs - > count = 0 ;
bs - > lo_x = 0 ;
bs - > lo_y = 0 ;
bs - > hi_x = 0 ;
bs - > hi_y = 0 ;
}
2002-11-07 20:25:48 +08:00
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 ;
}
}
2002-09-16 17:19:24 +08:00
}
if ( ! blobs ) return ( false ) ;
if ( zone - > min_blobs & & blobs < zone - > min_blobs ) return ( false ) ;
if ( zone - > max_blobs & & blobs > zone - > max_blobs ) return ( false ) ;
int alarm_lo_x = hi_x + 1 ;
int alarm_hi_x = lo_x - 1 ;
int alarm_lo_y = hi_y + 1 ;
int alarm_hi_y = lo_y - 1 ;
for ( int i = 1 ; i < WHITE ; i + + )
{
BlobStats * bs = & blob_stats [ i ] ;
if ( 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 ;
}
}
zone - > alarm_pixels = alarm_pixels ;
zone - > alarm_filter_pixels = filter_pixels ;
2002-11-07 20:25:48 +08:00
zone - > alarm_blob_pixels = blob_pixels ;
zone - > alarm_blobs = blobs ;
zone - > min_blob_size = min_blob_size ;
zone - > max_blob_size = max_blob_size ;
2002-09-16 17:19:24 +08:00
zone - > alarm_box = Box ( Coord ( alarm_lo_x , alarm_lo_y ) , Coord ( alarm_hi_x , alarm_hi_y ) ) ;
2002-11-07 20:25:48 +08:00
zone - > score = ( ( 100 * blob_pixels ) / blobs ) / ( zone - > limits . Size ( ) . X ( ) * zone - > limits . Size ( ) . Y ( ) ) ;
2002-09-16 17:19:24 +08:00
if ( zone - > Type ( ) = = Zone : : INCLUSIVE )
{
zone - > score / = 2 ;
}
else if ( zone - > Type ( ) = = Zone : : EXCLUSIVE )
{
zone - > score * = 2 ;
}
2002-11-07 20:25:48 +08:00
score = zone - > score ;
2002-09-16 17:19:24 +08:00
// Now outline the changed region
if ( zone - > alarm_blobs )
{
Image * high_image = zone - > image = new Image ( * diff_image ) ;
high_image - > Colourise ( ) ;
alarm = true ;
memset ( high_image - > buffer , 0 , high_image - > size ) ;
for ( int y = lo_y ; y < = hi_y ; y + + )
{
unsigned char * pdiff = & diff_image - > buffer [ ( y * diff_image - > width ) + lo_x ] ;
unsigned char * phigh = & high_image - > buffer [ 3 * ( ( y * high_image - > width ) + lo_x ) ] ;
for ( int x = lo_x ; x < = hi_x ; x + + , pdiff + + , phigh + = 3 )
{
bool edge = false ;
if ( * pdiff )
{
if ( ! edge & & x > 0 & & ! * ( pdiff - 1 ) ) edge = true ;
if ( ! edge & & x < ( diff_image - > width - 1 ) & & ! * ( pdiff + 1 ) ) edge = true ;
if ( ! edge & & y > 0 & & ! * ( pdiff - diff_image - > width ) ) edge = true ;
if ( ! edge & & y < ( diff_image - > height - 1 ) & & ! * ( pdiff + diff_image - > width ) ) edge = true ;
}
if ( edge )
{
RED ( phigh ) = RGB_RED_VAL ( zone - > alarm_rgb ) ;
GREEN ( phigh ) = RGB_GREEN_VAL ( zone - > alarm_rgb ) ;
BLUE ( phigh ) = RGB_BLUE_VAL ( zone - > alarm_rgb ) ;
}
}
}
delete diff_image ;
//high_image->WriteJpeg( "diff4.jpg" );
2002-11-07 20:25:48 +08:00
Info ( ( " %s: Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d \n " , zone - > Label ( ) , alarm_pixels , filter_pixels , blob_pixels , blobs , score ) ) ;
2002-09-16 17:19:24 +08:00
}
2002-11-07 20:25:48 +08:00
return ( true ) ;
2002-09-16 17:19:24 +08:00
}
unsigned int Image : : Compare ( const Image & image , int n_zones , Zone * zones [ ] ) const
{
bool alarm = false ;
unsigned int score = 0 ;
if ( n_zones < = 0 ) return ( alarm ) ;
const Image * delta_image = Delta ( image ) ;
// Blank out all exclusion zones
unsigned char * psrc = buffer ;
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + )
{
Zone * zone = zones [ n_zone ] ;
zone - > alarmed = false ;
if ( zone - > Type ( ) ! = Zone : : INACTIVE )
{
continue ;
}
int lo_x = zone - > limits . Lo ( ) . X ( ) ;
int lo_y = zone - > limits . Lo ( ) . Y ( ) ;
int hi_x = zone - > limits . Hi ( ) . X ( ) ;
int hi_y = zone - > limits . Hi ( ) . Y ( ) ;
2002-10-14 17:11:44 +08:00
Debug ( 3 , ( " Zeroing zone %s, from %d,%d -> %d,%d " , zone - > Label ( ) , lo_x , lo_y , hi_x , hi_y ) ) ;
2002-09-16 17:19:24 +08:00
for ( int y = lo_y ; y < = hi_y ; y + + )
{
2002-10-14 17:11:44 +08:00
unsigned char * pdelta = & delta_image - > buffer [ ( y * delta_image - > width ) + lo_x ] ;
2002-09-16 17:19:24 +08:00
for ( int x = lo_x ; x < = hi_x ; x + + )
{
* pdelta + + = BLACK ;
}
}
}
// Find all alarm pixels in active zones
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + )
{
Zone * zone = zones [ n_zone ] ;
if ( zone - > Type ( ) ! = Zone : : ACTIVE )
{
continue ;
}
2002-10-14 17:11:44 +08:00
Debug ( 3 , ( " Checking active zone %s " , zone - > Label ( ) ) ) ;
2002-11-07 20:25:48 +08:00
if ( CheckAlarms ( zone , delta_image ) )
2002-09-16 17:19:24 +08:00
{
alarm = true ;
2002-11-07 20:25:48 +08:00
score + = zone - > score ;
2002-09-16 17:19:24 +08:00
zone - > alarmed = true ;
2002-11-07 20:25:48 +08:00
Debug ( 3 , ( " Zone is alarmed, zone score = %d " , zone - > score ) ) ;
2002-09-16 17:19:24 +08:00
}
}
if ( alarm )
{
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + )
{
Zone * zone = zones [ n_zone ] ;
if ( zone - > Type ( ) ! = Zone : : INCLUSIVE )
{
continue ;
}
2002-10-14 17:11:44 +08:00
Debug ( 3 , ( " Checking inclusive zone %s " , zone - > Label ( ) ) ) ;
2002-11-07 20:25:48 +08:00
if ( CheckAlarms ( zone , delta_image ) )
2002-09-16 17:19:24 +08:00
{
alarm = true ;
2002-11-07 20:25:48 +08:00
score + = zone - > score ;
2002-09-16 17:19:24 +08:00
zone - > alarmed = true ;
2002-11-07 20:25:48 +08:00
Debug ( 3 , ( " Zone is alarmed, zone score = %d " , zone - > score ) ) ;
2002-09-16 17:19:24 +08:00
}
}
}
else
{
// Find all alarm pixels in exclusion zones
for ( int n_zone = 0 ; n_zone < n_zones ; n_zone + + )
{
Zone * zone = zones [ n_zone ] ;
if ( zone - > Type ( ) ! = Zone : : EXCLUSIVE )
{
continue ;
}
2002-10-14 17:11:44 +08:00
Debug ( 3 , ( " Checking exclusive zone %s " , zone - > Label ( ) ) ) ;
2002-11-07 20:25:48 +08:00
if ( CheckAlarms ( zone , delta_image ) )
2002-09-16 17:19:24 +08:00
{
alarm = true ;
2002-11-07 20:25:48 +08:00
score + = zone - > score ;
2002-09-16 17:19:24 +08:00
zone - > alarmed = true ;
2002-11-07 20:25:48 +08:00
Debug ( 3 , ( " Zone is alarmed, zone score = %d " , zone - > score ) ) ;
2002-09-16 17:19:24 +08:00
}
}
}
delete delta_image ;
2002-11-07 20:25:48 +08:00
// This is a small and innocent hack to prevent scores of 0 being returned in alarm state
return ( score ? score : alarm ) ;
2002-09-16 17:19:24 +08:00
}
void Image : : Annotate ( const char * text , const Coord & coord , const Rgb colour )
{
int len = strlen ( text ) ;
int text_x = coord . X ( ) ;
int text_y = coord . Y ( ) ;
if ( text_x > width - ( len * CHAR_WIDTH ) )
{
text_x = width - ( len * CHAR_WIDTH ) ;
}
if ( text_y > height - CHAR_HEIGHT )
{
text_y = height - CHAR_HEIGHT ;
}
for ( int y = text_y ; y < ( text_y + CHAR_HEIGHT ) ; y + + )
{
JSAMPLE * ptr = & buffer [ ( ( y * width ) + text_x ) * 3 ] ;
for ( int x = 0 ; x < len ; x + + )
{
int f = fontdata [ text [ x ] * CHAR_HEIGHT + ( y - text_y ) ] ;
for ( int i = CHAR_WIDTH - 1 ; i > = 0 ; i - - )
{
if ( f & ( CHAR_START < < i ) )
{
RED ( ptr ) = RGB_VAL ( colour , 0 ) ;
GREEN ( ptr ) = RGB_VAL ( colour , 1 ) ;
BLUE ( ptr ) = RGB_VAL ( colour , 2 ) ;
}
ptr + = colours ;
}
}
}
}
void Image : : Annotate ( const char * text , const Coord & coord )
{
int len = strlen ( text ) ;
int text_x = coord . X ( ) ;
int text_y = coord . Y ( ) ;
if ( text_x > width - ( len * CHAR_WIDTH ) )
{
text_x = width - ( len * CHAR_WIDTH ) ;
}
if ( text_y > height - CHAR_HEIGHT )
{
text_y = height - CHAR_HEIGHT ;
}
for ( int y = text_y ; y < ( text_y + CHAR_HEIGHT ) ; y + + )
{
JSAMPLE * ptr = & buffer [ ( ( y * width ) + text_x ) * colours ] ;
for ( int x = 0 ; x < len ; x + + )
{
int f = fontdata [ text [ x ] * CHAR_HEIGHT + ( y - text_y ) ] ;
for ( int i = CHAR_WIDTH - 1 ; i > = 0 ; i - - )
{
if ( f & ( CHAR_START < < i ) )
{
if ( colours = = 1 )
{
* ptr + + = WHITE ;
continue ;
}
else
{
RED ( ptr ) = GREEN ( ptr ) = BLUE ( ptr ) = WHITE ;
ptr + = 3 ;
continue ;
}
}
else
{
if ( colours = = 1 )
{
* ptr + + = BLACK ;
continue ;
}
else
{
RED ( ptr ) = GREEN ( ptr ) = BLUE ( ptr ) = BLACK ;
ptr + = 3 ;
continue ;
}
}
//ptr += colours;
}
}
}
}
void Image : : Timestamp ( const char * label , time_t when , const Coord & coord )
{
char time_text [ 64 ] ;
strftime ( time_text , sizeof ( time_text ) , " %y/%m/%d %H:%M:%S " , localtime ( & when ) ) ;
char text [ 64 ] ;
if ( label )
{
sprintf ( text , " %s - %s " , label , time_text ) ;
Annotate ( text , coord ) ;
}
else
{
Annotate ( time_text , coord ) ;
}
}
void Image : : Colourise ( )
{
if ( colours = = 1 )
{
colours = 3 ;
size = width * height * 3 ;
JSAMPLE * new_buffer = new JSAMPLE [ size ] ;
JSAMPLE * psrc = buffer ;
JSAMPLE * pdest = new_buffer ;
while ( pdest < ( new_buffer + size ) )
{
RED ( pdest ) = GREEN ( pdest ) = BLUE ( pdest ) = * psrc + + ;
pdest + = 3 ;
}
delete [ ] buffer ;
buffer = new_buffer ;
}
}
void Image : : DeColourise ( )
{
if ( colours = = 3 )
{
colours = 1 ;
size = width * height ;
JSAMPLE * psrc = buffer ;
JSAMPLE * pdest = buffer ;
while ( pdest < ( buffer + size ) )
{
* pdest + + = ( JSAMPLE ) sqrt ( ( RED ( psrc ) + GREEN ( psrc ) + BLUE ( psrc ) ) / 3 ) ;
psrc + = 3 ;
}
}
}
2002-10-29 17:08:52 +08:00
Camera : : Camera ( int p_id , char * p_name , int p_device , int p_channel , int p_format , int p_width , int p_height , int p_colours , bool p_capture ) : id ( p_id ) , device ( p_device ) , channel ( p_channel ) , format ( p_format ) , width ( p_width ) , height ( p_height ) , colours ( p_colours ) , capture ( p_capture )
2002-09-16 17:19:24 +08:00
{
name = new char [ strlen ( p_name ) + 1 ] ;
strcpy ( name , p_name ) ;
if ( ! camera_count + + & & capture )
{
Initialise ( device , channel , format , width , height , colours ) ;
}
}
Camera : : ~ Camera ( )
{
if ( ! - - camera_count & & capture )
{
Terminate ( ) ;
}
}
2003-01-16 22:53:26 +08:00
bool Camera : : GetCurrentSettings ( int device , char * output , bool verbose )
{
char device_path [ 64 ] ;
output [ 0 ] = 0 ;
sprintf ( device_path , " /dev/video%d " , device ) ;
if ( verbose )
sprintf ( output , output + strlen ( output ) , " Checking Video Device: %s \n " , device_path ) ;
if ( ( m_videohandle = open ( device_path , O_RDONLY ) ) < = 0 )
{
Error ( ( " Failed to open video device %s: %s \n " , device_path , strerror ( errno ) ) ) ;
if ( verbose )
sprintf ( output + strlen ( output ) , " Error, failed to open video device: %s \n " , strerror ( errno ) ) ;
else
sprintf ( output + strlen ( output ) , " error%d \n " , errno ) ;
return ( false ) ;
}
struct video_capability vid_cap ;
if ( ! ioctl ( m_videohandle , VIDIOCGCAP , & vid_cap ) )
{
if ( verbose )
{
sprintf ( output + strlen ( output ) , " Video Capabilities \n " ) ;
sprintf ( output + strlen ( output ) , " Name: %s \n " , vid_cap . name ) ;
sprintf ( output + strlen ( output ) , " Type: %d \n %s%s%s%s%s%s%s%s%s%s%s%s%s%s " , vid_cap . type ,
vid_cap . type & VID_TYPE_CAPTURE ? " Can capture \n " : " " ,
vid_cap . type & VID_TYPE_TUNER ? " Can tune \n " : " " ,
vid_cap . type & VID_TYPE_TELETEXT ? " Does teletext \n " : " " ,
vid_cap . type & VID_TYPE_OVERLAY ? " Overlay onto frame buffer \n " : " " ,
vid_cap . type & VID_TYPE_CHROMAKEY ? " Overlay by chromakey \n " : " " ,
vid_cap . type & VID_TYPE_CLIPPING ? " Can clip \n " : " " ,
vid_cap . type & VID_TYPE_FRAMERAM ? " Uses the frame buffer memory \n " : " " ,
vid_cap . type & VID_TYPE_SCALES ? " Scalable \n " : " " ,
vid_cap . type & VID_TYPE_MONOCHROME ? " Monochrome only \n " : " " ,
vid_cap . type & VID_TYPE_SUBCAPTURE ? " Can capture subareas of the image \n " : " " ,
vid_cap . type & VID_TYPE_MPEG_DECODER ? " Can decode MPEG streams \n " : " " ,
vid_cap . type & VID_TYPE_MPEG_ENCODER ? " Can encode MPEG streams \n " : " " ,
vid_cap . type & VID_TYPE_MJPEG_DECODER ? " Can decode MJPEG streams \n " : " " ,
vid_cap . type & VID_TYPE_MJPEG_ENCODER ? " Can encode MJPEG streams \n " : " "
) ;
sprintf ( output + strlen ( output ) , " Video Channels: %d \n " , vid_cap . channels ) ;
sprintf ( output + strlen ( output ) , " Audio Channels: %d \n " , vid_cap . audios ) ;
sprintf ( output + strlen ( output ) , " Maximum Width: %d \n " , vid_cap . maxwidth ) ;
sprintf ( output + strlen ( output ) , " Maximum Width: %d \n " , vid_cap . maxheight ) ;
sprintf ( output + strlen ( output ) , " Minimum Width: %d \n " , vid_cap . minwidth ) ;
sprintf ( output + strlen ( output ) , " Minimum Width: %d \n " , vid_cap . minheight ) ;
}
else
{
sprintf ( output + strlen ( output ) , " N:%s, " , vid_cap . name ) ;
sprintf ( output + strlen ( output ) , " T:%d, " , vid_cap . type ) ;
sprintf ( output + strlen ( output ) , " nC:%d, " , vid_cap . channels ) ;
sprintf ( output + strlen ( output ) , " nA:%d, " , vid_cap . audios ) ;
sprintf ( output + strlen ( output ) , " mxW:%d, " , vid_cap . maxwidth ) ;
sprintf ( output + strlen ( output ) , " mxH:%d, " , vid_cap . maxheight ) ;
sprintf ( output + strlen ( output ) , " mnW:%d, " , vid_cap . minwidth ) ;
sprintf ( output + strlen ( output ) , " mnH:%d, " , vid_cap . minheight ) ;
}
}
else
{
Error ( ( " Failed to get video capabilities: %s " , strerror ( errno ) ) ) ;
if ( verbose )
sprintf ( output , " Error, failed to get video capabilities: %s \n " , strerror ( errno ) ) ;
else
sprintf ( output , " error%d \n " , errno ) ;
return ( false ) ;
}
struct video_window vid_win ;
if ( ! ioctl ( m_videohandle , VIDIOCGWIN , & vid_win ) )
{
if ( verbose )
{
sprintf ( output + strlen ( output ) , " Window Attributes \n " ) ;
sprintf ( output + strlen ( output ) , " X Offset: %d \n " , vid_win . x ) ;
sprintf ( output + strlen ( output ) , " Y Offset: %d \n " , vid_win . y ) ;
sprintf ( output + strlen ( output ) , " Width: %d \n " , vid_win . width ) ;
sprintf ( output + strlen ( output ) , " Height: %d \n " , vid_win . height ) ;
}
else
{
sprintf ( output + strlen ( output ) , " X:%d, " , vid_win . x ) ;
sprintf ( output + strlen ( output ) , " Y:%d, " , vid_win . y ) ;
sprintf ( output + strlen ( output ) , " W:%d, " , vid_win . width ) ;
sprintf ( output + strlen ( output ) , " H:%d, " , vid_win . height ) ;
}
}
else
{
Error ( ( " Failed to get window attributes: %s " , strerror ( errno ) ) ) ;
if ( verbose )
sprintf ( output , " Error, failed to get window attributes: %s \n " , strerror ( errno ) ) ;
else
sprintf ( output , " error%d \n " , errno ) ;
return ( false ) ;
}
struct video_picture vid_pic ;
if ( ! ioctl ( m_videohandle , VIDIOCGPICT , & vid_pic ) )
{
if ( verbose )
{
sprintf ( output + strlen ( output ) , " Picture Atributes \n " ) ;
sprintf ( output + strlen ( output ) , " Palette: %d - %s \n " , vid_pic . palette ,
vid_pic . palette = = VIDEO_PALETTE_GREY ? " Linear greyscale " : (
vid_pic . palette = = VIDEO_PALETTE_HI240 ? " High 240 cube (BT848) " : (
vid_pic . palette = = VIDEO_PALETTE_RGB565 ? " 565 16 bit RGB " : (
vid_pic . palette = = VIDEO_PALETTE_RGB24 ? " 24bit RGB " : (
vid_pic . palette = = VIDEO_PALETTE_RGB32 ? " 32bit RGB " : (
vid_pic . palette = = VIDEO_PALETTE_RGB555 ? " 555 15bit RGB " : (
vid_pic . palette = = VIDEO_PALETTE_YUV422 ? " YUV422 capture " : (
vid_pic . palette = = VIDEO_PALETTE_YUYV ? " YUYV " : (
vid_pic . palette = = VIDEO_PALETTE_UYVY ? " UVYV " : (
vid_pic . palette = = VIDEO_PALETTE_YUV420 ? " YUV420 " : (
vid_pic . palette = = VIDEO_PALETTE_YUV411 ? " YUV411 capture " : (
vid_pic . palette = = VIDEO_PALETTE_RAW ? " RAW capture (BT848) " : (
vid_pic . palette = = VIDEO_PALETTE_YUV422P ? " YUV 4:2:2 Planar " : (
vid_pic . palette = = VIDEO_PALETTE_YUV411P ? " YUV 4:1:1 Planar " : (
vid_pic . palette = = VIDEO_PALETTE_YUV420P ? " YUV 4:2:0 Planar " : (
vid_pic . palette = = VIDEO_PALETTE_YUV410P ? " YUV 4:1:0 Planar " : " Unknown "
) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ) ;
sprintf ( output + strlen ( output ) , " Colour Depth: %d \n " , vid_pic . depth ) ;
sprintf ( output + strlen ( output ) , " Brightness: %d \n " , vid_pic . brightness ) ;
sprintf ( output + strlen ( output ) , " Hue: %d \n " , vid_pic . hue ) ;
sprintf ( output + strlen ( output ) , " Colour :%d \n " , vid_pic . colour ) ;
sprintf ( output + strlen ( output ) , " Contrast: %d \n " , vid_pic . contrast ) ;
sprintf ( output + strlen ( output ) , " Whiteness: %d \n " , vid_pic . whiteness ) ;
}
else
{
sprintf ( output + strlen ( output ) , " P:%d, " , vid_pic . palette ) ;
sprintf ( output + strlen ( output ) , " D:%d, " , vid_pic . depth ) ;
sprintf ( output + strlen ( output ) , " B:%d, " , vid_pic . brightness ) ;
sprintf ( output + strlen ( output ) , " h:%d, " , vid_pic . hue ) ;
sprintf ( output + strlen ( output ) , " Cl:%d, " , vid_pic . colour ) ;
sprintf ( output + strlen ( output ) , " Cn:%d, " , vid_pic . contrast ) ;
sprintf ( output + strlen ( output ) , " w:%d, " , vid_pic . whiteness ) ;
}
}
else
{
Error ( ( " Failed to get picture attributes: %s " , strerror ( errno ) ) ) ;
if ( verbose )
sprintf ( output , " Error, failed to get picture attributes: %s \n " , strerror ( errno ) ) ;
else
sprintf ( output , " error%d \n " , errno ) ;
return ( false ) ;
}
for ( int chan = 0 ; chan < vid_cap . channels ; chan + + )
{
struct video_channel vid_src ;
vid_src . channel = chan ;
if ( ! ioctl ( m_videohandle , VIDIOCGCHAN , & vid_src ) )
{
if ( verbose )
{
sprintf ( output + strlen ( output ) , " Channel %d Attributes \n " , chan ) ;
sprintf ( output + strlen ( output ) , " Name: %s \n " , vid_src . name ) ;
sprintf ( output + strlen ( output ) , " Channel: %d \n " , vid_src . channel ) ;
sprintf ( output + strlen ( output ) , " Flags: %d \n %s%s " , vid_src . flags ,
vid_src . flags & VIDEO_VC_TUNER ? " Channel has a tuner \n " : " " ,
vid_src . flags & VIDEO_VC_AUDIO ? " Channel has audio \n " : " "
) ;
sprintf ( output + strlen ( output ) , " Type: %d - %s \n " , vid_src . type ,
vid_src . type = = VIDEO_TYPE_TV ? " TV " : (
vid_src . type = = VIDEO_TYPE_CAMERA ? " Camera " : " Unknown "
) ) ;
sprintf ( output + strlen ( output ) , " Format: %d - %s \n " , vid_src . norm ,
vid_src . norm = = VIDEO_MODE_PAL ? " PAL " : (
vid_src . norm = = VIDEO_MODE_NTSC ? " NTSC " : (
vid_src . norm = = VIDEO_MODE_SECAM ? " SECAM " : (
vid_src . norm = = VIDEO_MODE_AUTO ? " AUTO " : " Unknown "
) ) ) ) ;
}
else
{
sprintf ( output + strlen ( output ) , " n%d:%d, " , chan , vid_src . name ) ;
sprintf ( output + strlen ( output ) , " C%d:%d, " , chan , vid_src . channel ) ;
sprintf ( output + strlen ( output ) , " Fl%d:%x, " , chan , vid_src . flags ) ;
2003-01-16 22:57:30 +08:00
sprintf ( output + strlen ( output ) , " T%d:%d " , chan , vid_src . type ) ;
2003-01-16 22:53:26 +08:00
sprintf ( output + strlen ( output ) , " F%d:%d%s, " , chan , vid_src . norm , chan = = ( vid_cap . channels - 1 ) ? " " : " , " ) ;
}
}
else
{
Error ( ( " Failed to get channel %d attributes: %s \n " , chan , strerror ( errno ) ) ) ;
if ( verbose )
sprintf ( output , " Error, failed to get channel %d attributes: %s \n " , chan , strerror ( errno ) ) ;
else
sprintf ( output , " error%d \n " , errno ) ;
return ( false ) ;
}
}
return ( true ) ;
}
2002-09-16 17:19:24 +08:00
void Camera : : Initialise ( int device , int channel , int format , int width , int height , int colours )
{
char device_path [ 64 ] ;
sprintf ( device_path , " /dev/video%d " , device ) ;
if ( ( m_videohandle = open ( device_path , O_RDONLY ) ) < = 0 )
{
Error ( ( " Failed to open video device %s: %s \n " , device_path , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
struct video_window vid_win ;
if ( ! ioctl ( m_videohandle , VIDIOCGWIN , & vid_win ) )
{
Info ( ( " X:%d \n " , vid_win . x ) ) ;
Info ( ( " Y:%d \n " , vid_win . y ) ) ;
Info ( ( " W:%d \n " , vid_win . width ) ) ;
Info ( ( " H:%d \n " , vid_win . height ) ) ;
}
else
{
Error ( ( " Failed to get window attributes: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
vid_win . x = 0 ;
vid_win . y = 0 ;
vid_win . width = width ;
vid_win . height = height ;
if ( ioctl ( m_videohandle , VIDIOCSWIN , & vid_win ) )
{
Error ( ( " Failed to set window attributes: %s \n " , strerror ( errno ) ) ) ;
2003-01-24 23:25:38 +08:00
if ( ! ZM_STRICT_VIDEO_CONFIG ) exit ( - 1 ) ;
2002-09-16 17:19:24 +08:00
}
struct video_picture vid_pic ;
if ( ! ioctl ( m_videohandle , VIDIOCGPICT , & vid_pic ) )
{
Info ( ( " P:%d \n " , vid_pic . palette ) ) ;
Info ( ( " D:%d \n " , vid_pic . depth ) ) ;
Info ( ( " B:%d \n " , vid_pic . brightness ) ) ;
Info ( ( " h:%d \n " , vid_pic . hue ) ) ;
Info ( ( " Cl:%d \n " , vid_pic . colour ) ) ;
Info ( ( " Cn:%d \n " , vid_pic . contrast ) ) ;
}
else
{
Error ( ( " Failed to get picture attributes: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
if ( colours = = 1 )
{
vid_pic . palette = VIDEO_PALETTE_GREY ;
vid_pic . depth = 8 ;
}
else
{
vid_pic . palette = VIDEO_PALETTE_RGB24 ;
vid_pic . depth = 24 ;
}
if ( ioctl ( m_videohandle , VIDIOCSPICT , & vid_pic ) )
{
Error ( ( " Failed to set picture attributes: %s \n " , strerror ( errno ) ) ) ;
2003-01-24 23:25:38 +08:00
if ( ! ZM_STRICT_VIDEO_CONFIG ) exit ( - 1 ) ;
2002-09-16 17:19:24 +08:00
}
if ( ! ioctl ( m_videohandle , VIDIOCGMBUF , & m_vmb ) )
{
m_vmm = new video_mmap [ m_vmb . frames ] ;
Info ( ( " vmb.frames = %d \n " , m_vmb . frames ) ) ;
Info ( ( " vmb.size = %d \n " , m_vmb . size ) ) ;
}
else
{
Error ( ( " Failed to setup memory: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
for ( int loop = 0 ; loop < m_vmb . frames ; loop + + )
{
m_vmm [ loop ] . frame = loop ;
m_vmm [ loop ] . width = width ;
m_vmm [ loop ] . height = height ;
m_vmm [ loop ] . format = ( colours = = 1 ? VIDEO_PALETTE_GREY : VIDEO_PALETTE_RGB24 ) ;
}
m_buffer = ( unsigned char * ) mmap ( 0 , m_vmb . size , PROT_READ , MAP_SHARED , m_videohandle , 0 ) ;
if ( ! ( ( long ) m_buffer > 0 ) )
{
Error ( ( " Could not mmap video: %s " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
2003-01-16 22:53:26 +08:00
struct video_channel vid_src ;
vid_src . channel = channel ;
if ( ! ioctl ( m_videohandle , VIDIOCGCHAN , & vid_src ) )
{
Info ( ( " C:%d \n " , vid_src . channel ) ) ;
Info ( ( " F:%d \n " , vid_src . norm ) ) ;
Info ( ( " Fl:%x \n " , vid_src . flags ) ) ;
Info ( ( " T:%d \n " , vid_src . type ) ) ;
}
else
{
Error ( ( " Failed to get camera source: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
2002-09-16 17:19:24 +08:00
2003-01-16 22:53:26 +08:00
//vid_src.norm = VIDEO_MODE_AUTO;
vid_src . norm = format ;
vid_src . flags = 0 ;
vid_src . type = VIDEO_TYPE_CAMERA ;
if ( ioctl ( m_videohandle , VIDIOCSCHAN , & vid_src ) )
2002-09-16 17:19:24 +08:00
{
Error ( ( " Failed to set camera source %d: %s \n " , channel , strerror ( errno ) ) ) ;
2003-01-24 23:25:38 +08:00
if ( ! ZM_STRICT_VIDEO_CONFIG ) exit ( - 1 ) ;
2002-09-16 17:19:24 +08:00
}
if ( ! ioctl ( m_videohandle , VIDIOCGWIN , & vid_win ) )
{
Info ( ( " X:%d \n " , vid_win . x ) ) ;
Info ( ( " Y:%d \n " , vid_win . y ) ) ;
Info ( ( " W:%d \n " , vid_win . width ) ) ;
Info ( ( " H:%d \n " , vid_win . height ) ) ;
}
else
{
Error ( ( " Failed to get window data: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
if ( ! ioctl ( m_videohandle , VIDIOCGPICT , & vid_pic ) )
{
Info ( ( " P:%d \n " , vid_pic . palette ) ) ;
Info ( ( " D:%d \n " , vid_pic . depth ) ) ;
Info ( ( " B:%d \n " , vid_pic . brightness ) ) ;
Info ( ( " h:%d \n " , vid_pic . hue ) ) ;
Info ( ( " Cl:%d \n " , vid_pic . colour ) ) ;
Info ( ( " Cn:%d \n " , vid_pic . contrast ) ) ;
}
else
{
Error ( ( " Failed to get window data: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
}
void Camera : : Terminate ( )
{
munmap ( ( char * ) m_buffer , m_vmb . size ) ;
delete [ ] m_vmm ;
close ( m_videohandle ) ;
}
int Camera : : m_cap_frame = 0 ;
int Camera : : m_sync_frame = 0 ;
video_mbuf Camera : : m_vmb ;
video_mmap * Camera : : m_vmm ;
int Camera : : m_videohandle ;
unsigned char * Camera : : m_buffer = 0 ;
int Camera : : camera_count = 0 ;
Event : : Event ( Monitor * p_monitor , time_t p_start_time ) : monitor ( p_monitor ) , start_time ( p_start_time )
{
static char sql [ 256 ] ;
2002-10-30 19:17:04 +08:00
static char start_time_str [ 32 ] ;
strftime ( start_time_str , sizeof ( start_time_str ) , " %Y-%m-%d %H:%M:%S " , localtime ( & start_time ) ) ;
sprintf ( sql , " insert into Events set MonitorId=%d, Name='Event', StartTime='%s' " , monitor - > Id ( ) , start_time_str ) ;
2002-09-16 17:19:24 +08:00
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't insert event: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
id = mysql_insert_id ( & dbconn ) ;
start_frame_id = 0 ;
end_frame_id = 0 ;
end_time = 0 ;
frames = 0 ;
alarm_frames = 0 ;
2002-09-19 17:38:17 +08:00
tot_score = 0 ;
max_score = 0 ;
2003-01-13 19:50:47 +08:00
sprintf ( path , ZM_DIR_EVENTS " /%s/%04d " , monitor - > Name ( ) , id ) ;
2002-09-16 17:19:24 +08:00
struct stat statbuf ;
errno = 0 ;
stat ( path , & statbuf ) ;
if ( errno = = ENOENT | | errno = = ENOTDIR )
{
2002-09-21 03:48:48 +08:00
if ( mkdir ( path , 0755 ) )
2002-09-16 17:19:24 +08:00
{
Error ( ( " Can't make %s: %s \n " , path , strerror ( errno ) ) ) ;
}
}
}
Event : : ~ Event ( )
{
static char sql [ 256 ] ;
2002-10-30 19:17:04 +08:00
static char end_time_str [ 32 ] ;
strftime ( end_time_str , sizeof ( end_time_str ) , " %Y-%m-%d %H:%M:%S " , localtime ( & end_time ) ) ;
2003-01-07 22:32:23 +08:00
sprintf ( sql , " update Events set Name='Event-%d', EndTime = '%s', Length = %d, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d " , id , end_time_str , ( end_time - start_time ) , frames , alarm_frames , tot_score , ( int ) ( tot_score / alarm_frames ) , max_score , id ) ;
2002-09-16 17:19:24 +08:00
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't update event: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
}
void Event : : AddFrame ( time_t timestamp , const Image * image , const Image * alarm_image , unsigned int score )
{
frames + + ;
2002-09-21 19:14:12 +08:00
static char event_file [ PATH_MAX ] ;
2002-09-16 17:19:24 +08:00
sprintf ( event_file , " %s/capture-%03d.jpg " , path , frames ) ;
image - > WriteJpeg ( event_file ) ;
static char sql [ 256 ] ;
sprintf ( sql , " insert into Frames set EventId=%d, FrameId=%d, AlarmFrame=%d, ImagePath='%s', TimeStamp=from_unixtime(%d), Score=%d " , id , frames , alarm_image ! = 0 , event_file , timestamp , score ) ;
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't insert frame: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
end_frame_id = mysql_insert_id ( & dbconn ) ;
if ( ! start_frame_id ) start_frame_id = end_frame_id ;
end_time = timestamp ;
if ( ! start_time ) start_time = end_time ;
if ( alarm_image )
{
alarm_frames + + ;
sprintf ( event_file , " %s/analyse-%03d.jpg " , path , frames ) ;
alarm_image - > WriteJpeg ( event_file ) ;
2002-09-19 17:38:17 +08:00
tot_score + = score ;
if ( score > max_score )
max_score = score ;
2002-09-16 17:19:24 +08:00
}
}
2002-10-29 17:08:52 +08:00
void Event : : StreamEvent ( const char * path , int event_id , unsigned long refresh , FILE * fd )
2002-09-16 17:19:24 +08:00
{
static char sql [ 256 ] ;
sprintf ( sql , " select Id, EventId, ImagePath, TimeStamp from Frames where EventId = %d order by Id " , event_id ) ;
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't run query: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
if ( ! result )
{
Error ( ( " Can't use query result: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
fprintf ( fd , " Server: ZoneMinder Stream Server \r \n " ) ;
fprintf ( fd , " Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame \r \n " ) ;
fprintf ( fd , " \r \n " ) ;
fprintf ( fd , " --ZoneMinderFrame \n " ) ;
int n_frames = mysql_num_rows ( result ) ;
Info ( ( " Got %d frames \n " , n_frames ) ) ;
FILE * fdj = NULL ;
int n_bytes = 0 ;
static unsigned char buffer [ 0x10000 ] ;
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + )
{
2002-09-21 19:14:12 +08:00
char filepath [ PATH_MAX ] ;
2002-09-16 17:19:24 +08:00
sprintf ( filepath , " %s/%s " , path , dbrow [ 2 ] ) ;
if ( fdj = fopen ( filepath , " r " ) )
{
fprintf ( fd , " Content-type: image/jpg \n \n " ) ;
while ( n_bytes = fread ( buffer , 1 , sizeof ( buffer ) , fdj ) )
{
fwrite ( buffer , 1 , n_bytes , fd ) ;
}
fprintf ( fd , " \n --ZoneMinderFrame \n " ) ;
fflush ( fd ) ;
fclose ( fdj ) ;
}
else
{
Error ( ( " Can't open %s: %s " , filepath , strerror ( errno ) ) ) ;
}
usleep ( refresh * 1000 ) ;
}
if ( mysql_errno ( & dbconn ) )
{
Error ( ( " Can't fetch row: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
// Yadda yadda
mysql_free_result ( result ) ;
}
2002-10-31 01:02:06 +08:00
Monitor : : Monitor ( int p_id , char * p_name , int p_function , int p_device , int p_channel , int p_format , int p_width , int p_height , int p_colours , bool p_capture , char * p_label_format , const Coord & p_label_coord , int p_image_buffer_count , int p_warmup_count , int p_pre_event_count , int p_post_event_count , int p_alarm_frame_count , int p_fps_report_interval , int p_ref_blend_perc , int p_n_zones , Zone * p_zones [ ] ) : Camera ( p_id , p_name , p_device , p_channel , p_format , p_width , p_height , p_colours , p_capture ) , function ( ( Function ) p_function ) , image ( p_width , p_height , p_colours ) , ref_image ( p_width , p_height , p_colours ) , label_coord ( p_label_coord ) , image_buffer_count ( p_image_buffer_count ) , warmup_count ( p_warmup_count ) , pre_event_count ( p_pre_event_count ) , post_event_count ( p_post_event_count ) , alarm_frame_count ( p_alarm_frame_count ) , fps_report_interval ( p_fps_report_interval ) , ref_blend_perc ( p_ref_blend_perc ) , n_zones ( p_n_zones ) , zones ( p_zones )
2002-09-16 17:19:24 +08:00
{
2002-10-30 22:00:42 +08:00
strcpy ( label_format , p_label_format ) ;
2002-09-16 17:19:24 +08:00
fps = 0.0 ;
event_count = 0 ;
image_count = 0 ;
first_alarm_count = 0 ;
last_alarm_count = 0 ;
state = IDLE ;
2002-10-30 22:00:42 +08:00
int shared_images_size = sizeof ( SharedImages ) + ( image_buffer_count * sizeof ( time_t ) ) + ( image_buffer_count * colours * width * height ) ;
2002-10-31 01:02:06 +08:00
shmid = shmget ( ZM_SHM_KEY | id , shared_images_size , IPC_CREAT | 0777 ) ;
2002-09-16 17:19:24 +08:00
if ( shmid < 0 )
{
Error ( ( " Can't shmget: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
unsigned char * shm_ptr = ( unsigned char * ) shmat ( shmid , 0 , 0 ) ;
shared_images = ( SharedImages * ) shm_ptr ;
if ( shared_images < 0 )
{
Error ( ( " Can't shmat: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
if ( capture )
{
memset ( shared_images , 0 , shared_images_size ) ;
shared_images - > state = IDLE ;
2002-10-30 22:00:42 +08:00
shared_images - > last_write_index = image_buffer_count ;
shared_images - > last_read_index = image_buffer_count ;
2002-11-28 00:23:04 +08:00
shared_images - > last_event = 0 ;
2002-12-11 07:29:22 +08:00
shared_images - > forced_alarm = false ;
2002-09-16 17:19:24 +08:00
}
shared_images - > timestamps = ( time_t * ) ( shm_ptr + sizeof ( SharedImages ) ) ;
2002-10-30 22:00:42 +08:00
shared_images - > images = ( unsigned char * ) ( shm_ptr + sizeof ( SharedImages ) + ( image_buffer_count * sizeof ( time_t ) ) ) ;
2002-09-16 17:19:24 +08:00
2002-10-30 22:00:42 +08:00
image_buffer = new Snapshot [ image_buffer_count ] ;
for ( int i = 0 ; i < image_buffer_count ; i + + )
2002-09-16 17:19:24 +08:00
{
image_buffer [ i ] . timestamp = & ( shared_images - > timestamps [ i ] ) ;
image_buffer [ i ] . image = new Image ( width , height , colours , & ( shared_images - > images [ i * colours * width * height ] ) ) ;
//Info(( "%d: %x - %x", i, image_buffer[i].image, image_buffer[i].image->buffer ));
//*(image_buffer[i].timestamp) = time( 0 );
//image_buffer[i].image = new Image( width, height, colours );
//delete[] image_buffer[i].image->buffer;
//image_buffer[i].image->buffer = &(shared_images->images[i*colours*width*height]);
}
if ( ! n_zones )
{
n_zones = 1 ;
zones = new Zone * [ 1 ] ;
2002-11-07 20:25:48 +08:00
zones [ 0 ] = new Zone ( this , 0 , " All " , Zone : : ACTIVE , Box ( width , height ) , RGB_RED ) ;
2002-09-16 17:19:24 +08:00
}
start_time = last_fps_time = time ( 0 ) ;
event = 0 ;
Info ( ( " Monitor %s has function %d \n " , name , function ) ) ;
2002-10-31 01:02:06 +08:00
Info ( ( " Monitor %s LBF = '%s', LBX = %d, LBY = %d \n " , name , label_format , label_coord . X ( ) , label_coord . Y ( ) ) ) ;
Info ( ( " Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, FRI = %d, RBP = %d \n " , name , image_buffer_count , warmup_count , pre_event_count , post_event_count , fps_report_interval , ref_blend_perc ) ) ;
2002-09-16 17:19:24 +08:00
if ( ! capture )
{
ref_image . Assign ( width , height , colours , image_buffer [ shared_images - > last_write_index ] . image - > buffer ) ;
2002-09-21 19:14:12 +08:00
}
else
{
static char path [ PATH_MAX ] ;
2002-09-21 03:48:48 +08:00
2003-01-13 19:50:47 +08:00
sprintf ( path , ZM_DIR_EVENTS ) ;
2002-09-21 03:48:48 +08:00
struct stat statbuf ;
errno = 0 ;
stat ( path , & statbuf ) ;
if ( errno = = ENOENT | | errno = = ENOTDIR )
{
if ( mkdir ( path , 0755 ) )
{
Error ( ( " Can't make %s: %s \n " , path , strerror ( errno ) ) ) ;
}
}
2003-01-13 19:50:47 +08:00
sprintf ( path , ZM_DIR_EVENTS " /%s " , name ) ;
2002-09-21 03:48:48 +08:00
errno = 0 ;
stat ( path , & statbuf ) ;
if ( errno = = ENOENT | | errno = = ENOTDIR )
{
if ( mkdir ( path , 0755 ) )
{
Error ( ( " Can't make %s: %s \n " , path , strerror ( errno ) ) ) ;
}
}
2002-09-16 17:19:24 +08:00
}
2002-12-12 06:41:40 +08:00
record_event_stats = ZM_RECORD_EVENT_STATS ;
2002-09-16 17:19:24 +08:00
}
Monitor : : ~ Monitor ( )
{
delete [ ] image_buffer ;
2002-10-31 01:02:06 +08:00
2002-10-31 01:11:43 +08:00
struct shmid_ds shm_data ;
if ( shmctl ( shmid , IPC_STAT , & shm_data ) )
{
Error ( ( " Can't shmctl: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
if ( shm_data . shm_nattch < = 1 )
2002-10-31 01:02:06 +08:00
{
if ( shmctl ( shmid , IPC_RMID , 0 ) )
{
Error ( ( " Can't shmctl: %s \n " , strerror ( errno ) ) ) ;
exit ( - 1 ) ;
}
}
2002-09-16 17:19:24 +08:00
}
2002-11-07 20:25:48 +08:00
void Monitor : : AddZones ( int p_n_zones , Zone * p_zones [ ] )
{
n_zones = p_n_zones ;
zones = p_zones ;
}
2002-09-16 17:19:24 +08:00
Monitor : : State Monitor : : GetState ( ) const
{
return ( shared_images - > state ) ;
}
int Monitor : : GetImage ( int index ) const
{
2002-10-30 22:00:42 +08:00
if ( index < 0 | | index > image_buffer_count )
2002-09-16 17:19:24 +08:00
{
index = shared_images - > last_write_index ;
}
Snapshot * snap = & image_buffer [ index ] ;
Image * image = snap - > image ;
2002-11-01 06:17:23 +08:00
char filename [ 64 ] ;
sprintf ( filename , " %s.jpg " , name ) ;
image - > WriteJpeg ( filename ) ;
2002-09-16 17:19:24 +08:00
return ( 0 ) ;
}
time_t Monitor : : GetTimestamp ( int index ) const
{
2002-10-30 22:00:42 +08:00
if ( index < 0 | | index > image_buffer_count )
2002-09-16 17:19:24 +08:00
{
index = shared_images - > last_write_index ;
}
Snapshot * snap = & image_buffer [ index ] ;
return ( * ( snap - > timestamp ) ) ;
}
unsigned int Monitor : : GetLastReadIndex ( ) const
{
return ( shared_images - > last_read_index ) ;
}
unsigned int Monitor : : GetLastWriteIndex ( ) const
{
return ( shared_images - > last_write_index ) ;
}
2002-11-28 00:23:04 +08:00
unsigned int Monitor : : GetLastEvent ( ) const
{
return ( shared_images - > last_event ) ;
}
2002-09-16 17:19:24 +08:00
double Monitor : : GetFPS ( ) const
{
int index1 = shared_images - > last_write_index ;
2003-01-07 18:44:25 +08:00
int index2 = ( index1 + 1 ) % image_buffer_count ;
2002-09-16 17:19:24 +08:00
2003-01-07 18:44:25 +08:00
//Snapshot *snap1 = &image_buffer[index1];
//time_t time1 = *(snap1->timestamp);
time_t time1 = time ( 0 ) ;
2002-09-16 17:19:24 +08:00
Snapshot * snap2 = & image_buffer [ index2 ] ;
time_t time2 = * ( snap2 - > timestamp ) ;
2002-10-30 22:00:42 +08:00
double fps = double ( image_buffer_count ) / ( time1 - time2 ) ;
2002-09-16 17:19:24 +08:00
return ( fps ) ;
}
2002-12-11 07:29:22 +08:00
void Monitor : : ForceAlarm ( )
{
shared_images - > forced_alarm = true ;
}
void Monitor : : CancelAlarm ( )
{
shared_images - > forced_alarm = false ;
}
2002-09-16 17:19:24 +08:00
void Monitor : : DumpZoneImage ( )
{
int index = shared_images - > last_write_index ;
Snapshot * snap = & image_buffer [ index ] ;
Image * image = snap - > image ;
Image zone_image ( * image ) ;
zone_image . Colourise ( ) ;
for ( int i = 0 ; i < n_zones ; i + + )
{
unsigned char * psrc = zone_image . buffer ;
int lo_x = zones [ i ] - > Limits ( ) . Lo ( ) . X ( ) ;
int lo_y = zones [ i ] - > Limits ( ) . Lo ( ) . Y ( ) ;
int hi_x = zones [ i ] - > Limits ( ) . Hi ( ) . X ( ) ;
int hi_y = zones [ i ] - > Limits ( ) . Hi ( ) . Y ( ) ;
for ( int y = 0 ; y < zone_image . height ; y + + )
{
for ( int x = 0 ; x < zone_image . width ; x + + , psrc + = 3 )
{
if ( ( ( x = = lo_x | | x = = hi_x ) & & ( y > = lo_y & & y < = hi_y ) )
| | ( ( y = = lo_y | | y = = hi_y ) & & ( x > = lo_x & & x < = hi_x ) )
| | ( ( x > lo_x & & x < hi_x & & y > lo_y & & y < hi_y ) & & ! ( x % 2 ) & & ! ( y % 2 ) ) )
{
if ( zones [ i ] - > Type ( ) = = Zone : : ACTIVE )
{
RED ( psrc ) = RGB_RED_VAL ( RGB_RED ) ;
GREEN ( psrc ) = RGB_GREEN_VAL ( RGB_RED ) ;
BLUE ( psrc ) = RGB_BLUE_VAL ( RGB_RED ) ;
}
else if ( zones [ i ] - > Type ( ) = = Zone : : INCLUSIVE )
{
RED ( psrc ) = RGB_RED_VAL ( RGB_GREEN ) ;
GREEN ( psrc ) = RGB_GREEN_VAL ( RGB_GREEN ) ;
BLUE ( psrc ) = RGB_BLUE_VAL ( RGB_GREEN ) ;
}
else if ( zones [ i ] - > Type ( ) = = Zone : : EXCLUSIVE )
{
RED ( psrc ) = RGB_RED_VAL ( RGB_BLUE ) ;
GREEN ( psrc ) = RGB_GREEN_VAL ( RGB_BLUE ) ;
BLUE ( psrc ) = RGB_BLUE_VAL ( RGB_BLUE ) ;
}
else
{
RED ( psrc ) = RGB_RED_VAL ( RGB_WHITE ) ;
GREEN ( psrc ) = RGB_GREEN_VAL ( RGB_WHITE ) ;
BLUE ( psrc ) = RGB_BLUE_VAL ( RGB_WHITE ) ;
}
}
}
}
}
char filename [ 64 ] ;
sprintf ( filename , " %s-Zones.jpg " , name ) ;
zone_image . WriteJpeg ( filename ) ;
}
void Monitor : : DumpImage ( Image * image ) const
{
if ( image_count & & ! ( image_count % 10 ) )
{
static char new_filename [ 64 ] ;
static char filename [ 64 ] ;
//sprintf( filename, "%s%04d.jpg", name, image_count );
sprintf ( filename , " %s.jpg " , name ) ;
sprintf ( new_filename , " %s-new.jpg " , name ) ;
image - > WriteJpeg ( new_filename ) ;
rename ( new_filename , filename ) ;
}
}
2002-09-23 18:13:51 +08:00
bool Monitor : : Analyse ( )
2002-09-16 17:19:24 +08:00
{
if ( shared_images - > last_read_index = = shared_images - > last_write_index )
{
2002-09-23 18:13:51 +08:00
return ( false ) ;
2002-09-16 17:19:24 +08:00
}
time_t now = time ( 0 ) ;
2002-10-30 22:00:42 +08:00
if ( image_count & & ! ( image_count % fps_report_interval ) )
2002-09-16 17:19:24 +08:00
{
2002-10-30 22:00:42 +08:00
fps = double ( fps_report_interval ) / ( now - last_fps_time ) ;
2002-09-16 17:19:24 +08:00
Info ( ( " %s: %d - Processing at %.2f fps \n " , name , image_count , fps ) ) ;
last_fps_time = now ;
}
2002-10-30 22:00:42 +08:00
int index = shared_images - > last_write_index % image_buffer_count ;
2002-09-16 17:19:24 +08:00
Snapshot * snap = & image_buffer [ index ] ;
time_t timestamp = * ( snap - > timestamp ) ;
Image * image = snap - > image ;
unsigned int score = 0 ;
if ( Ready ( ) )
{
2002-12-11 07:29:22 +08:00
score = ref_image . Compare ( * image , n_zones , zones ) ;
if ( shared_images - > forced_alarm )
score = ZM_FORCED_ALARM_SCORE ;
if ( score )
2002-09-16 17:19:24 +08:00
{
if ( state = = IDLE )
{
event = new Event ( this , timestamp ) ;
Info ( ( " %s: %03d - Gone into alarm state \n " , name , image_count ) ) ;
2002-10-30 22:00:42 +08:00
int pre_index = ( ( index + image_buffer_count ) - pre_event_count ) % image_buffer_count ;
for ( int i = 0 ; i < pre_event_count ; i + + )
2002-09-16 17:19:24 +08:00
{
event - > AddFrame ( * ( image_buffer [ pre_index ] . timestamp ) , image_buffer [ pre_index ] . image ) ;
2002-10-30 22:00:42 +08:00
pre_index = ( pre_index + 1 ) % image_buffer_count ;
2002-09-16 17:19:24 +08:00
}
//event->AddFrame( now, &image );
}
shared_images - > state = state = ALARM ;
last_alarm_count = image_count ;
}
else
{
if ( state = = ALARM )
{
shared_images - > state = state = ALERT ;
}
else if ( state = = ALERT )
{
2002-10-30 22:00:42 +08:00
if ( image_count - last_alarm_count > post_event_count )
2002-09-16 17:19:24 +08:00
{
Info ( ( " %s: %03d - Left alarm state (%d) - %d(%d) images \n " , name , image_count , event - > id , event - > frames , event - > alarm_frames ) ) ;
2002-11-28 00:23:04 +08:00
shared_images - > last_event = event - > id ;
2002-09-16 17:19:24 +08:00
delete event ;
shared_images - > state = state = IDLE ;
}
}
}
if ( state ! = IDLE )
{
if ( state = = ALARM )
{
Image alarm_image ( * image ) ;
for ( int i = 0 ; i < n_zones ; i + + )
{
if ( zones [ i ] - > Alarmed ( ) )
{
alarm_image . Overlay ( zones [ i ] - > AlarmImage ( ) ) ;
2002-12-12 06:41:40 +08:00
if ( record_event_stats )
2002-11-07 20:25:48 +08:00
{
zones [ i ] - > RecordStats ( event ) ;
}
2002-09-16 17:19:24 +08:00
}
}
event - > AddFrame ( now , image , & alarm_image , score ) ;
}
else
{
event - > AddFrame ( now , image ) ;
}
}
}
2002-10-30 22:00:42 +08:00
ref_image . Blend ( * image , ref_blend_perc ) ;
2003-01-03 23:07:55 +08:00
//DumpImage( image );
2002-09-16 17:19:24 +08:00
2002-10-30 22:00:42 +08:00
shared_images - > last_read_index = index % image_buffer_count ;
2002-09-16 17:19:24 +08:00
image_count + + ;
2002-09-23 18:13:51 +08:00
return ( true ) ;
2002-09-16 17:19:24 +08:00
}
void Monitor : : ReloadZones ( )
{
Info ( ( " Reloading zones for monitor %s \n " , name ) ) ;
for ( int i = 0 ; i < n_zones ; i + + )
{
delete zones [ i ] ;
}
//delete[] zones;
2002-11-07 20:25:48 +08:00
n_zones = Zone : : Load ( this , zones ) ;
2002-09-16 17:19:24 +08:00
DumpZoneImage ( ) ;
}
int Monitor : : Load ( int device , Monitor * * & monitors , bool capture )
{
static char sql [ 256 ] ;
if ( device = = - 1 )
{
2002-10-30 22:00:42 +08:00
strcpy ( sql , " select Id, Name, Function+0, Device, Channel, Format, Width, Height, Colours, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' " ) ;
2002-09-16 17:19:24 +08:00
}
else
{
2002-10-30 22:00:42 +08:00
sprintf ( sql , " select Id, Name, Function+0, Device, Channel, Format, Width, Height, Colours, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, FPSReportInterval, RefBlendPerc from Monitors where Function != 'None' and Device = %d " , device ) ;
2002-09-16 17:19:24 +08:00
}
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't run query: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
if ( ! result )
{
Error ( ( " Can't use query result: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
int n_monitors = mysql_num_rows ( result ) ;
Info ( ( " Got %d monitors \n " , n_monitors ) ) ;
delete [ ] monitors ;
monitors = new Monitor * [ n_monitors ] ;
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + )
{
2003-02-01 01:13:35 +08:00
monitors [ i ] = new Monitor ( atoi ( dbrow [ 0 ] ) , dbrow [ 1 ] , atoi ( dbrow [ 2 ] ) , atoi ( dbrow [ 3 ] ) , atoi ( dbrow [ 4 ] ) , atoi ( dbrow [ 5 ] ) , atoi ( dbrow [ 6 ] ) , atoi ( dbrow [ 7 ] ) , atoi ( dbrow [ 8 ] ) / 8 , capture , dbrow [ 9 ] , Coord ( atoi ( dbrow [ 10 ] ) , atoi ( dbrow [ 11 ] ) ) , atoi ( dbrow [ 12 ] ) , atoi ( dbrow [ 13 ] ) , atoi ( dbrow [ 14 ] ) , atoi ( dbrow [ 15 ] ) , atoi ( dbrow [ 16 ] ) , atoi ( dbrow [ 17 ] ) , atoi ( dbrow [ 18 ] ) ) ;
2002-09-16 17:19:24 +08:00
Zone * * zones = 0 ;
2002-11-07 20:25:48 +08:00
int n_zones = Zone : : Load ( monitors [ i ] , zones ) ;
monitors [ i ] - > AddZones ( n_zones , zones ) ;
2002-09-16 17:19:24 +08:00
Info ( ( " Loaded monitor %d(%s), %d zones \n " , atoi ( dbrow [ 0 ] ) , dbrow [ 1 ] , n_zones ) ) ;
}
if ( mysql_errno ( & dbconn ) )
{
Error ( ( " Can't fetch row: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
// Yadda yadda
mysql_free_result ( result ) ;
return ( n_monitors ) ;
}
Monitor * Monitor : : Load ( int id , bool load_zones )
{
static char sql [ 256 ] ;
2002-10-30 22:00:42 +08:00
sprintf ( sql , " select Id, Name, Function+0, Device, Channel, Format, Width, Height, Colours, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, FPSReportInterval, RefBlendPerc from Monitors where Id = %d " , id ) ;
2002-09-16 17:19:24 +08:00
if ( mysql_query ( & dbconn , sql ) )
{
Error ( ( " Can't run query: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
MYSQL_RES * result = mysql_store_result ( & dbconn ) ;
if ( ! result )
{
Error ( ( " Can't use query result: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
int n_monitors = mysql_num_rows ( result ) ;
Info ( ( " Got %d monitors \n " , n_monitors ) ) ;
Monitor * monitor = 0 ;
for ( int i = 0 ; MYSQL_ROW dbrow = mysql_fetch_row ( result ) ; i + + )
{
2003-02-01 01:13:35 +08:00
monitor = new Monitor ( atoi ( dbrow [ 0 ] ) , dbrow [ 1 ] , atoi ( dbrow [ 2 ] ) , atoi ( dbrow [ 3 ] ) , atoi ( dbrow [ 4 ] ) , atoi ( dbrow [ 5 ] ) , atoi ( dbrow [ 6 ] ) , atoi ( dbrow [ 7 ] ) , atoi ( dbrow [ 8 ] ) / 8 , false , dbrow [ 9 ] , Coord ( atoi ( dbrow [ 10 ] ) , atoi ( dbrow [ 11 ] ) ) , atoi ( dbrow [ 12 ] ) , atoi ( dbrow [ 13 ] ) , atoi ( dbrow [ 14 ] ) , atoi ( dbrow [ 15 ] ) , atoi ( dbrow [ 16 ] ) , atoi ( dbrow [ 17 ] ) , atoi ( dbrow [ 18 ] ) ) ;
2002-09-16 17:19:24 +08:00
int n_zones = 0 ;
if ( load_zones )
{
2002-11-07 20:25:48 +08:00
Zone * * zones = 0 ;
n_zones = Zone : : Load ( monitor , zones ) ;
monitor - > AddZones ( n_zones , zones ) ;
2002-09-16 17:19:24 +08:00
}
Info ( ( " Loaded monitor %d(%s), %d zones \n " , atoi ( dbrow [ 0 ] ) , dbrow [ 1 ] , n_zones ) ) ;
}
if ( mysql_errno ( & dbconn ) )
{
Error ( ( " Can't fetch row: %s \n " , mysql_error ( & dbconn ) ) ) ;
exit ( mysql_errno ( & dbconn ) ) ;
}
// Yadda yadda
mysql_free_result ( result ) ;
return ( monitor ) ;
}
void Monitor : : StreamImages ( unsigned long idle , unsigned long refresh , FILE * fd )
{
fprintf ( fd , " Server: ZoneMinder Stream Server \r \n " ) ;
fprintf ( fd , " Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame \r \n " ) ;
fprintf ( fd , " \r \n " ) ;
fprintf ( fd , " --ZoneMinderFrame \n " ) ;
2002-10-30 22:00:42 +08:00
int last_read_index = image_buffer_count ;
2002-09-16 17:19:24 +08:00
JOCTET img_buffer [ width * height * colours ] ;
int img_buffer_size = 0 ;
int loop_count = ( idle / refresh ) - 1 ;
while ( true )
{
if ( last_read_index ! = shared_images - > last_write_index )
{
// Send the next frame
last_read_index = shared_images - > last_write_index ;
2002-10-30 22:00:42 +08:00
int index = shared_images - > last_write_index % image_buffer_count ;
2002-09-16 17:19:24 +08:00
//Info(( "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ));
Snapshot * snap = & image_buffer [ index ] ;
Image * image = snap - > image ;
image - > EncodeJpeg ( img_buffer , & img_buffer_size ) ;
fprintf ( fd , " Content-type: image/jpg \n \n " ) ;
fwrite ( img_buffer , 1 , img_buffer_size , fd ) ;
fprintf ( fd , " \n --ZoneMinderFrame \n " ) ;
fflush ( fd ) ;
}
usleep ( refresh * 1000 ) ;
for ( int i = 0 ; shared_images - > state = = IDLE & & i < loop_count ; i + + )
{
usleep ( refresh * 1000 ) ;
}
}
}