【图像处理】彩色图像自适应对比度增强(OpenCV实现) 心已赠人 2022-04-18 04:57 847阅读 0赞 ### **【fishing-pan:https://blog.csdn.net/u013921430 转载请注明出处】** ### 提到图像增强,第一印象就是[直方图均衡][Link 1]与直方图规定化,这是最常见的也是非常有效的全局图像增强方法。在前不久的一次组会讨论中,课题组的一位同学提到了“自适应图像增强”,虽然自己以前也用过,但是一时间忘记了原理,就去复习了一下,其实他使用的方法的全称应该叫自适应直方图均衡,对应的是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)构成一张图,其实就是均值滤波的结果,而在《[数字图像傅里叶变换的物理意义及简单应用][Link 2]》中,我提到过均值滤波是一种低通滤波,获得的是图像的低频部分,也就是背景部分, 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))可以实现对图像的高频部分的放大,进而对图像进行增强。 ![在这里插入图片描述][20181108175957362.png_pic_center] 再来看看参数 G G G,经过上面的过程可以看出,如果 G G G 是一个固定参数,比如都取5,通过式子中的局部均值,我们已经能够将图像实现一定程度上的自适应增强了。那么为什么还要在参数G中引入标准差呢? 我们回忆一下对比度增强的初衷,对比度增强是为了让本身对比度不强的图像的对比度变得明显,而对本身对比度很强的图像,是没必要做增强的。那么在同一图像中,我们尤其需要增强对比度不强的部分。而方差表示的是图像的像素值的均匀性,我们可以认为方差越大的局部区域,其像素值越不均匀,对比度越强;反之,方差越小的局部区域,其像素值越均匀,对比度越弱。因此,在参数 G G G中除以了局部标准差,可以让图像中对比度较弱的部分的增强效果更加明显。 其次,如果对整张图像中所有点进行等比例增强,图像中本身就是高频的部分出现过增强的现象,图像看起来十分奇怪。 ## 彩色图像的ACE ## 在网上看到有人说,对彩色图像增强可以分别对RGB三通道进行增强后进行合并。这个观点是错误的,因为分别对各个通道进行增强,会引起图像色相的变化,图像会变的不是其原来的颜色了。 所以需要将图像转到HSI色彩空间,或者是YCrCb颜色空间。前者只需要对I亮度通道进行增强,而H、S分别代表的色调和饱和度通道不需要变化。后者用Y通道表示亮度,只需要对Y通道进行增强即可。增强之后再合并通道,转换回RGB空间便完成了对彩色图像的增强。 ## 代码 ## 在这里我只提供一个**adaptContrastEnhancement**函数的代码,完整的代码也很简单,可以点击[下载][Link 3]。无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() ## 运行结果 ## ![在这里插入图片描述][20181108193422489.png_pic_center] ![在这里插入图片描述][20181108175236431.png_pic_center] 可以明显看出,图像中原先对比度不强的部分,如树干、江面、远景。经过增强后,都有了明显的改善,而本身对比度已经明显的区域,就没有太大的改变。 已完。。 ## 参考 ## 1. [Narendra P M, Fitch R C. Real-time adaptive contrast enhancement\[J\]. IEEE transactions on pattern analysis and machine intelligence, 1981 (6): 655-661.][Real-Time Adaptive Contrast Enhancement] 2. http://www.cnblogs.com/Imageshop/p/3324282.html [Link 1]: https://blog.csdn.net/u013921430/article/details/79884163 [Real-Time Adaptive Contrast Enhancement]: https://ieeexplore.ieee.org/abstract/document/4767166 [Link 2]: https://blog.csdn.net/u013921430/article/details/79934162 [20181108175957362.png_pic_center]: /images/20220418/a35346e8837540529f94386b43f8efc6.png [Link 3]: https://download.csdn.net/download/u013921430/10773437 [20181108193422489.png_pic_center]: /images/20220418/2e1452783e13404fb4b5a90c47d67699.png [20181108175236431.png_pic_center]: /images/20220418/1845f9ac35574bdc9660db2bd2911624.png
还没有评论,来说两句吧...