给定d维空间 Rd 中的n个样本点 xi,i=1,2,...,n, 在 x 点的mean shift向量的基本形式定义为:
Mh(x)=1k∑xi∈Sk(xi−x)
其中, Sh 是一个半径为h的高维球区域,满足以下关系的y点的集合,
Sh(x)={y:(y−x)T(y−x)≤h2}
k表示在这n个样本点 xi 中落入 Sh 区域中。
(x−xi) 是样本点 xi 相对于点 x 的偏移量,mean shift向量 Mh(x) 就是对落入区域 Sh 中的k个样本点相对于点x的偏移量求和然后再平均。
mean shift 示意图:
可以看出平均偏移量 Mh(x) 会指向样本分布最多的区域。公式中落入 Sh 的采样点,无论离中心x的远近,对最终计算的影响都一样。但现实跟踪过程中,较远的像素容易受背景或遮挡等因素的影响,因此靠近中心较近的像素应该更重要。故每个样本点的重要性应与其与中心的距离成反比。故引入核函数和权重系数来提高算法的鲁棒性。
核函数即窗口函数,在核估计中起到平滑的作用。常见的有:
Flat 核函数:
k(x)={1,ifx≤λ0,ifx>λ
Gaussian 核函数:
k(x)=e−x2σ2
部分核函数:
给出一个核函数:
K(x−xi)
该函数确定重新估计平均值的附近点的权重。当选择的搜索窗口类型为高斯核时:
K(x−xi)=e−c∥X−Xi∥2
概率分布近似为:
P(x)=1n∑ni=1K(x−xi)
由K确定的窗口密度的加权平均值是:
m(x)=∑ni=1xig(∥x−xi∥)∑ni=1g(∥x−xi∥)
其中:
g(x)=−K′(x)
P(x) 求梯度:
▽P(x)=cn∑ni=1▽Ki=cn[∑ni=1gi[∥x−xi∥b]][∑ni=1xig(∥x−xi∥)∑ni=1g(∥x−xi∥)−x]
表达式构成如图:
mean shift算法用于视觉跟踪时,将基于前一图像中的对象的颜色直方图在新图像中创建置信度图,并使用均值平移来找到靠近对象旧位置的置信度图的峰值。 置信度图是新图像上的概率密度函数,为新图像的每个像素指定一个概率,该概率是前一图像中的对象中出现的像素颜色的概率。
mean shift跟踪算法步骤:
① 选择搜索窗口,包括窗口的初始位置、大小、形状(对称或歪斜,矩形或圆心)、类型(均匀、多项式、指数或高斯);
② 计算窗口的重心;
③ 将窗口的中心设置在计算出的重心处;
④ 返回②步,直到窗口位置不再变化。
设目标区域的中心为x,其中n个像素用 xi,i=1,...,n 表示,特征值个数为m,则目标模型的特征值 u=1,...,m 的概率密度估计为:
qu→=C∑ni=1k[∥∥x−xih∥∥2]σ[b(xi)−u]
q→={qu}u=1,...,m
∑mu=1qu=1
其中,k为权值系数,赋予靠近中心的像素一个较大权值,远离中心的像素给定较小的权值。
σ[b(xi)−u] 的作用是判断目标区域中像素值是否属于第u个特征值。属于时为1,不属于时为0; b(xi) 为灰度值索引函数。
C为一标准化的常量系数:
C=1∑ni=1k[∥∥x−xih∥∥2]
这样可以使 ∑mu=1qu=1 。
于是可以得到基于图像灰度特征的颜色直方图。
运动目标在第二帧以后的每帧中可能包含目标的区域称为候选区域,其中心坐标即核函数的中心坐标y。该区域中的像素用 xi,i=1,...,nk 表示;候选模型的特征值 u=1,...,m 的概率密度为:
pu→(y)=Ch∑nki=1k[∥∥y−xih∥∥2]σ[b(xi)−u]
p→(y)={pu(y)}u=1,...,m
∑mu=1pu=1
Ch 为标准化常量:
Ch=1∑nki=1k[∥∥y−xih∥∥2]
利用相似函数描述目标模型和候选模型之间的相似度,理想情况下两个模型的概率分布一样。使用Bhattacharyya系数作为相似函数。
令 BC(p,q) 为Bhattacharyya系数。
Bhattacharyya系数是衡量两个统计样本之间重叠量或相对接近度的指标。
对于离散概率分布: BC(p,q)=∑x∈Xp(x)q(x)−−−−−−−√
对于连续概率分布: BC(p,q)=∫p(x)q(x)−−−−−−−√dx
另,Bhattacharyya距离为: DB(p,q)=−ln(BC(p,q))
模板区域: q→=(q1,...,qm)
候选区域: p→(y)=(p1(y),...,pm(y))
相似性函数: f(y)=f[p→(y),q→]=∑mu=1pu(y)qu−−−−−−√
Bhattacharyya系数的值在0到1之间,值越大表示两个模型月相似。跟踪时选择使Bhattacharyya系数最大的候选区域作为当前帧中的目标位置。
camshift算法就是将meanshift算法扩展到连续图像序列。camshift将视频的所有帧做meanshift运算,并将上一帧的结果(搜索窗的大小和中心),作为下一帧meanshift算法搜索窗的初始值。然后一直迭代下去,实现对目标的跟踪。
函数CamShift()参数说明:
//查找对象中心,大小和方向。
RotatedRect CamShift(
InputArray probImage,//对象直方图的反向投影
CV_IN_OUT Rect& window,//初始搜索窗口
TermCriteria criteria//底层meanShift的停止标准
);
#include
#include
using namespace cv;
int main()
{
VideoCapture capture;
Mat frame;
//保存目标轨迹
std::vector 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(0, i) = Vec3b(saturate_cast(i * 180 / bins), 255, 255);
}
cvtColor(colorIndex, colorIndex, COLOR_HSV2BGR);
for (int i = 0; i < bins; i++)
{
int val = saturate_cast(hist.at(i)*drawImg.rows/255);
rectangle(drawImg, Point(i*binw, drawImg.rows), Point((i+1)*binw, drawImg.rows * val), Scalar(colorIndex.at(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
{
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;
}