基本思路:
图像处理中,最常用的颜色空间是RGB模型,常用于颜色显示和图像处理,三维坐标的模型形式,非常容易被理解。而HSV模型,是针对用户观感的一种颜色模型,侧重于色彩表示,什么颜色、深浅如何、明暗如何。将RGB转换为HSV模型,H是色彩、S是深浅, S = 0时,只有灰度、V是明暗,表示色彩的明亮程度。为解决过曝光问题,我们拟在V分量上做统计处理。
将得到的人脸图像从RGB空间转换到HSV空间,同时在HSV空间的V分量上利用局部均值直方图指标进行判定,具体实现为:先去除噪声,在V(0-255)分量人脸图像采用8*8小窗口遍历整个图像,求得该窗口里像素的均值,若此均值在区间[55,240](可适当调整)外, 则认为该局部区域为过曝光区域,以此遍历整个V分量并统计过曝光区域个数,最终除以总的遍历次数得到最终评判指标,若该指标大于预先设定的阈值则认为该人脸图像为过曝光,反之,则为正常图像。
//HSV颜色空间上图像过曝光检测,如果图像过曝光,返回过曝光值
double overExposeDetect(Mat imgMat){
Mat hsvSpaceImage;
Mat hsvImageVChannels;
vector<Mat> channels;
double thre = 0.175; //阈值
if (imgMat.channels() != 1)
{
cvtColor(imgMat, hsvSpaceImage, CV_BGR2HSV); // hsv转换
}else
{
hsvSpaceImage = imgMat.clone();
}
split(hsvSpaceImage, channels); // 分离颜色通道
hsvImageVChannels = channels.at(2); // 得到V通道图像
unsigned int step = 8; // 以8*8小窗口遍历V通道图像, V通道包含亮度信息
uchar *pt;
int imageOverExposeBlockNum = 0;
int imageOverExposeBlocks = 0;
unsigned int rowTraNum = hsvImageVChannels.rows/step * step;
unsigned int colTraNum = hsvImageVChannels.cols/step * step;
//遍历
for (unsigned int i = 0; i < rowTraNum; i += step)
{
pt = hsvImageVChannels.ptr<uchar>(i);
for (unsigned int j = 0; j < colTraNum; j += step)
{
Mat imageBlock = Mat(hsvImageVChannels, Rect(j, i, step, step));
double mea = mean(imageBlock)[0]; // 求小矩形的均值
if (mea > 233.0 || mea < 53.0)
{
//imageOverExposeBlockVec.push_back(Rect(j, i, step, step));
//cout << "Mean: " << mea << endl;
imageOverExposeBlockNum ++;
}
imageOverExposeBlocks ++;
}
}
return imageOverExposeBlockNum/(double)imageOverExposeBlocks * 100; //返回一个归一化到0-100之间的值
}
此方法缺陷:有些图片 不是过曝,但是非常灰暗(即计算出阈值在17.5以下的数),此方法无法筛选出,于是在方法最后加一条判断。
bool overExposeDetect2(Mat imgMat)
{
Mat hsvSpaceImage;
Mat hsvImageVChannels;
vector<Mat> channels;
double thre = 0.175; //阈值
if (imgMat.channels() != 1)
{
cvtColor(imgMat, hsvSpaceImage,CV_BGR2HSV); // hsv转换
}else
{
hsvSpaceImage = imgMat.clone();
}
split(hsvSpaceImage, channels); // 分离颜色通道
hsvImageVChannels = channels.at(2); // 得到V通道图像
unsigned int step = 8; // 以8*8小窗口遍历V通道图像
uchar *pt;
int imageOverExposeBlockNum = 0;
int imageOverExposeBlocks = 0;
unsigned int rowTraNum = hsvImageVChannels.rows/step * step;
unsigned int colTraNum = hsvImageVChannels.cols/step * step;
//遍历
for (unsigned int i = 0; i < rowTraNum; i += step)
{
pt = hsvImageVChannels.ptr<uchar>(i);
for (unsigned int j = 0; j < colTraNum; j += step)
{
Mat imageBlock = Mat(hsvImageVChannels, Rect(j, i, step, step));
double mea = mean(imageBlock)[0]; // 求小矩形的均值
if (mea > 233.0 || mea < 53.0)
{
imageOverExposeBlockNum ++;
}
imageOverExposeBlocks ++;
}
}
if (imageOverExposeBlockNum/(double)imageOverExposeBlocks < thre)
return 0;
else
{
return 1;
}
}
以下为一张普通人脸与一张过曝光人脸图片过曝光指数对比: