目标
在本章中,我们将学习直方图反投影的知识。
理论
它是由Michael J. Swain和Dana H. Ballard在他们的论文中提出的,通过颜色直方图进行索引。
**简单地说,它到底是什么?**它用于图像分割或寻找图像中感兴趣的对象。简单地说,它创建了一个与我们的输入图像相同大小(但为单通道)的图像,其中每个像素对应于该像素属于我们的对象的概率。用更简单的话说,输出的图像与其余部分相比,我们感兴趣的对象会有更多的白色。好吧,这就是一个直观的解释。(我不能让它更简单)。直方图背投是与凸轮移位算法等一起使用的。
**我们怎么做呢?**我们创建一个包含我们感兴趣的对象(在我们的例子中,地面、离开的球员和其他东西)的图像的直方图。为了获得更好的效果,物体应该尽可能地填满图像。彩色直方图比灰度直方图更受欢迎,因为物体的颜色比其灰度强度更能定义物体。然后,我们在需要寻找物体的测试图像上 "反投影 "这个直方图,即换句话说,我们计算每个像素属于地面的概率并显示出来。适当的阈值处理后的输出结果单独给了我们地面的信息。
Numpy中的算法
- 首先,我们需要计算我们需要寻找的对象(让它成为 “M”)和我们要搜索的图像(让它成为 “I”)的颜色直方图。
import numpy as np
import cv2 as cvfrom matplotlib import pyplot as plt
#roi是我们需要寻找的对象或对象的区域
roi = cv.imread('rose_red.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
#目标是我们搜索的图像
target = cv.imread('rose.png')
hsvt = cv.cvtColor(target,cv.COLOR_BGR2HSV)
# 使用calcHist查找直方图。也可以用np.histogram2d来完成
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
=
M
I
R\ =\ \frac{M}{I}
R = IM
然后反推R,即用R作为调色板,创建一个新的图像,每个像素都是其对应的目标概率。即B(x,y) = R[h(x,y),s(x,y)] 其中h是色调,s是(x,y)处像素的饱和度。之后,应用条件 B ( x , y ) = m i n [ B ( x , y ) , 1 ] B\left( x,y \right) =min\left[ B\left( x,y \right) ,1 \right] 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
B=D∗B
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函数之前,对象的直方图应该被归一化。它返回概率图像。然后我们用圆盘核对图像进行卷积,并应用阈值。下面是我的代码和输出:
import numpy as np
import cv2 as cv
roi = cv.imread('rose_red.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
target = cv.imread('rose.png')
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)
下面是我工作的一个例子。我使用蓝色矩形内的区域作为样本对象,我想提取整个地面。
其他资源
《通过颜色直方图进行索引》,Swain,Michael J.,第三届计算机视觉国际会议,1990
(“Indexing via color histograms”, Swain, Michael J. , Third international conference on computer vision,1990)