简易人脸分割

很多时候,我们需要对脸部位置进行提取以达到某种定位的需求。

这时候就需要对脸部进行分割处理。

脸部的分割算法有好多种,识别的算法更是一片浩洋。

本文只探讨初步的人脸分割,最简单也最常用的方法就是基于肤色的人脸分割,现记录于此。

算法思想:

1.从摄像头获取图像。

2.将图像转化到YCbCr空间。

3.对Cr分量进行OTSU阈值分割。

4.求取分割结果的轮廓,求取面积最大的轮廓。

5.填充最大轮廓得到脸部掩码图,根据掩码图得到人脸。

 

代码如下:

#include "cxcore.h"
#include "math.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;


void cvSkinOtsu(IplImage* src,IplImage* dst)
{    
assert(dst->nChannels == 1 && src->nChannels == 3);
IplImage* ycrcb = cvCreateImage(cvGetSize(src),8,3);
IplImage* cr = cvCreateImage(cvGetSize(src),8,1);
cvCvtColor(src,ycrcb,CV_BGR2YCrCb);
cvSplit(ycrcb,0,cr,0,0);
cvThreshold(cr,cr,40,255,CV_THRESH_OTSU);
cvCopyImage(cr,dst);
cvReleaseImage(&cr);
cvReleaseImage(&ycrcb);
}


//主函数
int main()
{        
CvCapture* capture = cvCaptureFromCAM(0);//从对摄像头的初始化捕获
if(!cvQueryFrame(capture))  cout<<"Video capture failed, please check the camera."< else cout<<"Video camera capture status: OK"< CvSize sz = cvGetSize(cvQueryFrame( capture));//得到摄像头图像大小


IplImage* src = cvCreateImage( sz, 8, 3 );//3通道,每个通道8位
IplImage* srcTemp = cvCreateImage( sz, 8, 3 );//3通道,每个通道8位
IplImage* dst_crotsu = cvCreateImage(sz, 8, 1);
IplImage* dst_MaxSecond = cvCreateImage(sz, 8, 1);
CvMemStorage* storage = cvCreateMemStorage(0);//分配大小为0的内存空间
CvSeq* contour = 0;//用于选取最大两区域
cvNamedWindow("source", CV_WINDOW_AUTOSIZE);//创建用于显示的窗口
cvNamedWindow("SkinArea", CV_WINDOW_AUTOSIZE);
cvNamedWindow("bg", CV_WINDOW_AUTOSIZE);
//两行是为了计算图形的重心做准备 
CvMoments moments;
CvMat* region;
//定义一些点和具体的参数 
double m00 = 0,max,sum,average;
int n = 0,Nc;
src = cvQueryFrame(capture);
int imageArea = src->height*src->width;
IplImage* bg = cvCreateImage( sz, 8, 3);
while(1)
{
//画矩形,参数:Image,两个顶点坐标,线的颜色,线的厚度(CV_FILLED时绘制填充了色彩的矩形),线条类型,坐标点的小数点位数
cvRectangle( bg, cvPoint(0,0), cvPoint(bg->width,bg->height), CV_RGB( 255, 255, 255), -1, 8, 0 );bg->origin = 0;//表示坐标系统的原点,0表示左上,1表示左下
for(int b = 0; b< int(bg->width/10); b++)//画网格
{
cvLine( bg, cvPoint(b*20, 0), cvPoint(b*20, bg->height), CV_RGB( 200, 200, 200), 1, 8, 0 );//画竖线
//cvLine(图像,线段的第一个端点,第二个端点,颜色,粗细,类型,坐标点的小数点位数)
cvLine( bg, cvPoint(0, b*20), cvPoint(bg->width, b*20), CV_RGB( 200, 200, 200), 1, 8, 0 );//画横线
}
src = cvQueryFrame(capture);//得到一帧图像
cvZero(dst_crotsu);
cvSkinOtsu(src, dst_crotsu);//得到二值化图像
cvCopy(src,srcTemp);
/* cvRectangle( srcTemp, cvPoint(bg->width/3,bg->height/5), cvPoint(bg->width*2/3,bg->height*4/5), CV_RGB( 0, 255, 0), 2, 8, 0 );*/
cvShowImage("source", srcTemp);
cvShowImage("SkinArea", dst_crotsu);
//得到最大轮廓
int contour_num = cvFindContours(dst_crotsu, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
double maxarea = 0;//
double secondarea = 0;
double minarea = 100;
cvZero(dst_MaxSecond);
CvSeq* _contour = contour;
int m = 0;
for(;contour != 0; contour = contour->h_next)
{
m++;
double tmparea = fabs(cvContourArea(contour));
if(tmparea < minarea) {cvSeqRemove(contour, 0); continue;}//删除噪声
if(tmparea > maxarea) { maxarea = tmparea;}//得到最大面积
}
contour = _contour;    
for(; contour != 0; contour = contour->h_next)//画出最大两区域
{
double tmparea = fabs(cvContourArea(contour));
if (tmparea == maxarea)
{
CvScalar color = CV_RGB(255, 255, 255);
cvDrawContours(dst_MaxSecond, contour, color, color, 0, CV_FILLED);
//在bg图上确定脸的区域
CvRect rect = cvBoundingRect( contour, 0 );//返回一个2d矩形的点集合
cvRectangle( bg, cvPoint(rect.x, rect.y + rect.height), 
cvPoint(rect.x + rect.width, rect.y), 
CV_RGB(200, 0, 200), 1, 8, 0 );
}
}


//如果面积过大,则认为不是脸部区域
if(maxarea>imageArea/3)
cvZero(dst_MaxSecond);
//将脸部区域拷贝到bg图像中
for(int i = 0;iheight;i++)
for(int j =0;jwidth;j++)
{
if(cvGet2D(dst_MaxSecond,i,j).val[0]==255)
{
CvScalar temp= cvGet2D(src,i,j);
cvSet2D(bg,i,j,temp);
}
}
cvShowImage("FaceArea", dst_MaxSecond);
cvShowImage("bg", bg);
cvWaitKey(33);
}
cvReleaseCapture(&capture);
cvReleaseImage(&src);
cvReleaseImage(&dst_crotsu);
cvReleaseImage(&dst_MaxSecond);
cvReleaseImage(&bg);
cvReleaseImage(&srcTemp);
cvReleaseMemStorage(&storage);
cvDestroyAllWindows();
}

你可能感兴趣的:(图像处理,opencv,C++)