OpenCV  4.10.0
开放源代码计算机视觉
正在加载...
正在搜索...
无匹配项
直方图 - 4:直方图反向投影

目标

在本章中,我们将了解直方图反向投影。

理论

它由 **Michael J. Swain、Dana H. Ballard** 在他们的论文 **基于彩色直方图的索引** 中提出。

**简单来说它是什么?** 它用于图像分割或在图像中查找感兴趣的对象。简单来说,它创建了一幅与输入图像相同大小(但单通道)的图像,其中每个像素都对应于该像素属于我们对象的概率。用更简单的话来说,输出图像将具有我们感兴趣的对象,与其他部分相比有更多的白色。嗯,这是一个直观的解释。(我无法再简单化它)。直方图反向投影用于 camshift 算法等。

**我们如何做到这一点?** 我们创建一个包含我们感兴趣的对象(在我们的情况下是地面、跳出玩家和其他东西)的图像的直方图。对象应尽可能填充图像以获得更好的结果。并且彩色直方图优于灰度直方图,因为对象的彩色是定义对象的一种比灰度强度更好的方式。然后我们在这个测试图像上“反向投影”此直方图,我们需要在其中找到对象,换句话说,我们计算每个像素属于地面的概率并显示它。在适当的阈值处理上生成的结果仅给我们地面。

NumPy 中的算法

  1. 首先,我们需要计算我们需要的对象(设为“M”)和我们将在其中搜索的图像(设为“I”)的彩色直方图。
    import numpy as np
    import cv2 as cvfrom matplotlib import pyplot as plt
    #roi 是我们要找到的对象或对象区域
    roi = cv.imread('rose_red.png')
    assert roi is not None, "file could not be read, check with os.path.exists()"
    hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
    #target 是我们在其中搜索的图像
    target = cv.imread('rose.png')
    assert target is not None, "file could not be read, check with os.path.exists()"
    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] )
    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)
    计算一组数组的直方图。
  2. 找到比率\(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])
    void split(const Mat &src, Mat *mvbegin)
    将多通道数组分割成多个单通道数组。
  3. 现在对圆盘应用卷积,\(B = D \ast 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)
    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))
    针对形态学操作,返回指定尺寸和形状的结构元素。
  4. 现在,最大强度的值为给出了对象的位置。如果我们在图像中预期一个区域,针对一个合适的值进行阈值化会得到一个好的结果。
    ret,thresh = cv.threshold(B,50,255,0)
    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
roi = cv.imread('rose_red.png')
assert roi is not None, "file could not be read, check with os.path.exists()"
hsv = cv.cvtColor(roi,cv.COLOR_BGR2HSV)
target = cv.imread('rose.png')
assert target is not None, "file could not be read, check with os.path.exists()"
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)
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 > &params=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)
计算直方图的反向投影。

以下是我做过的示例。我使用蓝框内的区域作为示例对象,我想提取完整的背景。

图片

更多资源

  1. "基于颜色直方图的索引",斯韦恩·迈克尔,第三届计算机视觉国际会议,1990 年

练习