了解直方图反投影以分割彩色对象,在本章中,我们将学习直方图反投影。
理论
它是由 Michael J. Swain 和 Dana H. Ballard 在他们的论文“ 通过颜色直方图索引”中提出的。
简单来说到底是什么?它用于图像分割或在图像中查找感兴趣的对象。简而言之,它创建的图像大小与输入图像相同(但只有一个通道),其中每个像素对应于该像素属于我们物体的概率。在更简单的环境中,与其余部分相比,输出图像将使我们感兴趣的对象具有更多的白色。好吧,这是一个直观的解释。(我无法使其更简单)。直方图反投影与camshift算法等配合使用。
我们该怎么做呢 ?我们创建一个图像的直方图,其中包含我们感兴趣的对象(在我们的示例中是地面,离开播放器等)。对象应尽可能填充图像以获得更好的效果。而且颜色直方图比灰度直方图更可取,因为对象的颜色比其灰度强度是定义对象的更好方法。然后,我们将该直方图“反向投影”到需要找到对象的测试图像上,换句话说,我们计算出属于地面的每个像素的概率并将其显示出来。在适当的阈值下产生的输出仅使我们有基础。
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('rose_red.png')
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
#target is the image we search in
target = cv.imread('rose.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,
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)
# calculating object histogram
roihist = cv.calcHist([hsv],[0, 1], None, [180, 256], [0, 180, 0, 256] )
# normalize histogram and apply backprojection
cv.normalize(roihist,roihist,0,255,cv.NORM_MINMAX)
dst = cv.calcBackProject([hsvt],[0,1],roihist,[0,180,0,256],1)
# Now convolute with circular disc
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
cv.filter2D(dst,-1,disc,dst)
# threshold and binary AND
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)
以下是我处理过的一个示例。我将蓝色矩形内的区域用作示例对象,我想提取整个地面。