Opencv——用均值平移法meanshift做目标追踪
OpenCV里的MeanShift跟踪方法涉及像素直方图和直方图反向投影的知识。
均值平移算法定义
直方图反向投影的结果是一个概率分布图,表示一个指定图像片段出现在特定的位置的概率。如果我们已经知道图像中某个物体的大概位置,就可以用概率分布图找到物体的准确位置。窗口的概率最大的位置就是物体最可能出现的位置。因此,我们可以从一个初始位置开始,在周围反复移动以提高局部匹配率,也就能找到物体的准确位置,这个实现方法称为均值平移算法。
1. MeanShift( )跟踪的思路
- 输入模型图片
- 在模型图片中定位到目标区域(指定ROI)
- 求得ROI区域中的直方图
- 对求得的直方图进行归一化(可选)
- 输入检测图片
- 将图片BGR转换为HSV(因为这次采用HSV色彩空间的色调通道来描述物体,所以需要将图片转换成HSV色彩空间并提取色调通道,然后计算指定ROI的 一维色调直方图)
- 得到色调直方图的反向投影
- 用均值偏移法cv::TermCriteria()搜索目标,用cv::meanShift()确定新目标的位置
2.用到的函数
void calcHist( const Mat* images, //输入图像
int nimages, //输入的图像个数
const int* channels, //需要统计直方图的第几通道
InputArray mask, //掩膜,,计算掩膜内的直方图 ...Mat()
OutputArray hist, //输出的直方图数组
int dims, //需要统计直方图通道的个数
const int* histSize, //指的是直方图分成多少个区间,就是 bin的个数
const float** ranges, //统计像素值得区间
bool uniform = true, //是否对得到的直方图数组进行归一化处理
bool accumulate = false //在多个图像时,是否累计计算像素值得个数
);
void calcBackProject( const Mat* images, // 输入的图像
int nimages, //输入的图像个数
const int* channels, //需要统计直方图的第几通道
const SparseMat& hist, //直方图
OutputArray backProject, //输出反向投影的图像
const float** ranges, //每个维度的范围
double scale = 1, //选用的换算系数,使得概率值从1映射到255
bool uniform = true //表示直方图是否均匀
);
//返回值是停止迭代的准则
cv::TermCriteria(int type, //终止标准的类型
int maxCount, //要计算的迭代的最大数量
double epsilon //迭代算法所要求的精度
);
//返回值是迭代的次数。这个window则是最终目标区域。
cv::meanShift( InputArray probImage, //对象直方图的反投影
CV_IN_OUT Rect& window, //搜索到的新的区域坐标
TermCriteria criteria //停止准则迭代搜索算法的准则
);
3.代码
#include <iostream>
#include <iomanip>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/tracking.hpp>
#include "colorhistogram.h"
#include "contentFinder.h"
#include"trackpr.h"
using namespace std;
int main()
{
std::vector<std::string> filelist;
cv::Mat image;
for (int i = 120; i <= 164; i++)
{
std::stringstream str;
str << "pc\\goose" << std::setw(3) << std::setfill('0') << i << ".bmp";
std::cout << str.str() << std::endl; //输出相应的字符串即图片名称
filelist.push_back(str.str()); //输出图片way1;将生成的字符串依次放入向量数组中,再从数组中读取图片
}
trackpr tp;
cv::Mat image1 = cv::imread(filelist[0]);
tp.firstpc(image1); //输入第一幅图,并求出目标区域的像素直方图
for(int i=0;i<44;i++)
{
cv::Mat image2 = cv::imread(filelist[i+1]); //输入一系列的视频帧
tp.nextpc(image2); //在视频帧中的反向投影直方图中搜索与第一幅图目标区域直方图匹配概率大的位置
cv::waitKey(600);
}
cv::waitKey(0);
return 0;
}
执行结果:
4.总结
总的来说就是,现在第一副图中找到感兴趣区域,计算出该区域的直方图,再在第二幅图中找到与第一幅直方图中匹配概率大的位置,并标记出来。