本次实验的目的是,利用opencv来进行手势识别。基本流程就是:
肤色检测-速度建模
通过检测肤色块连续八帧的坐标最大值最小值之差与肤色块的宽高的大小之比来确定手势的运动方向。大概的思想过程如下图所示。
下面直接上代码。
// color_detection_video.cpp : 定义控制台应用程序的入口点。
//
//----------------------------------------本程序用于从视频中进行肤色检测-------------------------------------------//
#include "stdafx.h"
#include <cv.h>
#include <cvaux.h>
#include <highgui.h>
#include <ml.h>
#include <cxcore.h>
#include <stdlib.h>
#include <iostream>
void skin_detection_draw_video()
{
CvCapture *pCapture=NULL;
IplImage *pImage=NULL;
IplImage *pMask=NULL;
IplImage *dst=NULL;
IplImage *mask=NULL;
int num=0;
int step = 0;
int channels =0;
int l;
int num_count=0;
double x_pos=0;
double y_pos=0;
double x_pos_min=0;
double y_pos_min=0;
double x_pos_max=0;
double y_pos_max=0;
int width=0;
int height=0;
int t=0;
CvMat* M_x=cvCreateMat(1,8,CV_64FC1);
CvMat* M_y=cvCreateMat(1,8,CV_64FC1);
static int B=0;
static int G=0;
static int R=0;
cvNamedWindow("video",1);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contour = 0;
CvSeq *contmax = 0;
CvRect rect=cvRect(0,0,0,0);
CvRect rect_backup=cvRect(0,0,0,0);
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX,1,1,0,2,8);
if (!(pCapture=cvCaptureFromCAM(-1)))
{
fprintf(stderr,"Could not open the video\n");
}
while (pImage=cvQueryFrame(pCapture))
{
num++;
if(num==1)
{
step = pImage->widthStep;
channels = pImage->nChannels;
pMask=cvCreateImage(cvSize(pImage->width,pImage->height),IPL_DEPTH_8U,3);
dst=cvCreateImage(cvSize(pImage->width,pImage->height),IPL_DEPTH_8U,3);
cvZero(dst);
mask=cvCreateImage(cvSize(pImage->width,pImage->height),IPL_DEPTH_8U,1);
}
else
{
l=(num-2)%8;
cvCopyImage(pImage,pMask);
for (int i = 0; i < pMask->height; i++)
{
for (int j = 0; j < pMask->width; j++)
{
uchar *data_src = (uchar *)pMask->imageData+i*step+j*channels;
uchar *data_dst = (uchar *)dst->imageData+i*step+j*channels;
B=*(data_src+0);
G=*(data_src+1);
R=*(data_src+2);
if (R>95 && G>40 && B>20 && MAX(MAX(R,G),B) - MIN(MIN(R,G),B) && abs(R-G)>15 && R>G && R>B) //满足肤色的条件
{
memcpy(data_dst,data_src,channels);
}
else
{
continue;
}
}
}
//mark
cvCvtColor(dst,mask,CV_RGB2GRAY);
cvZero(dst);
cvThreshold(mask, mask, 1, 255, CV_THRESH_BINARY );//二值化
cvFindContours(mask, storage, &contour, sizeof(CvContour),
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //提取出最外面的轮廓
double area,maxArea =100;
for( ; contour != 0; contour = contour->h_next )
{
area =fabs(cvContourArea( contour, CV_WHOLE_SEQ ));
if (area>maxArea)
{
contmax=contour;//找出最大连通区域
maxArea=area;
rect=cvBoundingRect(contmax,0); //得到最佳连通域的外接矩形
x_pos=rect.x;
y_pos=rect.y;
width=rect.width;
height=rect.height;
}
}
cvmSet(M_x,0,l,x_pos);
cvmSet(M_y,0,l,y_pos);
num_count++;
if (num_count==8)
{
cvMinMaxLoc(M_x,&x_pos_min,&x_pos_max);
cvMinMaxLoc(M_y,&y_pos_min,&y_pos_max);
printf("xmin=%f,xmax=%f,ymin=%f,ymax=%f\n",x_pos_min,x_pos_max,y_pos_min,y_pos_max);
num_count=0;
if(abs(x_pos_max-x_pos_min)>width)
{
if(x_pos>((x_pos_max+x_pos_min)*0.5))
cvPutText(pMask,"R",cvPoint(x_pos+width,y_pos+height),&font,cvScalar(0,0,255));
else
cvPutText(pMask,"L",cvPoint(x_pos+width,y_pos+height),&font,cvScalar(0,0,255));
}
if (abs(y_pos_max-y_pos_min)>height)
{
if(y_pos>((y_pos_max+y_pos_min)*0.5))
cvPutText(pMask,"D",cvPoint(x_pos+width,y_pos+height),&font,cvScalar(0,0,255));
else
cvPutText(pMask,"U",cvPoint(x_pos+width,y_pos+height),&font,cvScalar(0,0,255));
}
num_count=7;
}
}
cvRectangle(pMask,cvPoint(rect.x,rect.y),
cvPoint(rect.x+rect.width,rect.y+rect.height),
cvScalar(0,0,255),2,8,0);
cvShowImage("video",pMask);
rect=cvRect(0,0,0,0);
if(cvWaitKey(33)>=0)
break;
}
cvDestroyWindow("video");
cvReleaseImage(&dst);
cvReleaseImage(&pMask);
cvReleaseImage(&mask);
cvReleaseMat(&M_x);
cvReleaseMat(&M_y);
cvReleaseCapture(&pCapture);
}
int _tmain(int argc, _TCHAR* argv[])
{
skin_detection_draw_video();
return 0;
}
运行结果不大好截屏,我就懒得截了,大家可以把代码复制下来直接试试运行结果。
雁过留声,人过留名。