把这一段的工程实践东西总结一下。(VC++6.0 基于MFC的对话框模式,VFW库)
我的USB摄像头是YUY2格式的,本来打算先将其转为GRB的,再做进一步的图像处理。可是转换完之后,为一个一维数组,我不知道该怎么把它实时显示出来。用openecv 比较简单(img2->imageData=(char*)RGBimgbuff;cvShowImage("RGBImageWin", img2 );),老师不许用OPENCV,索性就直接按照YUV的格式进行处理。
YUY2格式(打包格式:通常YUV分量存放在同一个数组中,通常是几个相邻的像素组成的宏像素)为每个像素保留Y分量,而UV分量在水平方向上每两个像素采样一次。一个宏像素为4个字节,实际表示2个像素。(4:2:2的意思为一个宏像素中有4个Y分量、2个U分量和2个V分量。)图像数据中YUV分量排列顺序如下:
Y0 U0 Y1 V0 Y2 U2 Y3 V2 Y4 U4 Y5 V4
肤色相似度计算后结果
从一篇论文看到的肤色相似度计算方法:
u=(156.560,117.436)T 开始不知道怎么来的。查文献发现是从人脸库中提取的人脸肤色均值。看来这个只适合黄种人,我把摄像头对准我的康师傅冰红茶也能检测到,估计要是黑种人就框不住了,全是黑的O(∩_∩)O哈哈~。
关键代码如下:
#define Brcov00 0.00626
#define Brcov01 -0.000254
#define Brcov10 -0.000254
#define Brcov11 0.00335
CVideoProcess::CVideoProcess()
{
cloreshowflag = true;
//初始化协方差矩阵的逆矩阵
brcov[0][0] = Brcov00;
brcov[0][1] = Brcov01;
brcov[1][0] = Brcov10;
brcov[1][1] = Brcov11;
}
//肤色相似度计算
void CVideoProcess::CalLikeHood(BYTE *Img)
{
BYTE *YUY2buff;
BYTE *Ptr;
float matrix_a,matrix_b,matrix_aa,matrix_bb,Similardegree=0.0;
float exponent;
BYTE unitarygray=0;
YUY2buff=Img;
Ptr=Img;
for(DWORD count=0;count<nWidth*nHeight*2;count+=4) //一个宏像素为4个字节,实际表示两个像素
{
++YUY2buff;///u0
matrix_a=*YUY2buff-Cr_aver; //与均值的差值
*YUY2buff=127;
++YUY2buff;//y1
++YUY2buff;///v0
matrix_b=*YUY2buff-Cb_aver; //与均值的差值
*YUY2buff=127;
//计算相似度
matrix_aa=matrix_a*brcov[0][0]+matrix_b*brcov[0][1];
matrix_bb=matrix_a*brcov[1][0]+matrix_b*brcov[1][1];
exponent=(matrix_aa*matrix_a+matrix_bb*matrix_b)*(-0.5);
if(exponent<-4)
Similardegree=1.0; //4.605
else if(exponent>=-0.009)
Similardegree=0.0; //0.009
else
Similardegree=exp(exponent);
//将像素的相似度归一化到 0--255,并写入其中
unitarygray=BYTE(255*Similardegree);
*Ptr=unitarygray;//Y0
++Ptr;//U0
*(++Ptr)=unitarygray;//Y1
++Ptr;//V0
++Ptr;
++YUY2buff;
}
}
滤波之后效果:
用最大连通域检测后的结果:
运动目标检测用的帧差法:
帧差法主要代码:
void CMotionProcess::FramDiffer(BYTE *Img)
{
BYTE *currentframe;
currentframe=Img;
BYTE *lastframe=YUY2FramBuff;
BYTE temp;
for(DWORD count=0;count<nHeight*nWidth*2;count+=4) //
{
temp=*currentframe;
if( ((int)abs(*currentframe - *lastframe) > 3)) //这里只对Y分量(亮度分量)进行说明有目标运动,设定为白
*currentframe=255;//Y0
else
*currentframe=0;//
*lastframe=temp;
*(++currentframe)=127;//U0
++lastframe;
++currentframe;//y1
++lastframe;
temp=*currentframe;
if((int)abs(*currentframe - *lastframe) > 3)//这里只对Y分量(亮度分量)进行说明有目标运动,设定为白
*currentframe=255;//Y1
else
*currentframe=0;//
*lastframe=temp;
*(++currentframe)=127;//V0
++lastframe;
++currentframe;
++lastframe;
}
}
检测对象:
运动检测结果:
滤波后结果:
运动检测结果:
VFW视频处理是基于帧回调的机制对图像进行实时处理的,
//登记回调函数,他们应该被提前声明
capSetCallbackOnFrame(m_hWndVideo,FrameCallbackProc);//这个必须在窗口被创建之后调用,否则回调函数不起作用。
帧回调函数:
LRESULT CALLBACK FrameCallbackProc(HWND caphwnd, LPVIDEOHDR lpVHdr)//帧回调函数
{
if (!caphwnd)
return FALSE;
BYTE *Img=lpVHdr->lpData ; //图像数据在内存中首地址
switch(flag)
{
case 0: break;
case 1:ImgProcess.GrayConvert(Img);break;//灰度变换
case 2:ImgProcess.CalLikeHood(Img);break;//肤色相似度计算
case 3:ImgProcess.CalBinary(Img);break;//肤色二值化
case 4:ImgProcess.Filtering(Img);break;//形态学滤波
case 5:ImgProcess.LinkRegionFace(Img);break;//彩色人脸检测
case 6:MotionProcess.FramDiffer(Img);break;//帧差并二值化
case 7:MotionProcess.MotionFilter(Img);break;//运动形态学滤波
case 8:MotionProcess.LinkRegionMotion(Img);break;//
default:break;
}
return (LRESULT) TRUE;
}
在做运动检测的时候,发现了一个有趣的问题:我靠近窗户,晚上同学从窗户对面过的时候,摄像头可以跟踪窗户里面的像: 我觉得这个要是做运动跟踪应用,或许可以利用这个避免摄像头死角的问题。
我用镜子实验了一下: