把这一段的工程实践东西总结一下。(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;///u0matrix_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;}
在做运动检测的时候,发现了一个有趣的问题:我靠近窗户,晚上同学从窗户对面过的时候,摄像头可以跟踪窗户里面的像: 我觉得这个要是做运动跟踪应用,或许可以利用这个避免摄像头死角的问题。
我用镜子实验了一下: