图像增强目的使得模糊图片变得更加清晰、图片模糊的原因是因为像素灰度差值变化不大,如片各区域产生视觉效果似乎都是一样的, 没有较为突出的地方,看起来不清晰的感觉
解决这个问题的最直接简单办法,放大像素灰度值差值、使图像中的细节更加清晰。
目前较为常用的几个方法:伽马变换、线性变换、分段线性变换、直方图均衡化,对于图像对比度增强,都能取得不错的效果!
本文将对每种方法 简单介绍一下,并借助于 Python 、OpenCV 进行代码实现,提前说一下哈,下面处理的图像对象都是单通道灰度图,不是三通道彩色图!
线性变换的原理是对所有像素值乘上一个扩张因子 f a c t o r factor factor,像素值大的变得越大,像素值小的变得越小,从而达到图像增强的效果,这里利用 Numpy 的数组进行操作;
O ( x , y ) = I ( x , y ) ∗ f a c t o r O ( x , y ) 为 输 出 像 素 值 、 I ( x , y ) 为 输 入 像 素 值 ; O(x,y) = I(x,y)*factor\\ O(x,y)为输出像素值、I(x,y)为输入像素值; O(x,y)=I(x,y)∗factorO(x,y)为输出像素值、I(x,y)为输入像素值;
需要注意的是,像素值最大为255,因此在数组相乘之后需要进行数值截断操作,最终代码如下:
def line_trans_img(img,coffient):
if len(img.shape) == 3:
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
out = 2*img
#像素截断;;;
out[out>255] = 255
out = np.around(out)
return out
这里 f a c t o r factor factor 设置为 2 ,变换结果如下,会看到强光处出现失真效果
(这里对排列图片做一下说明,从左到右依次为 原图灰度图、原图灰度直方图、处理之后的灰度图、处理之后的灰度直方图,以下的图片排列方式相同)
伽马变换对像素值做的是幂次方变换,主要是图像的灰度级发生改变,转换的原理公式为:
O ( x , y ) = I ( x , y ) γ O ( x , y ) 、 I ( x , y ) 定 义 与 前 面 一 致 ; O(x,y) = I(x,y)^\gamma\\ O(x,y)、I(x,y)定义与前面一致; O(x,y)=I(x,y)γO(x,y)、I(x,y)定义与前面一致;
参数 γ \gamma γ 的设定 可以参照下面:
def gama_transfer(img,power1):
if len(img.shape) == 3:
img= cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
img = 255*np.power(img/255,power1)
img = np.around(img)
img[img>255] = 255
out_img = img.astype(np.uint8)
return out_img
这里 Gamma 分别取 1.5,0.5,结果如下:
结果来看,相对来说 γ > 1 \gamma >1 γ>1 对图像增强的结果会更好一点
分段线性分割,提前把图像的灰度级分为几部分,然后对每一部分的像素值做不同的线性变换,像素值基本变换原理:
$$
O(x,y) = \left{
\begin{aligned}
a_1* I(x,y) + b_1 & & 0 a_2* I(x,y) + b_2 & & H<=I(x,y)
\end{aligned}
\right.
$$
这里写的代码总感觉效率特别慢(逐像素改变),知道改进方法的小伙伴们望告知:
def seg_augment_img(img,start,c1,end,c2,b2,c3,b3):
if len(img.shape) == 3:
img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
out_img = np.zeros(img.shape)
for i in range(img.shape[0]):
for j in range(img.shape[1]):
if img[i][j] <start:
out_img[i][j] = img[i][j]*c1
elif img[i][j] < end:
out_img[i][j] = img[i][j] *c2 + b2
else:
out_img[i][j] = img[i][j] * c3 +b3
out_img[out_img>255] = 255
out = np.around(out_img)
out = out.astype(np.uint8)
return out
函数中的参数分别为 50,0.5,150,3.6,-310,0.238,194,结果如下:
每个灰度图像都有自己的灰度直方图,均衡化的原理是,先根据灰度直方图计算累加灰度直方图,根据灰度图与累加灰度图的映射关系关联输入图像与输出图图像的映射关系
映射关系原理如下:
O = ∑ k = 0 p h i s t I ( k ) h ∗ w ∗ 256 − 1 h , w 表 示 图 像 的 高 和 宽 O 表 示 输 出 像 素 、 h i s t ( k ) 表 示 图 像 灰 度 图 中 像 素 为 k 的 个 数 ; O = \frac{\sum_{k=0}^{p}hist_{I}(k)}{h*w}*256 -1\\ h,w表示图像的高和宽\\ O表示输出像素、hist(k)表示图像灰度图中像素为k的个数;\\ O=h∗w∑k=0phistI(k)∗256−1h,w表示图像的高和宽O表示输出像素、hist(k)表示图像灰度图中像素为k的个数;
因此,这里几个重要部分:1,计算出灰度直方图;2,计算累加灰度直方图;3,根据 1 和 2 得到映射关系,最终输出灰度像素值;
def get_imghist(img):
# 判断图像是否为三通道;
if len(img.shape) == 3:
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 无 Mask,256个bins,取值范围为[0,255]
hist = cv2.calcHist([img],[0],None,[256],[0,255])
return hist
def cal_equalhist(img):
if len(img.shape) == 3:
img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
h,w = img.shape[:2]
grathist = get_imghist(img)
zerosumMoment = np.zeros([256],np.uint32)
for p in range(256):
if p ==0:
zerosumMoment[p] = grathist[0]
else:
zerosumMoment[p] = zerosumMoment[p-1] +grathist[p]
output_q = np.zeros([256],np.uint8)
cofficient = 256.0/(h*w)
for p in range(256):
q = cofficient *float(zerosumMoment[p]) - 1
if q >= 0:
output_q[p] = math.floor(q)
else:
output_q[p] = 0
equalhistimage = np.zeros(img.shape,np.uint8)
for i in range(h):
for j in range(w):
equalhistimage[i][j] = output_q[img[i][j]]
# 第二种方法,opencv 库函数自带一种:
#equalhistimage = cv2.equalizeHist(img)
return equalhistimage
结果如下,看起来还是不错的!(这里图片失真是因为灯光的原因)
根据以上几个增强方法来看,针对于本案例选取的图像,线性增强方法相对效果并不太好,可能会适用于其它的种类图像,而 Gamma转换 和直方图均衡化取得相对不错的结果
但图像增强、锐化没有最优方法,每种方法都有自己的特点,需要根据自己选择合适的
最后还是要提醒一下感兴趣的小伙伴们,记得跟着敲一下代码,加深一下应用原理!