OpenCV2应用Meanshift查找相似物体

来源:互联网 发布:软件设计师考试大纲 编辑:程序博客网 时间:2024/06/02 05:29

1.概念

这里说的是OpenCV中实现的Meanshift算法的大体概念。
在OpenCV中meanshift算法的原理,大体上是这样的:
首先,预先定义一个窗口(可以通过openCv中的ROI在图像上定义一个感兴趣的窗口),然后计算窗口内所有像素(数据)点的重心,然后将窗口中的中心移动到重心点。重复这个过程,直到满足迭代终止条件。
OpenCV2中,实现meanshift算法的函数是:

cv::meanShift(参数,参数,参数)

当然,调用该函数前要进行一些设置,获得符合函数要求的参数(下面会详细讲)
Meanshift查找相似物体的流程
1、在图片1在定义感兴趣区域(ROI),获得感兴趣的物体A。
2、对ROI区域,提取合适的特征(这里提取归一化后的直方图特征)。
3、读取包含与物体A相似物体的图片2,通过上面提取出的ROI区域的直方图特征。对图片2进行直方图反投影,获得一张“图片2“关于”ROI区域“的概率图。
直方图特征反投影:简单起见,下面以灰度图(彩色图与其类似)大体来说,直方图归一化后,可以将直方图看成一个概率函数。例如,假设在归一化后的直方图中,灰度值为60的,对应的值为0.4,而所有灰度值对应的值,加起来为1。这样,便成为了一个概率函数 yi=f(xi), i=0…255,y1+y2+…y255=1.现在,对于一张新的图片,将它的每个像素点,用上面归一化后的直方图去映射成新的值,这个值可以说成是该像素点属于直方图的概率(如,图片中像素的灰度值为60,那么将其变为0.4)。这个过程,就叫对某一张图片,用某个直方图特征反投影,投影后得到的图片,可以叫作对于该直方图特征的概率图。
概率图有什么用?
仔细想想,一个直方图特征对应的一张图像。用这个直方图特征在某张图像在投影,计算出来的概率图,就是该图像每一个像素点与直方图特征对应的图像的相似度。
4、获得一张“图片2“关于”ROI区域“的概率图后,在概率图中与定义一块与ROI区域位置、大小一张的区域,以这个区域为起始点,通过Meanshift算法迭代,找到概率图中,概率最大的区域。该区域(位置),即为图片2中与图片1感兴趣区域(ROI)最相似部分的位置。

2.代码

histogram.h

//Histogram类,计算彩色图像的灰度值#ifndef HISTOGRAM#define HISTOGRAM#include <opencv2\core\core.hpp>#include <opencv2\imgproc\imgproc.hpp>#include<opencv2\highgui\highgui.hpp>class Histogram {  private:    int histSize[3];    float hranges[2];    const float* ranges[3];    int channels[3];    public:    Histogram() {        histSize[0]= histSize[1]= histSize[2]= 256;        hranges[0]= 0.0;    // 灰度值区域0到255        hranges[1]= 255.0;        ranges[0]= hranges; //三个通道的灰度值的范围        ranges[1]= hranges;         ranges[2]= hranges;         channels[0]= 0;     // 三个通道        channels[1]= 1;         channels[2]= 2;     }    //计算机hsv图像色调通道直方图,并去除低饱和的像素点    cv::MatND getHueHistogram(const cv::Mat &image, int minSaturation = 0)    {        //直方图        cv::MatND hist;        //HSV空间        cv::Mat hsv;        //转换到HSV空间        cv::cvtColor(image, hsv, CV_BGR2HSV);        //掩码,只处理非零点        cv::Mat mask;        //剔除低于设置饱和度的点        if (minSaturation > 0)        {            std::vector<cv::Mat>v;            cv::split(hsv, v);            cv::threshold(v[1], mask, minSaturation, 255, cv::THRESH_BINARY);        }        //色调值的范围        hranges[0] = 0.0;        hranges[1] = 180.0;        //只处理0通道        channels[0] = 0;        //计算直方图        cv::calcHist(&hsv,            1,            channels,            mask,            hist,            1,            histSize,            ranges            );        return hist;    }};#endif

contentFinder.h

#ifndef OFINDER#define OFINDER#include <opencv2\core\core.hpp>#include <opencv2\imgproc\imgproc.hpp>#include<opencv2\highgui\highgui.hpp>class ContentFinder {  private:    float hranges[2];//像素值的范围    const float* ranges[3];//指向三个通道像素值范围的指针    int channels[3];//通道    float threshold;//阈值    cv::MatND histogram;//直方图  public:      //初始化      ContentFinder() : threshold(-1.0f){        ranges[0]= hranges; // 所有通道有相同的范围        ranges[1]= hranges;         ranges[2]= hranges;     }    // 设置阈值[0~1]    void setThreshold(float t) {        threshold= t;    }    // 得到阈值    float getThreshold() {        return threshold;    }    // 设置直方图    void setHistogram(const cv::MatND& h) {        histogram= h;        //直方图归一化        cv::normalize(histogram,histogram,1.0);    }    // 反投影直方图    cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim) {        cv::Mat result;        hranges[0]= minValue;        hranges[1]= maxValue;        for (int i=0; i<dim; i++)            this->channels[i]= channels[i];           cv::calcBackProject(&image,                      1,            //1张图片                      this->channels,     //通道                      histogram,    // 直方图                      result,       // 结果                      ranges,       // 每个维度的灰度值范围                      255.0         // 缩放因子            );           // 阈值化        if (threshold>0.0)            cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);        return result;    }};#endif

源.cpp

//不显示CMD窗口,或者关闭CMD窗口,程序不退出#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")#include"histogram.h"#include"contentFinder.h"//需要包含此文件才能调用Meanshift函数#include<opencv2\video\tracking.hpp>using namespace cv;int main(){    //读取第一张图片    Mat image = imread("0001.jpg");    //定义感兴趣区域    Mat imaRoi = image(Rect(213, 121, 21, 95));    //设置最小饱和度    int minSat = 65;    Histogram h;    //获得imaRoi的直方图特征    MatND hist = h.getHueHistogram(imaRoi, minSat);    ContentFinder finder;    finder.setHistogram(hist);    //读取第二张图片    Mat findimage = imread("0024.jpg");    Mat hsv;    //将第二张图片转换为HSV格式    cvtColor(findimage, hsv, CV_BGR2HSV);    std::vector<Mat>v;    split(hsv, v);    //将低于 最小饱和度的像素点 设置为0    cv::threshold(v[1], v[1], minSat, 255, THRESH_BINARY);    int ch[1] = { 0 };    cv::Mat result = finder.find(hsv, 0.0f, 180.0f, ch, 1);    //剔除低饱和的点    bitwise_and(result, v[1], result);    //在第一张图片上画出感兴趣区域的位置    rectangle(image, Rect(213, 121, 21, 95), Scalar(255, 0, 0));    //设置迭代停止条件    TermCriteria criteria(TermCriteria::MAX_ITER, 100, 0.01);    //预定义初始矩形区域    Rect rect(213, 121, 21, 95);    //调用meanshift算法,最终得到rect,相似区域的位置    meanShift(result, rect, criteria);    //在第二张图片上,显示相似区域的位置    rectangle(findimage, rect, Scalar(255, 0, 0));    imshow("第一张", image);    imshow("第二张", findimage);    cv::waitKey(0);}

实验结果
这里写图片描述

最后,可以对连续的视频序列中,当前帧的某一个目标,通过某种方法得到下一帧图像对于该目标的相似度判别数据,然后应用Meanshift算法实现目标的跟踪。(博主本人的主要研究方向就是目标跟踪~~)

0 0
原创粉丝点击