像素值在整幅图像中的分布情况是该图像的一个重要属性。
计算图像直方图
直方图是一个简单的表格, 表示一个图像(有时是一组图像) 中具有某个值的像素的数量。
也可以把直方图归一化, 即所有箱子的累加和等于1。
这时每个箱子的数值表示对应的像素数量占总数的百分比。
1.使用cv.calHist()
使用cv.calHist()计算每个颜色通道的直方图
cv.calcHist函数。 是一个通用的直方图计算函数, 可处理包含任何值类型和范围的多通道图像。
calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None)
参数列表:
- images: source image of type uint8 or float32,given in “[img]”
- channels:the index of channel for which we calculate histogram.
grayscale: [0], color image: [0] or [1] or [2] - mask:To find histogram of full image, it is given as “None”.
find histogram of particular region of image,given as mask - histSize:BIN count,For full scale, we pass [256].
- ranges:[0,256]
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
# 载入图像并显示
src_img = cv.imread("test.png")
hist_result0 = cv.calcHist([src_img], [0], None, [256], [0, 256])
hist_result1 = cv.calcHist([src_img], [1], None, [256], [0, 256])
hist_result2 = cv.calcHist([src_img], [2], None, [256], [0, 256])
plt.figure(figsize=(10, 4))
plt.subplot(1, 3, 1)
plt.title("Blue channel")
plt.plot(hist_result0)
plt.subplot(1, 3, 2)
plt.title("Green channel")
plt.plot(hist_result1)
plt.subplot(1, 3, 3)
plt.title("Red channel")
plt.plot(hist_result2)
plt.show()
2.使用np.histogram()
使用np.histogram()计算每个颜色通道的直方图
np.histogram()是一个生成直方图的函数
np.histogram() 默认地使用10个相同大小的区间(箱),边界的数量比分箱数多一个.
histogram(a, bins=10, range=None, normed=None, weights=None, density=None)
parameter:
- a:Input data. The histogram is computed over the flattened array.
- bins:int or sequence of scalars or str int: defines the number of equal-width bins in the given range sequence: defines a monotonically increasing array of bin edges, including the rightmost edge.
- range: (float, float), optional. The lower and upper range of the bins. Values outside the range are ignored. If not provided, range is simply
(a.min(), a.max())
. - density: bool, optional (False default) True:the result is the value of the probability False:the result will contain the number of samples in each bin.
return:
- hist:array, The values of the histogram.
- bin_edges: array, bin edges ,(length(hist)+1).
np_hist, bin_edges = np.histogram(src_img.ravel(), 256)
# 自动进行转换,直接使用src_img效果相同。
# ravel和flatten都是将多维数组转换为一维数组
# numpy.flatten()返回一份拷贝,对拷贝所做的修改不会影响原始矩阵
# 而numpy.ravel()返回的是视图,会影响原始矩阵。
plt.title("numpy histogram")
plt.plot(np_hist)
plt.show()
3.使用np.bincount()
Count number of occurrences of each value in array of non-negative ints. faster than (around 10X) np.histogram().
bincount(x, weights=None, minlength=0)
Parameters:
- x : array_like, 1 dimension, nonnegative ints
- weights : array_like, optional Weights, array of the same shape as
x
. - minlength : int, optional A minimum number of bins for the output array.
Returns :
- out : ndarray of ints The result of binning the input array. The length of
out
is equal tonp.amax(x)+1
.
np_hist= np.bincount(src_img.ravel(), minlength=256)
plt.title("numpy bincount")
plt.plot(np_hist)
plt.show()
图像均衡化
若一个图象中的像素值仅限于某些特定值范围,其图像的对比度就会很差。一个好的图像中的像素值范围应该覆盖所有的亮度值。这就是直方图均衡化并且这通常会提高图像的对比度。
函数的输入只能是灰度图像,输出为均衡化后的图像。
gray_img = cv.cvtColor(src_img, cv.COLOR_BGR2GRAY)
equ_img = cv.equalizeHist(gray_img)
np_hist, _ = np.histogram(gray_img.ravel(), 256, [0, 256])
equ_hist, _ = np.histogram(equ_img.ravel(), 256, [0, 256])
plt.figure(figsize=(8,5))
plt.subplot(1,2,1)
plt.title("src histogram")
plt.plot(np_hist)
plt.subplot(1,2,2)
plt.title("equ_hist histogram")
plt.plot(equ_hist)
plt.show()
二维图像直方图(colorHistograms)
在二维直方图中,需要考虑两个特征,每个像素的色调和饱和度值。 函数的输入只能是HSV图像,因此需要先将图像转换为HSV格式。
1.使用numpy.histogram2d()
Compute the bi-dimensional histogram of two data samples.
histogram2d(x, y, bins=10, range=None, normed=None, weights=None, density=None)
Parameters:
- x : array_like, shape (N,)
- y : array_like, shape (N,)
- bins : int or array_like or [int, int] or [array, array], optional
- range : array_like, shape(2,2), optional
- The leftmost and rightmost edges of the bins along each dimension
Returns:
- H : ndarray, shape(nx, ny) The bi-dimensional histogram of samples
x
andy
.- Values in
x
are histogrammed along the first dimension - values in
y
are histogrammed along the second dimension.
- Values in
- xedges : ndarray, shape(nx+1,) The bin edges along the first dimension.
- yedges : ndarray, shape(ny+1,) The bin edges along the second dimension.
hsv_img = cv.cvtColor(src_img, cv.COLOR_BGR2HSV)
hue, sat, _ = cv.split(hsv_img)
hist, xbins, ybins = np.histogram2d(hue.ravel(), sat.ravel(), [180, 256], [[0, 180], [0, 256]])
plt.imshow(hist)
plt.show()
2.使用重载的cv.calcHist()
For color histograms, we need to convert the image from BGR to HSV.
For 1D histogram, we converted from BGR to Grayscale.
For 2D histograms, its parameters will be modified as follows:
channels = [0,1], because we need to process both H and S plane.
bins = [180,256], 180 for H plane and 256 for S plane.
range = [0,180,0,256] , Hue value lies between 0 and 180 Saturation lies between 0 and 256.
hsv_img = cv.cvtColor(src_img, cv.COLOR_BGR2HSV)
hist = cv.calcHist([hsv_img], [0, 1], None, [180, 256], [0, 180, 0, 256])
plt.imshow(hist)
plt.show()
图像直方图反向投影
反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的特征。
反向投影在某一位置的值就是原图对应位置像素值在原图像中的总数目,即反向投影矩阵中某点的值就是它对应的原图像中的点所在区间的灰度直方图值。
OpenCV提供了一个内置函数cv.calcBackProject(),参数几乎与cv.calcHist()函数相同。
先求出原图像的直方图,再由直方图得到反向投影矩阵。
import numpy as np
import cv2 as cv
import matplotlib.pyplot as plt
roi_img = cv.imread('mask.png')
hsv_roi = cv.cvtColor(roi_img, cv.COLOR_BGR2HSV)
src_img = cv.imread('lena.jpg')
hsv_src = cv.cvtColor(src_img, cv.COLOR_BGR2HSV)
# 计算二维直方图
roi_hist = cv.calcHist([hsv_roi], [0, 1], None, [180, 256], [0, 180, 0, 256])
# 均一化
# 归一化就是把数据转换到一定范围内,
# 使得没有可比性的数据变得具有可比性,同时又保持相比较的两个数据之间的相对关系
# (src, dst, alpha=None, beta=None, norm_type=None, dtype=None, mask=None)
# alpha:范围的下限,beta:范围的上限
roi_hist = cv.normalize(roi_hist, 0, 255, cv.NORM_MINMAX)
# 计算反向投影矩阵
back_project_mat = cv.calcBackProject([hsv_src], [0, 1], roi_hist, [0, 180, 0, 256], 1)
# 生成结构化元素
# cv.getStructuringElement返回指定形状和尺寸的结构元素
# 第一个参数表示内核的形状
# 第二和第三个参数分别是内核的尺寸以及锚点的位置
# 对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心点。
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
# 使用生成的椭圆元素对反向矩阵进行卷积
res_back_project_mat = cv.filter2D(back_project_mat, -1, disc)
# threshold
ret, thresh = cv.threshold(res_back_project_mat, 50, 255, 0)
thresh = cv.merge((thresh, thresh, thresh))
# 与原图求与,获取原图中的像素值
res = cv.bitwise_and(src_img, thresh)
cv.imshow('res mat', res)
key = cv.waitKey()
if key == 27:
cv.destroyAllWindows()
threshold(res_back_project_mat, 50, 255, 0)
thresh = cv.merge((thresh, thresh, thresh))
# 与原图求与,获取原图中的像素值
res = cv.bitwise_and(src_img, thresh)
cv.imshow('res mat', res)
key = cv.waitKey()
if key == 27:
cv.destroyAllWindows()