肤色检测 - OpenCV2.0

来源:互联网 发布:特朗普再喷朝鲜 知乎 编辑:程序博客网 时间:2024/04/28 00:11


#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <cxcore.h>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <Windows.h>
#include <fstream>
#include <cmath>
using namespace std;

#pragma comment(lib,"cv200d")
#pragma comment(lib,"cvaux200d")
#pragma comment(lib,"cxcore200d")
#pragma comment(lib,"highgui200d")


#define SIN sin(2.53)
#define COS cos(2.53)
#define CrRows 65
#define CbRows 65 // 每个颜色分量的级数

int maxof3(int x, int y, int z)
{
int ret = -1000000;
if( ret < x ) ret = x;if( ret < y ) ret = y;if( ret < z ) ret = z;
return ret;
}

int minof3(int x, int y, int z)
{
int ret = 1000000;
if( ret > x ) ret = x;if( ret > y ) ret = y;if( ret > z ) ret = z;
return ret;
}

int _tmain(int argc, char* argv[])
{
CvCapture *cap = cvCreateFileCapture( "data/Lucero.avi" ) ;
IplImage *imgmask;
IplImage *imgsrc;
IplImage *imgYCrCb;
char path[20] = "data/a.bmp";
int crPerLen , cbPerLen, i, j, k;
int SkinArea = 0;
double Pskin = 0, NPskin = 0;
double Pc_skin[CrRows][CbRows], NPc_skin[CrRows][CbRows]; // P(c|skin)
char pathsrc[20] = "data/1.bmp";
crPerLen = cbPerLen = (235-16) / (CbRows);      // 段长
int cr, y, cb, r, g, b;
memset(Pc_skin, 0, sizeof(Pc_skin)), memset(NPc_skin, 0, sizeof(NPc_skin));

CvFont font;
double hScale=1.0;double vScale=1.0;int lineWidth=3;
cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX|CV_FONT_ITALIC, hScale,vScale,0,lineWidth);

// 获取先验知识`
for( i = 0; i < 4; i ++)
{
   imgmask = cvLoadImage(path, 1);
   imgsrc = cvLoadImage(pathsrc, 1);
   imgYCrCb = cvCreateImage(cvGetSize(imgsrc), imgmask->depth, imgmask->nChannels);
   cvCvtColor(imgsrc, imgYCrCb,CV_BGR2YCrCb);
   for( j = 0; j < imgmask->height; j++) for( k = 0; k < imgmask->width; k ++)
   {
    r=CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+2);
    g=CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+1);
    b=CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3); // 从Ycrcb颜色空间的原图中取出来,并且摒弃Y分量
   
    cb=CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3+2);
    cr=CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3+1);
    y = CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3);
    if( r == g && r == b && r == 0 )
    {
     SkinArea ++;   //累计皮肤区域面积,有多少个点是皮肤颜色就是面积
     Pc_skin[int(cr/crPerLen)][int(cb/cbPerLen)] += 1; // 统计在对应区间的颜色的拥有的个数,即直方图CrCb;
    }
    else
    {
     NPc_skin[int(cr/crPerLen)][int(cb/cbPerLen)] += 1;
    }
   }
   pathsrc[5]++;
   path[5]++;
   if(imgsrc) cvReleaseImage( &imgsrc );
   if(imgmask) cvReleaseImage( &imgmask);
   if(imgmask) cvReleaseImage( &imgYCrCb);

}
imgmask = cvLoadImage("data/a.bmp", 1);
CvSize sz;
sz.height = 64, sz.width = 64;
double shit = 0;
IplImage *x = cvCreateImage(sz, 8, 3);
Pskin = double(SkinArea)/double(4*imgmask->height*imgmask->width); // P(skin)
NPskin = 1 - Pskin; // P(-skin)
CvScalar d;
for( j = 0; j < CrRows; j++) for( k = 0; k < CbRows; k ++)
{
   Pc_skin[j][k] /= double(SkinArea), NPc_skin[j][k] /= double(SkinArea);
}


// P(skin): Pskin;
// P(-skin): NPskin;
// P(c|skin): Pc_skin;
// P(c|-skin): NPc_skin;

double theta, Bayesclsfy; //门限
if(Pskin != 0 ) theta = NPskin/Pskin;
else theta = 10000000000;
int crs, cbs;
cvNamedWindow("SkinDetect_Bayes");
cvNamedWindow("SkinDetect_RGB");
int cnt = 0;
while ( imgsrc = cvQueryFrame(cap) )
{
  
   cvCvtColor(imgsrc, imgYCrCb,CV_BGR2YCrCb);
   cvCopyImage(imgsrc, imgmask);

   Sleep(1000);
   for( j = 0; j < imgsrc->height; j++) for( k = 0; k < imgsrc->width; k ++)
   {   

    // 贝叶斯方法肤色区域检测
    cb=CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3+2);
    cr=CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3+1);
    y = CV_IMAGE_ELEM(imgYCrCb,uchar,j,(k)*3);
    crs = int(cr/crPerLen), cbs = int(cb/cbPerLen);
    Bayesclsfy = (Pc_skin[crs][cbs]*Pskin)/(NPc_skin[crs][cbs]*NPskin);
    if(Pc_skin[crs][cbs]/NPc_skin[crs][cbs] > theta * 0.095 )
    {

    }
    else
    {
     CV_IMAGE_ELEM(imgsrc,uchar,j,(k)*3+2) = 0;
     CV_IMAGE_ELEM(imgsrc,uchar,j,(k)*3+1)= 0;
     CV_IMAGE_ELEM(imgsrc,uchar,j,(k)*3) = 255;
    }

    r = CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+2) ;
    g = CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+1);
    b = CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3) ;

    //RGB量化检测肤色
    if( r > 95 && g > 40 && b > 20 && maxof3(r,g,b) - minof3(r,g,b) > 15 && abs(r-g) > 15 && r > b )
    {
     CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+2) = 0;
     CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3+1)= 0;
     CV_IMAGE_ELEM(imgmask,uchar,j,(k)*3) = 255;
    }
   }
   cvPutText (imgmask,"JiangRuiYing, 20921069",cvPoint( 10,23 ), &font, cvScalar(0,0,255));
   cvPutText (imgsrc,"JiangRuiYing, 20921069",cvPoint( 10,23 ), &font, cvScalar(0,0,255));
   cvShowImage("SkinDetect_Bayes", imgsrc);
   cvShowImage("SkinDetect_RGB", imgmask);
  
   cvWaitKey(300);
}
if(imgsrc) cvReleaseImage( & imgsrc );
if(imgYCrCb) cvReleaseImage( &imgYCrCb );
if(imgmask) cvReleaseImage( &imgmask );
return 0;
}

 

// 主要是用了bayes方法的统计,统计处肤色大致的在cbcr这个二维空间上聚集的一些地方,另外照片的选取和mask的处理也比较的重要,和结果直接挂钩的.

其方法主要是这样的:

王东辉 老师入市说到:

P(skin)的计算方法:

任意挑选N幅图片,手工标注(比如利用photoshop软件)其中的皮肤区域和非皮肤区域,并另存为mask图像文件。然后编写程序,读取该mask文件。统计N幅图片中皮肤区域的总面积与所有图片总面积的比值,得到皮肤先验P(skin)。非皮肤先验P(~skin)=1-P(skin)。

P(c|skin)的计算方法:

对于N幅图片,根据相应的mask文件,读取每幅图片中皮肤区域的像素点RGB颜色值。转换RGB到合适的颜色坐标系统(比如HSV、YCbCr等),舍弃亮度维(V、Y等),在剩下的两维颜色空间中(HS、CbCr等)统计皮肤区域像素点的分布。假设你量化每个颜色坐标为64级,那么你可以得到P(c|skin)为一个64*64的两维数组。注意P(c|skin)需要归1化处理!

 

恩, 结果还是比较不错的比较起那个RGB量化方法的好的太多了. 贴图如下:

输入图片:

 




原创粉丝点击