python图像增强算法_图像增强算法小结(直方图、Gamma变换、CLAHE 和 Retinex)

这几天一直在做一个图像处理的任务,简单来说就是对一个过曝的图片进行处理,使得被亮光“遮盖”的一些细节信息显现出来。

注:我处理的是医学图像,所以某些细节问题可能不太一样(比如对RGB不考虑,全部转为灰度图)

环境:win10、python 3.6.6、Jupyter Notebook、opencv 3.4.2

相关代码已上传至个人github

一、直方图均衡化(Histogram Equalization)

一个很经典的算法就是利用直方图均衡化的方法。这个内容我在之前的一篇文章中有所提及。首先,直方图就是一种对图的像素强度分布的图形表达方式,它能直观地向我们展示一张图片的像素分布情况。

举一个例子:

经常地,在一张直方图中,可能会存在一个或多个尖峰,而尖峰的高度差非常大的情况。均衡化就是拉伸像素强度的分布范围,增强图像局部的对比度。

换句话说就是 将像素值重新分配。我们需要建立一个映射,将原图的像素分布映射到另一个分布上。而要实现均衡化,映射函数就应该是一个累积分布函数(CDF)

实际上我们不需要知道这个分布函数的内容到底是什么,因为opencv已经给我们封装处理好了。我直接说我的实验结果。

opencv和numpy各有各的实现方式,不过效果差不多。

1.opencv 实现

equ = cv2.equalizeHist(tmp)

复制代码

调用这个函数就行了,全都封装好了。

2.numpy 实现

这里我展示一下手动制作直方图的代码

img = cv2.imread("test.jpg",0)

cv2.normalize(img,img, 0, 255, cv2.NORM_MINMAX)

#归一化

lut = np.zeros(256, dtype = img.dtype )

#创建空的查找表

hist,bins = np.histogram(img.flatten(),256,[0,256])

#计算原图的直方图

#计算累积直方图

cdf = hist.cumsum()

cdf_m = np.ma.masked_equal(cdf,0) #除去直方图中的0值

cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())

#等同于公式lut[i] = int(255.0 *p[i]) 将像素重新分配

cdf = np.ma.filled(cdf_m,0).astype('uint8')

#将掩模处理掉的元素补为0

res = cdf[img] #本质上就是这一行公式

复制代码

效果如下

二、Gamma、Laplace变换

1.Gamma变换非常常见,印象中Lightroom有一个专门的Gamma处理,显示屏的矫正也与gamma变换有关。

公式:

(A是一个常数,通常取1)

具体来说就是将灰度过高或者灰度过低的图片进行修正,增强对比度。变换公式就是对原图像上每一个像素值做乘积运算。

γ>1 时,对图像的灰度分布直方图具有拉伸作用(使灰度向高灰度值延展),而γ<1,对图像的灰度分布直方图具有收缩作用(是使灰度向低灰度值方向靠拢)

【the effect of gamma correction on an image】

适用于对比度较低,相机过曝的情况

2.Laplace变换我觉得起到一个锐化的作用。

在opencv中Lapalce更多用于边缘检测。简单来说就是先构造一个卷积核kernel,然后在图像的每一个区块上分别做变换。

一个详细的数理讲解戳此

3.代码展示

基于伽马变换

#主要用于图像的校正

#将灰度过高或者灰度过低的图片进行修正,增强对比度

img = cv2.imread("test.jpg",0)

cv2.normalize(img,img, 0, 255, cv2.NORM_MINMAX)

#归一化

img_t= cv2.convertScaleAbs(img)

#将格式从uint16转为uint8

fgamma = 120

img_gamma = np.power((img_t/255.0),fgamma)*255.0

复制代码

效果如下:

Laplace变换

#利用图像的二次微分对图像进行锐化

#说实话,这么亮的图当然是没什么用啦

cv2.normalize(crop_1,crop_1, 0, 255, cv2.NORM_MINMAX)

img_a= cv2.convertScaleAbs(crop_1)

kernel = np.array([ [0, -1, 0],

[-1, 5, -1],

[0, -1, 0] ])

img_lap = cv2.filter2D(img_a,cv2.CV_8UC3 , kernel)

复制代码

效果如下(是的没错就成了这样)

三、CLAHE

我已经在之前的文章中谈过CLAHE。这里就不多赘述了,总之CLAHE的效果挺好,调用也是简单方便。只说一下调用式子:

img = cv2.imread("test.jpg",0)

clahe = cv2.createCLAHE(clipLimit=600.0, tileGridSize=(1,1))

img_clahe = clahe.apply(img)

复制代码

clipLimit 是对比度,tileGridSize控制每个处理区域的大小。

处理效果

其实仔细观察的话你们可能会发现,比起前面直方图均衡与Gamma变换处理的结果,这张图里我们看到了第四个点。(在中间那里)

我想可能是因为之前的算法里都只是简单的式子转换,比较简单粗糙,而在自适应的直方图均衡化里,虽然可能不及之前的处理结果中黑白对比显著,但我们没有丧失重要的细节。

四、Retinex

这个是NASA官方的图像处理算法。看了一些示例,效果好得惊人(毕竟卫星图像处理要求很高)。

Retinex 是一种常用的建立在科学实验和科学分析基础上的图像增强方法,它是Edwin.H.Land于1963年提出的。Retinex是由两个单词合成的一个词语,他们分别是retina 和cortex,即视网膜和皮层。Land的retinex模式是建立在以下三个假设之上的:

1.真实世界是无颜色的,我们所感知的颜色是光与物质的相互作用的结果。我们见到的水是无色的,但是水膜—肥皂膜却是显现五彩缤纷,那是薄膜表面光干涉的结果。

2.每一颜色区域由给定波长的红、绿、蓝三原色构成的

3.三原色决定了每个单位区域的颜色。

原理非常的光学,简单来说,参考相机成像原理,入射光源遇到障碍物后会有一部分被物体吸收,而剩下的部分以一定的反射角反射,再进入人眼。

根据Retinex理论,人眼感知物体的亮度取决于环境的照明和物体表面对照射光的反射,其数学表达式为:

指二维平面的两个参数。

对式子1.1两边取对数,得

取对数的一个考虑就是将乘法降为加法,计算更方便。

通常来说,R(x,y)表示了物体的反射性质,即图像内在属性,它携带了很多的图像信息,我们应该最大程度的保留。S(x,y)是我们已经观察并获得的图像数据,L(x,y)是入射光的照射分量。

所以为了得到R,我们必须先估计出S,这也是这个算法的关键所在。Retinex理论的提出者指出这个L(x,y)可以通过对S(x,y)进行高斯模糊而得到。

在这个算法的发展过程中,很多人对算法的效果提出了改进,如早先的单尺度视网膜增强SSR,和多尺度视网膜增强算法MSR,以及后来的带色彩恢复的多尺度视网膜增强算法MSRCR....等等,各种论文都提出了自己的改进方式,在这里我参考了这篇文章自己写了一下MSRCR的函数。

一开始的时候效果并不怎么好,甚至和Laplace的效果都一样(一片空白),不过在参考一些大佬们的使用和研究文章之后,我才明白 Retinex算法适用于较暗的图,而对亮度比较高的图是没什么效果的。

另外Retinex算法还容易出现光晕,可能会影响观察效果。不过在我这个问题里,光晕的干扰并不是很重要。

效果如图:

可以看到这四个点都非常明显地出现了。比起上面的CLAHE的算法,视觉效果更好了一些,当然你也看到了存在着光晕。

相关调用代码如下:

def msrcr(img, sigma,dynamic):

img_msrcr = np.zeros_like(img*1.0)

img = ssr(img,sigma)

## log[R(x,y)]

img_arr = img

mean = np.mean(img_arr)

sum1 = img_arr.sum()

img_arr2 = img_arr * img_arr

sum2 = img_arr2.sum()

var_squ = sum2 - 2*mean*sum1 + 1024*1024*mean*mean

var = np.sqrt(var_squ)

Min = mean - dynamic*var

Max = mean + dynamic*var

for i in range(img.shape[0]):

for j in range(img.shape[1]):

img_msrcr[i][j] = (img[i][j] - Min) / \

(Max-Min)*255

## 溢出判断

if img_msrcr[i][j] > 255:

img_msrcr[i][j] = 255

if img_msrcr[i][j] < 0:

img_msrcr[i][j] = 0

return img_msrcr

sigma = 30

## 指定尺度(模糊的半径)

dy = 2

#Dynamic取值越小,图像的对比度越强。

#一般来说Dynamic取值2-3之间能取得较为明显的增强效果

retinex_msrcr = msrcr(img_rev, sigma,dy)

cv2.normalize(retinex_msrcr,retinex_msrcr, 0, 255, cv2.NORM_MINMAX)

img_c= cv2.convertScaleAbs(retinex_msrcr)

fgamma = 1.4

img_redinex = np.power((img_c/255.0),fgamma)*255.0

## Gamma矫正减少一点光晕

复制代码

你可能感兴趣的:(python图像增强算法)