文章目录
- 对比度增强
- 一、对比度增强的方法?
- 二、各方法的原理
- 1.线性变换
- 2.直方图正规化
- 3.伽马变换
- 4.全局直方图均衡化
- 5.限制对比度的自适应直方图均衡化
- 总结
对比度增强
在图像处理中,由于获取的图像质量不好,需要通过对比度增强来提升图片质量,主要解决的是由于图像灰度级范围较小造成的对比度较低的问题,作用是使图像的灰度级范围放大,从而让图像更加清晰。
本文所总结的内容出自张平的《opencv算法精讲》
一、对比度增强的方法?
对比度提升的几种常用方法:
线性变换、直方图正规化、伽马变换、全局直方图均衡化、限制对比度的自适应直方图均衡化等。
二、各方法的原理
1.线性变换
我们知道简单的线性方程公式是 y = a*x+b,对于图像亮度提升来说,此时的x,y都是二维矩阵,通过系数a来调整原始图像中的图像灰度级的范围(指的是图像转换为灰度图后的范围 [最小灰度值,最大灰度值] )
当0<a<1时,图像灰度级方位缩小,当a>0时,图像灰度级范围扩大。
当b>0时,亮度增加,当b<0时,亮度减小。
代码如下:
/*
线性变换
变换后的矩阵B = a*原矩阵A+b
image:原图像
rtype:输出矩阵的数据类型,CV_8U等
a:上述公式中的a
b:上述公式中的b
*/
Mat LinearTransform(const Mat& image, int rtype, double a, double b) {
Mat Out;
image.convertTo(Out, rtype, a, b);
return Out;
}
2.直方图正规化
直方图正规化其实是一种自动选取a和b的线性变换的方法。
假设输入矩阵 I,高为H、宽为W,I(r,c)代表第r行第c列的灰度值,将 I 中的最小灰度值记为 I min,最大值记为 I max,即 I(r,c)∈[ I min, I max],为使输出矩阵 ***O***的灰度级范围为[ O min, O max],则 I(r,c)和 O(r,c)的映射关系如下:
其中,,代表第r行第c列的灰度值。这个过程就是直方图正规化。因为,所以,一般令,。我们将上面的公式展开后花间,可达到线性变换的a和b,
代码如下:
Mat HistgramNormalization(Mat gray_image)
{
//获取输入矩阵的最大值和最小值
double Imax, Imin;
minMaxLoc(gray_image, &Imin, &Imax, NULL, NULL);
// 定义输出矩阵的最大值和最小值
double Omin = 0, Omax = 255;
// 确定映射关系函数中的系数,其实就是确定线性变换中的系数a和b
double a = (Omax - Omin) / (Imax - Imin);
double b = Omin - a * Imin;
Mat Out;//输出矩阵
convertScaleAbs(gray_image, Out, a, b);//进行线性变换
return Out;
}
3.伽马变换
伽马变换的第一步是将灰度值归一化带的范围。归一化后的图像矩阵记为,高为H,宽为W,第r行第c列的值记为,输出矩阵击为,伽马变换就是:
当时,图像不变,当时,可以增加对比度,当 时,可以降低对比度,接下来我们在看一下Gamma变换的的图示,这样理解起来容易点。下图中从左上角到右下角的曲线为,a∈[0.125,0.25,0.5,1,2,4,8],其实就是高中的幂函数,只是定义域在[0,1]之间,
代码如下:
Mat GammaTransform(Mat gray_image, double gamma)
{
//1.归一化
Mat fI;
gray_image.convertTo(fI, CV_64F, 1 / 255, 0);
//2.伽马变换,其实就是幂运算,gamma的区间为(0,1)
Mat Out;
pow(fI, gamma, Out);
//3.再次进行线性变换,转换为0,255之间的像素值
Out.convertTo(Out, CV_8U, 255, 0);
return Out;
}
4.全局直方图均衡化
假设输入的图像为 ,高为,宽为,代表的灰度直方图,代表灰度值等于k的像素点的个数,其中。全局直方图均衡化的操作就是对图像进行改变,使得输出图像的灰度直方图是‘平的’,即每一个像素值的个数是‘相等’的。这里的相等不是严格意义上的相等,而是‘约等于’,即,那么对于任意灰度级p,,总能找到q,,使得
其中 和称为和的累加直方图,又因为,所以
化简上式得
上式给出了一个从亮度级为p的输入像素到亮度级为q的输出像素的映射关系,p代表的是I(r,c),是原矩阵第r行第c列的像素值,此时令p=I(r,c);q代表的是O(r,c),是输出矩阵的第r行第c列的像素值,将两个进行替换得到最终的映射关系
代码如下:
Mat equalHist(Mat image)
{
//检查图像数据类型是否符合要求,如不符合,则抛出异常
// CV_Assert的官方简要说明如下
// Checks a condition at runtime and throws exception if it fails
CV_Assert(image.type() == CV_8UC1);
// 灰度图像的宽和高
int rows = image.rows;
int cols = image.cols;
//第一步:计算图像的灰度直方图
Mat gray_hist = CalcGrayHist(image);
//第二步:计算累加灰度直方图
Mat zero_comu_moment = Mat::zeros(Size(256, 1), CV_32SC1); //生成一行256列的零矩阵
for (int p = 0; p < 256; p++)
{
if (p==0)
{
zero_comu_moment.at<int>(0, p) = gray_hist.at<int>(0, 0);
}
else
{
zero_comu_moment.at<int>(0, p) = zero_comu_moment.at<int>(0, p - 1) + gray_hist.at<int>(0, p);
}
}
//第三步:根据累加直方图得到输入灰度级和输出灰度级直方图的映射关系
Mat output_q = Mat::zeros(Size(256, 1), CV_8UC1);
float cofficient = 256.0 / (rows * cols);
for (int p = 0; p < 256; p++)
{
float q = cofficient * zero_comu_moment.at<int>(0, p) - 1;
if (q>=0)
{
output_q.at<uchar>(0, p) = uchar(floor(q));
}
else
{
output_q.at<uchar>(0, p) = 0;
}
}
//第四步:得到直方图均衡化后的图像
Mat equal_hist_image = Mat::zeros(image.size(), CV_8UC1);
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < cols; c++)
{
int p = image.at<uchar>(r, c);
//根据原像素值p获得新的像素值q重新赋值给对应位置
equal_hist_image.at<uchar>(r, c) = output_q.at<uchar>(0, p);
}
}
return equal_hist_image;
}
5.限制对比度的自适应直方图均衡化
自适应直方图均衡化首先将图像划分为不重叠的区域块,然后对每一个快进行直方图均衡化,同时为了避免噪声被放大,提出了“限制对比度”,即如果某个像素点的统计数据超过某个界限,便将多余的数量平均分配到每一个像素点上。
由此我们可以大致总结其流程:
1.将图像分割成不重叠的区域块;
2.对每个区域块进行直方图均衡化;
3.为避免噪声的出现,设置一个像素点出现的最高数量,如果超过这个数量,将多出来的平均分配到每个像素点上。
代码如下:
Mat AdaptEqualHist(Mat gray_image)
{
//构建CLAHE对象,其默认设置“限制对比度”为40,块的大小为8*8
Ptr<CLAHE> clahe = createCLAHE(2.0, Size(8, 8));
Mat dst;
//限制对比度的自适应直方图均衡化
clahe->apply(gray_image, dst);
return dst;
}
总结
以上为亮度提升的一些基础算法,个人认为亮度提升的关键是要提升图像像素级的范围和亮度值,最基础的就是一个的线性变换。