给定两个点p1与p2的坐标,肯定这两点所构成的直线,要求对于输入的任意点p3,均可以判断它是否在该直线上。初中解析几何知识告诉咱们,判断一个点在直线上,只需其与直线上任意两点点斜率都相同便可。实际操做当中,每每会先根据已知的两点算出直线的表达式(点斜式、截距式等等),而后经过向量计算便可方便地判断p3是否在该直线上。
生产实践中的数据每每会有必定的误差。例如咱们知道两个变量X与Y之间呈线性关系,Y=aX+b,咱们想肯定参数a与b的具体值。经过实验,能够获得一组X与Y的测试值。虽然理论上两个未知数的方程只须要两组值便可确认,但因为系统偏差的缘由,任意取两点算出的a与b的值都不尽相同。咱们但愿的是,最后计算得出的理论模型与测试值的偏差最小。大学的高等数学课程中,详细阐述了最小二乘法的思想。经过计算最小均方差关于参数a、b的偏导数为零时的值。事实上,在不少状况下,最小二乘法都是线性回归的代名词。
遗憾的是,最小二乘法只适合与偏差较小的状况。试想一下这种状况,假使须要从一个噪音较大的数据集中提取模型(比方说只有20%的数据时符合模型的)时,最小二乘法就显得力不从心了。例以下图,肉眼能够很轻易地看出一条直线(模式),但算法却找错了。
RANSAC算法的输入是一组观测数据(每每含有较大的噪声或无效点),一个用于解释观测数据的参数化模型以及一些可信的参数。RANSAC经过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
算法
- 有一个模型适应于假设的局内点,即全部的未知参数都能从假设的局内点计算得出。
- 用1中获得的模型去测试全部的其它数据,若是某个点适用于估计的模型,认为它也是局内点。
- 若是有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
- 而后,用全部假设的局内点去从新估计模型(譬如使用最小二乘法),由于它仅仅被初始的假设局内点估计过。
- 最后,经过估计局内点与模型的错误率来评估模型。
- 上述过程被重复执行固定的次数,每次产生的模型要么由于局内点太少而被舍弃,要么由于比现有的模型更好而被选用。
整个过程可参考下图:
关于算法的源代码,Ziv Yaniv曾经写一个不错的C++版本,我在关键处增补了注释: less
- #include <math.h>
- #include "LineParamEstimator.h"
-
- LineParamEstimator::LineParamEstimator(double delta) : m_deltaSquared(delta*delta) {}
- void LineParamEstimator::estimate(std::vector<Point2D *> &data,
- std::vector<double> ¶meters)
- {
- parameters.clear();
- if(data.size()<2)
- return;
- double nx = data[1]->y - data[0]->y;
- double ny = data[0]->x - data[1]->x;
- double norm = sqrt(nx*nx + ny*ny);
-
- parameters.push_back(nx/norm);
- parameters.push_back(ny/norm);
- parameters.push_back(data[0]->x);
- parameters.push_back(data[0]->y);
- }
- void LineParamEstimator::leastSquaresEstimate(std::vector<Point2D *> &data,
- std::vector<double> ¶meters)
- {
- double meanX, meanY, nx, ny, norm;
- double covMat11, covMat12, covMat21, covMat22;
- int i, dataSize = data.size();
-
- parameters.clear();
- if(data.size()<2)
- return;
-
- meanX = meanY = 0.0;
- covMat11 = covMat12 = covMat21 = covMat22 = 0;
- for(i=0; i<dataSize; i++) {
- meanX +=data[i]->x;
- meanY +=data[i]->y;
-
- covMat11 +=data[i]->x * data[i]->x;
- covMat12 +=data[i]->x * data[i]->y;
- covMat22 +=data[i]->y * data[i]->y;
- }
-
- meanX/=dataSize;
- meanY/=dataSize;
-
- covMat11 -= dataSize*meanX*meanX;
- covMat12 -= dataSize*meanX*meanY;
- covMat22 -= dataSize*meanY*meanY;
- covMat21 = covMat12;
-
- if(covMat11<1e-12) {
- nx = 1.0;
- ny = 0.0;
- }
- else {
-
-
- double lamda1 = (covMat11 + covMat22 + sqrt((covMat11-covMat22)*(covMat11-covMat22) + 4*covMat12*covMat12)) / 2.0;
- nx = -covMat12;
- ny = lamda1 - covMat22;
- norm = sqrt(nx*nx + ny*ny);
- nx/=norm;
- ny/=norm;
- }
- parameters.push_back(nx);
- parameters.push_back(ny);
- parameters.push_back(meanX);
- parameters.push_back(meanY);
- }
- bool LineParamEstimator::agree(std::vector<double> ¶meters, Point2D &data)
- {
- double signedDistance = parameters[0]*(data.x-parameters[2]) + parameters[1]*(data.y-parameters[3]);
- return ((signedDistance*signedDistance) < m_deltaSquared);
- }
RANSAC寻找匹配的代码以下: 测试
- template<class T, class S>
- double Ransac<T,S>::compute(std::vector<S> ¶meters,
- ParameterEsitmator<T,S> *paramEstimator ,
- std::vector<T> &data,
- int numForEstimate)
- {
- std::vector<T *> leastSquaresEstimateData;
- int numDataObjects = data.size();
- int numVotesForBest = -1;
- int *arr = new int[numForEstimate];
- short *curVotes = new short[numDataObjects];
- short *bestVotes = new short[numDataObjects];
-
-
-
- if(numDataObjects < numForEstimate)
- return 0;
-
- computeAllChoices(paramEstimator,data,numForEstimate,
- bestVotes, curVotes, numVotesForBest, 0, data.size(), numForEstimate, 0, arr);
-
-
- for(int j=0; j<numDataObjects; j++) {
- if(bestVotes[j])
- leastSquaresEstimateData.push_back(&(data[j]));
- }
-
- paramEstimator->leastSquaresEstimate(leastSquaresEstimateData,parameters);
-
- delete [] arr;
- delete [] bestVotes;
- delete [] curVotes;
-
- return (double)leastSquaresEstimateData.size()/(double)numDataObjects;
- }
在模型肯定以及最大迭代次数容许的状况下,RANSAC老是能找到最优解。通过个人实验,对于包含80%偏差的数据集,RANSAC的效果远优于直接的最小二乘法。
RANSAC能够用于哪些场景呢?最著名的莫过于图片的拼接技术。优于镜头的限制,每每须要多张照片才能拍下那种巨幅的风景。在多幅图像合成时,事先会在待合成的图片中提取一些关键的特征点。计算机视觉的研究代表,不一样视角下物体每每能够经过一个透视矩(3X3或2X2)阵的变换而获得。RANSAC被用于拟合这个模型的参数(矩阵各行列的值),由此即可识别出不一样照片中的同一物体。可参考下图:
另外,RANSAC还能够用于图像搜索时的纠错与物体识别定位。下图中,有几条直线是SIFT匹配算法的误判,RANSAC有效地将其识别,并将正确的模型(书本)用线框标注出来:
ui