这篇讲的是使用函数cvCalcBackProjectPatch()在一幅图中寻找某个特定的小物体。
先简单讲下这个函数吧,cvCalcBackProject()计算一个像素是否是一个已知目标的一部分,cvCalcBackProjectPatch()
计算一块区域是否包含已知的目标。函数cvCalcBackProjectPatch()在整个输入图像使用一个滑动窗口,如图1所示。
在输入图像矩阵的每一个位置,块中所有的像素点都被设置为在目标图像中对应的块中心位置的像素点。这一点很重
要,因为图像的许多特性(如纹理)在单一的像素级别上无法确定,但可从一组像素确定。
图1:
cvCalcBackProjectPatch()有两种用法:当采样窗口小于目标时,作为一个区域检测器,当采样窗口和目标一样大
时,作为目标检测器。
接下来讲讲具体实现步骤:
1.首先需要一幅只包含小物体的图片(图片大小和物体一样大即可),计算其二维直方图 (HSV空间中的 h(亮度)和 s(饱和度) 二维直方图;
2.输入一幅待搜索的图片,转化为HSV空间 ,只取h 和 s 分量;
3. 设置搜索块大小和物体大小相同(即和第1步中图片大小相同),使用cvCalcBackProjectPatch反向块投影和图片result;
4.在result中取最大值所在位置,即可得到物体在输入图片中的位置了。最大值用cvMinMaxLoc()函数求即可。
示例代码如下:
#include<cv.h>
#include<highgui.h>
void GetHSV (const IplImage* image, IplImage** h, IplImage** s, IplImage** v);
int main()
{
IplImage* src = cvLoadImage ("bluecup.jpg", 1);
IplImage* h_src = NULL;
IplImage* s_src = NULL;
GetHSV (src, &h_src, &s_src, NULL);
IplImage *images[] = {h_src,s_src};
CvHistogram* hist_src = NULL;
/*计算二维直方图*/
int dims = 2;
int size[] = {30, 32};
float range_h[] = {0, 180};
float range_s[] = {0, 256};
float* ranges[] = {range_h, range_s};
hist_src = cvCreateHist (dims, size, CV_HIST_ARRAY, ranges);
cvCalcHist (images, hist_src);
cvNormalizeHist (hist_src, 1);
IplImage* dst = cvLoadImage ("adrian1.jpg", 1);
IplImage* h_dst = NULL;
IplImage* s_dst = NULL;
GetHSV (dst, &h_dst, &s_dst, NULL);
images[0] = h_dst;
images[1] = s_dst;
CvSize patch_size = cvSize (src->width, src->height);
IplImage* result = cvCreateImage (cvSize(h_dst->width - patch_size.width + 1, h_dst->height - patch_size.height + 1),
IPL_DEPTH_32F, 1);
cvCalcBackProjectPatch (images, result, patch_size, hist_src, CV_COMP_CORREL, 1);
cvShowImage ("result", result);
/*找出最大值位置,从而得到杯子的所在位置*/
CvPoint max_location;
cvMinMaxLoc(result, NULL, NULL, NULL, &max_location, NULL);
max_location.x += cvRound (patch_size.width / 2);
max_location.y += cvRound (patch_size.height / 2);
/*在dst图像中用红框标出我们要检测的物体*/
CvPoint top = cvPoint(max_location.x - patch_size.width / 2,max_location.y - patch_size.height / 2);
CvPoint bottom = cvPoint(max_location.x + patch_size.width / 2, max_location.y + patch_size.height / 2);
cvRectangle (dst, top, bottom, CV_RGB(255, 0, 0), 1, 8, 0);
cvShowImage ("dst", dst);
cvWaitKey (0);
cvReleaseImage(&src);
cvReleaseImage(&dst);
cvReleaseImage(&h_src);
cvReleaseImage(&h_dst);
cvReleaseImage(&s_dst);
cvReleaseImage(&s_src);
cvReleaseHist(&hist_src);
cvReleaseImage(&result);
cvDestroyAllWindows();
}
void GetHSV (const IplImage* image, IplImage** h, IplImage** s, IplImage** v)
{
IplImage* hsv = cvCreateImage (cvGetSize (image), 8, 3);
cvCvtColor (image, hsv, CV_BGR2HSV);
if ((h != NULL) && (*h == NULL))
*h = cvCreateImage (cvGetSize(image), 8, 1);
if ((s != NULL) && (*s == NULL))
*s = cvCreateImage (cvGetSize(image), 8, 1);
if ((v != NULL) && (*v == NULL))
*v = cvCreateImage (cvGetSize(image), 8, 1);
cvSplit (hsv, *h, (s == NULL)?NULL:*s, (v == NULL)?NULL:*v, NULL);
cvReleaseImage (&hsv);
}
被检测物体图像:
待检测的图像:
程序运行结果:
不过通过这个方法来检测物体效率确实很低,如果使用Opencv提供的分类器的话,效率会有所提高。关于分类器,我之前已经写过了相关文章,下面是链接:
http://blog.csdn.net/bruce_zeng/article/details/8087204
产于cvCalcBackProjectPatch的大概意义 :
应该是通过块窗口搜索图像,比较窗口中对应像素的二维直方图与给定直方图的差异。
参考文献:
学习Opencv
http://blog.csdn.net/fdl19881/article/details/6726438