OpenCV 直方图反向投影

Posted 流楚丶格念

tags:

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

文章目录

反向投影介绍

反向投影概念

反向投影是反映直方图模型在目标图像中的分布情况

简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型

反射投影理解

反向投影查找原理:查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。

假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。


举例说明:
例如下面图像的像素矩阵:

求其灰度直方图时,分块的指定区间:[0,3),[4,7),[8,11),[12,16)。区间对应的直方图求解得到 hist=4,4,6,2

根据直方图反向投影原理可得下面矩阵:

可以看到,原图像的256个灰度值被置为很少的几个值了,具体有几个值,要看把0~255划分为多少个区间。

反向投影矩阵中某点的值就是它对应的原图像中的点所在区间的灰度直方图值。所以我们可以看出,一个区间点越多,在反向投影矩阵中就越亮

那么怎么理解反向投影矩阵中的“反向”二字呢?

从这个过程可以看出,我们是先求出原图像的直方图,再由直方图得到反向投影矩阵,由直方图到反向投影矩阵实际上就是一个反向的过程,所以叫反向

反向投影作用:目标检测

一幅图像的反向投影利用了其原始图像(或目标区域)的直方图,将该直方图作为一张查找表来找对应像素点的像素值,即将目标图像像素点的值设置为原始图像(或目标区域)直方图上对应的bin值。该bin值代表了(目标区域)上该像素值出现的概率。从而得到一幅图像的概率值。从而我们可以通过这幅概率图可以得知在这幅图像中,目标出现可能出现的位置。

反向投影步骤

  • 加载图片imread
  • 将图像从RGB色彩空间转换到HSV色彩空间cvtColor
  • 计算直方图和归一化calcHist与normalize
  • Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。
  • 计算反向投影图像 - calcBackProject

相关API

calcBackProject

函数作用:

  • 计算反向投影图像

函数原型:

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

函数参数:

  • images:输入图像,图像深度必须位CV_8U, CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
  • nimages : 输入图像的数量
  • channels : 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels() - 1, 第二个数组通道从图像image[0].channels()到image[0].channels() + image[1].channels() - 1计数
  • hist : 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
  • backProject : 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
  • ranges : 直方图中每个维度bin的取值范围
  • scale = 1 : 可选输出反向投影的比例因子
  • uniform = true : 直方图是否均匀分布(uniform)的标识符,有默认值true

mixChannels

函数功能:

  • 从输入图像中拷贝某通道到输出图像中特定的通道。

函数原型:

void mixChannels(
	const Mat*src,     
	size_t nsrcs,      
	Mat* dst,          
	size_t ndsts,      
	const int* fromTo, 
	size_t npairs      
);

函数参数:

  • src:一系列输入图像的数组, 被拷贝的通道的来源一系列输入图像的数组, 被拷贝的通道的来源
  • nsrcs:输入图像的个数
  • dst:一系列目的图像的数组, 储存拷贝的通道,所有的数组必须事先分配空间(如用create),大小和深度须与输入数组等同。
  • ndsts:目的数组中图像的数目
  • fromTo:通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道。偶数下标的用来标识输入矩阵,奇数下标的用来标识输出矩阵。如果偶数下标为负数,那么相应的输出矩阵为零矩阵。
  • npairs:fromTo中的序号对数(两个算1对)。

代码示例

#include <iostream>
#include <math.h>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/highgui/highgui_c.h> 

using namespace std;
using namespace cv;

Mat src; Mat hsv; Mat hue; 
int bins = 12;
void Hist_And_Backprojection(int, void*);

int main(int argc, char** argv)

	// 加载图像
	src = imread("./test2.jpg");
	if (src.empty()) 
		printf("could not load image...\\n");
		return -1;
	
	const char*  window_image = "input image";
	namedWindow(window_image, CV_WINDOW_AUTOSIZE);
	namedWindow("BackProj", CV_WINDOW_AUTOSIZE);
	namedWindow("Histogram", CV_WINDOW_AUTOSIZE);

	// 转为HSV通道
	cvtColor(src, hsv, CV_BGR2HSV);
	hue.create(hsv.size(), hsv.depth());
	int nchannels[] =  0, 0 ;
	mixChannels(&hsv, 1, &hue, 1, nchannels, 1);

	createTrackbar("Histogram Bins:", window_image, &bins, 180, Hist_And_Backprojection);
	Hist_And_Backprojection(0, 0);

	imshow(window_image, src);
	waitKey(0);
	return 0;


void Hist_And_Backprojection(int, void*) 

	float range[] =  0, 180 ;
	const float *histRanges =  range ;
	Mat h_hist;
	calcHist(&hue, 1, 0, Mat(), h_hist, 1, &bins, &histRanges, true, false);
	normalize(h_hist, h_hist, 0, 255, NORM_MINMAX, -1, Mat());

	Mat backPrjImage;
	calcBackProject(&hue, 1, 0, h_hist, backPrjImage, &histRanges, 1, true);
	imshow("BackProj", backPrjImage);

	int hist_h = 400;
	int hist_w = 400;
	Mat histImage(hist_w, hist_h, CV_8UC3, Scalar(0, 0, 0));
	int bin_w = (hist_w / bins);
	for (int i = 1; i < bins; i++) 
		rectangle(histImage, 
			Point((i - 1)*bin_w, (hist_h - cvRound(h_hist.at<float>(i - 1) * (400 / 255)))),
			//Point(i*bin_w, (hist_h - cvRound(h_hist.at<float>(i) * (400 / 255)))),
			Point(i*bin_w, hist_h),
			Scalar(0, 0, 255), -1);
	
	imshow("Histogram", histImage);

	return;

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

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

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

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

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

OpenCV---直方图反向投影

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