直方图规定化
数学原理
在直方图的规定化中有一个十分重要的条件,也是理解构建映射与反映射的关键:原图像经过均衡化之后与规定图像经过均衡化之后所展现的像素的灰度概率密度函数都是均匀分布。
于是可以进行如下操作:
1:将原图像的r进行均衡化,得到s1=T®。
2:将规定图像的z进行规定化,得到s2=G(z)。
3:对步骤2进行反变换:-G(s2)=z,
4:将s1与s2进行拟合,在连续的灰度变化中,s1严格等于s2,但是离散情况下,不能追求严格的相等,将其进行拟合,做到大概的相等即可。
5:总结:r --> s1 --> s2 --> z ,进行灰度数值的替换,得到我们的规定化图像。
工程实现
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
# 展示图像的直方图
def Showimg(imgname):
plt.hist(imgname.ravel(), bins=255, rwidth=0.8, range=(0, 255),density=True) # 设定坐标值域
plt.xlabel('Gray value') # 设定x轴名字
plt.ylabel('number') # 设定y轴名字
plt.title(r'Grayscale') # 设定直方图名字
plt.show()
# 图像均衡化之后的灰度级,是个列表的形式,下标代表输入的灰度级,值是输出的灰度级,形成映射,函数返回均衡化之后的图像与灰度值
def Equalize_img(imgname):
__img = imgname # 原始图像读取
__img1 = __img.copy()
# 生成列表
__hist1 = np.zeros(256, dtype=np.float32) # 直方图统计结果-频次
__Ph = np.zeros(256, dtype=np.float32) # 灰度频率
__s = np.zeros(256, dtype=np.float32) # 均衡化之后的频率
__S = np.zeros(256, dtype=np.float32) # 均衡化之后的灰度级是
__ABS = np.zeros(256, dtype=np.int) # 四舍五入之后的灰度级
__value = 255 # value的值取决于图像的位数,8位就是(256-1)
# 直方图统计
__row, __col = np.shape(__img) # 图像的高度,与图像的宽度
# 统计灰度频次
for i in range(__row):
for j in range(__col):
__hist1[__img[i][j]] += 1
# 计算灰度频率
for i in range(0,256):
__Ph[i] = __hist1[i]/(__row*__col)
# 计算均衡化之后的频率
for i in range(1,256):
__s[0] = __Ph[0]
__s[i] = (__s[i-1]+__Ph[i])
# 得出均衡化之后的灰度级
for i in range(0,256):
__S[i] = __value*__s[i] # 原始图像均衡化之后的映射,下标是原图像的灰度级
# 值是均衡化之后的灰度级,形成映射
for i in range(0,256):
__ABS[i] = math.ceil(__S[i]-0.5)
# 进行对文件的灰度值重写
for i in range(__row):
for j in range(__col):
__img1[i,j] = __ABS[__img[i,j]]
return (__img1,__S)
r = cv2.imread(r"C:\Users\li_lei\Desktop\picture\111.png", cv2.IMREAD_GRAYSCALE) # 原图像的读取
h, w = np.shape(r) # 用在图像的替换时使用
img_Equal,S1 = Equalize_img(r) # img_Equal是均衡化之后的图像,S1是原始图像均衡化之后的映射
# 下标是原图像的灰度级,值是均衡化之后的灰度级,形成映射
z = cv2.imread(r"C:\Users\li_lei\Desktop\picture\ec_lena.jpg", cv2.IMREAD_GRAYSCALE) # 标定图像的读取
A,S2 = Equalize_img(z) # A是均衡化之后的图像(这里没有用),原始图像均衡化之后的映射
# 下标是原图像的灰度级,值是均衡化之后的灰度级,形成映射
# 构建原图形均衡化之后的映射表,将S1相应的值给S与GZ拟合
S = list() # S是数学原理中的s1,这里是将列表进行精简,生成列表
for i in range(256):
if S1[i] in S:
pass
else:
S.append(S1[i])
# 构建映射表,将S2的的下标给Z,将S2相应的值给GZ,形成映射表
GZ = list() # GZ是数学原理中的s2
Z = list() # Z是我们想要的灰度数值
for i in range(256):
if S2[i] in GZ:
pass
else:
GZ.append(S2[i])
Z.append(i)
# 拟合并重写图像灰度值 S中与GZ相同的直接重写,其他的不变//可以自行进行代码的优化
for i in range(h):
for j in range(w):
if img_Equal[i,j] in GZ:
index1 = GZ.index(img_Equal[i,j])
img_Equal[i,j] = Z[index1]
else:
pass
# 查看均衡化之后的直方图
Showimg(img_Equal)
效果展示
原图形为lena图:
直方图为:
目标图像为均衡化之后的lena图(参考笔者上篇文章):
直方图为:
规定化之后的图形为:
直方图为:
总结:
本次算法还有很大的优化空间,最后呈现的直方图并不是完全拟合目标图形,但是对于实际效果来看,目视相差不大。不能完全拟合的原因:在算法的工程实现当中s1–>s2出现了一些灰度值没有合适的拟合数值,笔者没有对这些灰度值进行处理,其保持原值,大家可以自己探索更好的处理方法。