OpenCV2应用Meanshift查找相似物体

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值