直方图规定化公式推导及代码实现
1. 概述¶
所谓直方图规定化,就是通过一个灰度映像函数,将原灰度直方图改造成所希望的直方图
理想情况下,直方图均衡化实现了图像灰度的均衡分布,对提高图像对比度、提升图像 亮度具有明显的作用。在实际应用中,有时并不需要图像的直方图具有整体的均匀分布,而 希望直方图与规定要求的直方图一致,这就是直方图规定化。它可以人为地改变原始图像直方图的形状,使其成为某个特定的形状,即增强特定灰度级分布范围内的图像
2. 公式推导¶
设有一张原始图像 A,已知其概率密度为,若对其进行直方图均衡化得到图像 ,对应的变换函数为,则有:
设有一张目标图像 B,已知其概率密度为,若对其进行直方图均衡化得到图像 ,对应的变换函数为,则有:
由上图,易知:
显然,有:
当:,针对离散图像,则有:
由于本质属于灰度变换,并不希望改变像素坐标,所以:
综上:
从而:
进而:
由于 是变限积分,无法直接得到其逆变换
在工程应用中,一般图像灰度取值为0-255,由于目标图像的灰度直方图已知,即变换函数 已知,就可以建立一张灰度0-255对应的映射表,对应的是 的映射关系
由于,从而可以反推出目标图像
- 这种反推目标图像是一种近似估算
3. 代码实现¶
总结第二小节的理论公式,代码实现步骤为:
- 计算输入图像的概率密度
- 对输入图像进行直方图均衡化得到
- 设置目标图像的概率密度函数
- 构造概率密度函数对应的0-255的灰度映射表
- 将对应灰度映射表得到目标图像
计算输入图像的概率密度计算与直方图均衡化:
In [1]:
# 获取图像的概率密度 def get_pdf(in_img): total = in_img.shape[0] * in_img.shape[1] #计算图片总像素数 return [ np.sum(in_img == i)/total for i in range(256) ] #求概率密度 # 直方图均衡化(核心代码) def hist_equal(in_img): # 1.求原始图像的概率密度 Pr = get_pdf(in_img) # 2.构造输出图像(初始化成输入) out_img = np.copy(in_img) # 3.执行“直方图均衡化”(执行累积分布函数变换) y_points = [] # 存储点集,用于画图 SUMk = 0 # 累加值存储变量 for i in range(256): SUMk = SUMk + Pr[i] out_img[(in_img == i)] = SUMk*255 #灰度值逆归一化 y_points.append(SUMk*255) #构造绘制函数图像的点集(非核心代码,可忽略) return out_img.astype("int32"), y_points
设置目标图像的概率密度函数:
In [2]:
# 生成多峰正态分布 # means 各峰均值 # stds 各峰标准差 # ampl 各正态分布幅值(和为1) # bias 概率密度函数总体抬升量(若没有抬升量,容易造成图像过暗) def gen_mul_norm_pdf(x, means, stds, ampl, bias): pdf = np.zeros([256,], dtype=float) for i in range(len(means)): pdf_temp = ampl[i]*np.exp(-((x-means[i])**2)/(2*stds[i]**2))\ /(stds[i] * np.sqrt(2 * np.pi)) pdf = pdf + pdf_temp pdf = pdf + bias #总体抬升概率密度,避免出现零值 pdf2 = pdf/np.sum(pdf) #由于做了抬升,所以要重新归一化 return pdf2.tolist()
- 这里的函数可以根据输入图像进行确定
构造概率密度函数对应的0-255的灰度映射表:
In [3]:
# 构造目标图像的映射表(核心代码) def gen_target_table(Pv): table = [] SUMq = 0. for i in range(256): SUMq = SUMq + Pv[i] table.append(round(SUMq*255, 0)) #四舍五入 return table
将均衡化后的图像对应映射表从而得到目标图像:
为了简化编码,我们进一步简化映射规则:
- 映射表中若未找到某个数字,则直接取前一个逆映射值
- 映射表中若某个数字有多个逆映射值,默认取最大逆映射值
In [4]:
# 直方图规定化(核心代码) def hist_specify(in_img=None): # 1.拿到目标图像规定概率密度,并构造映射表 Pv = gen_mul_norm_pdf(np.arange(0,256,1),[38,191],[13,13],[0.93,0.07],0.002) table = gen_target_table(Pv) # 2.对原始图像做直方图均衡 ori_eq_img, T_points = hist_equal(in_img) # 3.构造输出图像(初始化成输入) out_img = np.copy(ori_eq_img) # 4.执行“直方图规定化”(根据映射表,做逆映射 B'->B) map_val = 0 # 逆映射值初始化为0 for v in range(256): if v in ori_eq_img: # 存在于B' if v in table: # 存在于映射表 map_val = len(table)-table[::-1].index(v)-1 #拿到指定值最后出现的索引 out_img[(ori_eq_img == v)] = map_val # 找不到映射关系时,取前一个映射值 return out_img, T_points, table, Pv
进行数字图像处理:
In [5]:
from PIL import Image import numpy as np import matplotlib.pyplot as plt # 读入原图 gray_img = np.asarray(Image.open('./image/mars_moon_phobos.tif').convert('L')) # 对原图执行“直方图规定化” out_img, T_pts, G_pts, spec_hist = hist_specify(gray_img) # 创建1个显示主体,并分成6个显示区域 fig = plt.figure() ax1, ax2, ax3 = fig.add_subplot(231), fig.add_subplot(232), fig.add_subplot(233) ax4, ax5, ax6 = fig.add_subplot(234), fig.add_subplot(235), fig.add_subplot(236) # 窗口显示:原图,原图灰度分布,规定PDF,结果图像,结果图像灰度分布,对应变换函数 ax1.set_title('origin', fontsize=12) ax1.imshow(gray_img, cmap='gray', vmin=0, vmax=255) ax1.axes.xaxis.set_visible(False) ax1.axes.yaxis.set_visible(False) ax2.grid(True, linestyle=':', linewidth=1) ax2.set_title('origin histogram', fontsize=12) ax2.set_xlim(-10, 255) # 设置x轴分布范围 ax2.set_ylim(0, 1) # 设置y轴分布范围 ax2.hist(gray_img.flatten(),bins=255,density=True,color='black',edgecolor='black') ax2.axes.xaxis.set_visible(False) ax2.axes.yaxis.set_visible(False) ax4.set_title('result', fontsize=12) ax4.imshow(out_img, cmap='gray', vmin=0, vmax=255) ax5.grid(True, linestyle=':', linewidth=1) ax5.set_title('result histogram', fontsize=12) ax5.set_xlim(0, 255) # 设置x轴分布范围 ax5.set_ylim(0, 1) # 设置y轴分布范围 ax5.hist(out_img.flatten(),bins=255,density=True,color='black',edgecolor='black') # 窗口显示:绘制规定概率密度函数 ax3.set_title("regulation pdf", fontsize=12) ax3.grid(True, linestyle=':', linewidth=1) ax3.plot(np.arange(0, 256, 1), spec_hist, color='r',lw=1) ax3.axes.xaxis.set_visible(False) ax3.axes.yaxis.set_visible(False) # 窗口显示:绘制对应的灰度变换函数 ax6.set_title("gray transformation", fontsize=12) ax6.grid(True, linestyle=':', linewidth=1) ax6.plot(np.arange(0, 256, 1), T_pts, color='orange',lw=2) ax6.plot(np.arange(0, 256, 1), G_pts, color='green', lw=2) plt.show()

可以看到有效增强了实验图像的对比度
利用已知的图像直方图分布,从而对输入图像进行处理,是直方图规定化的应用
已知常规环境下的图像,对异常情况下的图像进行处理,比如去除有雾的图像等
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律