//第一種: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++風格的朋友使用