openCV中如何实现meanshift算法查找目标

openCV中如何实现meanshift算法查找目标

这篇文章将为大家详细讲解有关openCV中如何实现meanshift算法查找目标,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    一、简介

    图像直方图的反向投影是一个概率分布图,表示一个指定图像片段出现在特定位置的概率。当我们已知图像中某个物体的大体位置时,可以通过概率分布图找到物体在另一张图像中的准确位置。我们可以设定一个初始位置,在其周围反复移动来提高局部匹配概率,从而找到物体的准确位置,这个实现过程叫做均值平移算法。

    二、实现过程

    因为人物的面部特征相对于其他位置更明显,本次实验主要应用于人物的面部识别。

    1、设定感兴趣的区域

    感兴趣区域的设定有两种方式,一种是已知图片人物脸部位置的像素坐标,通过设定矩形框来定位到人物脸部位置,另一种是使用opencv自带的selectROI函数,手动框选自己感兴趣的位置。

    2、获取脸部直方图并做归一化

    设置一个ColorHistogram类增加一个获取色调直方图的函数getHueHistogram。此函数包含将图像转换成HSV色彩空间,屏蔽低饱和度的像素(可能用到,也可能用不到),计算图像直方图。

    cv::MatgetHueHistogram(constcv::Mat&image2,intminSaturation=0){cv::Mathist;//转换成HSV色彩空间cv::Mathsv;cv::cvtColor(image2,hsv,CV_BGR2HSV);//cv::imshow("hsv",hsv);//掩码(可能用的到也可能用不到)cv::Matmask;if(minSaturation>0){std::vector<cv::Mat>v;cv::split(hsv,v);//将3个通道分割进3幅图像cv::threshold(v[1],mask,minSaturation,255,cv::THRESH_BINARY);//屏蔽低饱和度的像素}//准备一维色调直方图的参数hranges[0]=0.0;hranges[1]=180.0;//范围是0~180channels[0]=0;//色调通道//计算直方图cv::calcHist(&hsv,1,//仅为一幅图像的直方图channels,//使用的通道mask,//二值掩码hist,//作为结果的直方图1,//这是一维的直方图histSize,//箱子数量ranges);//像素值的范围returnhist;}

    然后,对获取的直方图做归一化。

    voidsetHistogram(constcv::Mat&h){histogram=h;cv::normalize(histogram,histogram,1.0);}

    3、反向投影,用meanshift查找目标

    打开第二张图像,并将其转换成HSV色彩空间(代码中对输入的图像做了resize,避免有些图像尺寸过大,显示不全),然后对第一幅图像的直方图做反向投影。下面result是反向投影的结果,目前是框选了路飞的脸部作为感兴趣区域,如果框选路飞的帽子,反向投影会有不一样的效果,大家可以自己尝试。

    //打开第二幅图像,并转换成HSV,对第一幅图像的直方图做反向投影image=cv::imread("lufei2.JPG");resize(image,image3,cv::Size(500,700));cv::cvtColor(image3,hsv,CV_BGR2HSV);//转换成HSV色彩空间intch[1]={0};cv::Matresult=finder.find(hsv,0.0f,180.0f,ch);

    使用openCV的meanshift算法可以将初始矩形区域修改成图像人物脸部的新位置。

    cv::TermCriteriacriteria(cv::TermCriteria::MAX_ITER|cv::TermCriteria::EPS,10,//最多迭代10次1);//或者重心移动距离小于1个像素cv::meanShift(result,rect,criteria);

    至此,就找到了另一张图像中人物的脸部。

    三、其他实验结果

    除了进行从单人图像找另一个单人图像的实验,还做了从单人图像找多人合影的图像,下面是对NBA球星做的一个实验。

    四、部分原理补充

    本实验为了突出感兴趣目标特征,使用了HSV色彩空间的色调分量,使用CV_BGR2HSV标志转换图像后,得到的第一个通道就是色调分量。这是一个8位分量,值范围为0~180(如果使用cv::cvtColor,转换后的图像与原始图像的类型就会是相同的)。为了提取色调图像,cv::split 函数把三通道的 HSV 图像分割成三个单通道图像。这三幅图像存放在一个 std::vector 实例中,并且色调图像是向量的第一个入口(即索引为 0)。

    在使用颜色的色调分量时,要把它的饱和度考虑在内(饱和度是向量的第二个入口),当颜色的饱和度很低时,它的色调信息就会变得不稳定且不可靠。这是因为低饱和度颜色的 B、G 和 R 分量几乎是相等的,这导致很难确定它所表示的准确颜色。因此,在 getHueHistogram 方法中使用 minSat 参数屏蔽掉饱和度低于此阈值的像素,不把它们统计进直方图中。

    均值偏移算法是一个迭代过程,用于定位概率函数的局部最大值,方法是寻找预定义窗口内部数据点的重心或加权平均值。然后,把窗口移动到重心的位置,并重复该过程,直到窗口中心收敛到一个稳定的点。OpenCV 实现该算法时定义了两个停止条件:迭代次数达到最大值 (MAX_ITER);窗口中心的偏移值小于某个限值(EPS),可认为该位置收敛到一个稳定点。这两个条件存储在一个 cv::TermCriteria 实例中。

    五、完整代码

    #include<iostream>#include<Windows.h>#include<opencv2/core.hpp>//图像数据结构的核心#include<opencv2/highgui.hpp>//所有图形接口函数#include<opencv2/imgproc.hpp>#include<opencv2/imgproc/types_c.h>#include<opencv2/imgproc/imgproc.hpp>#include<opencv2/opencv.hpp>usingnamespacestd;usingnamespacecv;//获得色调直方图classColorHistogram{private:inthistSize[3];//每个维度的大小floathranges[2];//值的范围(三个维度用同一个值)constfloat*ranges[3];//每个维度的范围intchannels[3];//需要处理的通道public:ColorHistogram(){//准备用于彩色图像的默认参数//每个维度的大小和范围是相等的histSize[0]=histSize[1]=histSize[2]=256;hranges[0]=0.0;//BGR范围为0~256hranges[1]=256.0;ranges[0]=hranges;//这个类中ranges[1]=hranges;//所有通道的范围都相等ranges[2]=hranges;channels[0]=0;//三个通道:Bchannels[1]=1;//Gchannels[2]=2;//R}//计算一维直方图,BGR的原图转换成HSV,忽略低饱和度的像素cv::MatgetHueHistogram(constcv::Mat&image2,intminSaturation=0){cv::Mathist;//转换成HSV色彩空间cv::Mathsv;cv::cvtColor(image2,hsv,CV_BGR2HSV);//cv::imshow("hsv",hsv);//掩码(可能用的到也可能用不到)cv::Matmask;if(minSaturation>0){std::vector<cv::Mat>v;cv::split(hsv,v);//将3个通道分割进3幅图像cv::threshold(v[1],mask,minSaturation,255,cv::THRESH_BINARY);//屏蔽低饱和度的像素}//准备一维色调直方图的参数hranges[0]=0.0;hranges[1]=180.0;//范围是0~180channels[0]=0;//色调通道//计算直方图cv::calcHist(&hsv,1,//仅为一幅图像的直方图channels,//使用的通道mask,//二值掩码hist,//作为结果的直方图1,//这是一维的直方图histSize,//箱子数量ranges);//像素值的范围returnhist;}};classContentFinder{private://直方图参数floathranges[2];constfloat*ranges[3];intchannels[3];floatthreshold;//判断阈值cv::Mathistogram;//输入直方图public:ContentFinder():threshold(0.1f){//本类中所有通道的范围相同ranges[0]=hranges;ranges[1]=hranges;ranges[2]=hranges;}//对直方图做归一化voidsetHistogram(constcv::Mat&h){histogram=h;cv::normalize(histogram,histogram,1.0);}//查找属于直方图的像素cv::Matfind(constcv::Mat&image,floatminValue,floatmaxValue,int*channels){cv::Matresult;hranges[0]=minValue;hranges[1]=maxValue;//直方图的维度数与通道列表一致for(inti=0;i<histogram.dims;i++)this->channels[i]=channels[i];cv::calcBackProject(&image,1,//只使用一幅图像channels,//通道histogram,//直方图result,//反向投影的图像ranges,//每个维度的值范围255.0//选用的换算系数//把概率值从1映射到255);cv::imshow("result",result);returnresult;}};intmain(){/************均值检测meanshift***********/cv::Matimage=cv::imread("ZMS1.jpg");cv::Matimage2;cv::Matimage3;cv::Mathsv;resize(image,image2,cv::Size(500,700));cv::Rectrect;rect=cv::selectROI("image",image2,false,false);cv::MatimageROI=image2(rect).clone();//手动框选/*cv::Rectrect(227,108,108,104);cv::MatimageROI=image2(rect);*///手动设置矩形框选范围cv::rectangle(image2,rect,cv::Scalar(255,0,0),1,cv::LINE_8,0);cv::imshow("image2",image2);//得到人脸直方图intminsat=65;//最小饱和度ColorHistogramhc;cv::Matcolorhist=hc.getHueHistogram(imageROI,minsat);//把直方图传给ContentFinder类ContentFinderfinder;finder.setHistogram(colorhist);//对直方图做归一化//打开第二幅图像,并转换成HSV,对第一幅图像的直方图做反向投影image=cv::imread("ZMS2.JPG");resize(image,image3,cv::Size(500,700));cv::cvtColor(image3,hsv,CV_BGR2HSV);//转换成HSV色彩空间intch[1]={0};cv::Matresult=finder.find(hsv,0.0f,180.0f,ch);cv::TermCriteriacriteria(cv::TermCriteria::MAX_ITER|cv::TermCriteria::EPS,10,//最多迭代10次1);//或者重心移动距离小于1个像素cv::meanShift(result,rect,criteria);cv::rectangle(image3,rect,cv::Scalar(0,255,0),1,cv::LINE_8,0);cv::imshow("image3",image3);waitKey(0);}

    关于“openCV中如何实现meanshift算法查找目标”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

    发布于 2022-03-03 21:23:21
    收藏
    分享
    海报
    0 条评论
    36
    上一篇:python脚本框架webpy模板赋值的示例分析 下一篇:怎么通过Python实现定时打卡小程序
    目录

      0 条评论

      本站已关闭游客评论,请登录或者注册后再评论吧~

      忘记密码?

      图形验证码