【图像处理】彩色图像自适应对比度增强(OpenCV实现)
【fishing-pan:https://blog.csdn.net/u013921430 转载请注明出处】
提到图像增强,第一印象就是直方图均衡与直方图规定化,这是最常见的也是非常有效的全局图像增强方法。在前不久的一次组会讨论中,课题组的一位同学提到了“自适应图像增强”,虽然自己以前也用过,但是一时间忘记了原理,就去复习了一下,其实他使用的方法的全称应该叫自适应直方图均衡,对应的是Matlab 中的adapthisteq 函数;
我在复习的过程中,偶然发现了另一个图像增强的算法,也就是这篇文章中要提到的——自适应对比度增强(Adaptive Contrast Enhancement,ACE),下面就从原理到实现,来好好聊一下这种方法。
自适应对比度增强
在图像处理的方法中,自适应方法是与图像本身信息相关,根据图像对图特征对图像进行处理的一系列方法,这些方法往往具有更好的鲁棒性、普适性。而本文中提到的这种ACE方法由 N a r e n d r a P M Narendra P M NarendraPM等人在《Real-Time Adaptive Contrast Enhancement》中提到,原理简单易懂,有兴趣的朋友可以点击链接去阅读。
对于图像中的每一个点,分别计算其局部均值与局部标准差;
M
(
i
,
j
)
=
1
(
2
n
+
1
)
(
2
m
+
1
)
∑
s
=
i
−
n
i
+
n
∑
k
=
j
−
m
j
+
m
f
(
s
,
k
)
M(i,j)=\frac{1}{(2n+1)(2m+1)}\sum_{s=i-n}^{i+n}\sum_{k=j-m}^{j+m}f(s,k)
M(i,j)=(2n+1)(2m+1)1s=i−n∑i+nk=j−m∑j+mf(s,k)
σ
2
(
i
,
j
)
=
1
(
2
n
+
1
)
(
2
m
+
1
)
∑
s
=
i
−
n
i
+
n
∑
k
=
j
−
m
j
+
m
(
f
(
s
,
k
)
−
M
(
i
,
j
)
)
2
\sigma ^{2}(i,j)=\frac{1}{(2n+1)(2m+1)}\sum_{s=i-n}^{i+n}\sum_{k=j-m}^{j+m}(f(s,k)-M(i,j))^{2}
σ2(i,j)=(2n+1)(2m+1)1s=i−n∑i+nk=j−m∑j+m(f(s,k)−M(i,j))2
上述式子中,
f
(
s
,
k
)
f(s,k)
f(s,k)代表坐标为
(
s
,
k
)
(s,k)
(s,k)的点的像素值,
M
(
i
,
j
)
M(i,j)
M(i,j)为以点
(
i
,
j
)
(i,j)
(i,j)为中心,窗口大小为
[
(
2
n
+
1
)
,
(
2
m
+
1
)
]
[(2n+1),(2m+1)]
[(2n+1),(2m+1)]的区域的局部均值,对应的
σ
2
(
i
,
j
)
\sigma ^{2}(i,j)
σ2(i,j)为局部的方差,
σ
(
i
,
j
)
\sigma (i,j)
σ(i,j)为局部图像的标准差。
在求得局部均值与标准差后,就可以对图像进行增强了,具体的公式如下;
I
(
i
,
j
)
=
M
(
i
,
j
)
+
G
(
f
(
i
,
j
)
−
M
(
i
,
j
)
)
I(i,j)=M(i,j)+G(f(i,j)-M(i,j))
I(i,j)=M(i,j)+G(f(i,j)−M(i,j))
G
=
α
M
σ
(
i
,
j
)
0
<
α
<
1
G=\alpha \frac{M}{\sigma (i,j)}\qquad 0<\alpha<1
G=ασ(i,j)M0<α<1
上式中,
I
(
i
,
j
)
I(i,j)
I(i,j)为增强后的像素值,
M
M
M为全局均值(你也可以把它设为某一合理数值),
α
\alpha
α是一个系数参数,一般取小于1大于0的小数。
再来分析一下,上面式子的含义;如果将每个点的局部均值
M
(
i
,
j
)
M(i,j)
M(i,j)构成一张图,其实就是均值滤波的结果,而在《数字图像傅里叶变换的物理意义及简单应用》中,我提到过均值滤波是一种低通滤波,获得的是图像的低频部分,也就是背景部分,
f
(
i
,
j
)
−
M
(
i
,
j
)
f(i,j)-M(i,j)
f(i,j)−M(i,j)就可以用来量化图像中的一个点是高频还是低频。而在一般情况下,
G
G
G 都是大于1的,所以通过
G
(
f
(
i
,
j
)
−
M
(
i
,
j
)
)
G(f(i,j)-M(i,j))
G(f(i,j)−M(i,j))可以实现对图像的高频部分的放大,进而对图像进行增强。
再来看看参数
G
G
G,经过上面的过程可以看出,如果
G
G
G 是一个固定参数,比如都取5,通过式子中的局部均值,我们已经能够将图像实现一定程度上的自适应增强了。那么为什么还要在参数G中引入标准差呢?
我们回忆一下对比度增强的初衷,对比度增强是为了让本身对比度不强的图像的对比度变得明显,而对本身对比度很强的图像,是没必要做增强的。那么在同一图像中,我们尤其需要增强对比度不强的部分。而方差表示的是图像的像素值的均匀性,我们可以认为方差越大的局部区域,其像素值越不均匀,对比度越强;反之,方差越小的局部区域,其像素值越均匀,对比度越弱。因此,在参数 G G G中除以了局部标准差,可以让图像中对比度较弱的部分的增强效果更加明显。
其次,如果对整张图像中所有点进行等比例增强,图像中本身就是高频的部分出现过增强的现象,图像看起来十分奇怪。
彩色图像的ACE
在网上看到有人说,对彩色图像增强可以分别对RGB三通道进行增强后进行合并。这个观点是错误的,因为分别对各个通道进行增强,会引起图像色相的变化,图像会变的不是其原来的颜色了。
所以需要将图像转到HSI色彩空间,或者是YCrCb颜色空间。前者只需要对I亮度通道进行增强,而H、S分别代表的色调和饱和度通道不需要变化。后者用Y通道表示亮度,只需要对Y通道进行增强即可。增强之后再合并通道,转换回RGB空间便完成了对彩色图像的增强。
代码
在这里我只提供一个adaptContrastEnhancement函数的代码,完整的代码也很简单,可以点击下载。无C币的可以直接在博客留言,我也会发送到你的邮箱。
C++ 版本代码
//--------------------------
//Adaptive Contrast Enhancement(自适应对比度增强,ACE)
//不用先生,2018.11.08
//
//函数功能:获取图像的局部均值与局部标准差的图
//函数名称:adaptContrastEnhancement
//函数参数:Mat &scr:输入图像,为三通道RGB图像;
//函数参数:Mat &dst:增强后的输出图像,为三通道RGB图像;
//函数参数:int winSize:局部均值的窗口大小,应为单数;
//函数参数:int maxCg:增强幅度的上限;
//返回类型:bool
//--------------------
bool adaptContrastEnhancement(Mat &scr, Mat &dst, int winSize,int maxCg)
{
if (!scr.data) //判断图像是否被正确读取;
{
cerr << "自适应对比度增强函数读入图片有误";
return false;
}
Mat ycc; //转换空间到YCrCb;
cvtColor(scr, ycc, COLOR_RGB2YCrCb);
vector<Mat> channels(3); //分离通道;
split(ycc, channels);
Mat localMeansMatrix(scr.rows , scr.cols , CV_32FC1);
Mat localVarianceMatrix(scr.rows , scr.cols , CV_32FC1);
if (!getVarianceMean(channels[0], localMeansMatrix, localVarianceMatrix, winSize)) //对Y通道进行增强;
{
cerr << "计算图像均值与标准差过程中发生错误";
return false;
}
Mat temp = channels[0].clone();
Scalar mean;
Scalar dev;
meanStdDev(temp, mean, dev);
float meansGlobal = mean.val[0];
Mat enhanceMatrix(scr.rows, scr.cols, CV_8UC1);
for (int i = 0; i < scr.rows; i++) //遍历,对每个点进行自适应调节
{
for (int j = 0; j < scr.cols; j++)
{
if (localVarianceMatrix.at<float>(i, j) >= 0.01)
{
float cg = 0.2*meansGlobal / localVarianceMatrix.at<float>(i, j);
float cgs = cg > maxCg ? maxCg : cg;
cgs = cgs < 1 ? 1 : cgs;
int e = localMeansMatrix.at<float>(i, j) + cgs* (temp.at<uchar>(i, j) - localMeansMatrix.at<float>(i, j));
if (e > 255){ e = 255; }
else if (e < 0){ e = 0; }
enhanceMatrix.at<uchar>(i, j) = e;
}
else
{
enhanceMatrix.at<uchar>(i, j) = temp.at<uchar>(i, j);
}
}
}
channels[0] = enhanceMatrix; //合并通道,转换颜色空间回到RGB
merge(channels, ycc);
cvtColor(ycc, dst, COLOR_YCrCb2RGB);
}
Python 版本代码(2020.03.11)
最近有不少人私信我,问我有没有python版本的代码,所以我晚上写了一份;完整的脚本都在这里了,里面提供了两种求均值方差的方法!
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 11 20:20:47 2020
@author: 不用先生
"""
import numpy as np
import cv2
def getVarianceMean(scr, winSize):
if scr is None or winSize is None:
print("The input parameters of getVarianceMean Function error")
return -1
if winSize % 2 == 0:
print("The window size should be singular")
return -1
copyBorder_map=cv2.copyMakeBorder(scr,winSize//2,winSize//2,winSize//2,winSize//2,cv2.BORDER_REPLICATE)
shape=np.shape(scr)
local_mean=np.zeros_like(scr)
local_std=np.zeros_like(scr)
for i in range(shape[0]):
for j in range(shape[1]):
temp=copyBorder_map[i:i+winSize,j:j+winSize]
local_mean[i,j],local_std[i,j]=cv2.meanStdDev(temp)
if local_std[i,j]<=0:
local_std[i,j]=1e-8
return local_mean,local_std
def adaptContrastEnhancement(scr, winSize, maxCg):
if scr is None or winSize is None or maxCg is None:
print("The input parameters of ACE Function error")
return -1
YUV_img=cv2.cvtColor(scr,cv2.COLOR_BGR2YUV) ##转换通道
Y_Channel = YUV_img[:,:,0]
shape=np.shape(Y_Channel)
meansGlobal=cv2.mean(Y_Channel)[0]
##这里提供使用boxfilter 计算局部均质和方差的方法
# localMean_map=cv2.boxFilter(Y_Channel,-1,(winSize,winSize),normalize=True)
# localVar_map=cv2.boxFilter(np.multiply(Y_Channel,Y_Channel),-1,(winSize,winSize),normalize=True)-np.multiply(localMean_map,localMean_map)
# greater_Zero=localVar_map>0
# localVar_map=localVar_map*greater_Zero+1e-8
# localStd_map = np.sqrt(localVar_map)
localMean_map, localStd_map=getVarianceMean(Y_Channel,winSize)
for i in range(shape[0]):
for j in range(shape[1]):
cg = 0.2*meansGlobal/ localStd_map[i,j];
if cg >maxCg:
cg=maxCg
elif cg<1:
cg=1
temp = Y_Channel[i,j].astype(float)
temp=max(0,min(localMean_map[i,j]+cg*(temp-localMean_map[i,j]),255))
# Y_Channel[i,j]=max(0,min(localMean_map[i,j]+cg*(Y_Channel[i,j]-localMean_map[i,j]),255))
Y_Channel[i,j]=temp
YUV_img[:,:,0]=Y_Channel
dst=cv2.cvtColor(YUV_img,cv2.COLOR_YUV2BGR)
return dst
def main():
img=cv2.imread(input_fn)
if img is None:
print("The file name error,please check it")
return -1
print(np.shape(img))
dstimg=adaptContrastEnhancement(img,15,10)
cv2.imwrite('output.jpg',dstimg)
cv2.waitKey(0)
return 0
input_fn='temp1.jpg'
if __name__ == '__main__':
main()
运行结果
可以明显看出,图像中原先对比度不强的部分,如树干、江面、远景。经过增强后,都有了明显的改善,而本身对比度已经明显的区域,就没有太大的改变。
已完。。
参考
- Narendra P M, Fitch R C. Real-time adaptive contrast enhancement[J]. IEEE transactions on pattern analysis and machine intelligence, 1981 (6): 655-661.
- http://www.cnblogs.com/Imageshop/p/3324282.html
更多推荐
所有评论(0)