算法来源于GIMP中的Color-enhance插件,主要思路就是对HSV空间的V值进行重新量化。其代码是开源的,但用起来不太方便,所以我用Opencv重新改写了一下。
1.图像转换到CMY空间,每个像素点(三通道,包含C、M、Y三个值)各减去最小值,即C -=min(C,M,Y),M -=min(C,M,Y),Y -=min(C,M,Y)。
2.1中得到的图像转换到HSV空间。
3.计算V的极大极小值,重新量化V值:newPixel = oldPixel / 255- vMin) / (vMax - vMin)。
4.按原步骤,将图像转换回去,得到颜色增强的图像。
环境:win10 x64,vs2013+OpenCV2.4.13
1.图像转换到CMY空间,每个像素点(三通道,包含C、M、Y三个值)各减去最小值
vector BGR;
split(img, BGR);
BGR[0] = 255 - BGR[0];
BGR[1] = 255 - BGR[1];
BGR[2] = 255 - BGR[2];
Mat CMY(img.size(), CV_8UC3);
merge(BGR, CMY);
Mat minMat(img.size(), CV_8UC1);
for (int i = 0; i < height; i++)
{
Vec3b *pImg = CMY.ptr(i);
uchar *pMin = minMat.ptr(i);
for (int j = 0; j < width; j++)
{
min = pImg[j][0];
if (pImg[j][1] < min) min = pImg[j][1];
if (pImg[j][2] < min) min = pImg[j][2];
pMin[j] = min;
for (int k = 0; k < 3; k++)
{
pImg[j][k] -= min;
}
}
}
2.计算V的极大极小值。
void find_vhi_vlo(const Mat &img, double &vhi, double &vlo)
{
int width = img.cols;
int height = img.rows;
uchar min;
vector BGR;
split(img, BGR);
//conver to CMY
BGR[0] = 255 - BGR[0];
BGR[1] = 255 - BGR[1];
BGR[2] = 255 - BGR[2];
Mat CMY(img.size(), CV_8UC3);
merge(BGR, CMY);
for (int i = 0; i < height; i++)
{
Vec3b *pImg = CMY.ptr(i);
for (int j = 0; j < width; j++)
{
min = pImg[j][0];
if (pImg[j][1] < min) min = pImg[j][1];
if (pImg[j][2] < min) min = pImg[j][2];
for (int k = 0; k < 3; k++)
{
pImg[j][k] -= min;
}
}
}
Mat HSV = Mat(CMY.size(), CV_8UC3);
cvtColor(CMY, HSV, COLOR_BGR2HSV);
vector vHSV;
split(HSV, vHSV);
////find Vmin and Vmax
double vMin = 1.0;
double vMax = .0;
for (int i = 0; i < height; i++)
{
uchar *pImg = vHSV[2].ptr(i);
for (int j = 0; j < width; j++)
{
double v = (double)pImg[j] / 255.0;
if (v > vMax) vMax = v;
if (v < vMin) vMin = v;
}
}
vhi = vMax;
vlo = vMin;
}
3.重新量化V值:newPixel = oldPixel / 255- vMin) / (vMax - vMin)。
void quantizing_v(Mat &img, double vMax, double vMin)
{
if (vMax == vMin) return;
if (img.channels() != 1) return;
int width = img.cols;
int height = img.rows;
for (int i = 0; i < height; i++)
{
uchar *pImg = img.ptr(i);
for (int j = 0; j < width; j++)
{
double newPixel = ((double)pImg[j] / vdelta - vMin) / (vMax - vMin);
int tmp = int(newPixel * 255);
if (tmp >255)
pImg[j] = 255;
else if (tmp < 0)
pImg[j] = 0;
else
pImg[j] = (uchar)tmp;
}
}
}
4.按原步骤,将图像转换回去,得到颜色增强的图像。
cvtColor(hsv, dst, COLOR_HSV2BGR);
for (int i = 0; i < height; i++)
{
Vec3b *pDst = dst.ptr(i);
uchar *pMin = minMat.ptr(i);
for (int j = 0; j < width; j++)
{
for (int k = 0; k<3; k++)
{
int tmp = pDst[j][k] + pMin[j];
if (tmp > 255)
pDst[j][k] = 0;
else
pDst[j][k] = 255 - tmp;
}
}
}
当然,这个算法比较简单,当图像亮度极大值比较小时,容易出现严重失真。并且,因为是全局处理的,当处理人像时,人的肤色部分容易出现过饱和现象。
附自己写的工程,结构比较乱,勉强能用。
测试工程
如果采用以上算法,对大像素图片的处理比较耗时消耗Cpu,幸好opencv提供了GPU的加速功能,利用OpenCV+Cuda,可以很好的实现加速功能,降低Cpu消耗。
自己写的供参考