在图像处理中,也经常需要分析图像的亮度(即像素级的分布情况),这就需要用到直方图了,如颜色直方图、灰度直方图等。
颜色直方图是最常用的表达颜色特征的方法,其优点是不受图像旋转和平移变化的影响,进一步借助归一化还可以不受图像尺度变化的影响,其缺点是没有表达出颜色空间分布的信息。
颜色直方图处理方法
颜色直方图是对RGB三通道分别做统计,这个逻辑比较简单,我直接用opencv的函数来实现吧,先看代码:
import cv2
import numpy as np
from matplotlib import pyplot as plt
image = cv2.imread("data.jpg")
cv2.imshow("Original",image)
#cv2.waitKey(0)
chans = cv2.split(image)
colors = ("b","g","r")
plt.figure()
plt.title("Flattened Color Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
for (chan,color) in zip(chans,colors):
hist = cv2.calcHist([chan],[0],None,[256],[0,256])
plt.plot(hist,color = color)
plt.xlim([0,256])
plt.show()
还拿以前老照片做实验吧。如下图所示(只是统计RGB的整体色调级的分布情况,没有位置信息):
颜色直方图
图像的灰度直方图是灰度级的函数,描述的是图像中具有该灰度级的像素的个数:其中,横 坐标是灰度级,纵坐标是该灰度级出现的频率,描述了图像中灰度分布情况,能够很直观的展示出图像中各个灰度级所 占的多少。
灰度直方图处理方法
灰度直方图的处理相比颜色直方图更加简单,因为他只是对灰度图的灰度级别做分布统计,还是用同一张图片吧
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("data.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#cv2.imshow("image_gray", gray)
# 灰度图像的直方图,方法一
plt.figure()
plt.hist(gray.ravel(), 256)
plt.show()
如下路所示:
从这两个直方图中我们也可以看出,不管是颜色直方图还是灰度直方图,直方图中所表达出的图像亮度信息都几乎是相同的
这个应该好理解,因为直方图里忽略了位置信息,所以不同的图片的直方图可能会相同,我们举个例子,比如一张4*4的图片,我们假设黑白的像素点是相同的,我们把像素坐标调换一下,直方图还是不会变的,但是图片却变了
直方图可以很直观的看出图像的亮暗程度
如上图所示,直方图可以直观的看到图像的像素级的分布情况,能够直观的了解到图像的亮度信息
有时候我们拿到的图像要么偏亮,要么偏暗,就会影响计算机的判断,直方图均衡化的目的就是使图像增强
直方图均衡化就是将原图像的直方图通过变换函数变为均匀的直方图,然后按均匀直方图修改原图像,从而获得一幅灰度分布均匀的新图像(注意这里的均匀不是平均)。直方图均衡化就是用一定的算法使直方图大致平和的方法
为了将原图像的亮度范围进行扩展,需要一个映射函数,将原图像的像素值均衡映射到新直方图中,这个映射函数有两个条件:
1.为了不打乱原有的顺序,映射后亮、暗的大小关系不能改变
2.映射后必须在原有的范围内,比如(0-255)
1.依次扫描原始灰度图像的每一个像素,计算出图像的灰度直方图H
2.计算灰度直方图的累加直方图
3.根据累加直方图和直方图均衡化原理得到输入与输出之间的映射关系。
4.最后根据映射关系得到结果:dst(x,y)=H’(src(x,y))进行图像变换
对于输入图像的任意一个像素p,p∈[0,255],总能在输出图像里有对应的像素q,q∈[0,255]使得下面等式成立(输入和输出的像素总量相等)
∑ k = 0 p h i s t i n p u t ( k ) = ∑ k = 0 q h i s t o u t p u t ( k ) ( 1 ) \textstyle\sum_{k=0}^p{hist_{input}(k)}=\textstyle\sum_{k=0}^q {hist_{output}(k)}\space\space\space\space\space\space\space(1) ∑k=0phistinput(k)=∑k=0qhistoutput(k) (1)
其中,输出图像每个灰度级的个数:
h i s t o u t p u t ( k ) ≈ H ∗ W 256 , k ∈ [ 0 , 255 ] ( 2 ) hist_{output}(k)\approx \frac {H*W}{256},k\in[0,255]\space\space\space\space\space\space\space\space\space\space\space\space\space\space(2) histoutput(k)≈256H∗W,k∈[0,255] (2)
我们将公式(2)代入公式(1)
∑ k = 0 p h i s t i n p u t ( k ) ≈ ( q + 1 ) H ∗ W 256 \textstyle\sum_{k=0}^p{hist_{input}(k)}\approx(q+1)\frac{H*W}{256} ∑k=0phistinput(k)≈(q+1)256H∗W
那么均衡化后的对应的像素点的像素值
q = ∑ k = 0 p h i s t i n p u t ( k ) H ∗ W ∗ 256 − 1 q=\textstyle\sum_{k=0}^p\frac{hist_{input}(k)}{H*W}*{256}-1 q=∑k=0pH∗Whistinput(k)∗256−1
我举个例子:现有一个亮度很暗的4*4的图像,像素值如下:
我们从像素值就能看出这个图像几乎是一片黑,肉眼几乎是看不清任何图像信息的,现在我们套用上面的公式进行计算:
均衡化后的对应的像素点的像素值为:
单从像素值来看,是不是要比原始图像亮度更加亮一些呢,并且像素点之间的差别也可以肉眼分辨出来了
拿张下面的图片做个实验
先看原图的灰度图吧
import cv2
# 获取灰度图像
img = cv2.imread("data3.png", 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite("data3gray.png",gray)
cv2.imshow("image_gray", gray)
然后直接调用opencv库的直方图均衡化函数对上面的灰度图做直方图均衡化
import cv2
# 灰度图像直方图均衡化
dst = cv2.equalizeHist(gray)
# 直方图
hist = cv2.calcHist([dst],[0],None,[256],[0,256])
cv2.imwrite("data3hist.png",dst)
cv2.imshow("Histogram Equalization", dst)
cv2.waitKey(0)
直方图均衡化处理后灰度图
可以很明显的看到经过直方图均衡化后的图像要比愿图像的灰度图清晰的多
再对原图做个颜色直方图均衡化处理,需要将三个通道分别直方图均衡化处理再合并三个通道进行合并
import cv2
# 彩色图像直方图均衡化
img = cv2.imread("data3.png", 1)
cv2.imshow("src", img)
# 彩色图像均衡化,需要分解通道 对每一个通道均衡化
(b, g, r) = cv2.split(img)
bH = cv2.equalizeHist(b)
gH = cv2.equalizeHist(g)
rH = cv2.equalizeHist(r)
# 合并每一个通道
result = cv2.merge((bH, gH, rH))
cv2.imwrite("data3rgbhist.png",result)
cv2.imshow("dst_rgb", result)
cv2.waitKey(0)
颜色图像直方图均衡化后的图像
虽然图像的颜色和原图相比发生了变化,但是从图像信息来看,是不是增强了很多呢