直方图的反向投影的原理详解及OpenCV下的示例源码

Posted 昊虹算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直方图的反向投影的原理详解及OpenCV下的示例源码相关的知识,希望对你有一定的参考价值。

博主注:如果想清楚明白理解本文的内容,建议大家先阅读博主的另两篇博文,链接分别如下:
对OpenCV的函数calcHist()进行透彻解析,并利用它实例绘制一个一维灰度直方图
详解什么叫二维直方图,并利用OpenCV的函数calcHist()绘制图像的H-S二维直方图

简单地讲,直方图的反向投影就是首先计算某一特征的直方图,然后将这个直方图反向投影到图像中,进而可以判断图像中是否存在该特征。

再简单点讲,所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中是否存在该特征的方法。

直方图的反向投影常用于对目标的跟踪的定位。

OpenCV提供了函数calcBackProject()用于对图像进行直方图的反向投影运算。

函数calcBackProject()的C++原型如下:

void cv::calcBackProject(	const Mat * 	images,
							int 	nimages,
							const int * 	channels,
							InputArray 	hist,
							OutputArray 	backProject,
							const float ** 	ranges,
							double 	scale = 1,
							bool 	uniform = true 
						)	

Python原型如下:

calcBackProject(images, channels, hist, ranges, scale[, dst]) -> dst

原型中各参数的意义如下:

images---需要做直方图反向投影的图像,所有的图像应具有相同的尺寸和数据类型,在OpenCV3中,数据类型只能是CV_8U或CV_32F,但是不同图像的通道数可以不同。

nimages---输入图像数量。

channels---输入图像需要参与做直方图反向投影的通道列表。这里需要注意的是,输入直方图hist是几维的就需要有几个输入图像参与。

hist---输入直方图。

backProject---输入图像的直方图反投影图像,与输入图像具有相同尺寸和数据类型的单通道图像。

ranges---输入直方图每个维度的取值范围。

scale---输出直方图反投影图像的比例因子。

uniform----输入直方图是否均匀的标志。

在使用函数calcBackProject()进行直方图反向投影计算前,我们首先需要知道OpenCV的这个函数是怎么计算图像的反向投影的。

我们以一个简单的例子来说明这个问题。

设有某特征矩阵A:

 令函数calcHist()中的参数histSize和ranges如下:

histSize[] =  6 ; 
ranges[] = 0, 6;

则计算A的直方图数据A_hist的python代码如下:

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1

import numpy as np
import cv2 as cv

A = np.array([[4, 2, 5],
              [1, 2, 3],
              [4, 4, 2]], dtype='uint8')

A_hist = cv.calcHist([A], [0], None, [6], [0, 6])

A_hist= 0,1,3,1,3,1 其意义如下:

0,1,3,1,3,1 依次代表:

灰度值为0的像素个数为0;
灰度值为1的像素个数为1;
灰度值为2的像素个数为3;
灰度值为3的像素个数为1;
灰度值为4的像素个数为3;
灰度值为5的像素个数为1;

下面我们根据A的直方图数据A_hist计算矩阵B(图像B)的直方图反向投影B_back_projection:

矩阵B(图像B)的内容如下:

 因为B[0,0]=4,又因为A_hist[4] = 3 所以B_back_projection[0,0] = 3

 因为B[0,1]=4,又因为A_hist[4] = 3 所以B_back_projection[0,0] = 3

 因为B[0,2]=2,又因为A_hist[2] = 3 所以B_back_projection[0,0] = 3

 因为B[1,0]=1,又因为A_hist[1] = 1 所以B_back_projection[0,0] = 1

 因为B[1,1]=5,又因为A_hist[5] = 1 所以B_back_projection[0,0] = 1

 因为B[1,2]=3,又因为A_hist[3] = 1 所以B_back_projection[0,0] = 1

所以有

我们用程序验证一下是不是我们手工计算的结果:

代码如下:

# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也可联系博主
# 图像处理技术交流QQ群 271891601

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1

import numpy as np
import cv2 as cv

A = np.array([[4, 2, 5],
              [1, 2, 3],
              [4, 4, 2]], dtype='uint8')

A_hist = cv.calcHist([A], [0], None, [6], [0, 6])

B = np.array([[4, 4, 2],
              [1, 5, 3]], dtype='uint8')

B_back_projection = cv.calcBackProject([B], [0], A_hist, [0, 6], 1)

运行结果如下:

 可见,与我们的手工计结果一样,所以我们通过以上过程也就知道了函数calcHist()是怎样计算图像的直方图反向投影了,也就间接知道了图像的直方图反向投影是怎么计算出的了。

从以上过程我们看出,整个过程实际上是把特征A的直方图作为概率模板,按这个模板把矩阵B(图像B)的像素值进行重映射。这样,符合特征A的直方图的部分就会被显现出来,从而实现对特征A的查找,进而实现目标的区配、定位、跟踪。

接下来我们来看一个实例。

下面这幅图是某歌手在唱歌时的截图:

我们希望借助上面这幅图中歌手的面部直方图特征,查找出另一幅图中此歌手的面部大概位置。

另一幅图(lml-02.jpg)如下:

 我们可以像下面这样操作完成这个任务。

 首先我们从这幅图中截取歌手的面部(lml-template.jpg),如下:

用这个截取的面部作为特征,计算待查找图像的直方图反向投影。

代码如下:

# 博主微信/QQ 2487872782
# 有问题可以联系博主交流
# 有图像处理需求也可联系博主
# 图像处理技术交流QQ群 271891601

# !/usr/bin/env python
# -*- coding: utf-8 -*-
# OpenCV的版本为4.1

import numpy as np
import cv2 as cv
import sys

if __name__ == '__main__':
    # 读取图像并判断是否读取成功
    lml_02_image = cv.imread('F:/material/images/histogram_back_projection/lml-02.jpg')
    template_image = cv.imread('F:/material/images/histogram_back_projection/lml-template.jpg')

    if lml_02_image is None or template_image is None:
        print('Failed to read calcBackProject.jpg or calcBackProject_template.jpg.')
        sys.exit()
    # 分别将其颜色空间从BGR转换到HSV
    origin_hsv = cv.cvtColor(lml_02_image, cv.COLOR_BGR2HSV)
    template_hsv = cv.cvtColor(template_image, cv.COLOR_BGR2HSV)

    # 计算模板图像的直方图
    template_hist = cv.calcHist([template_hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])

    # 对模板图像的直方图进行偏移归一化处理
    cv.normalize(template_hist, template_hist, 0, 255, cv.NORM_MINMAX)
    # 计算直方图的反向投影
    result = cv.calcBackProject([origin_hsv], [0, 1], template_hist, [0, 180, 0, 256], 1)

    # 显示图像
    cv.imshow('lml-02', lml_02_image)
    cv.imshow('Template Image', template_image)
    cv.imshow('calcBackProject_result', result)
    cv.waitKey(0)
    cv.destroyAllWindows()

运行结果如下图所示:

 从运行结果可以看出,根据歌手的面部直方图特征,我们从其演唱时的另一幅截图中找到了它的头部位置。

以上是关于直方图的反向投影的原理详解及OpenCV下的示例源码的主要内容,如果未能解决你的问题,请参考以下文章

OpenCv 020---图像直方图反向投影

OpenCV中直方图反向投影算法详解与实现

opencv——图像直方图与反向投影

OpenCV实战——基于反向投影直方图检测图像内容

OpenCV实战——基于反向投影直方图检测图像内容

OpenCV 学习笔记(直方图反向投影 BackProject)