OpenCV2应用Meanshift查找相似物体

Home / C++ MrLee 2016-10-23 3552

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算法实现目标的跟踪。(博主本人的主要研究方向就是目标跟踪~~)

本文链接:https://www.it72.com/11021.htm

推荐阅读
最新回复 (0)
返回