目标
本章我们将学习直方图反投影。
理论
该方法由Michael J. Swain 和 Dana H. Ballard 在他们的论文Indexing via color histograms中提出。
简单来说是什么? 它用于图像分割或查找图像中感兴趣的对象。简单来说,它会创建一个与输入图像大小相同(但只有一个通道)的图像,其中每个像素对应于该像素属于我们目标对象的概率。更简单地说,输出图像中我们感兴趣的目标对象会比其余部分更亮(白色)。嗯,这是一个直观的解释。(我无法再更简单地解释了)。直方图反投影与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是(x,y)处像素的色相,s是饱和度。之后应用条件\(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)
计算直方图的反向投影。
下面是一个我使用的例子。我使用蓝色矩形内的区域作为样本对象,并希望提取完整的背景。
图像
附加资源
- “基于颜色直方图的索引”,Swain, Michael J.,第三届国际计算机视觉会议,1990年。