【OpenCV】直方图(二)反向投影

反向投影是一种记录像素点或像素块如何适应直方图模型中分布的方式。通俗的解释下,就是首先计算出某一特征的直方图模型,然后根据直方图模型去图像中搜索该特征。反向投影可以分为记录像素点和像素块,这在OpenCV中是两个函数,分别为cvCalcBackProject()和cvCalcBackProjectionPatch()。下面对于这两个函数,将分别做介绍。反向投影应用非常广泛,在之前的博客中,也提到这个,记得是在目标跟踪运动camshift算法中用到了反向投影。

void cvCalcBackProject(
    IplImage** image,//单通道的图像数组
    CvArr*     back_project,//反向投影图
    const CvHistogram* hist//直方图
)

关于上面这个函数的典型应用,就是用肤色直方图来推导肤色图像的概率.下面的这个程序示例的思路大致是这样的:输入特征图像->BGR转换到HSV空间->分离出H分量图像,并计算出直方图模型->输入带有特征的图像->BGR转换到HSV空间,并分离出H分量图像->计算反向投影图, 下面就给出程序的完整示例:

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;

int main()
{
    //输入特征图像
    IplImage *src = cvLoadImage("1.jpg", CV_LOAD_IMAGE_UNCHANGED);
    IplImage *src_hsv = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,3);
    cvCvtColor(src, src_hsv, CV_BGR2HSV);//BGR转换到HSV空间
    cvNamedWindow("SRC", CV_WINDOW_AUTOSIZE);
    cvShowImage("SRC", src);
    cvNamedWindow("HSV", CV_WINDOW_AUTOSIZE);
    cvShowImage("HSV", src_hsv);


    IplImage *src_h_plane = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
    cvSplit(src_hsv, src_h_plane, NULL, NULL, NULL);//从HSV图像中分离出H分量图

    //计算特征图像的H分量的直方图
    int size[] = { 180 };
    float ranges_h[] = { 0, 181 };
    float *ranges[] = { ranges_h };
    CvHistogram *hist_src = cvCreateHist(1, size, CV_HIST_ARRAY, ranges);
    cvCalcHist(&src_h_plane, hist_src);

    //输入目标图像,BGR2HSV,分离出H分量图像
    IplImage *dst = cvLoadImage("2.jpg",CV_LOAD_IMAGE_UNCHANGED);
    IplImage *hsv_dst = cvCreateImage(cvGetSize(dst), 8, 3);
    cvCvtColor(dst, hsv_dst, CV_BGR2HSV);
    IplImage *h_plane_dst = cvCreateImage(cvGetSize(dst), 8, 1);
    cvSplit(hsv_dst, h_plane_dst, NULL, NULL, NULL);

    //计算反向投影图
    IplImage *dst_probably = cvCreateImage(cvGetSize(h_plane_dst), IPL_DEPTH_8U, 1);
    cvZero(dst_probably);
    cvCalcBackProject(&h_plane_dst, dst_probably, hist_src);
    cvNamedWindow("result", CV_WINDOW_AUTOSIZE);
    cvShowImage("result", dst_probably);
    cvThreshold(dst_probably, dst_probably, 250, 255, CV_THRESH_BINARY);
    cvNamedWindow("dst1", CV_WINDOW_AUTOSIZE);
    cvShowImage("dst1", dst_probably);

    //对得到的反向投影图进行腐蚀膨胀等操作处理下
    IplConvKernel* kernel = cvCreateStructuringElementEx(31, 31, 15, 15, CV_SHAPE_RECT);
    cvMorphologyEx(dst_probably, dst_probably, NULL, kernel, CV_MOP_OPEN);
    cvNamedWindow("dst2", CV_WINDOW_AUTOSIZE);
    cvShowImage("dst2", dst_probably);

    cvWaitKey();
    cvReleaseImage(&src);
    cvReleaseImage(&src_hsv);
    cvReleaseImage(&src_h_plane);
    cvReleaseImage(&dst);
    cvReleaseImage(&hsv_dst);
    cvReleaseImage(&h_plane_dst);
    cvReleaseImage(&dst_probably);
    cvDestroyWindow("SRC");
    cvDestroyWindow("HSV");
    cvDestroyWindow("result");
    cvDestroyWindow("dst1");
    cvDestroyWindow("dst2");
    return 0;
}

这个程序运行的结果就是类似于Learning OpenCV课本上的结果:
这里写图片描述
这里写图片描述
这里写图片描述

接下来,我们来讨论下基于块的反向投影。我们可以利用基本的back_project方法来为一个特定像素是否可能是一个特定目标类型的成员(以一个直方图作为该目标类型的模型)建模。这与某个特定目标是否存在的概率计算不完全相同。另一种方法考虑图像子区域以及子区域的特征直方图,并且想知道子区域特征的直方图是否与模型的直方图匹配。然后,我们把每一个这样的子区域与目标是否位于该子区域的概率联系起来。

在OpenCV中,基于块的反向投影函数为cvCalcBackProject(),在整个输入图像使用一个滑动窗口,在输入图像矩阵的每一个位置,块中所有的像素点都被设置为在目标图像中对应的块中心的位置的像素点。这一点非常重要,因为图像的许多特性在单一像素级别上无法确定,但可从一组像素确定。下面简介下函数cvCalcBackProjectPatch()。

void cvCalcBackProjectPatch(
    IplImage** images,//单通道图像
    //dst是一个单通道的浮点型图像,大小为
    //images[0][0].width-patch_size.x+1,image[0][0].height-patch_size.y+1
    CvArr*     dst,
    CvSize     patch_size,//窗口的大小,可以从cvSize(width,height)得到
    CvHistogram*  hist,//特征的直方图
    int        method,//与直方图比较cvCompareHist()中的参数一致
    float      factor   //归一化水平,默认为1
)

下面给出上述函数的示例程序,**程序的大致思路:输入特征图像->BGR2HSV->分离出H、S分量图像->计算出H,S分量直方图->输入目标图像->BGR2HSV->分离出H、S分量图像->计算反向投影->在反向投影图像找到最大值的位置。**cvCalcBackProjectPatch()中的参数dst,patch_size要注意下,下面是完整的程序示例:

#include <iostream>
#include<opencv2/opencv.hpp>
using namespace std;

void GetHSV(const IplImage *image, IplImage **h, IplImage **s, IplImage **v);

int main()
{
    IplImage *src = cvLoadImage("BlueCup.jpg",CV_LOAD_IMAGE_UNCHANGED);
    IplImage *h_src = NULL, *s_src = NULL;
    GetHSV(src, &h_src, &s_src, NULL);
    IplImage *images[] = { h_src, s_src };
    CvHistogram *hist_src;
    {//计算二维直方图
        int dims = 2;
        int size[] = { 30, 32 };
        float range_h[] = { 0, 180 } 
        , 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("adrian.jpg",CV_LOAD_IMAGE_UNCHANGED);
    IplImage *h_dst = NULL, *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);//块搜索时处理边缘是直接舍去,故result的大小比dst小path_size大小
    //32F类型,取值为0~1最亮为1,可直接显示
    //CV_COMP_CORREL相关度,1时最匹配,0时最不匹配
    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图像中用红色小圆点标出位置
    cvCircle(dst, max_location, 3, CV_RGB(255, 0, 0), -1);

    cvShowImage("dst", dst);
    cvWaitKey();

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

上述程序运行的效果如下:
这里写图片描述这里写图片描述

上图中白色的亮区就是检测到的杯子的位置,并在左图中做了红点标记。

使用局部块的时候,函数cvCalcBackProjectPatch()有两种用法:当采样窗口小于目标时,作为一个区域检测器,当采样窗口和目标一般大时,作为目标检测器。在上述的应用中,就是作为目标检测器。采样窗口和目标一般大,在反向投影图中找到了整个目标-杯子。

http://blog.csdn.net/fdl19881/article/details/6726438

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页