直方图均衡化
原理:
想象一下如果一副图像中的大多是像素点的像素值都集中在一个像素值范围之内会怎样呢?例如,如果一幅图片整体很亮,那所有的像素值应该都会很高。但是一副高质量的图像的像素值分布应该很广泛。所以你应该把它的直方图做一个横向拉伸(如下图),这就是直方图均衡化要做的事情。通常情况下这种操作会改善图像的对比度。直方图均衡化后面潜在的数学原理是一个分布(输入的亮度直方图)被映射到另一个分布(一个更宽,理想统一的亮度值分布),映射函数是一个累积分布函数。对于连续分布,结果将是准确的均衡化。在equalizeHist中,原始图像及目标图像必须是单通道,大小相同的8位图像,对于彩色图像,必须先将每个通道分开,再分别进行直方图均衡化处理,然后将通道合并形成新的图像。
# coding=utf-8
import cv2
import numpy as np
img = cv2.imread("/home/wl/4.jpg",0)
equ = cv2.equalizeHist(img)#灰度图像直方图均衡化
while(1):
cv2.imshow("2",equ)
cv2.imshow("1", img)
cv2.imwrite("/home/wl/baocun1.jpg", img)
cv2.imwrite("/home/wl/baocun2.jpg", equ)
k = cv2.waitKey(1) & 0XFF
if k==ord('q'):
break;
cv2.destroyAllWindows()
原图:
均值化后的图:
CLAHE 有限对比适应性直方图均衡化
的确在进行完直方图均衡化之后,图片背景的对比度被改变了。但可能会由于太亮而丢失了很多信息。造成这种结果的根本原因在于这幅图像的直方图并不是集中在某一个区域。为了解决这个问题,我们需要使用自适应的直方图均衡化。这种情况下,整幅图像会被分成很多块这些小块被称为“tiles”(在 OpenCV 中 tiles 的大小默认是 8x8),然后再对每一个小块分别进行直方图均衡化(跟前面类似)。所以在每一个的区域中,直方图会集中在某一个小的区域中(除非有噪声干扰)。如果有噪声的话,噪声会被放大。为了避免这种情况的出现要使用对比度限制。对于每个小块来说,如果直方图中的 bin 超过对比度的上限的话,就把其中的像素点均匀分散到其他 bins 中,然后在进行直方图均衡化。最后,为了去除每一个小块之间“人造的”(由于算法造成)边界,再使用双线性差值,对小块进行缝合。
import cv2
img = cv2.imread("/home/wl/4.jpg",0)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
效果图:
2D直方图
在前面的部分我们介绍了如何绘制一维直方图,之所以称为一维,是因为我们只考虑了图像的一个特征:灰度值。但是在 2D 直方图中我们就要考虑两个图像特征。对于彩色图像的直方图通常情况下我们需要考虑每个的色调(Hue)和饱和度(Saturation)。根据这两个特征绘制 2D 直方图。
使用函数 cv2.calcHist(),函数的参数要做如下修改:
channels=[0,1] 因为我们需要同时处理 H 和 S 两个通道。
bins=[180,256]H 通道为 180,S 通道为 256。
range=[0,180,0,256]H 的取值范围在 0 到 180,S 的取值范围在 0 到 256。
coding=utf-8
import cv2 import numpy as np from matplotlib import pyplot as plt
img = cv2.imread("/home/wl/4.jpg") hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV) hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256]) plt.imshow(hist,interpolation = 'nearest')#nearest插值参数 plt.show()
Numpy 中 2D 直方图
Numpy 同样提供了绘制 2D 直方图的函数:np.histogram2d()。
# coding=utf-8
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("/home/wl/4.jpg")
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
h=hsv[:,:,0]
s=hsv[:,:,1]
hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])
plt.imshow(hist,interpolation = 'nearest')
plt.show()
直方图反向投影
原理:
反向投影可以用来做图像分割,寻找感兴趣区间。它会输出与输入图像大小相同的图像,每一个像素值代表了输入图像上对应点属于目标对象的概率,简言之,输出图像中像素值越高的点越可能代表想要查找的目标。直方图投影经常与camshift(追踪算法)算法一起使用。算法实现的方法,首先要为包含我们感兴趣区域的图像建立直方图(样例要找一片草坪,其他的不要)。被查找的对象最好是占据整个图像(图像里全是草坪)。最好使用颜色直方图,物体的颜色信息比灰度图像更容易被分割和识别。再将颜色直方图投影到输入图像查找目标,也就是找到输入图像中每一个像素点的像素值在直方图中对应的概率,这样就得到一个概率图像,最后设置适当的阈值对概率图像进行二值化。
OpenCV 提供的函数 cv2.calcBackProject() 可以用来做直方图反向投影。它的参数与函数 cv2.calcHist 的参数基本相同。其中的一个参数是我们要查找目标的直方图。同样再使用目标的直方图做反向投影之前我们应该先对其做归一化处理。返回的结果是一个概率图像,我们再使用一个圆盘形卷积核对其做卷操作,最后使用阈值进行二值化。下面就是代码和结果:
# coding=utf-8
import cv2
import numpy as np
#roi图片,就想要找的的图片
roi = cv2.imread('1.jpg')
hsv = cv2.cvtColor(roi,cv2.COLOR_BGR2HSV)
#目标搜索图片
target = cv2.imread('2.jpg')
hsvt = cv2.cvtColor(target,cv2.COLOR_BGR2HSV)
#计算目标直方图
roihist = cv2.calcHist([hsv],[0,1],None,[180,256],[0,180,0,256])
#归一化,参数为原图像和输出图像,归一化后值全部在2到255范围
cv2.normalize(roihist,roihist,0,255,cv2.NORM_MINMAX)
dst = cv2.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
#卷积连接分散的点
disc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
dst = cv2.filter2D(dst,-1,disc)
ret,thresh = cv2.threshold(dst,50,255,0)
#使用merge变成通道图像
thresh = cv2.merge((thresh,thresh,thresh))
#蒙板
res = cv2.bitwise_and(target,thresh)
#矩阵按列合并,就是把target,thresh和res三个图片横着拼在一起
res = np.hstack((target,thresh,res))
cv2.imwrite('res.jpg',res)
#显示图像
cv2.imshow('1',res)
cv2.waitKey(0)
原图:
1.jpg 2.jpg
结果: