http://blog.csdn.net/u012372584/article/details/18308681
我们先通过一幅图来理解反向投影:图片来源点击打开链接
这副图的箭头表示了反向投影过程:直方图—>反向投影图像。
反向投影的作用是什么呢?Opencv中文网站这样说:所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。
更通俗一点,反向投影可以通过颜色直方图来理解,我们检测图像中某个像素点的颜色是否位于直方图中,如果位于则将颜色加亮,通过对图像的检测,得出结果图像,结果图像一定和直方图像匹配。那么对于图像颜色的取样点越多,越能更好的找出目标图形。这里直方图的作用在于提供一个比较标准(也就是模版),即对于要检测的图像来说,需要给它提供一个模版,用于识别出和模版相应的特征。
我们在sourse图像(原图像)中识别头部图像(模版图像,中间那个),在结果图像中(最右边那个)较亮部分就是人头部识别出的位置!
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.反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。可以把它看成是一个二维的浮点型数组,二维矩阵,或者单通道的浮点型图像。
可以这样理解:对于calcBackProjectPatch,也就是是基于块的反向投影形式,利用直方图做匹配,类似于模板匹配,只不过这些模板转换为直方图,而原图中以某点为基准,抠出来作对比的部分也转换为直方图,两个直方图作匹配,匹配的结果作为此点的值。 结果会是一张灰度图。
4.如果输入图像和模板图像一样大,那么反向投影相当于直方图对比。如果输入图像比模板图像还小,无法进行反向投影。
5.基于块的反向投影函数:
- void cvCalcBackProjectPatch(
- IplImage** image,
- CvArr* dst,
- CvSize patch_size,
- CvHistogram* hist,
- int method,
- float factor
- );
Opencv实现代码:
- #include "highgui.h"
- #include <cv.h>
-
-
- int main(int argc, char** argv)
- {
- const char* img_file = "d://12.jpg"; //原图像
- const char* model_file = "d://4.jpg";//模版图像
-
-
-
-
- const char* wd_imgsrc = "原图像";
- const char* wd_model_hist = "模板的HS直方图";
- const char* wd_template = "模板匹配结果";
- const char* wd_backpro = "反向投影匹配结果";
- const char* wd_block_backpro = "block反向投影匹配结果";
-
-
- cvNamedWindow(wd_imgsrc,1);
- cvNamedWindow(wd_model_hist,1);
- cvNamedWindow(wd_template,1);
- cvNamedWindow(wd_backpro,1);
- cvNamedWindow(wd_block_backpro,1);
- cvNamedWindow("模版图像",1);
-
-
-
-
- IplImage* imgsrc = cvLoadImage(img_file);
- IplImage* imgmodel = cvLoadImage(model_file);
-
-
-
- IplImage* img_template = cvCreateImage(cvSize(imgsrc->width - imgmodel->width + 1, imgsrc->height - imgmodel->height + 1), 32, 1);
- IplImage* img_backpro = cvCreateImage(cvGetSize(imgsrc), 8, 1);
- CvSize patch_size = cvGetSize(imgmodel);
- IplImage* img_block_backpro = cvCreateImage(cvGetSize(img_template), IPL_DEPTH_32F, 1);
-
-
-
- IplImage* model_hsv = cvCreateImage(cvGetSize(imgmodel), 8 , 3);
- cvCvtColor(imgmodel, model_hsv, CV_BGR2HSV);
- IplImage* h_plane = cvCreateImage(cvGetSize(imgmodel), 8 , 1);
- IplImage* s_plane = cvCreateImage(cvGetSize(imgmodel), 8 , 1);
- IplImage* v_plane = cvCreateImage(cvGetSize(imgmodel), 8 , 1);
- IplImage* planes[] = {h_plane, s_plane};
- cvSplit(model_hsv, h_plane, s_plane, v_plane, NULL);
-
-
- IplImage* imgsrc_hsv = cvCreateImage(cvGetSize(imgsrc), 8 , 3);
- cvCvtColor(imgsrc, imgsrc_hsv, CV_BGR2HSV);
- IplImage* src_h_plane = cvCreateImage(cvGetSize(imgsrc), 8 , 1);
- IplImage* src_s_plane = cvCreateImage(cvGetSize(imgsrc), 8 , 1);
- IplImage* src_v_plane = cvCreateImage(cvGetSize(imgsrc), 8 , 1);
- IplImage* imgsrc_planes[] = {src_h_plane, src_s_plane};
- cvSplit(imgsrc_hsv, src_h_plane, src_s_plane, src_v_plane, NULL);
-
-
-
- int h_bins = 30;
- int s_bins = 32;
- CvHistogram *model_hist, *model_hist_normalized;
- int hist_size[] = {h_bins, s_bins};
- float h_ranges[] = {0, 180};
- float s_ranges[] = {0,255};
- float* ranges[] = {h_ranges, s_ranges};
- model_hist = cvCreateHist(2,
- hist_size,
- CV_HIST_ARRAY,
- ranges,
- 1);
- model_hist_normalized = cvCreateHist(2,
- hist_size,
- CV_HIST_ARRAY,
- ranges,
- 1);
-
-
- cvCalcHist(planes, model_hist, 0, 0);
- cvCopyHist(model_hist, &model_hist_normalized);
- cvNormalizeHist( model_hist_normalized, 1.0);
-
-
-
- int scale =10;
- IplImage* model_hist_img = cvCreateImage( cvSize(h_bins * scale, s_bins * scale), 8, 3);
- cvZero( model_hist_img);
-
- float max_value = 0;
- cvGetMinMaxHistValue( model_hist, 0, &max_value, 0, 0);
-
- for( int h = 0; h< h_bins; h++)
- for( int s = 0; s< s_bins; s++)
- {
- float bin_val = cvQueryHistValue_2D( model_hist, h, s);
- int intensity = cvRound(bin_val * 255/ max_value);
-
-
- cvRectangle( model_hist_img,
- cvPoint(h*scale, s* scale),
- cvPoint((h+1)*scale-1, (s+1)*scale -1),
- CV_RGB(intensity, intensity, intensity),
- CV_FILLED
- );
- }
-
-
-
-
- cvCalcBackProject(imgsrc_planes, img_backpro, model_hist);
- cvCalcBackProjectPatch( imgsrc_planes, img_block_backpro, patch_size, model_hist_normalized, CV_COMP_CHISQR, 1);
- cvMatchTemplate( imgsrc, imgmodel, img_template, CV_TM_CCOEFF_NORMED);
-
-
-
-
- CvPoint min_loc_block, max_loc_templ;
- cvMinMaxLoc(img_block_backpro, 0, 0, &min_loc_block, 0, 0);
- cvMinMaxLoc(img_template, 0, 0, 0, &max_loc_templ, 0);
-
- cvCircle(img_block_backpro, min_loc_block, 30, CV_RGB(0,255,0));
- cvCircle(img_template, max_loc_templ, 30, CV_RGB(255,0,0));
- cvCircle(imgsrc,
- cvPoint(max_loc_templ.x + patch_size.width /2, max_loc_templ.y + patch_size.height/2),
- 40,
- CV_RGB(0,255,0)
- );
-
-
- cvShowImage(wd_imgsrc, imgsrc);
- cvShowImage("模版图像", imgmodel);
- cvShowImage(wd_model_hist, model_hist_img);
- cvShowImage(wd_template, img_template);
- cvShowImage(wd_backpro, img_backpro);
- cvShowImage(wd_block_backpro, img_block_backpro);
- cvWaitKey();
-
-
- cvReleaseImage(&imgsrc);
- cvReleaseImage(&model_hist_img);
- cvDestroyAllWindows();
-
- return 0;
- }
解释一下:(1)下面三张图的匹配结果中,手上都有空洞,原因应该就是原图像中手的颜色不一样,仔细观察发现,原图像中手的颜色有红有白,并且有暗亮之分。
(2)匹配结果正确。block反向投影匹配结果最黑的部分和模版匹配最亮的部分为匹配点。