天天看點

【新】opencv膚色檢測源碼

//第一種:RGB color space 
// skin region location using rgb limitation
void ImageSkin::ImageSkinRGB(const Mat& rgb, Mat& _dst)
{
    assert(rgb.channels() == 3 && _dst.channels() == 3);
 
    static const int R=2;
    static const int G=1;
    static const int B=0;
 
    Mat dst = Mat(_dst.size(), _dst.type(), 3);
    dst.setTo(Scalar(0, 0, 0));
 
    for (int h=0; h<rgb.rows; h++)
    {
        for (int w=0; w<rgb.cols; w++)
        {
            // 數組的一份拷貝
            Vec3b prgb = rgb.at<Vec3b>(h, w);
            Vec3b pdst = dst.at<Vec3b>(h, w);
            //auto* pdst111 = &dst.at<Vec3b>(h, w);
 
            if ((prgb[R]>95 && prgb[G]>40 && prgb[B]>20 &&
                    prgb[R]-prgb[B]>15 && prgb[R]-prgb[G]>15) || //uniform illumination
                    (prgb[R]>200 && prgb[G]>210 && prgb[B]>170 &&
                     abs(prgb[R]-prgb[B])<=15 && prgb[R]>prgb[B] && prgb[G]>prgb[B])
               )
            {
                // 指派切不可用pdst!
                memcpy(&dst.at<Vec3b>(h, w), &prgb/*rgb.at<Vec3b>(h, w)*/, 3);
                // 同上
                //dst.at<Vec3b>(h, w)[R] = rgb.at<Vec3b>(h, w)[R];
                //dst.at<Vec3b>(h, w)[G] = rgb.at<Vec3b>(h, w)[G];
                //dst.at<Vec3b>(h, w)[B] = rgb.at<Vec3b>(h, w)[B];
            }
        }
    }
    dst.copyTo(_dst);
    dst.release();
}
 
//第二種:RG color space 
// skin detection in rg space 
void ImageSkin::ImageSkinRG(const Mat& rgb, Mat& gray)
{
    assert(rgb.channels() == 3 && gray.channels() == 1);
 
    const int R=2;
    const int G=1;
    const int B=0;
 
    double Aup=-1.8423;
    double Bup=1.5294;
    double Cup=0.0422;
    double Adown=-0.7279;
    double Bdown=0.6066;
    double Cdown=0.1766;
 
    for (int h=0; h<rgb.rows; h++)
    {
        for (int w=0; w<rgb.cols; w++)
        {
            auto* pGray = &gray.at<uchar>(h, w);
            auto pRGB = rgb.at<Vec3b>(h, w);
 
            int s=pRGB[R]+pRGB[G]+pRGB[B];
            double r=(double)pRGB[R]/s;
            double g=(double)pRGB[G]/s;
            double Gup=Aup*r*r+Bup*r+Cup;
            double Gdown=Adown*r*r+Bdown*r+Cdown;
            double Wr=(r-0.33)*(r-0.33)+(g-0.33)*(g-0.33);
 
            if (g<Gup && g>Gdown && Wr>0.004)
            {
                *pGray=255;
            }
            else
            {
                *pGray=0;
            }
 
        }
    }
}
//
//第三種:otsu門檻值化【僅限單通道】
// reference: Rafael C. Gonzalez. Digital Image Processing Using MATLAB
void ImageSkin::ImageThresholdOtsu(const Mat& src, Mat& dst)
{
    int height=src.rows;
    int width=src.cols;
 
    // 統計直方圖
    float histogram[256]= {0};
    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width; j++)
        {
            uchar p = src.at<uchar>(i, j);
            histogram[p]++;
        }
    }
 
    // 直方圖歸一化
    int size=height*width;
    for(int i=0; i<256; i++)
    {
        histogram[i]=histogram[i]/size;
    }
 
    // 求像素平均值
    float avgValue=0;
    for(int i=0; i<256; i++)
    {
        avgValue+=i*histogram[i];
    }
 
    int tH;
    float maxVariance=0;
    float w=0,u=0;
    for(int i=0; i<256; i++)
    {
        w+=histogram[i];
        u+=i*histogram[i];
 
        float t=avgValue*w-u;
        float variance=t*t/(w*(1-w));
        if(variance>maxVariance)
        {
            maxVariance=variance;
            tH=i;
        }
    }
 
    threshold(src, dst, tH, 255, CV_THRESH_BINARY);
}
//
//第四種:Ycrcb之cr分量+otsu門檻值化 
void ImageSkin::ImageSkinOtsu(const Mat& src, Mat& dst)
{
    assert(dst.channels() == 1 && src.channels() == 3);
 
    Mat ycrcb = Mat(src.size(), src.type(), 3);
    Mat yCRcb[3];
    
    cvtColor(src, ycrcb, CV_BGR2YCrCb);
    split(ycrcb, yCRcb);
 
    ImageThresholdOtsu(yCRcb[1], yCRcb[1]);
    yCRcb[1].copyTo(dst);
 
    yCRcb[1].release();
    ycrcb.release();
}
//
//第五種:YCrCb中133<=Cr<=173 77<=Cb<=127 
void ImageSkin::ImageSkinYUV(const Mat& src, Mat& dst)
{
    Mat ycrcb = Mat(src.size(), src.type(), 3);
    cvtColor(src, ycrcb, CV_BGR2YUV);   // CV_BGR2YCrCb
 
    static const int Cb=2;
    static const int Cr=1;
    static const int Y=0;
 
    dst.setTo(Scalar(0, 0, 0));
 
    for (int h=0; h<src.rows; h++)
    {
        for (int w=0; w<src.cols; w++)
        {
            const Vec3b* pycrcb = &ycrcb.at<Vec3b>(h, w);
            const Vec3b* psrc = &src.at<Vec3b>(h, w);
            //const uchar* pdst = &dst.at<uchar>(h, w);
 
            if ((*pycrcb)[Cr]>=133 && (*pycrcb)[Cr]<=173 && (*pycrcb)[Cb]>=77 && (*pycrcb)[Cb]<=127)
            {
                memcpy(&dst.at<Vec3b>(h, w), psrc, 3);
            }
        }
    }
 
}      

測試代碼:

int _tmain(int argc, _TCHAR* argv[])
{
    Mat img = imread("..\\test.png", 1);        
    Mat dstRGB = Mat(img.size(), CV_8UC3);    
    Mat dstRG = Mat(img.size(), CV_8UC1);     
    Mat dst_crotsu = Mat(img.size(), CV_8UC1);       
    Mat dst_YUV = Mat(img.size(), CV_8UC3);       
 
    namedWindow("Original WIN", CV_WINDOW_AUTOSIZE);      
    imshow("Original WIN", img);   
    waitKey(0);      
 
    ImageSkin ImgS;  
    ImgS.ImageSkinRGB(img, dstRGB);   
    namedWindow("ImageSkinRGB WIN", CV_WINDOW_AUTOSIZE);      
    imshow("ImageSkinRGB WIN", dstRGB);  
    imwrite("..//ImageSkinRGB.jpg", dstRGB);
    waitKey(0);  
 
    ImgS.ImageSkinRG(img, dstRG);    
    namedWindow("ImageSkinRG WIN", CV_WINDOW_AUTOSIZE);      
    imshow("ImageSkinRG WIN", dstRG);
    imwrite("..//ImageSkinRG.jpg", dstRG);
    waitKey(0);   
 
    ImgS.ImageSkinOtsu(img, dst_crotsu);      
    namedWindow("ImageSkinOtsu WIN", CV_WINDOW_AUTOSIZE);      
    imshow("ImageSkinOtsu WIN", dst_crotsu); 
    imwrite("..//ImageSkinOtsu.jpg", dst_crotsu);
    waitKey(0);  
 
    ImgS.ImageSkinYUV(img, dst_YUV);      
    namedWindow("ImageSkinYUV WIN", CV_WINDOW_AUTOSIZE);      
    imshow("ImageSkinYUV WIN", dst_YUV);  
    imwrite("..//ImageSkinYUV.jpg", dst_YUV);
    waitKey(0);  
 
    return 0;
}      

結果顯示如圖:

【新】opencv膚色檢測源碼

本文代碼參考了這位大牛的部落格

點選打開連結

,我将它們修改為C++風格,便于自己和其他也習慣C++風格的朋友使用

繼續閱讀