opencv-python 4.10.4. 反投影直方图

发布时间 2023-04-04 10:43:04作者: 一枚码农

理论

它由Michael J. Swain,Dana H. Ballard在他们的论文“Indexing via color histograms”中提出。

用简单的话来说,它到底是什么?它用于图像分割或查找图像中感兴趣的对象。简单地说,它创建了与输入图像大小相同(但是是单一通道)的图像,其中每个像素对应于该像素属于对象的概率。在更简单的世界中,输出的图像将使我们感兴趣的对象与其余部分相比有更多的白色。这是一个直观的解释。(我说得再简单不过了)。直方图反投影与camshift算法等相结合。我们怎么做呢?我们创建一个an的直方图

我们怎么做呢?我们创建一个图像的直方图,其中包含我们感兴趣的对象(在我们的例子中是地面、离开玩家和其他东西)。对象应该尽可能地填充图像,以获得更好的结果。与灰度直方图相比,颜色直方图更受欢迎,因为与灰度强度相比,对象的颜色是一种更好的定义对象的方法。然后我们将这个直方图“向后投射”到我们需要找到目标的测试图像上,换句话说,我们计算出属于地面的每个像素的概率并显示出来。在适当的阈值上得到的输出只给我们提供了基础

以下算法运用我们将使用下边两张图片,我们将在被搜索的图像cow 上查找对象 cow_roi。
image

Numpy中的算法

首先,我们需要计算需要找到的对象(设为“M”)和要搜索的图像(设为“I”)的颜色直方图。

import numpy as np
import cv2 as cvfrom matplotlib import pyplot as plt

#roi is the object or region of object we need to find
roi = cv.imread('cow_roi.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)

#target is the image we search in
target = cv.imread('cow.png')
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)

# Find the histograms using calcHist. Can be done with np.histogram2d also
M = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv.calcHist([hsvt],[0, 1], None, [180, 256], [0, 180, 0, 256] )

求出比率 $$ R=\frac{M}{I} $$ 。然后反投影R,使用R作为调色板并创建一个新图像,每个像素作为其对应的目标概率。 即B(x,y)= R[h(x,y),s(x,y)]其中h是色调,s是(x,y)处像素的饱和度。 之后应用条件B(x,y)= min [B(x,y),1]。

h,s,v = cv.split(hsvt)
B = R[h.ravel(),s.ravel()]
B = np.minimum(B,1)
B = B.reshape(hsvt.shape[:2])

现在对圆盘进行卷积,B = D * B,其中D是盘卷积核。

disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(B,-1,disc,B)
B = np.uint8(B)
cv.normalize(B,B,0,255,cv.NORM_MINMAX)

现在最大强度的位置为我们提供了物体的位置。 如果我们期望图像中有一个区域,那么对适当值进行阈值处理会得到很好的结果。

ret,thresh = cv.threshold(B,50,255,0)

OpenCV中的反投影

OpenCV提供了一个内置函数cv.calcBackProject()。 它的参数与cv.calcHist()函数几乎相同。 它的一个参数是直方图,它是对象的直方图,我们必须找到它。 此外,在传递给backproject函数之前,应该对象直方图进行规范化。 它返回概率图像。 然后我们将图像与光盘卷积核卷积并应用阈值。 以下是我的代码和输出:
cv.calcBackProject(images,channels,hist,ranges,scale[,dst])->dst

  • images:输入图像,是一个图像集合,可以是包含多通道彩色图像的list或tuple,也可以是多个灰度图组成的list或者tuple;list或tuple形式的输入
  • channels:根据images确定,指明要用images里的哪个通道号,根据images的形式确定;list或tuple形式的输入;
  • hist:输入直方图;
  • ranges:图像元素取值的范围;包含2个元素的list或tuple;
  • scale:缩放比例;
  • dst:目标图像,单通道,和images[0]同样的尺寸和depth ;

其中入参images、channels、ranges参数用法同calcHist()。
入参hist是特征图像(roi)的的直方图,使用它在源图像中查找该特征。

import cv2 as cv
from matplotlib import pyplot as plt

cow = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\cow.jpg')
cow_roi = cv.imread(r'C:\Users\yuyalong\Pictures\Saved Pictures\cow_roi.jpg')

hsv_cow = cv.cvtColor(cow, cv.COLOR_BGR2HSV)
hsv_cow_roi = cv.cvtColor(cow_roi, cv.COLOR_BGR2HSV)

# 计算特征图像直方图
hist_roi = cv.calcHist([hsv_cow_roi], [0, 1], None, [180, 256], [0, 180, 0, 256])
cv.normalize(hist_roi, hist_roi, 0, 255, cv.NORM_MINMAX)
# 计算反投影
img_back = cv.calcBackProject([hsv_cow], [0, 1], hist_roi, [0, 180, 0, 256], 2)

# 二值化和叠加
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
img_dest = cv.filter2D(img_back, -1, disc)
ret, thresh = cv.threshold(img_dest, 30, 255, 0)
thresh = cv.merge((thresh, thresh, thresh))
img_merge = cv.bitwise_and(cow, thresh)

# 绘图
fig, ax = plt.subplots(1, 4)
ax[0].set_title('cow')
ax[0].imshow(cv.cvtColor(cow, cv.COLOR_BGR2RGB))
ax[1].set_title('cow_roi')
ax[1].imshow(cv.cvtColor(cow_roi, cv.COLOR_BGR2RGB))
ax[2].set_title('img_back')
ax[2].imshow(img_back, 'gray')
ax[3].set_title('img_merge')
ax[3].imshow(cv.cvtColor(img_merge, cv.COLOR_BGR2RGB))
plt.show()

image