meanshift基本原理:
给定d维空间 个样本点 ,=1,2,...,n,在 点的mean shift向量的基本形式定义为:
其中, 是一个半径为h的高维球区域,满足以下关系的y点的集合,
k表示在这n个样本点 中落入 区域中。
是样本点 相对于点的偏移量,mean shift向量 就是对落入区域中的k个样本点相对于点x的偏移量求和然后再平均。
mean shift 示意图:
可以看出平均偏移量 会指向样本分布最多的区域。公式中落入 的采样点,无论离中心x的远近,对最终计算的影响都一样。但现实跟踪过程中,较远的像素容易受背景或遮挡等因素的影响,因此靠近中心较近的像素应该更重要。故每个样本点的重要性应与其与中心的距离成反比。故引入核函数和权重系数来提高算法的鲁棒性。
核函数即窗口函数,在核估计中起到平滑的作用。常见的有:
Flat 核函数:
Gaussian 核函数:
部分核函数:
给出一个核函数:
该函数确定重新估计平均值的附近点的权重。当选择的搜索窗口类型为高斯核时:
概率分布近似为:
由K确定的窗口密度的加权平均值是:
其中:
求梯度:
表达式构成如图:
meanshift算法主要应用于聚类、平滑和跟踪。
meanshift跟踪算法:
meanshift算法用于视觉跟踪时,将基于前一图像中的对象的颜色直方图在新图像中创建置信度图,并使用均值平移来找到靠近对象旧位置的置信度图的峰值。 置信度图是新图像上的概率密度函数,为新图像的每个像素指定一个概率,该概率是前一图像中的对象中出现的像素颜色的概率。
meanshift跟踪算法步骤:
① 选择搜索窗口,包括窗口的初始位置、大小、形状(对称或歪斜,矩形或圆心)、类型(均匀、多项式、指数或高斯);
② 计算窗口的重心;
③ 将窗口的中心设置在计算出的重心处;
④ 返回②步,直到窗口位置不再变化。
设目标区域的中心为x,其中n个像素用 =1,...,n表示,特征值个数为m,则目标模型的特征值 u=1,...,m的概率密度估计为:
其中,k为权值系数,赋予靠近中心的像素一个较大权值,远离中心的像素给定较小的权值。
的作用是判断目标区域中像素值是否属于第u个特征值。属于时为1,不属于时为0;为灰度值索引函数。
C为一标准化的常量系数:
这样可以使
于是可以得到基于图像灰度特征的颜色直方图。
运动目标在第二帧以后的每帧中可能包含目标的区域称为候选区域,其中心坐标即核函数的中心坐标y。该区域中的像素用 =1,...,表示;候选模型的特征值 u=1,...,m的概率密度为:
为标准化常量:
Bhattacharyya系数
利用相似函数描述目标模型和候选模型之间的相似度,理想情况下两个模型的概率分布一样。使用Bhattacharyya系数作为相似函数。
令 为Bhattacharyya系数。
Bhattacharyya系数是衡量两个统计样本之间重叠量或相对接近度的指标。
对于离散概率分布:
对于连续概率分布:
Bhattacharyya距离为:
模板区域:
候选区域:
相似性函数:
Bhattacharyya系数的值在0到1之间,值越大表示两个模型月相似。跟踪时选择使Bhattacharyya系数最大的候选区域作为当前帧中的目标位置。
camshift算法
camshift算法就是将meanshift算法扩展到连续图像序列。camshift将视频的所有帧做meanshift运算,并将上一帧的结果(搜索窗的大小和中心),作为下一帧meanshift算法搜索窗的初始值。然后一直迭代下去,实现对目标的跟踪。
函数CamShift()参数说明:
//查找对象中心,大小和方向。
RotatedRect CamShift(
InputArray probImage,//对象直方图的反向投影
CV_IN_OUT Rect& window,//初始搜索窗口
TermCriteria criteria//底层meanShift的停止标准
);
OpenCV3 camshift跟踪:
#include<opencv2/opencv.hpp>
#include<opencv2/tracking.hpp>
using namespace cv;
int main()
{
VideoCapture capture;
Mat frame;
//保存目标轨迹
std::vector<Point> pt;
capture.open(0);
if (!capture.isOpened())
{
printf("can not open camera \n");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
namedWindow("output", WINDOW_AUTOSIZE);
capture.read(frame);
if (frame.empty())
return -1;
Rect2d first = selectROI("output", frame);
Rect selectionROI;
selectionROI.width = first.width;
selectionROI.height = first.height;
selectionROI.x = first.x;
selectionROI.y = first.y;
printf("x= %d, y=%d, width=%d, height=%d",selectionROI.x,selectionROI.y,selectionROI.width,selectionROI.height);
Mat mask, hist, backproject;
int bins = 120;
Mat drawImg = Mat::zeros(300, 300, CV_8UC3);
while (capture.read(frame))
{
Mat hsvimage;
cvtColor(frame, hsvimage, CV_BGR2HSV);
inRange(hsvimage, Scalar(25, 43, 46), Scalar(35, 256, 256), mask);
Mat hue = Mat(hsvimage.size(), hsvimage.depth());
int channels[] = {0, 0};
mixChannels(&hsvimage, 1, &hue, 1, channels, 1);
//ROI直方图计算
Mat roi(hue, first);
Mat maskroi(mask, first);
float hrange[] = {0, 180};
const float* hranges = hrange;
//直方图
calcHist(&roi, 1, 0, maskroi, hist, 1, &bins, &hranges);
normalize(hist, hist, 0, 255, NORM_MINMAX);
int binw = drawImg.cols/ bins;
Mat colorIndex = Mat(1, bins, CV_8UC3);
for (int i = 0; i < bins; i++)
{
colorIndex.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i * 180 / bins), 255, 255);
}
cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR);
for (int i = 0; i < bins; i++)
{
int val = saturate_cast<int>(hist.at<float>(i)*drawImg.rows/255);
rectangle(drawImg, Point(i*binw, drawImg.rows), Point((i+1)*binw, drawImg.rows * val), Scalar(colorIndex.at<Vec3b>(0, i)), -1, 8, 0);
}
//计算直方图的反投影
calcBackProject(&hue, 1, 0, hist, backproject, &hranges);
backproject &= mask;
RotatedRect trackBox = CamShift(backproject, selectionROI, TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1));
Rect rect;
rect.x = trackBox.center.x - trackBox.size.width/2.0;
rect.y = trackBox.center.y - trackBox.size.height/2.0;
rect.width = trackBox.size.width;
rect.height = trackBox.size.height;
rectangle(frame, rect, Scalar(255, 255, 0),3);
pt.push_back(Point(rect.x+rect.width/2,rect.y+rect.height/2));
for(int i=0;i<pt.size()-1;i++)
{
line(frame,pt[i],pt[i+1],Scalar(0,255,0),2.5);
}
imshow("input", frame);
imshow("output", drawImg);
waitKey(10);
}
capture.release();
return 0;
}