反向投影
- 反向投影是反映直方图模型在目标图像中的分布情况(此直方图模型是由目标图像计算生成的)
- 简单点说就是用直方图模型去目标图像中寻找是否有相似的对象。通常用HSV色彩空间的HS两个通道直方图模型
反向投影 – 步骤
- 建立直方图模型
- 计算待测图像直方图并映射到模型中
- 从模型反向计算生成图像
实现步骤与相关API
- 加载图片imread
- 将图像从RGB色彩空间转换到HSV色彩空间cvtColor
- 计算直方图和归一化calcHist与normalize
- Mat与MatND其中Mat表示二维数组,MatND表示三维或者多维数据,此处均可以用Mat表示。
- 计算反向投影图像 - calcBackProject
- mixChannels()参数说明:
mixChannels(//用于将输入数组的指定通道复制到输出数组的指定通道
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src[0]相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);
void calcBackProject(
const Mat* images,
int nimages,
const int* channels,
InputArray hist,
OutputArray backProject,
const float** ranges,
double scale=1,
bool uniform=true
);
/*
参数1:const Mat类型的images,输入的数组(或数组集),它们需为相同的深度(CV_8U或CV_32F)和相同的尺寸,而通道数可以任意。
参数2:int类型的nimages,输入数组的个数,也就是第一个参数中存放了多少张图像,有几个源数组
参数3:const int类型的channels,需要统计的通道索引。第一个数组通道从0到image[0].channels-1,第二个数组通道从images[0].channes()计算到images[0].channels()+images[1].channels()-1
参数4:InputArray类型的hist,输入的直方图
参数5:OutputArray类型的backProject,目标反向投影阵列,其须为单通道,并且和image[0]有相同的大小和深度
参数6:const float**类型的ranges,表示每一个维度数组(第六个参数dims)的每一维边界阵列,可以理解为每一维数值的取值范围。
参数7:double scale,有默认值1,输出的方向投影可选的缩放因子,默认值为1
参数8:boo类型的uniform,指示直方图是否均匀的标识符,有默认值true
*/
程序代码:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
// 申明图像矩阵,初始化bin数目:
Mat src, hsv, hue;
int bins = 25;
void Hist_and_Backproj(int, void* );
int main( int argc, char** argv )
{
// 1. 读取输入图像并转换到HSV 空间
src=imread("E:/Experiment/OpenCV/Pictures/dog2.jpg");
cvtColor( src, hsv, CV_BGR2HSV );
// 2. 使用Hue通道来创建1维直方图:
// 分离 Hue 通道。 色调在HSV颜色空间定义取值为0°到360°,实际在OpenCV中取值为0-180
hue.create( hsv.size(), hsv.depth() );// 这里要传深度,传type会报错
int nchannels[] = { 0, 0 };
/* // 将输入数组的指定通道复制到输出数组的指定通道
mixChannels(
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);
*/
mixChannels( &hsv, 1, &hue, 1, nchannels, 1 );
imshow("hue", hue);
// 3. 创建 Trackbar 来输入bin的数目
char* window_image = "Source image";
namedWindow( window_image, CV_WINDOW_AUTOSIZE );
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
Hist_and_Backproj(0, 0);
// 现实图像
imshow( window_image, src );
// 等待用户反应
waitKey(0);
return 0;
}
void Hist_and_Backproj(int, void* )
{
MatND hist;
int histSize = MAX( bins, 2 );
float hue_range[] = { 0, 180 };
const float* ranges = { hue_range };
// 计算直方图并归一化到范围[0,255]
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
// 调用函数 calcBackProject 计算同一张图像的反向投影:
MatND backproj;
/*
calcBackProject ( // 反向投影
const Mat * images, // 输入图像,图像深度必须位CV_8U,CV_16U或CV_32F中的一种,尺寸相同,每一幅图像都可以有任意的通道数
int nimages, // 输入图像的数量
const int * channels, // 用于计算反向投影的通道列表,通道数必须与直方图维度相匹配,第一个数组的通道是从0到image[0].channels()-1,
第二个数组通道从图像image[0].channels()到image[0].channels()+image[1].channels()-1计数
InputArray hist, // 输入的直方图,直方图的bin可以是密集(dense)或稀疏(sparse)
OutputArray backProject, // 目标反向投影输出图像,是一个单通道图像,与原图像有相同的尺寸和深度
const float ** ranges, // 直方图中每个维度bin的取值范围
double scale = 1, // 可选输出反向投影的比例因子
bool uniform = true // 直方图是否均匀分布(uniform)的标识符,默认值true
)
*/
calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );
// 显示反向投影
imshow( "BackProj", backproj );
// 绘制直方图
int w = 400; int h = 400;
int bin_w = cvRound( (double) w / histSize );
Mat histImg = Mat::zeros( w, h, CV_8UC3 );
for( int i = 0; i < bins; i ++ ){
rectangle( histImg, Point( i*bin_w, h ),
Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
Scalar( 0, 0, 255 ), -1 ); //-1表示填充矩形
}
imshow( "Histogram", histImg );
}
运行截图
参考博客
其他
– 参考自:http://blog.sina.com.cn/s/blog_6b7d710b0102v5m1.html
1.反向投影的作用是什么?
反向投影用于在输入图像(通常较大)中查找特定图像(通常较小或者仅1个像素,以下将其称为模板图像)最匹配的点或者区域,也就是定位模板图像出现在输入图像的位置。
2.反向投影如何查找(工作)?
查找的方式就是不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
3.反向投影的结果是什么?
反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。
4.特殊情况怎么样?
如果输入图像和模板图像一样大,那么反向投影相当于直方图对比。如果输入图像比模板图像还小,直接罢工~~。
5.使用时有什么要注意的地方?
需要注意的地方比较多,我们对照反向投影函数来说:
void cvCalcBackProjectPatch(
IplImage** image,
CvArr* dst,
CvSize patch_size,
CvHistogram* hist,
int method,
float factor
);
还有最需要注意的地方:这个函数的执行效率非常的低,在使用之前尤其需要注意图像的大小,直方图的维数,对比方式。如果说对比单个直方图对现在的电脑来说是清风拂面,那么反向投影是狂风海啸。对于1010x1010的RGB输入图像,10x10的模板图像,需要生成1百万次3维直方图,对比1百万次3维直方图。