📄 callbacks.c
字号:
for ( ; p1 < &ds1_buf[0]+sizeof(ds1_buf);p1++,p0++ )
{
*p1 = *p0 + correction;
// fix up overflows
if ( *p1 < *p0 ) *p1 = 255;
}
}
else if(avg_ds_intensity == avg_ref_intensity)
{
// make exact copy
// g_print("no image correction required\n");
memcpy(p1,p0,sizeof(ds1_buf));
}
else if(avg_ds_intensity > avg_ref_intensity)
{
correction = avg_ds_intensity - avg_ref_intensity;
// g_print("making image darker by %d\n",correction);
// make ds1 image darker to match ref image
for (; p1 < &ds1_buf[0]+sizeof(ds1_buf); p1++,p0++ )
{
*p1 = *p0 - correction;
// fix up underflows
if ( *p1 > *p0 ) *p1 = 0;
}
}
// for debug, its nice to know the avg intensity of the corrected buffer
// it should be close to the ref buffer unless clipping occured due to
// saturation at 0 or 255 limits
for ( p1 = &ds1_buf[0]; p1 < &ds1_buf[0]+sizeof(ds1_buf); p1++ )
{
avg += *p1;
}
avg /= sizeof(ds1_buf);
avg_ds1_intensity = avg;
}
//=====================================================================
// This procedure adds some of the new image in with lots of the old
// image. This gives us a long time constant average reference picture.
// This allows the system to adapt to new items in the image over time
// but still alarm again if they move in the picture. Also, changing
// lighting conditions are less of a problem.
//=====================================================================
void update_reference_image(void)
{
GtkWidget *wid;
gint x,y;
guchar *pdest;
guchar *psrc;
float old;
unsigned int avg = 0;
guchar *p1;
static int first = 1;
if ( first == 1 )
{
// on first pass, the first picture becomes the reference picture
memcpy(ref_buf,ds_buf,sizeof(ref_buf));
first = 0;
}
else
{
// now update reference buffer by using some of old image and some of new
psrc = &ds_buf[0];
pdest = &ref_buf[0];
old = 1.0 - ref_update_fraction;
for (y=0; y<240; y++)
{
for (x=0; x<320; x++)
{
// give the fpu something to do...
*pdest++ = old * *pdest + ref_update_fraction * *psrc++ + 0.5; // red
*pdest++ = old * *pdest + ref_update_fraction * *psrc++ + 0.5; // green
*pdest++ = old * *pdest + ref_update_fraction * *psrc++ + 0.5; // blue
}
}
}
// compute new avg intensity for the reference buffer
for ( p1 = &ref_buf[0]; p1 < &ref_buf[0]+sizeof(ref_buf); p1++ )
{
avg += *p1;
}
avg /= sizeof(ref_buf);
// g_print(_("reference image avg intensity = %u\n"),avg);
avg_ref_intensity = (guchar)avg;
// let the widget know that it needs to be updated
wid = GTK_WIDGET(lookup_widget(window1,"drawingarea2"));
gtk_widget_queue_draw(wid);
}
//=====================================================================
// call with a number 0..100 for graph update
//=====================================================================
void update_history_graph(int percent, int alarm_level)
{
GtkWidget *wid;
static int first = 1;
int y;
guchar *ptr;
static int call_cnt = 0; // used for threshold line on graph
call_cnt++;
// if first time through, clear graph to black
if (first)
{
memset(&grf_buf[0],0,sizeof(grf_buf));
first = 0;
}
for (y=0; y<50; y++)
{
// move each row of pixels over in memory (scroll left)
memmove( grf_buf+y*320*3,grf_buf+y*320*3+3,319*3);
// init last column to black
ptr = grf_buf + y*320*3 + 319*3;
*ptr++ = 0; //red
*ptr++ = 0; //grn
*ptr++ = 0; //blu
}
// turn on 1 pixel in last column
// limit range so pixel always gets displayed 0...99
if (percent < 0)
percent = 0;
if (percent > 99)
percent = 99;
// invert graph so that 0 is at bottom, scale range to fit 50 pixel image
ptr = grf_buf + ((99-percent)/2)*320*3 + 319*3;
while ( ptr < grf_buf + sizeof(grf_buf))
{
if ( percent > alarm_level )
{
*ptr = 255;
*(ptr+1) = 32;
*(ptr+2) = 32;
}
else
{
*ptr = 32;
*(ptr+1) = 255;
*(ptr+2) = 32;
}
ptr += 320 * 3; // bump down 1 line
}
// time to put a reference dot on the plot showing alarm threshold
if ( call_cnt %4 == 0)
{
if ( alarm_level < 0)
alarm_level = 0;
if (alarm_level > 99)
alarm_level = 99;
ptr = grf_buf + ((99-alarm_level)/2)*320*3 + 319*3;
*ptr++ = 32;
*ptr++ = 255;
*ptr = 255;
}
// let the widget know that it needs to be updated
wid = GTK_WIDGET(lookup_widget(window1,"drawingarea4"));
gtk_widget_queue_draw(wid);
}
//=====================================================================
// routine to subtract downsized image from the reference image
// this will give us an image with only the changes showing
// its not quite a simple subtraction, we look at each color and
// look for color changes as well. Significantly changed pixels are
// set to bright white. Result is a b/w image with additional
// blob filtering to look for areas of change (marked as red pixels).
// Each blob is a pixel surrounded by 8 neighbors
//=====================================================================
float do_motion_analysis(void)
{
GtkWidget *wid;
gint x,y;
guchar *pdest;
guchar *psrc;
guchar *pref;
guchar red;
guchar green;
guchar blue;
guchar cresult;
float fresult = 0.0f;
int pixcnt;
guchar threshold;
int blob_cnt;
int i;
time_t t;
struct tm *tm;
char line[128];
static int image_cnt = 0; // used to force saves on periodic basis
guint context;
char sbuf[256];
static int first = 1;
char *cp;
gchar **toks;
gchar *new_cmd;
// on the first pass, we dont have a ref image stored yet so
// we cannot do a difference that is meaningful.
if ( first == 1 )
{
first = 0;
return 0.0;
}
alarm_zone.x1 = 640; // init bounding box of alarm blobs
alarm_zone.x2 = -1;
alarm_zone.y1 = 480;
alarm_zone.y2 = -1;
// g_print(_("new image is %s, %u %u %u\n"),
// ( avg_ds_intensity <= avg_ref_intensity ) ? _("darker or ==") : _("lighter"),
// avg_ds_intensity, avg_ds1_intensity, avg_ref_intensity);
pref = &ref_buf[0];
psrc = &ds1_buf[0]; // intensity corrected buffer
pdest = &res_buf[0];
for (y=0; y<240; y++)
{
for (x=0; x<320; x++)
{
// give the cpu something to do...
// compute rms diff between all pixels
// detection of color or intensity changes detected
red = (*pref > *psrc) ? *pref- *psrc : *psrc- *pref; // red
pref++; psrc++;
green = (*pref > *psrc) ? *pref- *psrc : *psrc- *pref;; // green
pref++; psrc++;
blue = (*pref > *psrc) ? *pref- *psrc : *psrc- *pref;; // blue
pref++; psrc++;
cresult = sqrt((red*red + green*green + blue*blue)/3);
*pdest++ = cresult; // make b/w image
*pdest++ = cresult;
*pdest++ = cresult;
fresult += (float)cresult;
}
}
// calculate normalized difference over picture
fresult = fresult/(float)(320*240);
// detect and mark pixels that are significantly different
threshold = (guchar)sig_pix_threshold;
pixcnt = 0;
psrc = &res_buf[0];
while ( psrc < res_buf+sizeof(res_buf))
{
if ( *psrc > threshold )
{
pixcnt++;
*psrc = 255; // alarm active areas in white
*(psrc+1) = 255;
*(psrc+2) = 255;
}
psrc += 3; // only need to look at reds of each pixel
// because g and b are set to the same thing
}
// to eliminate small changes (wind blown objects) from causing so much
// havoc, we will try to eliminate false alarms by scanning for blobs
// A blob is a 3x3 pixel area, all must be 'hot' (white)
blob_cnt = 0;
psrc = res_buf + 320 * 3;
while ( psrc < (res_buf+sizeof(res_buf)-320*3))
{
for (i=0;i<1;i++)
{
if (*psrc < 255) break; // central pixel must be lite
if (*(psrc-320*3) < 255) break; // pixel above must be lite
if (*(psrc+320*3) < 255) break; // pixel below must be lite
if (*(psrc-3) < 255) break; // pixel to left must be lite
if (*(psrc+3) < 255) break; // pixel to right must be light
if (*(psrc-320*3+3) < 255) break; // pixel to NE must be light
if (*(psrc-320*3-3) < 255) break; // pixel to NW must be light
if (*(psrc+320*3+3) < 255) break; // pixel to SE must be light
if (*(psrc+320*3-3) < 255) break; // pixel to SW must be light
// mark blob as blob on screen
*(psrc+2) = 0; // shut off blue
*(psrc+1) = 0; // shut off green
blob_cnt++;
x = ((psrc - res_buf) % (320 * 3))/3;
y = (psrc - res_buf) / (320 * 3);
if ( x < alarm_zone.x1 ) // set first occurance location
alarm_zone.x1 = x;
if ( alarm_zone.x2 < x ) // keep highest occurance
alarm_zone.x2 = x;
if ( y < alarm_zone.y1 ) // set first occurance location
alarm_zone.y1 = y;
if ( alarm_zone.y2 < y ) // keep highest occurance
alarm_zone.y2 = y;
}
psrc += 3;
}
// update status bar with some of our calcs
wid = lookup_widget(window1,"statusbar1");
context = gtk_statusbar_get_context_id(GTK_STATUSBAR(wid),"do_motion_analysis()");
sprintf(sbuf,_("Image%d : alarmpix = %u alarm pixels = %d, alarm blobs = %d rmsdiff=%.1f"),
image_cnt,alarm_pix,pixcnt,blob_cnt, fresult);
gtk_statusbar_pop(GTK_STATUSBAR(wid),context);
gtk_statusbar_push(GTK_STATUSBAR(wid),context,sbuf);
// let the widget know that it needs to be updated
wid = GTK_WIDGET(lookup_widget(window1,"drawingarea3"));
gtk_widget_queue_draw(wid);
// label main view with time/date location stamp (display and filesave)
if (blob_cnt*graph_scale > alarm_threshold)
{
alarm_pix++;
if ( show_target_box )
mark_alarm_zone(&alarm_zone); // box in alarm window
add_rgb_text(rgb_buf,640,480, 1); // red text lable on pix
if (beep_on_alarm) // wakeup the operator
gdk_beep();
}
else
add_rgb_text(rgb_buf,640,480, 0); // white text label on pix
image_cnt++; // save image on motion or every xxx pictures
if (( blob_cnt*graph_scale > alarm_threshold ) ||
((image_cnt % periodic_save_interval) == 0))
{
time(&t);
tm = localtime(&t);
strftime(line,sizeof(line)-1,"GSPY%H%M%S.jpg",tm);
put_image_jpeg(line, rgb_buf, 640, 480, 80);
}
// on each alarm, execute a user command
if (( blob_cnt*graph_scale > alarm_threshold ) &&
( strlen(alarm_command) > 1 ) )
{
if ((cp = strstr(alarm_command,"%f%")) == NULL)
// no filename substitution required in user provided string
system(alarm_command);
else
{
// user put filename(s) in command line using %f%, then we need
// to insert the current filename into the user command string
toks = g_strsplit(alarm_command,"%f%",0);
new_cmd = g_strjoinv(full_name,toks);
system(new_cmd);
free(new_cmd);
g_strfreev(toks);
}
}
update_history_graph(blob_cnt*graph_scale,alarm_threshold);
return fresult;
}
//=====================================================================
//=====================================================================
gboolean on_drawingarea1_expose_event (GtkWidget *widget,
GdkEventExpose *event,
gpointer user_data)
{
gdk_draw_rgb_image(widget->window,widget->style->fg_gc[GTK_STATE_NORMAL],
0,0,640,480,GDK_RGB_DITHER_NORMAL,rgb_buf,3*640);
return FALSE;
}
//=====================================================================
//=====================================================================
gboolean on_drawingarea2_expose_event (GtkWidget *widget,
GdkEventExpose *event,
gpointer user_data)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -