最近在学习图像处理方面的基础知识,在这里简单总结和分享。
一.直方图均衡化
图像的灰度直方图是一种概率直方图,表示不同灰度值在图像中出现的概率
图像的灰度直方图可以用来判断图像曝光是否合适,一种对曝光度异常的图像进行图像增强的操作就是直方图均衡化。
那么怎样进行图像直方图均衡化呢?
主流的图像处理库都封装了直方图均衡化的函数,比如matlab中的histeq()函数,对于这一函数的讲解可参考这篇文章histeq从用法到原理。
这里介绍一下如何自己写代码实现这一功能,使用了python语言。
将读取文件的灰度矩阵,转化为直方图,这里的直方图定义为python的dict类型,索引为灰度级,值为对应的概率。
#将灰度数组映射为直方图字典,nums表示灰度的数量级
def arrayToHist(grayArray,nums):
if(len(grayArray.shape) != 2):
print("length error")
return None
w,h = grayArray.shape
hist = {}
for k in range(nums):
hist[k] = 0
for i in range(w):
for j in range(h):
if(hist.get(grayArray[i][j]) is None):
hist[grayArray[i][j]] = 0
hist[grayArray[i][j]] += 1
#normalize
n = w*h
for key in hist.keys():
hist[key] = float(hist[key])/n
return hist
直方图均衡化函数,传入原始图片的灰度矩阵和其直方图,计算累计的概率直方图,再进行均衡化。
#计算累计直方图计算出新的均衡化的图片,nums为灰度数,256
def equalization(grayArray,h_s,nums):
#计算累计直方图
tmp = 0.0
h_acc = h_s.copy()
for i in range(256):
tmp += h_s[i]
h_acc[i] = tmp
if(len(grayArray.shape) != 2):
print("length error")
return None
w,h = grayArray.shape
des = np.zeros((w,h),dtype = np.uint8)
for i in range(w):
for j in range(h):
des[i][j] = int((nums - 1)* h_acc[grayArray[i][j] ] +0.5)
return des
绘制直方图的函数,借助pyplot
#传入的直方图要求是个字典,每个灰度对应着概率
def drawHist(hist,name):
keys = hist.keys()
values = hist.values()
x_size = len(hist)-1#x轴长度,也就是灰度级别
axis_params = []
axis_params.append(0)
axis_params.append(x_size)
#plt.figure()
if name != None:
plt.title(name)
plt.bar(tuple(keys),tuple(values))#绘制直方图
#plt.show()
整个调用流程如下:
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
import matplotlib
matplotlib.rcParams['font.sans-serif']=['SimHei'] # 用黑体显示中文
imdir = "./hw1_s.jpg"#原始图片的路径
#打开文件并灰度化
im_s = Image.open(imdir).convert("L")
im_s = np.array(im_s)
print(np.shape(im_s))
#开始绘图,分成四个部分
plt.figure()
plt.subplot(2,2,1)
plt.imshow(im_s,cmap = 'gray')
plt.title("原始灰度图")
#plt.show()
#创建原始直方图
plt.subplot(2,2,3)
hist_s = arrayToHist(im_s,256)
drawHist(hist_s,"原始直方图")
#计算均衡化的新的图片,根据累计直方图
im_d = equalization(im_s,hist_s,256)
plt.subplot(2,2,2)
plt.imshow(im_d,cmap="gray")
plt.title("均衡的灰度图")
#根据新的图片的数组,计算新的直方图
plt.subplot(2,2,4)
hist_d = arrayToHist(im_d,256)
drawHist(hist_d,"均衡直方图")
plt.show()
最终结果
二.直方图匹配
直方图匹配类似于直方图均衡,不同之处在于直方图均衡的输出结果是固定的,较均衡的图像,而直方图匹配的结果则是我们指定的直方图,具体的数学原理不再赘述,不过注意,由于原始图像的累计直方图和我们指定的输出图片的直方图的累计直方图不可能完全相同,所以匹配结果并非百分之百的匹配。
下面看实现,
定义的匹配函数:注意第二个参数是我们指定的直方图,而非原始图片的直方图。
#直方图匹配函数,接受原始图像和目标灰度直方图
def histMatch(grayArray,h_d):
#计算累计直方图
tmp = 0.0
h_acc = h_d.copy()
for i in range(256):
tmp += h_d[i]
h_acc[i] = tmp
h1 = arrayToHist(grayArray,256)
tmp = 0.0
h1_acc = h1.copy()
for i in range(256):
tmp += h1[i]
h1_acc[i] = tmp
#计算映射
M = np.zeros(256)
for i in range(256):
idx = 0
minv = 1
for j in h_acc:
if (np.fabs(h_acc[j] - h1_acc[i]) < minv):
minv = np.fabs(h_acc[j] - h1_acc[i])
idx = int(j)
M[i] = idx
des = M[grayArray]
return des
具体调用,读取第二张图片,其直方图为我们指定的直方图
imdir = "./hw1_s2.jpg"
imdir_match = "./hw1_s22.jpg"
#直方图匹配
#打开文件并灰度化
im_s = Image.open(imdir).convert("L")
im_s = np.array(im_s)
print(np.shape(im_s))
#打开文件并灰度化
im_match = Image.open(imdir_match).convert("L")
im_match = np.array(im_match)
print(np.shape(im_match))
#开始绘图
plt.figure()
#原始图和直方图
plt.subplot(2,3,1)
plt.title("原始图片")
plt.imshow(im_s,cmap='gray')
plt.subplot(2,3,4)
hist_s = arrayToHist(im_s,256)
drawHist(hist_s,"原始直方图")
#match图和其直方图
plt.subplot(2,3,2)
plt.title("match图片")
plt.imshow(im_match,cmap='gray')
plt.subplot(2,3,5)
hist_m = arrayToHist(im_match,256)
drawHist(hist_m,"match直方图")
#match后的图片及其直方图
im_d = histMatch(im_s,hist_m)#将目标图的直方图用于给原图做均衡,也就实现了match
plt.subplot(2,3,3)
plt.title("match后的图片")
plt.imshow(im_d,cmap='gray')
plt.subplot(2,3,6)
hist_d = arrayToHist(im_d,256)
drawHist(hist_d,"match后的直方图")
plt.show()
最后结果
可以看到匹配出的图像与指定的图像灰度接近,灰度直方图相似。