我们目前比较好处理的对象是灰度图,因为它只有一个通道,也就意味着很多计算我们减少了一个维度,自然轻松一些。对于灰度图,首先要了解的是灰度直方图,也就是整体的图像中,灰度值的分布情况,通过直方图的形式来显示图片中是高灰度值的区域偏多还是低灰度值的区域偏多或者是其他情况,来指导我们后续的其他操作。以我们的之前转灰度的图片为例,结合matplotlib.pyplot库绘制归一化直方图的代码如下:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
gray_girl = "C:/Users/60214/Desktop/python_work/DigitalExecution/gray_girl.jpg"
def GrayHist(path, bins = 32):
im = Image.open(path)
imarray = np.array(im)
height, width = imarray.shape
data = imarray.reshape((height*width))
plt.hist(data, normed = 1, bins = bins)
plt.show()
GrayHist(gray_girl)
灰度线性变换。指的是将输入图像中某个点的灰度值,按一定的线性函数规律转化成另外的灰度值,所使用的一维线性函数的形式为
其中参数是线性函数的斜率,是线性函数在y轴的截距。这个做法的一个问题就是,计算后的数值有可能不是整数,不过这个可以用round函数实现四舍五入,也可以int取整。此外,计算后的数值有可能大于255,那么就取为255;也有可能是小于0的负数,就取为0。
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
gray_girl = "C:/Users/60214/Desktop/python_work/DigitalExecution/gray_girl.jpg"
def LinearTran(path, new_name, a = 1, b = 0):
im = Image.open(path)
imarray = np.array(im)
height, width = imarray.shape
for i in range(height):
for j in range(width):
aft = int(a*imarray[i, j] + b)
if aft <= 255 and aft >= 0:
imarray[i, j] = aft
elif aft > 255:
imarray[i, j] = 255
else:
imarray[i, j] = 0
new_im = Image.fromarray(imarray)
new_im.save(new_name)
new_path = "C:/Users/60214/Desktop/python_work/DigitalExecution/gray_girl_1.jpg"
LinearTran(gray_girl, new_path, a = 1, b = 55)
线性变换,在参数a和b取值不同的情况下有不同的效果。一般来说,如果a=1,然后b=50>0,很显然是提高灰度值,即增加亮度;a=1而b=-50<0,显然是减小灰度值,即降低亮度。a=2而b=-50时,由于系数a>1,所以原图的灰度值之间的差距被拉大了,也就是图片中明暗的差距更大,用专业的术语就是增加了对比度。而a=0.5,b=-50时,则是减小了对比度。将相关处理之后的图片放置在下方,从中确实可以看出亮度的变化和对比度的变化是两种效果不同的变化。
除了线性变换,因为线性变换可以说是在整个范围内一视同仁地增加或者是减小灰度的对比度,而没有说是集中在数值较高或者是数值较低的区域。于是对数变换和指数变换应运而生,先来看看对数变换。对数变换的公式为
c是尺度比例常数,s是源灰度值,t是变换后的目标灰度值。从对数函数的曲线可以得到,在数值较小的区域增加得较快,在数值较大的区域增加得慢一些,所以可以用来扩展图像中的较暗像素的区域。但通常较少在空间域中使用对数变换,在频谱图像的操作中用到的比较多,之后学习到频域变换再展开学习。
指数变换,也称为伽玛变换或幂次变换,一般表达式为
x和y的取值范围都为[0, 1]。esp为补偿系数,为伽玛系数。随着伽玛系数的变化,如果小于1(具体是小于0.1时)可以有类似有对数变换的效果,而大于1时则是相反的,局部增强高灰度值区域的对比度。在计算时,先将0-255的灰度值动态范围变换到0-1,然后计算玩伽玛变换之后再计算到0-255的范围。代码为
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
gray_girl = "C:/Users/60214/Desktop/python_work/DigitalExecution/gray_girl.jpg"
new_path = "C:/Users/60214/Desktop/python_work/DigitalExecution/gray_girl_2.jpg"
def ExpTran(src, new_path, esp = 0, gama = 1):
im = Image.open(src)
imarray = np.array(im)
height, width = imarray.shape
for i in range(height):
for j in range(width):
tmp = imarray[i, j]/255
tmp = int(pow(tmp + esp, gama)*255)
if tmp >=0 and tmp <= 255:
imarray[i, j] = tmp
elif tmp > 255:
imarray[i, j] = 255
else:
imarray[i, j] = 0
new_im = Image.fromarray(imarray)
new_im.save(new_path)
print("Suceessfully transform!")
ExpTran(gray_girl, new_path, 0, 0.5)
伽玛系数为0.1、0.5、1.5和5,esp为0时的四种变换结果如下图。可以看到当伽玛系数小于1时,低灰度值区域的对比度增强了,而这个的结果使得头发区域的颜色变浅变亮,变亮——低灰度值的区域数值增大得更多,也就是头发的细节变得清晰,高灰度值区域的数值增加得较小——而其他高亮区域(高亮意味着高灰度值基本不变);而伽玛系数大于1时,高灰度值区域——头发——变得模糊,反倒是眼睛和嘴巴这几个原本色泽较浅,属于高灰度值的区域比较更加清晰,对比度得到了提升。
总的来说,当伽玛系数大于1时,图像的高灰度区域对比度得到增强;当伽玛系数小于1时,图像的低灰度区域对比度得到增加;当伽玛系数为1时,这一灰度变换是线性的,即不改变原图像。而esp数值的选择,则是帮我们在以上的两种情况中可以更加着重一些突出某些范围。