反向投影

来源:互联网 发布:阿里云创建二级域名 编辑:程序博客网 时间:2024/05/27 00:33

在学习使用反向投影查找物体时,觉得对反向投影不甚理解。故在opencv官方说明中找到了关于反向投影的说明和实例,先记录分析如下:

什么是反向投影?
◆ 反向投影是一种记录给定图像中的像素点如何适应直方图模型像素分布的方式。
◆ 简单的讲, 所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像 中存在的该特征。
◆ 例如, 你有一个肤色直方图 ( Hue-Saturation 直方图 ),你可以用它来寻找图像中的肤色区域:

反向投影的工作原理?
◆ 我们使用肤色直方图为例来解释反向投影的工作原理:
◆ 假设你已经通过下图得到一个肤色直方图(Hue-Saturation), 下边的直方图就是 模型直方 图 ( 代表手掌的皮肤色调).你可以通过掩码操作来抓取手掌所在区域的直方图:
这里写图片描述
这里写图片描述
下图是另一张手掌图(测试图像) 以及对应的整张图像的直方图:
这里写图片描述
这里写图片描述

◆我们要做的就是使用 模型直方图 (代表手掌的皮肤色调) 来检测测试图像中的皮肤区域。以下是检测的步骤
对测试图像中的每个像素 ( p(i,j) ),获取色调数据并找到该色调( ( h_{i,j}, s_{i,j} ) )在直方图中的bin的位置。
查询 模型直方图 中对应的bin - ( h_{i,j}, s_{i,j} ) - 并读取该bin的数值。
将此数值储存在新的图像中(BackProjection)。 你也可以先归一化 模型直方图 ,这样测试图像的输出就可以在屏幕显示了。
通过对测试图像中的每个像素采用以上步骤, 我们得到了下面的 BackProjection 结果图:
这里写图片描述

e.使用统计学的语言, BackProjection 中储存的数值代表了测试图像中该像素属于皮肤区域的 概率 。比如以上图为例, 亮起的区域是皮肤区域的概率更大(事实确实如此),而更暗的区域则表示更低的概率(注意手掌内部和边缘的阴影影响了检测的精度)。

实例代码:

#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include <iostream>using namespace cv;using namespace std;/// 全局变量Mat src; Mat hsv; Mat hue;int bins = 25;/// 函数申明void Hist_and_Backproj(int, void* );/** @函数 main */int main( int argc, char** argv ){  /// 读取图像  src = imread( argv[1], 1 );  /// 转换到 HSV 空间  cvtColor( src, hsv, CV_BGR2HSV );  /// 分离 Hue 通道  hue.create( hsv.size(), hsv.depth() );  int ch[] = { 0, 0 };  mixChannels( &hsv, 1, &hue, 1, ch, 1 );  /// 创建 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;}/** * @函数 Hist_and_Backproj * @简介:Trackbar事件的回调函数 */void Hist_and_Backproj(int, void* ){  Mat hist;  int histSize = MAX( bins, 2 );  float hue_range[] = { 0, 180 };  const float* ranges = { hue_range };  /// 计算直方图并归一化  calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, &ranges, true, false );  normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );  /// 计算反向投影  Mat backproj;  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 ); }  imshow( "Histogram", histImg );}

本教程仅仅使用Hue通道来创建1维直方图 (你可以从上面的链接下载增强版本,增强版本使用了更常见的H-S直方图,以获取更好的结果):

hue.create( hsv.size(), hsv.depth() );int ch[] = { 0, 0 };mixChannels( &hsv, 1, &hue, 1, ch, 1 );

你可以看到这里我们使用 mixChannels 来抽取 HSV图像的0通道(Hue)。 该函数接受了以下的实参:

■&hsv: 一系列输入图像的数组, 被拷贝的通道的来源
■1: 输入数组中图像的数目
■&hue: 一系列目的图像的数组, 储存拷贝的通道
■1: 目的数组中图像的数目
■ch[] = {0,0}: 通道索引对的数组,指示如何将输入图像的某一通道拷贝到目的图像的某一通道。在这里,&hsv图像的Hue(0) 通道被拷贝到&hue图像(单通道)的0 通道。
■1: 通道索引对的数目

显示1维 Hue 直方图:

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 );

调用函数 calcBackProject 计算同一张图像的反向投影所有的实参都已经知道了(与计算直方图的实参一样), 仅仅增加了 backproj 矩阵,用来储存原图像(&hue)的反向投影

Mat backproj;calcBackProject( &hue, 1, 0, hist, backproj, &ranges, 1, true );

定义窗口字符串时,有如下两种形式一种是偏向c的模式一种偏向c++,具体区别见 基础知识中关于string 与char*的说明

char* window_image = "Source image";std::string window_image = "Source image";

创建滑动条:

cv::createTrackbar("* Hue  bins: ", window_image, &bins, 180, Hist_and_Backproj );Hist_and_Backproj(0, 0);// 需调用函数初值

此方式是一种简单方便的办法,应仔细体会
基于OpenCV 2.3.2 documentation,createTrackbar官方文档说明翻译如下:

C++: int createTrackbar(const string& trackbarname, const string& winname, int* value, int count, TrackbarCallback onChange=0, void* userdata=0

createTrackbar
创建一个跟踪条(轨迹条),并将跟踪条附到制定的窗口上。
参数:
trackbarname=》所创建的跟踪条的名字
winname=》跟踪条所依附的窗口的名字
value=》可选的指向整型变量的指针,整型变量的值对应于滑动条的位置。初始创建时,滑动条的值就是这个整型变量的值。
count=》滑动条最大的值。最小值总是为0。
onChange=》指向回调函数的指针,每次滚动条改变位置时,这个函数就会被调用。这个函数的原型应该为:void Foo(int, void*);其中第一个参数是跟踪条的位置,第二个参数是用户数据(见下一个参数)。如果回调为空,表示没有回调函数被调用,仅仅value会有变化。
userdata=》通过回调函数传递的用户数据。它可以控制跟踪条事件而不需要使用全局变量。
这个createTrackbar函数创建一个具有特定名称和范围的轨迹条(滚动条,或者说是滑块范围控制工具),指定一个和轨迹条位置同步的变量。而且要指定回调函数,在轨迹条位置改变的时候来调用这个回调函数。创建的轨迹条显示在指定的winname所代表的窗口上。

0 1
原创粉丝点击