我们首先了解一下什么是均值偏移(Mean-Shift)算法,如下:
均值偏移(mean shift,也叫均值漂移或均值平移)这个概念最早是由Fukunaga等人于1975年在《The estimation of the gradient of a density function with application in pattern recognitioin》这篇关于概率密度梯度函数的估计中提出来的,其最初含义正如其名,就是偏移的均值向量。 它是一种无参估计算法,沿着概率梯度的上升方向寻找分布的峰值。
然而在以后的很长一段时间内mean shift并没有引起人们的注意,直到20年以后,也就是1995年,另外一篇关于Mean shift的重要文献《Mean shift analysis and application》才发表。在这篇重要的文献中,Yizong cheng对基本Mean shift算法在以下两个方面做了推广,首先定义了一族核函数,使得随着样本与被偏移点的距离不同,其偏移量对均值偏移向量的贡献也不同,其次设定了一个权重系数,使得不同的样本点重要性不一样,这大大扩大了Mean shift的适用范围。直到1998年Bradski将MeanShift算法用于人脸的跟踪才使得此算法的优势在目标跟踪领域体现出来。
MeanShift算法是一种无参概率密度估计法,算法利用像素特征点概率密度函数的梯度推导而得, MeanShift算法通过迭代运算收敛于概率密度函数的局部最大值,实现目标定位和跟踪,也能对可变形状目标实时跟踪,对目标的变形,旋转等运动也有较强的鲁棒性。MeanShift算法是一种自动迭代跟踪算法,由 MeanShift补偿向量不断沿着密度函数的梯度方向移动。在一定条件下,MeanShift算法能收敛到局部最优点,从而实现对运动体准确地定位。
均值平移算法是一种非参数的统计迭代算法。
该算法可以使用在运动跟踪方面上。
我们现在开始用java实现,如下:
首先是计算欧式距离
//计算欧式距离
private static double countDistance(double[] p1,double[] p2) {
double sum=0;
for(int i=0;i<p1.length;i++)
sum+=Math.pow(p1[i]-p2[i],2);
return Math.sqrt(sum);
}
然后是随机抽取向量
//随机抽取向量
private static double[] randomVector(DenseMatrix64F src) {
double[] rs = new double[src.numCols];
Random r=new Random();
int inx = r.nextInt(src.numRows);
for(int i=0;i<src.numCols;i++) {
rs[i] = src.get(inx,i);
}
return rs;
}
然后是筛选指定向量指定半径内的所有向量
private static DenseMatrix64F findDatasInCircle(DenseMatrix64F src,double[] p,double r) {
DenseMatrix64F rs = new DenseMatrix64F(0,src.numCols);
for(int i=0;i<src.numRows;i++) {
double[] tmp = new double[src.numCols];
for(int j=0;j<src.numCols;j++)
tmp[j]=src.get(i, j);
if(countDistance(p,tmp) <= r) {
rs.reshape(rs.numRows+1, src.numCols, true);
for(int j=0;j<src.numCols;j++)
rs.set(rs.numRows-1, j, src.get(i, j));
}
}
return rs;
}
然后是计算新的向量叠加
private static double[] countVectorSuperposition(DenseMatrix64F src) {
double[] rs = new double[src.numCols];
for(int i=0;i<src.numCols;i++) {
double tmp = 0;
for(int j=0;j<src.numRows;j++)
tmp+=src.get(j,i);
rs[i] = tmp;
}
return rs;
}
然后是主方法
public static double[] meanShift(DenseMatrix64F dataSet,double r) {
//随机选取向量
double[] rs = randomVector(dataSet);
double diff = Double.MAX_VALUE;
double change = Double.MAX_VALUE;
//开始迭代
while(diff > 0 && change != 0) {
//筛选半径内所有向量
DenseMatrix64F subVectors = findDatasInCircle(dataSet,rs,r);
//计算向量叠加
double[] sumVectors = countVectorSuperposition(subVectors);
//计算目标向量
double vCount = countDistance(rs,sumVectors);
change = vCount-diff;
diff = vCount;
System.out.println("diff :"+diff);
//更新圆心向量
rs = sumVectors;
}
return rs;
}
ok现在开始测试
List<String> list = new ArrayList<String>();
try{
BufferedReader br = new BufferedReader(new FileReader("D:\\test.txt"));
String s = null;
while((s = br.readLine())!=null){
list.add(s);
}
br.close();
}catch(Exception e){
e.printStackTrace();
}
DenseMatrix64F dataMatIn = new DenseMatrix64F(list.size(),2);
for(int i=0;i<list.size();i++) {
String[] items = list.get(i).split(" ");
dataMatIn.set(i, 0, Double.parseDouble(items[0]));
dataMatIn.set(i,1, Double.parseDouble(items[1]));
}
System.out.println(meanShift(dataMatIn,2));
测试结果如下:
向量最终不再变化