本文基于opencv官方文档,是本人的学习笔记。版本是在linux下的opencv4.2.0,全部程序调通可运行,无bug。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

# 找到直方图
# opencv中的直方图
image = cv.imread('wq.png', 0)
hist = cv.calcHist([image], [0], None, [256], [0, 256])
# numpy中的直方图
hist, bins = np.histogram(image.ravel(), 256, [0, 256])
# 对于一维直方图可以用下面的方法
hist = np.bincount(image.ravel(), minlength=256)
# 注意,bincount比histogram快十倍左右,而opencv中的calcHist
# 比histogram快四十倍,所以尽量用opencv中的函数

# 绘制直方图
# 1.使用Matplotlib
plt.hist(image.ravel(), 256, [0, 256])
plt.show()
# 或者
img = cv.imread('wq.jpg')
color = ('b', 'g', 'r')
for i, col in enumerate(color):
    histr = cv.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(histr, color=col)
    plt.xlim([0, 256])
plt.show()
# 2.使用opencv
# 可以调整直方图的值及其bin值,
# 使其看起来像x,y坐标,以便您可以使用cv.line()
# 或cv.polyline()函数绘制它以生成与上述相同的图像

# 使用Mask
# 如果你想找到图像某些区域的直方图呢?
# 只需创建一个掩码图像,在你要找到直方图为白色,否则黑色。
# 然后把这个作为掩码传递。

img = cv.imread('wq.png', 0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[300:800, 300:800] = 255
masked_img = cv.bitwise_and(img, img, mask=mask)
# 计算掩码区域和非掩码区域的直方图
# 检查作为掩码的第三个参数
hist_full = cv.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv.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()

# 直方图的均衡
# 全局均衡
img = cv.imread('wq.jpg', 0)
equ = cv.equalizeHist(img)
# stacking images side-by-side
res = np.hstack((img, equ))
cv.imwrite('res.png', res)

# 对比度受限的自适应直方图均衡
img = cv.imread('wq.png', 0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
plt.subplot(221), plt.hist(img.ravel(), 256, [0, 256])
plt.subplot(222), plt.hist(cl1.ravel(), 256, [0, 256])
plt.show()
cv.imwrite("wq_hui.jpg", img)
cv.imwrite('clahe_2.jpg', cl1)

# 二维直方图
# OpenCV中的二维直方图
# 它非常简单,并且使用相同的函数cv.calcHist()进行计算。
# 对于颜色直方图,我们需要将图像从BGR转换为HSV。
# (请记住,对于一维直方图,我们从BGR转换为灰度)。
# 对于二维直方图,其参数将进行如下修改:
# channel = [0,1],因为我们需要同时处理H和S平面。
# bins = [180,256] 对于H平面为180,对于S平面为256。
# range = [0,180,0,256] 色相值介于0和180之间,饱和度介于0和256之间。
img = cv.imread('girl.jpg')
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
hist = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
# 我们得到的结果是尺寸为80x256的二维数组。因此,
# 可以使用cv.imshow()函数像平常一样显示它们。
# 它将是一幅灰度图像,除非您知道不同颜色的色相值,
# 否则不会对其中的颜色有太多了解。
cv.imshow('hist',hist)
cv.waitKey(0)
cv.destroyAllWindows()

# numpy中的二维直方图
# Numpy还为此提供了一个特定的函数:np.histogram2d()。
# 对于一维直方图使用np.histogram()
# 第一个参数是H平面,第二个是S平面,第三个是每个箱子的数量,第四个是它们的范围。
# hist, xbins, ybins = np.histogram2d(h.ravel(),s.ravel(),[180,256],[[0,180],[0,256]])

# 显示二维直方图
# 1.用cv.inshow() 示例见上面
# 2.使用Matplotlib
plt.imshow(hist,interpolation='nearest')
plt.show()

# 这是由Michael J. Swain和Dana H. Ballard在他们的论文《通过颜色直方图索引》中提出的。
# 用简单的话说是什么意思?它用于图像分割或在图像中查找感兴趣的对象。简而言之,
# 它创建的图像大小与输入图像相同(但只有一个通道),其中每个像素对应于该像素属于我们物体的概率。
# 用更简单的话来说,与其余部分相比,输出图像将在可能有对象的区域具有更多的白色值。
# 好吧,这是一个直观的解释。(我无法使其更简单)。直方图反投影与camshift算法等配合使用。
# 我们该怎么做呢?我们创建一个图像的直方图,其中包含我们感兴趣的对象。
# 对象应尽可能填充图像以获得更好的效果。而且颜色直方图比灰度直方图更可取,
# 因为对象的颜色对比灰度强度是定义对象的好方法。
# 然后,我们将该直方图“反投影”到需要找到对象的测试图像上,换句话说,
# 我们计算出属于背景的每个像素的概率并将其显示出来。在适当的阈值下产生的输出使我们仅获得背景。

# opencv中的反投影
# OpenCV提供了一个内建的函数cv.calcBackProject()。
# 它的参数几乎与cv.calchist()函数相同。
# 它的一个参数是直方图,也就是物体的直方图,我们必须找到它。
# 另外,在传递给backproject函数之前,应该对对象直方图进行归一化。
# 它返回概率图像。然后我们用圆盘内核对图像进行卷积并应用阈值。
# 下面是我的代码和结果:

roi = cv.imread('weijin.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
target = cv.imread('wq1.jpg')
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)
# 计算对象的直方图
roihist = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
# 直方图归一化并利用反传算法
cv.normalize(roihist,roihist,0,255,cv.NORM_MINMAX)
dst = cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
# 用圆盘进行卷积
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(dst,-1,disc,dst)
# 应用阈值作与操作
ret,thresh = cv.threshold(dst,50,255,0)
thresh = cv.merge((thresh,thresh,thresh))
res = cv.bitwise_and(target,thresh)
res = np.vstack((target,thresh,res))
cv.imwrite('res.jpg',res)