直方图均衡化及其python实现
#数字图像处理
文章目录
- 直方图均衡化及其python实现
- 展示效果
- 理论简介
- 什么是直方图
- 均衡化
- 流程图:
- 代码:
这里贴一下 我最近数字图像学习阶段写的代码 – github 仓库。有机会一起学习。
展示效果
先上一下均衡化的效果:
左图是均衡化之前,右图是均衡化之后。
理论简介
注意这里只是对直方图均衡化进行一个简要介绍,详细的理论基础,支撑希望从书中找答案。
代码在最下面,没有使用 opencv,主要基于numpy,这里主要方便我之后回顾以及同我一样的初学者整理。
什么是直方图
先引出《数字图像处理-第三版-冈萨雷斯》中3.3节第一段对直方图的介绍,我再简单介绍一下。
简单来说,直方图可以看做对一副灰度图像每个像素的灰度值进行统计,计算每一个灰度级的像素个数。
比方说下面这样一个用矩阵表示的 8x8 图像,每一个位置的数字对于该位置像素的灰度值。(一副单通道灰度图,本身可以看做一个对应分辨率的矩阵,比如一个 128x128 分辨率的单通道灰度图,可以看做一个128x128 的矩阵,空间域的处理本质上可以看做对这个矩阵的处理)。
对矩阵中每个数字(即灰度值)进行统计出现的次数,得到:
8 | 0 | 0 | 0 | 31 | 16 | 8 | 1 |
再根据以上表格数据建立类似于柱状图的直方图即可,需要注意的是,上表中出现次数为 0 的灰度值也要再图中表现出来。
均衡化
原始图像可能显得较暗或者太亮,导致图像辨识效果不好。图像较暗或者较亮在直方图上具体体现在,直方图的分量主要集中在高区间和低区间。如下图(图片取自《数字图像处理-第三版-冈萨雷斯》-p144)
均衡化的目的即使尽可能的让直方图的分量,在整个灰度范围内均匀(均衡)分布。这样使得图像的对比度较高。比如上图中的最后一副。
对比度:我们定义一幅图像中最高和最低灰度级间的灰度差为对比度。
这里根据这个公式来计算(即下面提到的 3.3-8式):
以及整个例子:
流程图:
代码:
from collections import defaultdict
import numpy as np
import matplotlib.image as mpimg # 用于读取
import matplotlib.pyplot as plt # 用于显示
import logging
logging.basicConfig(level=logging.INFO)
# 直方图均衡化
class Histogram(object):
def __init__(self):
pass
# 显示直方图
def showByHistogram(hist,*,title = ''):
x_axis = range(0,256)
y_axis = list(map(lambda x : hist[x] ,x_axis))
fig = plt.figure(num = title)
plot_1 = fig.add_subplot(111)
plot_1.set_title(title)
plot_1.plot(x_axis,y_axis)
plt.show()
del fig
def show(img,*,title = ''):
# 只取单通道
img = img[:,:,0]
size_h,size_w= img.shape
logging.info(f' size_h :{size_h} size_w:{size_w} MN:{size_w*size_h}')
hist = defaultdict(lambda: 0)
for i in range(size_h):
for j in range(size_w):
hist[img[i,j]] += 1
x_axis = range(0,256)
y_axis = list(map(lambda x : hist[x] ,x_axis))
fig = plt.figure(num = title)
plot_1 = fig.add_subplot(111)
plot_1.set_title(title)
plot_1.plot(x_axis,y_axis)
plt.show()
del fig
# 获取直方图,参数Normalized 决定是否归一化
def get_histogram(img ,*,Normalized = False):
# 只取 0 通道,若本身只有一个通道不会有影响
img = img[:,:,0]
size_h,size_w = img.shape
logging.info(f' size_h :{size_h} size_w:{size_w} MN:{size_w*size_h}')
hist = defaultdict(lambda: 0)
for i in range(size_h):
for j in range(size_w):
hist[img[i,j]] += 1
# 根据 Normalized 参数决定是否进行归一化
if Normalized == True:
sum = 0
MN = size_h * size_w
for pixel_value in hist: # Key 迭代
hist[pixel_value] = hist[pixel_value]/MN
sum += hist[pixel_value]
logging.info(f'归一化后加和为:{sum}')
del sum
return hist
# 直方图均衡化函数
def equalization(img):
size_h,size_w,size_c = img.shape
hist = Histogram.get_histogram(img)
MN = size_h * size_w
new_hist = defaultdict(lambda: 0)
# 公式 3.3-8 计算 S_k
for i in range(0,256):
for pixel in range(i+1):
new_hist[i] += hist[pixel]
new_hist[i] = new_hist[i] * 255/MN
for key in new_hist:
new_hist[key] = round(new_hist[key])
new_img = img.copy()
for i in range(new_img.shape[0]):
for j in range(new_img.shape[1]):
new_img[i,j] = new_hist[new_img[i,j,0]]
return new_img
pass
if __name__ == '__main__':
pic1 = mpimg.imread('.\hw2_picture1.jpg') # 读取图像
# Histogram.show(pic1,title='pic1_Histogram')
# Histogram.showByHistogram(Histogram.get_histogram(pic1,Normalized=True),title='pic1_Histogram')
# Histogram.get_histogram(pic1,Normalized=True)
# Histogram.show(pic1,title='pic1_Histogram')
new_pic = Histogram.equalization(pic1)
plt.imshow(pic1)
plt.axis('off')
plt.show()
plt.imshow(new_pic)
plt.axis('off')
plt.show()
Histogram.show(pic1,title='pic1_Histogram')
Histogram.show(new_pic,title='pic1_Histogram after equalization')
mpimg.imsave(r'.\output_HistogramEqualization.png',new_pic)