理论
什么是histogram?它可以给出图像的密度分布的总体概念,它的x轴是像素值(0到255)y轴是对应的像素在图像里的数量。
看histogram你可以得到对比度,亮度,密度分布等直观信息。今天的所有图像处理工具都提供了histogram属性,
你可以看到图像和他的histogram(记住这里的histogram是对于灰度图的,不是彩色图),histogram左边的区域显示出暗的像素的数量,右边的区域显示了亮的像素的数量,从这里可以得出暗的区域比亮区多,而中间亮度的很少。
找到Histogram
现在我们知道了什么是histogram,我们来看看怎么得到它,OpenCV和Numpy都内嵌了函数来干这个。在用之前我们需要知道一些相关术语。
BINS:上面的histogram显示了每个像素值的数量,比如从0到255,你需要256个值来显示上面的histogram。如果你不需要找各个像素的数量,而是需要某个间隔内的像素值得数量。比如你需要找到0到15之间的像素的数量,然后16到31的,...,240到255,你值需要16个值来表示histogram。
所以你需要做的只是把整个histogram分割成16个子部分,统计每个子部分内的所有像素的数量。每个子部分被叫做"BIN"。在第一个例子里,bins的数量是256。第二个例子里,BINS是16个。BINS由histSize来表示。
DIMS:这是我们手机的数据的参数的数量,在这个例子里,我们收集的数据只有一个属性:亮度。所以这里是1.
RANGE:这是你要统计的亮度值得范围,一般是[0,256], 所有值。
1.OpenCV Histogram 计算
所以现在我们使用cv2.calcHist()函数来找histogram。
cv2.calcHist(images, channels, mask, histSize, ranges[,hist[,accumulate]])
1.images:这是uint8或者float32的原图。应该是方括号方式传入:“[img]”
2.channels:也是用方括号给出的,我们计算histogram的channel的索引,比如,如果输入时灰度图,值就是[0],对于彩色图片,你可以传[0],[1]和[2]来分别计算蓝色,绿色和红色通道的histogram。
3.mask:掩图,要找到整个图像的histogram,这里传入"None"。但是如果你想找到特定区域图片的histogram,就得创建一个掩图
4.histSize:BIN数量,需要用户方括号传入,对于全刻度,我们传入[256].
5.ranges:RANGE,一般来说是[0,256].
来一个例子,简单的用灰度模式加载一个图像然后找他的histogram。
img=cv2.imread('home.jpg',0)
hist=cv2.calcHist([img],[0],None,[256],[0,256])
hist是256x1数组,每个值对应图像里的像素数量。
2.Numpy里的Histogram计算
Numpy也提供了函数np.histogram()。
hist,bins=np.histogram(img.ravel(),256,[0,256])
hist和我们前面计算的一样,但是bins有257个元素,因为Numpy计算bins是0-0.99,1-1.99,2-2.99,所以最后的范围是255-255.99.要表示这个,最后还加了256.但是我们不需要256.
Numpy还有另一个函数np.bincount(),比np.histogram()要快很多(10倍)。所以对于一个维度的Histogram,你可以试试,不要在np.bincount里忘记设置minlength=256。比如 hist = np.bincount(img.ravel(), minlength=256)
注意:
OpenCV函数要比np.histogram()要快很多(40x)。所以还是用OpenCV函数。
绘制Histograms
有两个方法:
1.短方法:使用Matplotlib绘制函数
2.长方法:使用OpenCV绘制函数
1.使用Matplotlib
Matplotlib有一个绘制histogram函数:matplotlib.pyplot.hist()
它直接找到histogram然后绘制。你不需要用calcHist()或者np.histogram()函数来找histogram。
import cv2
import numpy as np
from matplotlib import pyplot as pltimg = cv2.imread('home.jpg',0)
plt.hist(img.ravel(),256,[0,256]); plt.show()
或者你可以使用matplotlib的普通绘图,对于BGR绘图不错,但你需要先找到histogram。
import cv2
import numpy as np
from matplotlib import pyplot as pltimg = cv2.imread('home.jpg')
color = ('b','g','r')
for i,col in enumerate(color):
histr = cv2.calcHist([img],[i],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.show()
你可以从上面的图看出,蓝色在图像里值很高。
2.使用OpenCV
你调整了histogram的值,你可以用cv2.line()或者cv2.polyline()函数来生成
使用Mask
我们使用cv2.calcHist()来找整个图的histogram。如果你想找某个区域的histogram,就创建一个你想要的区域是白色而其他地方是黑色的mask图像。
img = cv2.imread('home.jpg',0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img,img,mask = mask)# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img],[0],None,[256],[0,256])
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask,'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0,256])plt.show()
下面的结果里,在histogram图上,蓝线显示了全图的histogram,绿色线显示了mask区域的histogram.
END