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) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2302
- 用户1336
- 访客10975106
每日一句
If you want to achieve greatness, stop asking for permission.
如果你想获得伟大,别再请求许可。
如果你想获得伟大,别再请求许可。
UAC的限制引起WM_DROPFILES无法响应的解决办法
MeasureSpec中三种模式:UNSPECIFIED,AT_MOST,EXACTLY
发几个实用的chrome插件
CentOS下使用 svnsync迁移SVN代码库
仙剑奇侠传3d回合-PC端辅助
【转载】C++实现EXE加载到内存执行
【收藏】OpenCV一些常用库函数
《闲来麻将》搭建教程
文本转语音系统Spark-TTS
wordpress转xiuno附件自动插入工具
Mac OS最简单及(Karabiner)快捷键设置
使用Putty上传文件?
ndk神奇问题之non-numeric second argument to `wordlist' function: '8.7z'
新会员