目标
在本章中,我们将学习关于直方图反向投影。
理论
它是由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, "无法读取文件,请检查 os.path.exists()"
assert target is not None, "无法读取文件,请检查 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_BGR)
从文件加载图像。
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0, AlgorithmHint hint=cv::ALGO_HINT_DEFAULT)
将图像从一个颜色空间转换为另一个颜色空间。
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, "无法读取文件,请检查 os.path.exists()"
assert target is not None, "无法读取文件,请检查 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)
计算直方图的反向投影。
下面是我处理过的一个例子。我使用了蓝色矩形内的区域作为样本对象,我想提取整个地面。
image
补充资源
- “通过颜色直方图索引”,Swain, Michael J. , 第三届计算机视觉国际会议, 1990。