//第一种: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; }
结果显示如图:
本文代码参考了这位大牛的博客点击打开链接,我将它们修改为C++风格,便于自己和其他也习惯C++风格的朋友使用