From 7c531193b06c0ec278b2e34baa56c4e93e822a21 Mon Sep 17 00:00:00 2001 From: Martin Molnar Date: Wed, 3 Dec 2008 12:59:42 +0100 Subject: [PATCH] Added demo application that grabs images fom webcam --- demo/Makefile | 14 + demo/Makefile.omk | 1 + demo/camera/Makefile | 14 + demo/camera/Makefile.omk | 9 + demo/camera/capture_v4l.c | 371 +++++++++++++++++++++ demo/camera/capture_v4l.h | 66 ++++ demo/camera/extract_skin_color.c | 86 +++++ demo/camera/extract_skin_color.h | 21 ++ demo/camera/old.Makefile | 43 +++ demo/camera/show_video_v4l.h | 38 +++ demo/camera/show_video_v4l_capture.c | 171 ++++++++++ demo/camera/show_video_v4l_main.c | 82 +++++ resources/cpu_aquosa/lib/.aqcpu_fra.c.swp | Bin 16384 -> 0 bytes resources/cpu_aquosa/lib/.frsh_aqcpu.c.swp | Bin 24576 -> 0 bytes 14 files changed, 916 insertions(+) create mode 100644 demo/Makefile create mode 100644 demo/Makefile.omk create mode 100644 demo/camera/Makefile create mode 100644 demo/camera/Makefile.omk create mode 100644 demo/camera/capture_v4l.c create mode 100644 demo/camera/capture_v4l.h create mode 100644 demo/camera/extract_skin_color.c create mode 100644 demo/camera/extract_skin_color.h create mode 100644 demo/camera/old.Makefile create mode 100644 demo/camera/show_video_v4l.h create mode 100644 demo/camera/show_video_v4l_capture.c create mode 100644 demo/camera/show_video_v4l_main.c delete mode 100644 resources/cpu_aquosa/lib/.aqcpu_fra.c.swp delete mode 100644 resources/cpu_aquosa/lib/.frsh_aqcpu.c.swp diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 0000000..b22a357 --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,14 @@ +# Generic directory or leaf node makefile for OCERA make framework + +ifndef MAKERULES_DIR +MAKERULES_DIR := $(shell ( old_pwd="" ; while [ ! -e Makefile.rules ] ; do if [ "$$old_pwd" = `pwd` ] ; then exit 1 ; else old_pwd=`pwd` ; cd -L .. 2>/dev/null ; fi ; done ; pwd ) ) +endif + +ifeq ($(MAKERULES_DIR),) +all : default +.DEFAULT:: + @echo -e "\nThe Makefile.rules has not been found in this or partent directory\n" +else +include $(MAKERULES_DIR)/Makefile.rules +endif + diff --git a/demo/Makefile.omk b/demo/Makefile.omk new file mode 100644 index 0000000..c459c78 --- /dev/null +++ b/demo/Makefile.omk @@ -0,0 +1 @@ +SUBDIRS=$(ALL_OMK_SUBDIRS) diff --git a/demo/camera/Makefile b/demo/camera/Makefile new file mode 100644 index 0000000..b22a357 --- /dev/null +++ b/demo/camera/Makefile @@ -0,0 +1,14 @@ +# Generic directory or leaf node makefile for OCERA make framework + +ifndef MAKERULES_DIR +MAKERULES_DIR := $(shell ( old_pwd="" ; while [ ! -e Makefile.rules ] ; do if [ "$$old_pwd" = `pwd` ] ; then exit 1 ; else old_pwd=`pwd` ; cd -L .. 2>/dev/null ; fi ; done ; pwd ) ) +endif + +ifeq ($(MAKERULES_DIR),) +all : default +.DEFAULT:: + @echo -e "\nThe Makefile.rules has not been found in this or partent directory\n" +else +include $(MAKERULES_DIR)/Makefile.rules +endif + diff --git a/demo/camera/Makefile.omk b/demo/camera/Makefile.omk new file mode 100644 index 0000000..3705968 --- /dev/null +++ b/demo/camera/Makefile.omk @@ -0,0 +1,9 @@ +LDFLAGS+= -L/usr/X11R6/lib +C_INCLUDE_PATH = -I/usr/X11R6/include +GLUT_LIBS = glut GL GLU +lib_LOADLIBES+= $(GLUT_LIBS) m + +bin_PROGRAMS = camera +camera_SOURCES = show_video_v4l_main.c show_video_v4l_capture.c capture_v4l.c\ + extract_skin_color.c +include_HEADERS = show_video_v4l.h capture_v4l.h extract_skin_color.h diff --git a/demo/camera/capture_v4l.c b/demo/camera/capture_v4l.c new file mode 100644 index 0000000..53922d9 --- /dev/null +++ b/demo/camera/capture_v4l.c @@ -0,0 +1,371 @@ +/*========================================================================== + $Id$ + capture_v4l.c: Functions for capturing image using Video4Linux. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include "capture_v4l.h" + +/*=== Global variable for this module ===*/ +static unsigned char *frame_buf; + +/*============================================================================ + Functions for Device Open and Check +============================================================================*/ +int CaptureV4LOpen( char *device_name ) +{ + + int fd; + +/*======= Open Video4Linux device ==========================*/ + if( ( fd = open( device_name , O_RDWR ) ) == -1 ) { + return -1; + } + + return fd; + +} + +int CaptureV4LGetDeviceCapability( int fd , struct video_capability *vcap ) +{ + +/*======= Get device capabilities ==========================*/ + if( ioctl( fd , VIDIOCGCAP , vcap ) == -1 ) { + return -1; + } + + return 0; + +} + +void CaptureV4LDisplayDeviceCapability( struct video_capability vcap ) +{ + + fprintf( stdout , "*** Device information ***\n" ); + +/*======= Display deivce name =============================*/ + fprintf( stdout , "Device name:%s.\n" , vcap.name ); + +/*======= Display deivce types ============================*/ + fprintf( stdout , "Type of device:\n" ); + if( vcap.type & VID_TYPE_CAPTURE ) { + fprintf( stdout , "Capture is OK.\n" ); + } else { + fprintf( stderr , "This device may NOT be used for capture. Are you sure about it ?\n" ); + } + if( vcap.type & VID_TYPE_TUNER ) { + fprintf( stdout , "Tuner exists.\n" ); + } + if( vcap.type & VID_TYPE_TELETEXT ) { + fprintf( stdout , "Teletext can be used.\n" ); + } + if( vcap.type & VID_TYPE_OVERLAY ) { + fprintf( stdout , "Overlay is OK.\n" ); + } + if( vcap.type & VID_TYPE_CHROMAKEY ) { + fprintf( stdout , "Overlay by chromakey is OK.\n" ); + } + if( vcap.type & VID_TYPE_CLIPPING ) { + fprintf( stdout , "Clipping overlay is OK.\n" ); + } + if( vcap.type & VID_TYPE_FRAMERAM ) { + fprintf( stdout , "Overlay for frame buffer is OK.\n" ); + } + if( vcap.type & VID_TYPE_SCALES ) { + fprintf( stdout , "Scaling by hardware can be used.\n" ); + } + if( vcap.type & VID_TYPE_MONOCHROME ) { + fprintf( stdout , "Capture of monochrome image is OK.\n" ); + } + if( vcap.type & VID_TYPE_SUBCAPTURE ) { + fprintf( stdout , "Capture of a part of image is OK.\n" ); + } + fprintf( stdout , "\n" ); + +/*======= Display number of video channels and audio ones ===*/ + fprintf( stdout , "Number of channels: %d\n" , vcap.channels ); + fprintf( stdout , "Number of audio devices: %d\n" , vcap.audios ); + + if( vcap.channels > MAX_NO_CHANNEL ) { + fprintf( stderr , "It seems that too many channels exist in this device: %d.\n" , vcap.channels ); + } + +/*======= Display image size ================================*/ + fprintf( stdout , "Maximum width of image: %d\n" , vcap.maxwidth ); + fprintf( stdout , "Maximum height of image: %d\n", vcap.maxheight ); + fprintf( stdout , "Minimum width of image: %d\n" , vcap.minwidth ); + fprintf( stdout , "Minimum height of image: %d\n" , vcap.minheight ); + fprintf( stdout , "\n" ); + +} + +int CaptureV4LGetChannelInfo( int fd , struct video_channel vch[MAX_NO_CHANNEL] , int no_channel ) +{ + + int i; + +/*======= Get channel information ==========================*/ + for( i = 0 ; i < no_channel ; i++ ) { + vch[i].channel = i; /* channel number */ + if( ioctl( fd , VIDIOCGCHAN , &vch[i] ) == -1 ) { + return -1; + } + } + + return 0; + +} + +void CaptureV4LDisplayChannelInfo( struct video_channel vch[MAX_NO_CHANNEL] , int no_channel ) +{ + + int i; + + for( i = 0 ; i < no_channel ; i++ ) { + fprintf( stdout , "*** Channel number: %d ***\n" , vch[i].channel ); + fprintf( stdout , "Channel name: %s\n" , vch[i].name ); + if( vch[i].flags & VIDEO_VC_TUNER ) { + fprintf( stdout , "This channel has a tuner.\n" ); + } + if( vch[i].flags & VIDEO_VC_AUDIO ) { + fprintf( stdout , "This channel has a audio.\n" ); + } + if( vch[i].type & VIDEO_TYPE_TV ) { + fprintf( stdout , "Channel type is TV.\n" ); + } + if( vch[i].type & VIDEO_TYPE_CAMERA ) { + fprintf( stdout , "Channel type is camera.\n" ); + } + if( vch[i].norm & VIDEO_MODE_NTSC ) { + fprintf( stdout , "Video mode is NTSC.\n" ); + } + if( vch[i].norm & VIDEO_MODE_PAL ) { + fprintf( stdout , "Video mode is PAL.\n" ); + } + if( vch[i].norm & VIDEO_MODE_SECAM ) { + fprintf( stdout , "Video mode is SECAM.\n" ); + } + } + fprintf( stdout , "\n" ); + +} + +int CaptureV4LGetPictureInfo( int fd , struct video_picture *vp ) +{ + +/*======= Get channel information ==========================*/ + if( ioctl( fd , VIDIOCGPICT , vp ) == -1 ) { + return -1; + } + + return 0; + +} + +void CaptureV4LDisplayPictureInfo( struct video_picture vp ) +{ + + fprintf( stdout , "*** Picture information ***\n" ); + fprintf( stdout , "Brightness: %d\n" , vp.brightness ); + fprintf( stdout , "Hue: %d\n" , vp.hue ); + fprintf( stdout , "Color: %d\n" , vp.colour ); + fprintf( stdout , "Contrast: %d\n" , vp.contrast ); + fprintf( stdout , "Whiteness: %d\n" , vp.whiteness ); + fprintf( stdout , "Depth: %d\n" , vp.depth ); + switch( vp.palette ) { + case VIDEO_PALETTE_GREY: + fprintf( stdout , "Gray scale.\n" ); + break; + + case VIDEO_PALETTE_RGB565: + fprintf( stdout , "16bit RGB.\n" ); + break; + + case VIDEO_PALETTE_RGB24: + fprintf( stdout , "24bit RGB.\n" ); + break; + + case VIDEO_PALETTE_RGB32: + fprintf( stdout , "32bit RGB.\n" ); + break; + + case VIDEO_PALETTE_YUV422: + fprintf( stdout , "YUV422.\n" ); + break; + + default: + break; + + } + +} + +int CaptureV4LGetMemoryMapInfo( int fd , struct video_mbuf *vm ) +{ + +/*======= Get channel information ==========================*/ + if( ioctl( fd , VIDIOCGMBUF , vm ) == -1 ) { + return -1; + } + + return 0; + +} + +void CaptureV4LDisplayMemoryMapInfo( struct video_mbuf vm ) +{ + + int i; + + fprintf( stdout , "Memory size: %d\n" , vm.size ); + fprintf( stdout , "Number of frames: %d\n" , vm.frames ); + for( i = 0 ; i < vm.frames ; i++ ) { + fprintf( stdout , "Offset (frame %d): %d\n" , i , vm.offsets[i] ); + } + +} + +/*============================================================================ + Functions for preparation of capture +============================================================================*/ +int CaptureV4LSelectChannel( int fd , struct video_channel vch[MAX_NO_CHANNEL] , int channel_no ) +{ + + vch[channel_no].norm = VIDEO_MODE_NTSC; + if( ioctl( fd , VIDIOCSCHAN , &vch[channel_no] ) == -1 ) { + return -1; + } + + return 0; + +} + +int CaptureV4LMemoryMapping( int fd , struct video_mbuf vm ) +{ + + if( ( frame_buf = (unsigned char *)mmap( 0 , (size_t)vm.size , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0 ) ) == MAP_FAILED ) { + return -1; + } + + return 0; + +} + +/*============================================================================ + Functions for capture +============================================================================*/ +int CaptureV4LSimpleCapture( int fd , struct video_mmap *vmap ) +{ + +/*======= Begin capture ====================================*/ + if( ioctl( fd , VIDIOCMCAPTURE , vmap ) == -1 ) { + return -1; + } + +/*======= Wait capture =====================================*/ + if( ioctl( fd , VIDIOCSYNC , &(vmap->frame) ) == -1 ) { + return -1; + } + +} + +int CaptureV4LDoubleBufferingInitCapture( int fd , struct video_mmap *vmap ) +{ + +/*======= Capture image into frame buffers at beginning ====*/ + vmap->frame = 0; + if( ioctl( fd , VIDIOCMCAPTURE , vmap ) == -1 ) { + return -1; + } + vmap->frame = 1; + if( ioctl( fd , VIDIOCMCAPTURE , vmap ) == -1 ) { + return -1; + } + /*=== Set initial frame number ===*/ + vmap->frame = 0; + +} + +int CaptureV4LDoubleBufferingCaptureWait( int fd , struct video_mmap *vmap ) +{ + +/*======= Wait Capture ====================================*/ + if( ioctl( fd , VIDIOCSYNC , &(vmap->frame) ) == -1 ) { + return -1; + } + + return 0; + +} + +int CaptureV4LDoubleBufferingCaptureNextFrame( int fd , struct video_mmap *vmap ) +{ + +/*======= Capture next frame into current buffer ==========*/ + if( ioctl( fd , VIDIOCMCAPTURE , vmap ) == -1 ) { + return -1; + } + +/*======= Change buffer ===================================*/ + if( vmap->frame == 0 ) { + vmap->frame = 1; + } else { + vmap->frame = 0; + } + + return 0; + +} + +/*============================================================================ + Functions for image array +============================================================================*/ +unsigned char *CaptureV4LSetImage( struct video_mmap vmap , struct video_mbuf vm ) +{ + + unsigned char tmpc; + unsigned char *pixel_pos; + + int i; + +/*======= Change order of data from BGR to RGB ===============*/ + pixel_pos = frame_buf+vm.offsets[vmap.frame]; + for( i = 0 ; i < vmap.height*vmap.width ; i++ ) { + tmpc = pixel_pos[2]; + pixel_pos[2] = pixel_pos[0]; + pixel_pos[0] = tmpc; + pixel_pos += RGB; + } + + return frame_buf+vm.offsets[vmap.frame]; + +} + +void CaptureV4LSetImageDownSamplingForOpenGL( struct video_mmap vmap , struct video_mbuf vm , int down_sampling_rate , unsigned char *image , unsigned char *disp_image ) +{ + + unsigned char *pixel_pos; + + int i,j; + int dheight,dwidth; + +/*======= Change order of data from BGR(capture board order) to RGB for processing ===============*/ + dheight = vmap.height/down_sampling_rate; + dwidth = vmap.width/down_sampling_rate; + pixel_pos = frame_buf+vm.offsets[vmap.frame]; + for( i = 0 ; i < dheight ; i++ ) { + for( j = 0 ; j < dwidth; j++ ) { + /* image for processing: RGB order */ + image[((dheight-1-i)*dwidth+j)*RGB] = pixel_pos[2]; + image[((dheight-1-i)*dwidth+j)*RGB+1] = pixel_pos[1]; + image[((dheight-1-i)*dwidth+j)*RGB+2] = pixel_pos[0]; + /* image for display: BRG order */ + disp_image[((dheight-1-i)*dwidth+j)*RGB] = pixel_pos[0]; + disp_image[((dheight-1-i)*dwidth+j)*RGB+1] = pixel_pos[1]; + disp_image[((dheight-1-i)*dwidth+j)*RGB+2] = pixel_pos[2]; + pixel_pos += RGB*down_sampling_rate; + } + pixel_pos += vmap.width*RGB*(down_sampling_rate-1); + } + +} diff --git a/demo/camera/capture_v4l.h b/demo/camera/capture_v4l.h new file mode 100644 index 0000000..52a622e --- /dev/null +++ b/demo/camera/capture_v4l.h @@ -0,0 +1,66 @@ +/*========================================================================== + $Id$ + capture_v4l.h: Header file for capturing image using Video4Linux. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include +#include +#include +#include + +#include /* for open() */ +#include +#include + +#include /* for ioctl() */ + +#include /* for mmap() */ +#include + +#include /* for V4L */ + +/*===================================================== + Definitions for Video4Linux +=====================================================*/ +#define DEFAULT_DEVICE_NAME "/dev/video0" /* device file name */ +#define MAX_NO_CHANNEL 10 /* maximum number of channel of frame grabber */ +#define CAPTURE_IMAGE_WIDTH 640 /* image width */ +#define CAPTURE_IMAGE_HEIGHT 480 /* image height */ + +#define RGB 3 /* number of bit plane */ + +#define COMPOSITE1 0 /* for IO-DATA GV-VCP2M/PCI */ +#define COMPOSITE2 1 +#define S_VIDEO 2 + +#define DOWN_SAMPLING_RATE 2 /* to remove even number field */ + +#define IMAGE_WIDTH_DS CAPTURE_IMAGE_WIDTH/DOWN_SAMPLING_RATE /* image size after down sampling */ +#define IMAGE_HEIGHT_DS CAPTURE_IMAGE_HEIGHT/DOWN_SAMPLING_RATE + +/*===================================================== + Function Prototypes +=====================================================*/ +int CaptureV4LOpen( char *device_name ); +int CaptureV4LGetDeviceCapability( int fd , struct video_capability *vcap ); +void CaptureV4LDisplayDeviceCapability( struct video_capability vcap ); +int CaptureV4LGetChannelInfo( int fd , struct video_channel vch[MAX_NO_CHANNEL] , int no_channel ); +void CaptureV4LDisplayChannelInfo( struct video_channel vch[MAX_NO_CHANNEL] , int no_channel ); +int CaptureV4LGetPictureInfo( int fd , struct video_picture *vp ); +void CaptureV4LDisplayPictureInfo( struct video_picture vp ); +int CaptureV4LGetMemoryMapInfo( int fd , struct video_mbuf *vm ); +void CaptureV4LDisplayMemoryMapInfo( struct video_mbuf vm ); + +int CaptureV4LSelectChannel( int fd , struct video_channel vch[MAX_NO_CHANNEL] , int channel_no ); +int CaptureV4LMemoryMapping( int fd , struct video_mbuf vm ); + +int CaptureV4LSimpleCapture( int fd , struct video_mmap *vmap ); +int CaptureV4LDoubleBufferingInitCapture( int fd , struct video_mmap *vmap ); +int CaptureV4LDoubleBufferingCaptureWait( int fd , struct video_mmap *vmap ) ; +int CaptureV4LDoubleBufferingCaptureNextFrame( int fd , struct video_mmap *vmap ); + +unsigned char *CaptureV4LSetImage( struct video_mmap vmap , struct video_mbuf vm ); +void CaptureV4LSetImageDownSamplingForOpenGL( struct video_mmap vmap , struct video_mbuf vm , int down_sampling_rate , unsigned char *image , unsigned char *disp_image ); + + diff --git a/demo/camera/extract_skin_color.c b/demo/camera/extract_skin_color.c new file mode 100644 index 0000000..f66268c --- /dev/null +++ b/demo/camera/extract_skin_color.c @@ -0,0 +1,86 @@ +/*========================================================================== + $Id$ + extract_skin_color.c: Functions for extracting skin color from image + using simple thresholding. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include "show_video_v4l.h" +#include "extract_skin_color.h" + +void ExtractSkinColorSimpleThresholding( unsigned char *image , int iheight , int iwidth , unsigned char *skin_map ) +{ + + static unsigned char tmp_skin_map[IMAGE_WIDTH_DS*IMAGE_HEIGHT_DS]; /* Skin color map */ + + int i,j,k,l; + int hblock_size; + int no_skin_pixel; + + double r,g,b; /* color components */ + double sum_cc; /* sum of color components */ + +/*======== Extract candidate pixels =======================================*/ + for( i = 0 ; i < iheight ; i++ ) { + for( j = 0 ; j < iwidth ; j++ ) { + + /*=== Calculate color component for skin color ===*/ + sum_cc = (double)(image[(i*iwidth+j)*RGB]+image[(i*iwidth+j)*RGB+1]+image[(i*iwidth+j)*RGB+2]); + r = (double)image[(i*iwidth+j)*RGB]/sum_cc; /* r=R/(R+G+B) */ + g = (double)image[(i*iwidth+j)*RGB+1]/sum_cc; /* g=G/(R+G+B) */ + b = (double)image[(i*iwidth+j)*RGB+2]/sum_cc; /* b=B/(R+G+B) */ + if( r > MIN_THRESH_r && r < MAX_THRESH_r ) { + tmp_skin_map[i*iwidth+j] = 1; + } else { + tmp_skin_map[i*iwidth+j] = 0; + } + + } + } + +/*======== Post processing: dilation by majority in block (3x3) ========*/ + hblock_size = BLOCK_SIZE/2; + for( i = hblock_size ; i < iheight-hblock_size ; i += BLOCK_SIZE ) { + for( j = hblock_size ; j < iwidth-hblock_size ; j += BLOCK_SIZE ) { + no_skin_pixel = 0; + for( k = -hblock_size ; k <= hblock_size ; k++ ) { + for( l = -hblock_size ; l <= hblock_size ; l++ ) { + if( tmp_skin_map[(i+k)*iwidth+(j+l)] ) { + no_skin_pixel++; + } + } + } + if( no_skin_pixel > BLOCK_SIZE*BLOCK_SIZE/2 ) { /* use majority rule */ + for( k = -hblock_size ; k <= hblock_size ; k++ ) { + for( l = -hblock_size ; l <= hblock_size ; l++ ) { + skin_map[(i+k)*iwidth+(j+l)] = 1; + } + } + } else { + for( k = -hblock_size ; k <= hblock_size ; k++ ) { + for( l = -hblock_size ; l <= hblock_size ; l++ ) { + skin_map[(i+k)*iwidth+(j+l)] = 0; + } + } + } + } + } + +} + +void ShowSkinRegion( unsigned char *skin_map , int iheight , int iwidth ) +{ + + int i,j; + + glColor3f( 0.0 , 1.0 , 0.0 ); /* green */ + glPointSize( 2.0 ); /* point size */ + for( i = 0 ; i < iheight ; i++ ) { + for( j = 0 ; j < iwidth ; j++ ) { + if( skin_map[i*iwidth+j] ) { + DisplayPoint( (double)j , (double)i ); + } + } + } + +} diff --git a/demo/camera/extract_skin_color.h b/demo/camera/extract_skin_color.h new file mode 100644 index 0000000..ec7ee74 --- /dev/null +++ b/demo/camera/extract_skin_color.h @@ -0,0 +1,21 @@ +/*========================================================================== + $Id$ + extract_skin_color.h: Header file for extracting skin color from image + using simple thresholding. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +/*=== Thresholds for skin color ====*/ +#define MIN_THRESH_r 0.38 +#define MAX_THRESH_r 0.48 + +/*=== for skin map =================*/ +#define BLOCK_SIZE 3 /* must be odd nubmer */ +#define THRESH_SKIN_PIXEL 50*50 + +/*=== Definitions for display point by OpenGL ===*/ +#define DisplayPoint( x , y ) glBegin(GL_POINTS); glVertex2f( (x) , (y) ); glEnd(); + +/*=== Prototypes ===*/ +void ExtractSkinColorSimpleThresholding( unsigned char *image , int iheight , int iwidth , unsigned char *skin_map ); +void ShowSkinRegion( unsigned char *skin_map , int iheight , int iwidth ); diff --git a/demo/camera/old.Makefile b/demo/camera/old.Makefile new file mode 100644 index 0000000..2a68e26 --- /dev/null +++ b/demo/camera/old.Makefile @@ -0,0 +1,43 @@ +# $Id$ +# Makefile: +# Make file for show_video_v4l +# + +# +# macros +# +GLUT_LDFLAG = -L/usr/X11R6/lib -lglut -lGL -lGLU +LDFLAGS = $(GLUT_LDFLAG) -lm + +GLUT_INCFLAG = -I/usr/X11R6/include +INCFLAGS = $(GLUT_INCFLAG) + +CC = gcc +CFLAGS = -O + +SRCS = show_video_v4l_main.c show_video_v4l_capture.c capture_v4l.c extract_skin_color.c +HDRS = show_video_v4l.h capture_v4l.h extract_skin_color.h +OBJS = $(SRCS:.c=.o) +TARGET = show_video_v4l + +# +# compile +# +.c.o: + $(CC) $(INCFLAGS) $(CFLAGS) -c $< +# +# link +# +$(TARGET) : $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) + +# +# compare time +# +show_video_v4l_main.o : show_video_v4l_main.c $(HDRS) +show_video_v4l_capture.o : show_video_v4l_capture.c $(HDRS) +capture_v4l.o : capture_v4l.c $(HDRS) +extract_skin_color.o : extract_skin_color.c $(HDRS) + +clean: + rm -f $(OBJS) $(TARGET) *~ diff --git a/demo/camera/show_video_v4l.h b/demo/camera/show_video_v4l.h new file mode 100644 index 0000000..a2e35b5 --- /dev/null +++ b/demo/camera/show_video_v4l.h @@ -0,0 +1,38 @@ +/*========================================================================== + $Id$ + show_video_v4l.h: Header file for capturing video using V4L. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include +#include +#include +#include + +#include /* for GLUT */ +#include +#include + +#include "capture_v4l.h" /* for capture by V4L */ + +/*========================================================== + Definitions for OpenGL +==========================================================*/ +#define DEFAULT_ZOOM_RATE 1 +#define INIT_WINDOW_POS_X 200 +#define INIT_WINDOW_POS_Y 200 + +/*========================================================== + Function Prototypes +==========================================================*/ +/*=== show_video_v4l_capture.c ===*/ +int ShowVideoInitCaptureDevice( char *device_name , int channel_no ); +void ShowVideoCaptureImage(); +void ShowVideoDisplayImage(); +void ShowVideoMouseCheck( int button , int status , int x , int y ); +void ShowVideoChangeOrderImageRow( unsigned char *image , int iwidth , int iheight ); +void ShowVideoSavePPMImage( unsigned char *image , int iheight , int iwidth , int frame_no , char *image_prefix ); + + + + diff --git a/demo/camera/show_video_v4l_capture.c b/demo/camera/show_video_v4l_capture.c new file mode 100644 index 0000000..8396d70 --- /dev/null +++ b/demo/camera/show_video_v4l_capture.c @@ -0,0 +1,171 @@ +/*========================================================================== + $Id$ + show_video_v4l_capture.c: Functions for capturing video using V4L. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include "show_video_v4l.h" +#include "extract_skin_color.h" + +#define PREFIX_IMAGE_FILE "video_image" + +/*=== Local global variables ===*/ +static unsigned char disp_image[IMAGE_WIDTH_DS*IMAGE_HEIGHT_DS*RGB];/* pointer to image buffer */ + +static int fd; /* file descriptor for frame buffer */ +static int exit_flag; /* flag to exit program */ +static int frame_no=1; /* frame number */ + +static struct video_mbuf vm; /* structure for frame buffer */ +static struct video_mmap vmap; /* structure for memory space for frame buffer */ + +/*=== global variable ===*/ +extern int extract_skin_color; /* global variable for enabling image processing */ + +void ShowVideoCaptureImage() +{ + + static char image_prefix[1024]=PREFIX_IMAGE_FILE; /* Prefix of output image file */ + static unsigned char image[IMAGE_WIDTH_DS*IMAGE_HEIGHT_DS*RGB]; /* Captured image */ + static unsigned char skin_map[IMAGE_WIDTH_DS*IMAGE_HEIGHT_DS]; /* Skin color map */ + +/*======= Wait capture for current frame ====================*/ + CaptureV4LDoubleBufferingCaptureWait( fd , &vmap ); + +/*======= Set data to image array ===========================*/ + CaptureV4LSetImageDownSamplingForOpenGL( vmap , vm , DOWN_SAMPLING_RATE , image , disp_image ); + +/*======= Begin capture for next frame ======================*/ + if( CaptureV4LDoubleBufferingCaptureNextFrame( fd , &vmap ) == -1 ) { + fprintf( stderr , "COuld not capture next frame.\n" ); + exit(-1); + } + +/*======= Display image =====================================*/ + ShowVideoDisplayImage(); + +/*======= Extract skin color by simple thresholding =========*/ + if( extract_skin_color ) { + ExtractSkinColorSimpleThresholding( image , IMAGE_HEIGHT_DS , IMAGE_WIDTH_DS , skin_map ); + ShowSkinRegion( skin_map , IMAGE_HEIGHT_DS , IMAGE_WIDTH_DS ); + } + +#if 0 +/*======= Save image ========================================*/ + ShowVideoSavePPMImage( image , IMAGE_HEIGHT_DS , IMAGE_WIDTH_DS , frame_no , image_prefix ); +#endif + +/*======= Swap Buffer: show graphics ========================*/ + glutSwapBuffers(); + +/*======= Increment frame number ============================*/ + frame_no++; + +/*======= Check exit flag ===================================*/ + if( exit_flag ) { + exit(0); + } + +} + +int ShowVideoInitCaptureDevice( char *device_name , int channel_no ) +{ + + struct video_capability vcap; /* structure for video capability */ + struct video_channel vch[MAX_NO_CHANNEL]; /* structure for video channel */ + struct video_picture vp; + +/*======= Open video device ================================*/ + if( ( fd = CaptureV4LOpen( device_name ) ) == -1 ) { + fprintf( stderr, "Could not open device %s.\n" , device_name ); + exit(-1); + } + +/*======= Get information of device =========================*/ + /*=== Get device capabilities ===*/ + if( CaptureV4LGetDeviceCapability( fd , &vcap ) == -1 ) { + fprintf( stderr , "Could not get capabilities of video device.\n" ); + exit(-1); + } + + /*=== Get channel information ===*/ + if( CaptureV4LGetChannelInfo( fd , vch , vcap.channels ) == -1 ) { + fprintf( stderr , "Could not get channel information of video device.\n" ); + exit(-1); + } + + /*=== Get memory map information ===*/ + if( CaptureV4LGetMemoryMapInfo( fd , &vm ) == -1 ) { + fprintf( stderr , "Could not get memory map information.\n" ); + exit(-1); + } + +/*======= Show picture information =========================*/ + CaptureV4LGetPictureInfo( fd , &vp ); + CaptureV4LDisplayPictureInfo( vp ); + +/*======= Select channel ===================================*/ + if( CaptureV4LSelectChannel( fd , vch , channel_no ) == -1 ) { + fprintf( stderr , "Could not select channel.\n" ); + exit(-1); + } + +/*======= Mapping frame buffer ==============================*/ + if( CaptureV4LMemoryMapping( fd , vm ) == -1 ) { + fprintf( stdout , "Could not map frame buffer.\n" ); + exit(-1); + } + +/*======= Set image size and formate ========================*/ + vmap.width = CAPTURE_IMAGE_WIDTH; + vmap.height = CAPTURE_IMAGE_HEIGHT; + vmap.format = VIDEO_PALETTE_RGB24; + +/*======= Begin Capture =====================================*/ + CaptureV4LDoubleBufferingInitCapture( fd , &vmap ); + + return 0; + +} + +void ShowVideoDisplayImage() +{ + + glRasterPos2i( 0 , 0 ); + glDrawPixels( IMAGE_WIDTH_DS , IMAGE_HEIGHT_DS , GL_BGR , GL_UNSIGNED_BYTE , disp_image ); + glFlush(); + +} + +void ShowVideoMouseCheck( int button , int status , int x , int y ) +{ + + if( button == GLUT_LEFT_BUTTON && status == GLUT_DOWN ) { + exit_flag = 1; + } + +} + +void ShowVideoSavePPMImage( unsigned char *image , int iheight , int iwidth , int frame_no , char *image_prefix ) +{ + + char file_name[1024]; + + FILE *fp; + + sprintf( file_name , "%s_%d.ppm" , image_prefix , frame_no ); + if( ( fp = fopen( file_name , "w" ) ) == NULL ) { + fprintf( stderr , "Could not open image file: %s\n" , file_name ); + exit(1); + } + fprintf( fp , "P6 %d %d 255\n" , iwidth , iheight ); + fwrite( image , iheight*iwidth*RGB , 1 , fp ); + fclose(fp); + +} + + + + + + diff --git a/demo/camera/show_video_v4l_main.c b/demo/camera/show_video_v4l_main.c new file mode 100644 index 0000000..17d1e27 --- /dev/null +++ b/demo/camera/show_video_v4l_main.c @@ -0,0 +1,82 @@ +/*========================================================================== + $Id$ + show_video_v4l_main.c: Main routine for capturing video using GLUT and V4L. + Written by Naoyuki Ichimura, AIST, 2001. +==========================================================================*/ + +#include "show_video_v4l.h" + +int extract_skin_color; /* global variable for enabling image processing */ + +int main( int argc , char *argv[] ) +{ + + static char window_title[1024]="Video Capture"; + + int i,j; + int video_input; + + double zoom_rate; /* for zooming */ + +/*======= Set option =========================================*/ + zoom_rate = DEFAULT_ZOOM_RATE; + video_input = S_VIDEO; + extract_skin_color = 0; + for( i = j = 1 ; i < argc ; i++ ) { + if( argv[i][0] == '-' || argv[i][0] == '/' ) { + switch( argv[i][1] ) { + case 'z': + zoom_rate = atof(&argv[i][2]); + break; + + case 'v': + video_input = atoi(&argv[i][2]); + break; + + case 's': + extract_skin_color = 1; + break; + + default: + break; + + } + } else { + argv[j++] = argv[i]; + } + } + argc = j; + +/*======= Initialize GLUT ====================================*/ + glutInit( &argc , argv ); + glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE ); /* enable double buffering of video board */ + +/*======= Initialize window ==================================*/ + glutInitWindowPosition( INIT_WINDOW_POS_X , INIT_WINDOW_POS_Y ); /* initial window position */ + glutInitWindowSize( (int)(zoom_rate*IMAGE_WIDTH_DS) , (int)(zoom_rate*IMAGE_HEIGHT_DS) ); /* set window size */ + glutCreateWindow( window_title ); /* creat window with name */ + +/*======= Initialize projection ==============================*/ + glOrtho( 0.0 , IMAGE_WIDTH_DS-1.0 , 0.0 , IMAGE_HEIGHT_DS , -1.0 , 1.0 ); + glPixelZoom( zoom_rate , zoom_rate ); + +/*======= Initilize capture device ============================*/ + ShowVideoInitCaptureDevice( DEFAULT_DEVICE_NAME , video_input ); + +/*======= Initilize buffer ====================================*/ + glClearColor( 0.0 , 0.0 , 0.0 , 0.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + glutSwapBuffers(); + glFlush(); + +/*======= Register callback functions =========================*/ + glutDisplayFunc( ShowVideoDisplayImage ); + glutMouseFunc( ShowVideoMouseCheck ); + glutIdleFunc( ShowVideoCaptureImage ); /* global idle callback */ + +/*======= Begin event loop ====================================*/ + glutMainLoop(); + + exit(0); + +} diff --git a/resources/cpu_aquosa/lib/.aqcpu_fra.c.swp b/resources/cpu_aquosa/lib/.aqcpu_fra.c.swp deleted file mode 100644 index d45cc2f89926df80b44d956307b6cca9b44f11f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 HUC{bZ&eY(6 diff --git a/resources/cpu_aquosa/lib/.frsh_aqcpu.c.swp b/resources/cpu_aquosa/lib/.frsh_aqcpu.c.swp deleted file mode 100644 index f18f46e3357076c212730215827060376e8e0433..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24576 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 -- 2.39.2