由于项目需要,这几天找了网上一个基于opencv的图像对比度增强算法的博客。但算法发布的日期太过久远了,2012年的代码放到现在很多类和类方法已经不再适用于新版本的opencv库了。所以我花了点时间重写了一下,并加入一些个人对于算法的理解与优化。
目标原代码:
《Opencv 图像增强算法 图像检测结果》
https://blog.youkuaiyun.com/abcjennifer/article/details/7401921
《opencv 彩色图像对比度增强》
https://blog.youkuaiyun.com/abcjennifer/article/details/7428737
对等重写:
对等重写就是原博客怎么写,我就怎么写,只是把一些已经弃用的类、方法、宏给替换掉而已。相当于就是翻新,然后加入一些个人理解的注释。
增强函数
/***********************************************************
增强算法的原理在于先统计每个灰度值在整个图像中所占的比例
然后以小于当前灰度值的所有灰度值在总像素中所占的比例,作为增益系数
对每一个像素点进行调整。由于每一个值的增益系数都是小于它的所有值所占
的比例和。所以就使得经过增强之后的图像亮的更亮,暗的更暗。
************************************************************/
void ImageStretchByHistogram(const Mat & src, Mat & dst)
{
//判断传入参数是否正常
if (!(src.size().width == dst.size().width))
{
cout << "error" << endl;
return;
}
double p[256], p1[256], num[256];
memset(p, 0, sizeof(p));
memset(p1, 0, sizeof(p1));
memset(num, 0, sizeof(num));
int height = src.size().height;
int width = src.size().width;
long wMulh = height * width;
//统计每一个灰度值在整个图像中所占个数
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
uchar v = src.at<uchar>(y, x);
num[v]++;
}
}
//使用上一步的统计结果计算每一个灰度值所占总像素的比例
for (int i = 0; i < 256; i++)
{
p[i] = num[i] / wMulh;
}
//计算每一个灰度值,小于当前灰度值的所有灰度值在总像素中所占的比例
//p1[i]=sum(p[j]); j<=i;
for (int i = 0; i < 256; i++)
{
for (int k = 0; k <= i; k++)
p1[i] += p[k];
}
//以小于当前灰度值的所有灰度值在总像素中所占的比例,作为增益系数对每一个像素点进行调整。
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++) {
uchar v = src.at<uchar>(y, x);
dst.at<uchar>(y, x) = p1[v] * 255 + 0.5;
}
}
return;
}
单通道灰度图增强函数
//调整图像对比度
Mat AdjustContrastY(const Mat & img)
{
Mat out = Mat::zeros(img.size(), CV_8UC1);
Mat workImg = img.clone();
//对图像进行对比度增强
ImageStretchByHistogram(workImg, out);
return Mat(out);
}
三通道彩图增强函数
//调整图像对比度
Mat AdjustContrast(const Mat & img)
{
Mat out;
Mat Y = Mat::zeros(img.size(), CV_8UC1);
Mat Cb = Mat::zeros(img.size(), CV_8UC1);
Mat Cr = Mat::zeros(img.size(), CV_8UC1);
Mat Compile_YCbCr = Mat::zeros(img.size(), CV_8UC3);
Mat dst = Mat::zeros(img.size(), CV_8UC1);
int i;
/*******************************************
对比度增强算法本质上就是对图像Y分量进行调整。
所以需要先把图像数据从BGR转换为YUV,YCrCb就是YUV
然后再对YUV数据进行拆分,最后单独对Y分量进行调整。
********************************************/
cvtColor(img, Compile_YCbCr, cv::COLOR_BGR2YCrCb);
//定义一个Mat向量容器保存拆分后的数据
vector<Mat> channels;
//进行图像通道拆分
split(Compile_YCbCr, channels);
channels.at(0).copyTo(Y);
channels.at(1).copyTo(Cb);
channels.at(2).copyTo(Cr);
//单独对Y分量进行调整
ImageStretchByHistogram(Y, dst);
//把对比度增强后的Y分量与原来的U、V分量重组为新的图像
for (int y = 0; y < img.size().height; y++)
{
for (int x = 0; x < img.size().width; x++)
{
//拼接一个像素的三通道
Mat cur = Mat::zeros(3, 1, CV_32FC1);
cur.at<float>(0, 0) = dst.at<uchar>(y, x);
cur.at<float>(1, 0) = Cb.at<uchar>(y, x);
cur.at<float>(2, 0) = Cr.at<uchar>(y, x);
//三通道顺序写入
for (i = 0; i < 3; i++)
{
double xx = cur.at<float>(i, 0);
(Compile_YCbCr).at<Vec3b>(y, x)[i] = xx;
}
}
}
//把重组之后的图像转换回BGR数据进行返回
cvtColor(Compile_YCbCr, out, COLOR_YCrCb2BGR);
return Mat(out);
}
优化重写
优化重写注意是针对三通道彩图增强函数“AdjustContrast()”这个函数的,这主要是由于原算法本身就存在一定的问题和在当前的opencv版本下存在一定的改进空间。就像原博客中的dst1定义的是3通道,但增强函数“ImageStretchByHistogram()”内部处理的时候只是使用了当通道进行赋值,虽然可以正常运行,但却容易让阅读代码的人产生误解(例如我),而且在新版本opencv的语法环境下会存在问题(原博客评论区就有人遇到了这个语法问题,我上面的代码中通过修改部分代码逻辑解决了这个问题)。且在测试中发现,三通道彩图的增强函数运行速度过慢。
所以我针对三通道彩图增强函数“AdjustContrast()”这个函数进行了重写。
//三通道彩图增强函数
Mat AdjustContrast(const Mat & img)
{
Mat out = Mat::zeros(img.size(), CV_8UC3);
Mat Compile_YCbCr = Mat::zeros(img.size(), CV_8UC3);
Mat dst = Mat::zeros(img.size(), CV_8UC1);
//进行BGR转YUV
cvtColor(img, Compile_YCbCr, cv::COLOR_BGR2YCrCb);
//定义一个Mat向量容器保存拆分后的数据
vector<Mat> channels;
//进行图像通道拆分
split(Compile_YCbCr, channels);
//单独对Y分量进行调整
ImageStretchByHistogram(channels.at(0), dst);
//把调整后的Y分量替换原来的Y分量
dst.copyTo(channels.at(0));
//进行通道的合并
merge(channels, Compile_YCbCr);
//把重组之后的图像转换回BGR数据进行返回
cvtColor(Compile_YCbCr, out, COLOR_YCrCb2BGR);
return Mat(out);
}
灰度效果:
增强前
增强后
彩图效果:
增强前
增强后
相关参考:
《CvMat用法详解》
https://blog.youkuaiyun.com/zx3517288/article/details/51760541
《OpenCV类型CV_32F和CV_32FC1之间的差异(OpenCV类型CV_32F和CV_32FC1之间的差异)》
https://www.it1352.com/541681.html
《opencv Mat 多通道 数据读取或赋值》
https://blog.youkuaiyun.com/xuexiaokkk/article/details/50251511
《openCv:IplImage的变量imageData解释》
https://blog.youkuaiyun.com/aic1999/article/details/83040442
《opencv split和merge操作》
https://blog.youkuaiyun.com/omuyejingfeng1/article/details/25685141
《opencv中cvSplit函数讲解》
https://blog.youkuaiyun.com/yankai0219/article/details/6630911
《opencv笔记——cvCreateImage函数说明》
https://blog.youkuaiyun.com/breeze5428/article/details/30050327
《IplImage结构体 nChannels depth含义》
https://blog.youkuaiyun.com/qq_41972382/article/details/89225522