目标
在本章中,我们将了解直方图反向投影。
理论
它由 **Michael J. Swain、Dana H. Ballard** 在他们的论文 **基于彩色直方图的索引** 中提出。
**简单来说它是什么?** 它用于图像分割或在图像中查找感兴趣的对象。简单来说,它创建了一幅与输入图像相同大小(但单通道)的图像,其中每个像素都对应于该像素属于我们对象的概率。用更简单的话来说,输出图像将具有我们感兴趣的对象,与其他部分相比有更多的白色。嗯,这是一个直观的解释。(我无法再简单化它)。直方图反向投影用于 camshift 算法等。
**我们如何做到这一点?** 我们创建一个包含我们感兴趣的对象(在我们的情况下是地面、跳出玩家和其他东西)的图像的直方图。对象应尽可能填充图像以获得更好的结果。并且彩色直方图优于灰度直方图,因为对象的彩色是定义对象的一种比灰度强度更好的方式。然后我们在这个测试图像上“反向投影”此直方图,我们需要在其中找到对象,换句话说,我们计算每个像素属于地面的概率并显示它。在适当的阈值处理上生成的结果仅给我们地面。
NumPy 中的算法
- 首先,我们需要计算我们需要的对象(设为“M”)和我们将在其中搜索的图像(设为“I”)的彩色直方图。
import numpy as np
import cv2 as cvfrom matplotlib import pyplot as plt
assert roi is not None, "file could not be read, check with os.path.exists()"
assert target is not None, "file could not be read, check with os.path.exists()"
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] )
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
从文件中加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
将图像从一种颜色空间转换为另一种颜色空间。
void calcHist(const Mat *images, int nimages, const int *channels, InputArray mask, OutputArray hist, int dims, const int *histSize, const float **ranges, bool uniform=true, bool accumulate=false)
计算一组数组的直方图。
- 找到比率\(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]\)。
B = R[h.ravel(),s.ravel()]
B = np.minimum(B,1)
B = B.reshape(hsvt.shape[:2])
void split(const Mat &src, Mat *mvbegin)
将多通道数组分割成多个单通道数组。
- 现在对圆盘应用卷积,\(B = D \ast B\),其中 D 是圆盘核。
B = np.uint8(B)
void normalize(InputArray src, InputOutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray())
对数组的范数或值范围进行归一化。
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT)
使用核对图像进行卷积。
Mat getStructuringElement(int shape, Size ksize, Point anchor=Point(-1,-1))
针对形态学操作,返回指定尺寸和形状的结构元素。
- 现在,最大强度的值为给出了对象的位置。如果我们在图像中预期一个区域,针对一个合适的值进行阈值化会得到一个好的结果。
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)
将固定级别阈值应用于每个数组元素。
就是这么简单!
OpenCV 中的反向投影
OpenCV 提供了一个内置函数 cv.calcBackProject()。它的参数几乎与 cv.calcHist() 函数相同。它的一个参数是对象直方图,我们必须找到它。此外,在传递给反向投影函数之前,应该先归一化对象直方图。它返回概率图像。然后,我们用一个圆盘卷积核对图像进行卷积并应用阈值。以下是我的代码和输出
import numpy as np
import cv2 as cv
assert roi is not None, "file could not be read, check with os.path.exists()"
assert target is not None, "file could not be read, check with os.path.exists()"
roihist =
cv.calcHist([hsv],[0, 1],
None, [180, 256], [0, 180, 0, 256] )
thresh =
cv.merge((thresh,thresh,thresh))
res = np.vstack((target,thresh,res))
void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray())
计算两个阵列的逐位合取 (dst = src1 & src2) 计算逐元素位...
void merge(const Mat *mv, size_t count, OutputArray dst)
用几个单通道数组创建了一个多通道数组。
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > ¶ms=std::vector< int >())
将图像保存到指定文件中。
void calcBackProject(const Mat *images, int nimages, const int *channels, InputArray hist, OutputArray backProject, const float **ranges, double scale=1, bool uniform=true)
计算直方图的反向投影。
以下是我做过的示例。我使用蓝框内的区域作为示例对象,我想提取完整的背景。
图片
更多资源
- "基于颜色直方图的索引",斯韦恩·迈克尔,第三届计算机视觉国际会议,1990 年
练习