OpenCV 基于色彩直方图进行肤色检测

色彩直方图是统计不同色彩在图像中的像素个数。例如,下图的猫咪在RGB色彩空间中每通道的灰度直方图(线条的颜色与其通道相对应)。
OpenCV 基于色彩直方图进行肤色检测_第1张图片

OpenCV 基于色彩直方图进行肤色检测_第2张图片

将图像转为HSV色彩空间(Hue:色调;Saturation:饱和度;Value:亮度),我们可以更稳定地提取特定的颜色。本示例采用HSV色彩空间的H、S通道来进行颜色提取。H、S通道下的色彩直方图二维的展现形式如下(竖列为Hue通道,横行为Saturation通道,图像中的像素亮度越高,表明该色彩在原始图像中出现的频次越高):
OpenCV 基于色彩直方图进行肤色检测_第3张图片

程序执行过程:

  1. 截取若干张不同肤色的图片,最好覆盖常见的所有肤色(如下图);
    OpenCV 基于色彩直方图进行肤色检测_第4张图片
  2. 对所有肤色的图片一起做色彩直方图(利用OpenCV中的calcHist函数);
  3. 新建一个与待检测图片同尺寸的灰度图片,找到待检测图片中每颗像素点的颜色在色彩直方图中对应栅格的数值(即统计中出现的次数),并将该数值赋值予新建灰度图片中与该检测像素同位置的像素(利用OpenCV中的calcBackProject函数)。下图是效果展示(图像中像素亮度越高,待检测图像该位置处是肤色的概率越大):
    OpenCV 基于色彩直方图进行肤色检测_第5张图片
#include 
#include 
#include 
#include 
#include 

void calcBackProject_demo() {
    int num_srcs = 6;
    vector<Mat> hsv_rscs;
    
    for (int i=1; i<num_srcs+1; i++) {
        Mat src = imread("./skin_"+ to_string(i) +".jpg");
        
        if (src.empty()) {
            cout << "Failed to load src image ..." << endl;
        }
        
        if (src.rows > 500 || src.cols > 500) {
            pyrDown(src, src);
        }
        
        Mat hsv_src;
        cvtColor(src, hsv_src, COLOR_BGR2HSV);
        hsv_rscs.push_back(hsv_src);
    }
    
    Mat dest = imread("./skin_test.png");
    
    if (dest.empty()) {
        cout << "Failed to load dest image ..." << endl;
    }
    
    Mat hsv_dest, hist;
    cvtColor(dest, hsv_dest, COLOR_BGR2HSV);
    
    int bin_w = 10;
    const int channels[] = {0,1};
    const int histSize[] = {static_cast<int>(ceil(180/bin_w)),static_cast<int>(ceil(256/bin_w))};
    float h_range[] = {0,180};
    float s_range[] = {0,256};
    const float* ranges[] = {h_range, s_range};
    hist.create(histSize[0], histSize[1], CV_32F);
    
    Mat hist_tmp;
    for (int i=0; i<num_srcs; i++) {
        calcHist(&hsv_rscs[i], 1, channels, Mat(), hist_tmp, 2, histSize, ranges);
        add(hist, hist_tmp, hist);
    }
    
    Mat histImg = Mat::zeros(histSize[0]*bin_w, histSize[1]*bin_w, CV_8UC1);
    normalize(hist, hist, 0, 255, NORM_MINMAX);
    
    double max, min;
    minMaxLoc(hist, &min, &max);
    cout << "rows: " << hist.rows << "\t" << "cols: " << hist.cols << "\t" << "max: " << max << "\t" << "min: " << min << endl << endl;
//    cout << hist << endl << endl;
    
    for (int i=1; i<=histSize[0]; i++) {
        for (int j=1; j<=histSize[1]; j++) {
            rectangle(histImg, Point((j-1)*bin_w, (i-1)*bin_w), Point(j*bin_w, i*bin_w), saturate_cast<int>(hist.at<float>(i-1,j-1)), -1);
        }
    }
    imshow("histImg", histImg);
    
    Mat backProj;
    calcBackProject(&hsv_dest, 1, channels, hist, backProj, ranges);
    imshow("backProj", backProj);
    
    waitKey(0);
    destroyAllWindows();
}

int main(int argc, char** argv){
    calcBackProject_demo();   
    return 0;
}

你可能感兴趣的:(计算机视觉)