2024年最全RANSAC与圆柱拟合,高级C C++程序员必会

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

可以看到RANSAC可以很好的拟合。RANSAC可以理解为一种采样的方式,所以对于多项式拟合、混合高斯模型(GMM)等理论上都是适用的。

一个简单的例子如下:简单的最小二乘法不能找到适应于局内点的直线,原因是最小二乘法尽量去适应包括局外点在内的所有点。相反,RANSAC能得出一个仅仅用局内点计算出模型,并且概率还足够高。但是,RANSAC并不能保证结果一定正确,为了保证算法有足够高的合理概率,我们必须小心的选择算法的参数。

二、概述

RANSAC算法的输入是一组观测数据,一个可以解释或者适应于观测数据的参数化模型,一些可信的参数。

RANSAC通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
    1.有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。
    2.用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。
    3.如果有足够多的点被归类为假设的局内点,那么估计的模型就足够合理。
    4.然后,用所有假设的局内点去重新估计模型,因为它仅仅被初始的假设局内点估计过。
    5.最后,通过估计局内点与模型的错误率来评估模型。
这个过程被重复执行固定的次数,每次产生的模型要么因为局内点太少而被舍弃,要么因为比现有的模型更好而被选用。

三、算法

输入:
data —— 一组观测数据
model —— 适应于数据的模型
n —— 适用于模型的最少数据个数
k —— 算法的迭代次数
t —— 用于决定数据是否适应于模型的阀值
d —— 判定模型是否适用于数据集的数据数目
输出:
best_model —— 跟数据最匹配的模型参数(如果没有找到好的模型,返回null)
best_consensus_set —— 估计出模型的数据点
best_error —— 跟数据相关的估计出的模型错误

iterations = 0
best_model = null
best_consensus_set = null
best_error = 无穷大
while ( iterations < k )
    maybe_inliers = 从数据集中随机选择n个点
    maybe_model = 适合于maybe_inliers的模型参数
    consensus_set局内点 = maybe_inliers

for ( 每个数据集中不属于maybe_inliers的点 )
        if ( 如果点适合于maybe_model,且错误小于t )
            将点添加到consensus_set局内点
    if ( consensus_set中的元素数目大于d )
        已经找到了好的模型,现在测试该模型到底有多好
        better_model = 用所有consensus_set拟合得到模型参数
        this_error = better_model计算该模型的拟合误差(就是cost,一般直接采用每个点的误差相加求和作为总cost)改进它就会变成MSAC
        if ( this_error < best_error )
            我们发现了比以前好的模型,保存该模型直到更好的模型出现
            best_model =  better_model
            best_consensus_set = consensus_set
            best_error =  this_error
    增加迭代次数
返回 best_model, best_consensus_set, best_error

from:https://www.cnblogs.com/xrwang/archive/2011/03/09/ransac-1.html

四、RANSAC简化版流程实例:

第一步:假定模型(如直线方程),并随机抽取Nums个(以2个为例)样本点,对模型进行拟合:

第二步:由于不是严格线性,数据点都有一定波动,假设容差范围为:sigma,找出距离拟合曲线容差范围内的点,并统计点的个数:

第三步:重新随机选取Nums个点,重复第一步~第二步的操作,直到结束迭代:

第四步:每一次拟合后,容差范围内都有对应的数据点数,找出数据点个数最多的情况,就是最终的拟合结果

其实RANSAC忽略了几个问题:

  • 每一次随机样本数Nums的选取:如二次曲线最少需要3个点确定,一般来说,Nums少一些易得出较优结果;
  • 抽样迭代次数Iter的选取:即重复多少次抽取,就认为是符合要求从而停止运算?太多计算量大,太少性能可能不够理想;
  • 容差Sigma的选取:sigma取大取小,对最终结果影响较大;

RANSAC的作用有点类似:将数据一切两段,一部分是自己人,一部分是敌人,自己人留下商量事,敌人赶出去。RANSAC开的是家庭会议,不像最小二乘总是开全体会议。

from:https://www.cnblogs.com/xingshansi/p/6763668.html

五、优点与缺点

RANSAC的优点是它能鲁棒的估计模型参数。例如,它能从包含大量局外点的数据集中估计出高精度的参数。RANSAC的缺点是它计算参数的迭代次数没有上限;如果设置迭代次数的上限,得到的结果可能不是最优的结果,甚至可能得到错误的结果。RANSAC只有一定的概率得到可信的模型,概率与迭代次数成正比。RANSAC的另一个缺点是它要求设置跟问题相关的阀值。
    RANSAC只能从特定的数据集中估计出一个模型,如果存在两个(或多个)模型,RANSAC不能找到别的模型。

六、改进RANSAC——MSAC

RANSAC与MSAC唯一的区别在于cost的计算方式。RANSAC对于阈值(Tolerance)选取过于敏感,这个值过大了算法就无效了,过小的话算法会变得很不稳定。而MSAC可以做到部分补偿这些负面影响。

RANSAC的cost算法伪代码:

count = 局内点个数 
cost = 0
For (n=0; n<count; n++){
	cost += Error[n] <= Tolerance?0:1;
}

对于每个局内点,计算它与拟合模型的误差,误差小于Tolerance,将误差值视为cost;误差大于Tolerance,则将“1”设为cost;将所有局内点的cost相加作为总cost。

MSAC的cost算法伪代码:

count = 局内点个数
cost = 0
For (n=0; n<count; n++){
	cost += Error[n] <= Tolerance?Error[n]:Tolerance;
}

对于每个局内点,计算它与拟合模型的误差,误差小于Tolerance,将误差值视为cost;误差大于Tolerance,则将Tolerance设为cost;将所有局内点的cost相加作为总cost。

from:https://blog.csdn.net/weixin_44558898/article/details/88986497

在MATLAB中:
 % Evaluate model with truncated loss评估具有截断损失
 dis = evalFunc(modelParams, allPoints);
 dis(dis > threshold) = threshold;
 accDis = sum(dis);

补充:三维RANSAC

matlab2018中的ransac源码:

% Load and plot a set of noisy 2D points.
%  load 'pointsForLineFitting.mat';
%  plot(points(:,1), points(:,2), '*');
%  hold on
%
%  % Fit a line using linear least squares.
%  modelLeastSquares = polyfit(points(:,1), points(:,2), 1);
%  x = [min(points(:,1)), max(points(:,1))];
%  y = modelLeastSquares(1)*x + modelLeastSquares(2);
%  plot(x, y, 'r-');
%
%  % Fit a line to the points using M-estimator SAmple Consensus algorithm.
%  sampleSize = 2;
%  maxDistance = 2;
%  fitLineFcn  = @(points) polyfit(points(:,1), points(:,2), 1);
%  evalLineFcn = ...
%     @(model, points) sum((points(:, 2) - polyval(model, points(:,1))).^2, 2);
%     
%  [modelRANSAC, inlierIdx] = RANSAC(points, fitLineFcn, evalLineFcn, ...
%     sampleSize, maxDistance);
%
%  % Re-fit a line to the inliers.
%  modelInliers = polyfit(points(inlierIdx,1), points(inlierIdx,2), 1);
%
%  % Display the line.
%  inlierPts = points(inlierIdx, :);
%  x = [min(inlierPts(:,1)); max(inlierPts(:,1))];
%  y = modelInliers(1)*x + modelInliers(2);
%  plot(x, y, 'g-');
%  legend('Noisy Points', 'Least Squares Fit', 'Robust Fit');
%  hold off

1、空间直线:已知两个点即可确定一条直线:

点向式:(x-x0)/u =(y-y0)/v=(z-z0) /w ,过点(x0,y0,z0) ,且有方向向量(u,v,w)。

设空间一点为P(x0,y0,z0),在直线上找一点Q(x1,y1,z1),直线的方向向量为:S=(l,m,n),则d=|PQ叉乘S|/|S|,

理由:|PQ叉乘S|为一平行四边形的面积,|S|为其一边.故=|PQ叉乘S|/|S|为平行四边形的高.即为点到直线的距离。

2、空间平面:三个不在一条直线上的点确定一个平面

Ax+By+Cz+D=0,即ax+by+cz+1=0

四个未知数求出三个就够啦,这三个未知数都可以用第四个未知数来表示,假设第四个未知数是D.则求出来的三个未知数一般是:A=a*D;B=b*D;C=c*D;(D不等于0),最终有a*Dx+b*Dy+cDz+D=0,等式两边同除以D,得平面方程ax+by+cz+1=0;

将已知三个点的坐标分别用P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)表示。

设通过P1,P2,P3三点的平面方程为:Ax + By + Cz + D = 0。将P1(x1,y1,z1)点数值代入方程Ax + By + Cz + D = 0。

即可得到:Ax1 + By 1+ Cz1 + D = 0。化简得D = -(A * x1 + B * y1 + C * z1)。

则可以根据P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)三点坐标分别求得A、B、C的值,如下:

A = (y3 - y1)*(z3 - z1) - (z2 -z1)*(y2 - y1);

B = (x3 - x1)*(z2 - z1) - (x2 - x1)*(z3 - z1);

C = (x2 - x1)*(y3 - y1) - (x3 - x1)*(y2 - y1);

又D = -(A * x1 + B * y1 + C * z1),所以可以求得D的值。

利用RANSAC拟合平面:

clc;clear all;close all;

%%%三维平面拟合
%%%生成随机数据
%内点
mu=[0 0 0];  %均值
S=[2 0 4;0 4 0;4 0 8];  %协方差
data1=mvnrnd(mu,S,300);   %产生200个高斯分布数据
%外点
mu=[2 2 2];
S=[8 1 4;1 8 2;4 2 8];  %协方差
data2=mvnrnd(mu,S,100);     %产生100个噪声数据
%合并数据
data=[data1',data2'];
iter = 1000; 

%%% 绘制数据点
 figure;plot3(data(1,:),data(2,:),data(3,:),'o');hold on; % 显示数据点
 number = size(data,2); % 总点数
 bestParameter1=0; bestParameter2=0; bestParameter3=0; % 最佳匹配的参数
 sigma = 1;
 pretotal=0;     %符合拟合模型的数据的个数

for i=1:iter
 %%% 随机选择三个点
     idx = randperm(number,3); 
     sample = data(:,idx); 

     %%%拟合直线方程 z=ax+by+c
     plane = zeros(1,3);
     x = sample(:, 1);
     y = sample(:, 2);
     z = sample(:, 3);

     a = ((z(1)-z(2))*(y(1)-y(3)) - (z(1)-z(3))*(y(1)-y(2)))/((x(1)-x(2))*(y(1)-y(3)) - (x(1)-x(3))*(y(1)-y(2)));
     b = ((z(1) - z(3)) - a * (x(1) - x(3)))/(y(1)-y(3));
     c = z(1) - a * x(1) - b * y(1);
     plane = [a b -1 c]

     mask=abs(plane*[data; ones(1,size(data,2))]);    %求每个数据到拟合平面的距离
     total=sum(mask<sigma);              %计算数据距离平面小于一定阈值的数据的个数

     if total>pretotal            %找到符合拟合平面数据最多的拟合平面
         pretotal=total;
         bestplane=plane;          %找到最好的拟合平面
    end  
 end
 %显示符合最佳拟合的数据
mask=abs(bestplane*[data; ones(1,size(data,2))])<sigma;    
hold on;
k = 1;
for i=1:length(mask)
    if mask(i)
        inliers(1,k) = data(1,i);
        inliers(2,k) = data(2,i);
        plot3(data(1,i),data(2,i),data(3,i),'r+');
        k = k+1;
    end
end

 %%% 绘制最佳匹配平面
 bestParameter1 = bestplane(1);
 bestParameter2 = bestplane(2);
 bestParameter3 = bestplane(4);
 xAxis = min(inliers(1,:)):max(inliers(1,:));
 yAxis = min(inliers(2,:)):max(inliers(2,:));
 [x,y] = meshgrid(xAxis, yAxis);
 z = bestParameter1 * x + bestParameter2 * y + bestParameter3;
 surf(x, y, z);
 title(['bestPlane:  z =  ',num2str(bestParameter1),'x + ',num2str(bestParameter2),'y + ',num2str(bestParameter3)]);

在编程中,我们知道三个空间点之后,可以用 [uu, dd, vv] = svd(A); plane = vv(:,end); plane = plane ./ plane(end); plane = plane’;来快速求解法向量,确定一个空间平面。

from:https://blog.csdn.net/u010128736/article/details/53422070

3、MATLAB中的圆柱拟合:初始模型参数表示为[x,y,z,dx,dy,dz,r]。其中(x,y,z)是圆柱中心轴上的一个点,(dx,dy,dz)指定轴的方向,r是半径。

  • 第一步:每次随机选两个点P0,P1,这两个点在拟合圆柱的表面上。用于拟合中心轴和半径:

输入是2*6的矩阵,每行代表一个样本,每行的前3列代表x,y,z,后三行代表该点的法向量。

function model = fitCylinder(points)
p1 = points(1,1:3);
n1 = points(1,4:6);
p2 = points(2,1:3);
n2 = points(2,4:6);
w = p1 + n1 - p2;

a = n1 * n1';%模
b = n1 * n2';%点乘
c = n2 * n2';%模
d = n1 * w';
e = n2 * w';
D = a * c - b * b;
if abs(D) < 1e-5
    s = 0;
    if b > c
        t = d / b;
    else
        t = e / c;
    end
else
    s = (b * e - c * d) / D;
    t = (a * e - b * d) / D;
end

% P0 is a point on the axis
p0 = p1 + n1 + s * n1;
% dp is a normalized vector for the direction of the axis
dp = p2 + t * n2 - p0;%(p2 + t * n2) - p0
dp = dp / norm(dp);

p1p0 = p1 - p0;
p2p1 = dp;
c = [p1p0(2)*p2p1(3) - p1p0(3)*p2p1(2), ...
     p1p0(3)*p2p1(1) - p1p0(1)*p2p1(3), ...
     p1p0(1)*p2p1(2) - p1p0(2)*p2p1(1)];%就是p1p0,p2p1两向量的叉乘,a*b*cos,模表示两向量围成的平行四边形的面积
% p2p1 is a unit vector, so the denominator is not needed ,p2p1是单位向量,模为1,
r = sqrt(sum(c.*c, 2));%即c的模(面积)就是高,也就是半径

model = [p0, dp, r];
  • 第二步:根据模型参数,然后求全体点云中的每个点P0到中心轴的距离。
function dis = evalCylinder(model, points)
p1p0 = [points(:,1)-model(1), points(:,2)-model(2), points(:,3)-model(3)];
p2p1 = model(4:6);
c = [p1p0(:,2)*p2p1(3) - p1p0(:,3)*p2p1(2), ...
     p1p0(:,3)*p2p1(1) - p1p0(:,1)*p2p1(3), ...
     p1p0(:,1)*p2p1(2) - p1p0(:,2)*p2p1(1)];
% p2p1 is a unit vector, so the denominator is not needed
D = sum(c.*c, 2);


![img](https://img-blog.csdnimg.cn/img_convert/f8a8d63a446c95a094a6a36c80215a59.png)
![img](https://img-blog.csdnimg.cn/img_convert/5cfea70a92cf729fa59e34e9a5e3a85b.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

     p1p0(:,1)*p2p1(2) - p1p0(:,2)*p2p1(1)];
% p2p1 is a unit vector, so the denominator is not needed
D = sum(c.*c, 2);


[外链图片转存中...(img-bLzhHQrV-1715605869110)]
[外链图片转存中...(img-Avo8zUf5-1715605869110)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值