📄 main.c
字号:
// main.c// This is a quick and dirty console version of gspy that does not use X or// gnome libraries. Its primary use is on servers that may have no user// interface. The only non standard library you may need to install is libjpeg// see http://www.rpmfind.net/linux/rpm2html/search.php?query=libjpeg// There is one x86 assembler file used for converting image formats. This could be // eliminated if your webcam kernel driver provides RGB data format.// Build this file using cc -o cspy -ljpeg -lm main.c ccvt.S// the executable will be called cspy/* Copyright (C) 1998 Lawrence Glaister VE7IT <ve7ot@shaw.ca> 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*/#include <stdio.h>#include <stdlib.h>#include <sys/ioctl.h>#include <sys/mman.h> // for munmap declarations#include <linux/videodev.h>#include <time.h>#include <string.h>#include <jpeglib.h>#include <fcntl.h>#include <errno.h>#include <math.h>#define DEF_WIDTH 640#define DEF_HEIGHT 480#define IN_TV 0#define IN_COMPOSITE 1#define IN_COMPOSITE2 2#define IN_SVIDEO 3#define IN_DEFAULT 8#define NORM_PAL 0#define NORM_NTSC 1#define NORM_SECAM 2#define NORM_DEFAULT 0#define TRUE 1#define FALSE 0#define QUAL_DEFAULT 80 // jpeg quality factorstruct alarm_zone{ int x1; // 0..319 1st pixel with alarm blob int x2; // 0..319 last pixel with alarm blob int y1; // 0..239 1st pixel with alarm blob int y2; // 0..239 last pixel with alarm blob};static unsigned char rgb_buf[3*640*480]; // main 640x480 live display image bufferstatic unsigned char ds_buf[3*320*240]; // temp for downsized live imagestatic unsigned char ds1_buf[3*320*240]; // temp for downsized live image corrected for avg intensitystatic unsigned char ref_buf[3*320*240]; // temp for reference picture buffer (time averaged)static unsigned char res_buf[3*320*240]; // temp for motion analysis picture buffer (graphical results)struct video_picture v_pict; // used to store prefered webcam palettechar full_name[350]; // a hack!. full path/filename of last written picturechar image_directory[1024]; // where the jpg's get putchar image_label[] = {"Room 204 %Y-%m-%d %H:%M:%S"}; // used by strftime to date stamp imagechar video_device[] = {"/dev/video0"}; // opened to read images via v4lint picture_interval = 2; // seconds between picturesfloat ref_update_fraction = 0.2; // amount a new image contributes to // reference image each cycleint alarm_threshold = 70; // percent of graph for alarmfloat sig_pix_threshold = 25.0; // amount pixel must be different // (x*avg difference) int periodic_save_interval = 300; // force a save every xx snapshots//int beep_on_alarm = TRUE; // console beep on alarmint show_target_box = TRUE; // puts active area on pictureschar alarm_command[] = {"echo CSPY -- Motion Detected"}; // user command to execute on alarmsint dev; // handle for v4l video camera devicestruct alarm_zone alarm_zone;unsigned int avg_ref_intensity;unsigned int avg_ds_intensity;unsigned int avg_ds1_intensity;unsigned int alarm_pix = 0;int v4l_open(char *device);void v4l_close(int dev);char *get_image(int dev, int width, int height, int input,int norm, int fmt, int *size);/* ---------------------------------------------------------------------- * This routine extracted and modified from webcam project by Gerd Knorr * (c) 1998-2000 Gerd Knorr * Modified for colored text by L.P. Glaister July 2000*/ # define CHAR_HEIGHT 11# define CHAR_WIDTH 6# define CHAR_START 4# include "font_6x11.h"void add_rgb_text(char *image, int width, int height, int alrm){ time_t t; struct tm *tm; char line[128],*ptr; int i,x,y,f,len; time(&t); tm = localtime(&t); len = strftime(line,127,image_label,tm); for (y = 0; y < CHAR_HEIGHT; y++) { // locate text in lower left corner of image ptr = image + 3 * width * (height-CHAR_HEIGHT-2+y) + 12; // loop for each character in the string for (x = 0; x < len; x++) { // locate the character in the fontdata array f = fontdata[line[x] * CHAR_HEIGHT + y]; // loop for each column of font data for (i = CHAR_WIDTH-1; i >= 0; i--) { // write a black background under text // comment out the following block to get white letters on picture background ptr[0] = 0; ptr[1] = 0; ptr[2] = 0; if (f & (CHAR_START << i)) { if (alrm) { // red text ptr[0] = 255; ptr[1] = 64; ptr[2] = 64; } else { // white text ptr[0] = 255; ptr[1] = 255; ptr[2] = 255; } } ptr += 3; } } }}/* */void put_image_jpeg(char *filename, char *image, int width, int height, int quality){ int y, line_width; JSAMPROW row_ptr[1]; struct jpeg_compress_struct cjpeg; struct jpeg_error_mgr jerr; char *line; FILE *fp; time_t t; struct tm *tm; int len; char dir_name[350]; // create the directory name as a combination of the user selected prefix + year,month,day sprintf(full_name,"%s%%Y%%m%%d/",image_directory); time(&t); tm = localtime(&t); len = strftime(dir_name,sizeof(dir_name)-1,full_name,tm); // make the directory that user wants pictures stored in to insure it exists // you may want to make dir permission 0700, so other users cannot see pix if (-1 == mkdir(dir_name,0777)) { if (errno != EEXIST ) { perror("put_image_jpeg() Unable to create file save directory"); } } strncpy(full_name,dir_name,sizeof(full_name)-1); strncat(full_name,filename,sizeof(full_name)-strlen(full_name)-1); line = malloc (width * 3); if (!line) return; if ((fp = fopen(full_name, "wb")) == NULL) { fprintf(stderr, "can't open %s for binary write", full_name); perror(" and the reason is"); return; } // fprintf(stderr, " writing image to %s\n", full_name); cjpeg.err = jpeg_std_error(&jerr); // errors get written to stderr jpeg_create_compress (&cjpeg); cjpeg.image_width = width; cjpeg.image_height= height; cjpeg.input_components = 3; cjpeg.in_color_space = JCS_RGB; jpeg_set_defaults (&cjpeg); jpeg_set_quality (&cjpeg, quality, TRUE); cjpeg.dct_method = JDCT_FASTEST;// jpeg_stdio_dest (&cjpeg, stdout); jpeg_stdio_dest (&cjpeg, fp); // data written to file jpeg_start_compress (&cjpeg, TRUE); row_ptr[0] = line; line_width = width * 3; for ( y = 0; y < height; y++) { memcpy(line,image,line_width); jpeg_write_scanlines (&cjpeg, row_ptr, 1); image += line_width; } jpeg_finish_compress (&cjpeg); jpeg_destroy_compress (&cjpeg); free (line); if (fp) { fflush(fp); fclose(fp); }}//=====================================================================// given a box containing the alarm window, modify pixels in main// display window to show box. Note main display is displayed at twice// the x and y dimensions so scaling of the alarm_zone is required.//=====================================================================void mark_alarm_zone( struct alarm_zone *zone ){ int x,y; char *p1,*p2; if (zone->x1 < 0 || zone->x2 < 0 || zone->y1 < 0 || zone->y2 < 0 ) return; if (zone->x1 > 639 || zone->x2 > 639 || zone->y1 > 479 || zone->y2 > 479 ) return; p1 = rgb_buf + (zone->y1 * 2)*640*3; p2 = rgb_buf + (zone->y2 * 2)*640*3; for( x = 0; x < 640; x++) { *p1 = ~*p1; // invert red component *(p1+1) = ~*(p1+1); // invert green *(p1+2) = ~*(p1+2); // invert blue *p2 = ~*p2; // draw both horizontal lines *(p2+1) = ~*(p2+1); *(p2+2) = ~*(p2+2); p1 += 3; p2 += 3; } p1 = rgb_buf + (zone->x1 * 2)*3; p2 = rgb_buf + (zone->x2 * 2)*3; for( y = 0; y < 480; y++) { *p1 = ~*p1; // invert red component *(p1+1) = ~*(p1+1); // invert green *(p1+2) = ~*(p1+2); // invert blue *p2 = ~*p2; // draw both vertical lines *(p2+1) = ~*(p2+1); *(p2+2) = ~*(p2+2); p1 += 640*3; p2 += 640*3; }}//=====================================================================// convert 640x480 picture down to 320x240// use pixel averaging for best quality instead of subsampling// 2 output buffers are filled...// ds_buf[] has the downsampled image// ds1_buf[] has a copy of the downsampled image corrected for// intensity to make its avg intensity match the reference image//=====================================================================void down_sample_image(void){ int x,y; unsigned char *p0; unsigned char *p1; unsigned char *p2; unsigned char *p3; unsigned char *p4; unsigned char *pdest; unsigned int avg = 0; unsigned char correction; pdest = &ds_buf[0]; for (y=0; y< 240; y++) { p1 = rgb_buf + (y*2) * 640 * 3; // upper left cell p2 = p1 + 3; // upper right cell p3 = p1 + (640 * 3); // lower left cell p4 = p3 + 3; // lower right cell for (x=0; x<320; x++) { *pdest++ = (*p1++ + *p2++ + *p3++ + *p4++) / 4; //red cells avg *pdest++ = (*p1++ + *p2++ + *p3++ + *p4++) / 4; //grn cells avg *pdest++ = (*p1++ + *p2++ + *p3++ + *p4++) / 4; //blu cells avg p1 += 3; p2 += 3; p3 += 3; p4 += 3; } } for ( p1 = &ds_buf[0]; p1 < &ds_buf[0]+sizeof(ds_buf); p1++ ) { avg += *p1; } avg /= sizeof(ds_buf); avg_ds_intensity = avg;// printf("downsampled image avg intensity = %u\n",avg); // now generate an intensity normalized image p0 = &ds_buf[0]; p1 = &ds1_buf[0]; if (avg_ds_intensity < avg_ref_intensity) { correction = avg_ref_intensity - avg_ds_intensity;// printf("making image lighter by %d\n",correction); // make ds1 image brighter to match ref image 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// printf("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;// printf("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){ int x,y; unsigned char *pdest; unsigned char *psrc; float old; unsigned int avg = 0; unsigned char *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 } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -