1-图像增强(Part1)
目标:处理图像,使处理结果比原图像更适合特定的应用
分类:按照作用域可分为空间域和频率域增强
空间域指图像平面本身,直接对图像像素进行处理,基本方法有亮度(灰度)变换和空间(邻域)滤波
频率域基于傅里叶变换,通过修改图像的傅里叶变换系数完成
一、空间变换
g ( x , y ) = T [ f ( x , y ) ] g(x,y)=T[f(x,y)] g(x,y)=T[f(x,y)]
T是点(x,y)的一个指定邻域上定义的对图像f进行处理的算子
1-1 灰度变换
T最简单的形式,处理1×1的情况,此时(x,y)处g的值仅由f在该点处的灰度决定,因此灰度变换又被成为点处理。又可根据T的形式线性变换(灰度反转)和幂次变换。
灰度反转:
s
=
L
−
1
−
r
s=L-1-r
s=L−1−r
输入和输出灰度级都为0,1,…,L-1
说明:灰度级代表像素强度值的变化范围,通常灰度级L与位深度k的关系是L=2^k,比如8bit图像的灰度级范围是0-255。
彩色图像,不管其图像格式是PNG,还是BMP,或者JPG,在PIL中,使用Image模块的open()函数打开后,返回的图像对象的模式都是“RGB”。而对于灰度图像,不管其图像格式是PNG,还是BMP,或者JPG,打开后,其模式为“L”。
PIL中有九种不同模式。分别为1,L,P,RGB,RGBA,CMYK,YCbCr,I,F
python实现代码如下:
import sys
import PIL.Image as Image
import numpy as np
import matplotlib.pyplot as plt
def InverseImage(f,L):
g = L-1-f # 核心:灰度级的反转
return g
def ReadImg(fn,ConvertToArray=False):
image = Image.open(fn)
if image.mode != 'L':
image = image.convert('L')
if ConvertToArray == True:
image = np.asarray(image)
return image
def main(fn,gn):
# 1. 读图
f = ReadImg(fn,True)
# 2. 灰度反转
g = InverseImage(f,256)
# 3. 保存
g = Image.fromarray(g)
g.save(gn)
fig,axelist = plt.subplots(1,2)
axelist.ravel()[0].imshow(f,cmap='gray')
axelist.ravel()[1].imshow(g,cmap='gray')
axelist.ravel()[0].set_axis_off()
axelist.ravel()[1].set_axis_off()
plt.show()
if __name__ == '__main__':
if(len(sys.argv)) == 3:
main(sys.argv[1],sys.argv[2])
else:
print('Usage:',sys.argv[0],"[image file 1] [image file 2]")
1-2 幂次变换
s = r γ s = r^\gamma s=rγ
γ = 1 \gamma=1 γ=1时,幂次变换等价于等值变换;
γ < 1 \gamma<1 γ<1和 γ > 1 \gamma>1 γ>1时,输入与输出灰度级之间的对应关系是:
说明:许多图像的输入输出设备(扫描仪,打印机,显示器)的输入输出响应都遵循幂次变换。CRT显示器的gamma值一般在1.8-2.5之间。
此外还有对数变换:
S
=
c
∗
l
o
g
(
1
+
r
)
S = c*log(1+r)
S=c∗log(1+r)
主要用于将图像的低灰度值部分扩展,高灰度值部分压缩,达到强调图像低灰度部分的目的。
灰度拉伸:
s
=
a
∗
r
+
b
s=a*r+b
s=a∗r+b
改善图像的动态范围,将原来低对比度的图像拉伸为高对比度图像。
python实现如下:
import sys
import PIL.Image as Image
import numpy as np
import matplotlib.pyplot as plt
def PowerTransform(f,gamma):
f = f/255.
g = f**gamma # 核心:灰度级的幂次变换
g *= 255.
g = np.uint8(g)
return g
def ReadImg(fn,ConvertToArray=False):
image = Image.open(fn)
if image.mode != 'L':
image = image.convert('L')
if ConvertToArray == True:
image = np.asarray(image)
return image
def main(fn,gn):
# 1. 读图
f = ReadImg(fn,True)
# 2. 幂次变换
g = PowerTransform(f,1.5) #0.5
# 3. 保存
g = Image.fromarray(g)
g.save(gn)
fig,axelist = plt.subplots(1,2)
axelist.ravel()[0].imshow(f,cmap='gray')
axelist.ravel()[1].imshow(g,cmap='gray')
axelist.ravel()[0].set_axis_off()
axelist.ravel()[1].set_axis_off()
plt.show()
if __name__ == '__main__':
if(len(sys.argv)) == 3:
main(sys.argv[1],sys.argv[2])
else:
print('Usage:',sys.argv[0],"[image file 1] [image file 2]")
1-3 直方图均衡
图像直方图反映图像像素分布情况,横坐标代表图像像素的种类(灰度或彩色),纵坐标代表每种颜色值在图像中的像素总数或占所有像素个数的百分比。
对于灰度值在[0,L-1]的数字图像,直方图是一个离散函数: h ( r k ) = n k h(r_k)=n_k h(rk)=nk
其中 r k r_k rk是第k级灰度值, n k n_k nk代表灰度值为 r k r_k rk的像素个数
使用归一化的直方图,表示为 p ( r k ) = n k / n p(r_k)=n_k/n p(rk)=nk/n,可以看作图像中出现灰度值的概率估计。低对比度图像动态范围窄,高对比度图像动态范围宽。
Q如何的得到直方图?
python,调用PIL.Image对象提供的histogram()方法;或者numpy提供的histogram()也能实现相应的功能
numpy.histogram(data, bins=10, range=None, normed=None, weights=None, density=None)
第一个参数即数据,一般为数组类型;第二个参数,对于8bit图像,bins取range(257),或者使用bins=256, range=(0,256)
直方图均衡:
基本思想:尽量使每个灰度级的像素数量相等。直方图均衡化就是把一个已知灰度概率密度分布的图像经过一种变换,使之演变为一幅具有均匀灰度概率密度分布的新图像。
对于连续图像: s = F r ( r ) = ∫ 0 r p r ( w ) d w s=F_r(r)=\int_0^rp_r(w)dw s=Fr(r)=∫0rpr(w)dw就是均衡化
对数字图像:用概率代替概率密度函数,用累加求和代替积分,灰度范围变为[0,L-1],此时:
s
k
=
F
r
(
r
k
)
=
∑
j
=
0
k
p
(
r
j
)
=
∑
j
=
0
k
n
j
n
,
k
=
0
,
1
,
.
.
.
,
L
−
1
s_k=F_r(r_k)=\sum_{j=0}^kp(r_j)=\sum_{j=0}^k\frac{n_j}{n} ,k=0,1,...,L-1
sk=Fr(rk)=j=0∑kp(rj)=j=0∑knnj,k=0,1,...,L−1
算法流程:
1)计算图像灰度直方图,并进行归一化,得到 p ( r k ) p(r_k) p(rk)
2)计算归一化直方图的累积直方图 s k s_k sk
3)累积直方图×灰度级L后得到均衡后的直方图分布
python代码实现:
import sys
import PIL.Image as Image
import numpy as np
import matplotlib.pyplot as plt
def HistEqu(hist,n):
s = np.zeros(256)
m = 0
for i in range(0,256):
m += hist[i]
s[i] += 255.*m/n
s = np.uint8(s)
return s
def ReadImg(fn,ConvertToArray=False):
image = Image.open(fn)
if image.mode != 'L':
image = image.convert('L')
if ConvertToArray == True:
image = np.asarray(image)
return image
def main(fn,gn):
# 1. 读图
f = ReadImg(fn,False)
# 2. 打印初始直方图
hist1 = f.histogram()
for i in range(0,256):
plt.bar(i,hist1[i],color='b',linewidth=0)
plt.show()
# 3. 直方图均衡
size = f.size
n = size[0] * size[1]
hist2 = HistEqu(hist1,n) #512*512
# 4. 打印处理后的直方图
for i in range(0,256):
plt.bar(i,hist2[i],color='g',linewidth=0)
plt.show()
# 5. 保存结果
g = np.array(f)
g = hist2[g]
g = Image.fromarray(g)
g.save(gn)
fig,axelist = plt.subplots(1,2)
axelist.ravel()[0].imshow(f,cmap='gray')
axelist.ravel()[1].imshow(g,cmap='gray')
axelist.ravel()[0].set_axis_off()
axelist.ravel()[1].set_axis_off()
plt.show()
if __name__ == '__main__':
if(len(sys.argv)) == 3:
main(sys.argv[1],sys.argv[2])
else:
print('Usage:',sys.argv[0],"[image file 1] [image file 2]")
参考:
【1】https://zhuanlan.zhihu.com/p/363895546
【2】数字图像处理 冈萨雷斯