RANSAC三维直线拟合

article/2025/11/8 4:58:03

RANSAC算法

(1)要得到一个直线模型,需要两个点唯一确定一个直线方程。所以第一步随机选择两个点;
(2)通过这两个点,可以计算出这两个点所表示的模型方程;
(3)将所有的数据点套到这个模型中计算误差;
(4)找到所有满足误差阈值的点;
(5)然后我们再重复(1)~(4)这个过程,直到达到一定迭代次数后,选出那个被支持的最多的模型,作为问题的解。

三维直线模型

#pragma once#include "AbstractModel.hpp"typedef std::array<GRANSAC::VPFloat, 3> Vector3VP;class Point3D: public GRANSAC::AbstractParameter
{
public:Point3D(GRANSAC::VPFloat x, GRANSAC::VPFloat y, GRANSAC::VPFloat z){m_Point3D[0] = x;m_Point3D[1] = y;m_Point3D[2] = z;};Vector3VP m_Point3D;
};class Line3DModel: public GRANSAC::AbstractModel<2>
{
protected:// Parametric formVector3VP			m_P, m_Q;GRANSAC::VPFloat	m_DistDenominator;virtual GRANSAC::VPFloat ComputeDistanceMeasure(std::shared_ptr<GRANSAC::AbstractParameter> Param) override{auto ExtPoint3D = std::dynamic_pointer_cast<Point3D>(Param);if (ExtPoint3D == nullptr)throw std::runtime_error("Line3DModel::ComputeDistanceMeasure() - Passed parameter are not of type Point3D.");// Return distance between passed "point" and this line// http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.htmldouble Q[3]{ m_Q[0],m_Q[1],m_Q[2] };double P[3]{ m_P[0],m_P[1],m_P[2] };double R[3]{ ExtPoint3D->m_Point3D[0] ,ExtPoint3D->m_Point3D[1] ,ExtPoint3D->m_Point3D[2] };double num_vec[3], den_vec[3];double numerator;double tempSubRP[3];vtkMath::Subtract(R, P, tempSubRP);double tempSubRQ[3];vtkMath::Subtract(R, Q, tempSubRQ);vtkMath::Cross(tempSubRP, tempSubRQ, num_vec);numerator = vtkMath::Norm(num_vec);auto Dist = numerator / m_DistDenominator;Debug//std::cout << "Point: " << ExtPoint3D->m_Point3D[0] << ", " << ExtPoint3D->m_Point3D[1] << std::endl;//std::cout << "Line: " << m_a << " x + " << m_b << " y + " << m_c << std::endl;//std::cout << "Distance: " << Dist << std::endl << std::endl;return Dist;};public:Line3DModel(const std::vector<std::shared_ptr<GRANSAC::AbstractParameter>> &InputParams){Initialize(InputParams);};virtual void Initialize(const std::vector<std::shared_ptr<GRANSAC::AbstractParameter>> &InputParams) override{if (InputParams.size() != 2)throw std::runtime_error("Line2DModel - Number of input parameters does not match minimum number required for this model.");// Check for AbstractParamter typesauto Point1 = std::dynamic_pointer_cast<Point3D>(InputParams[0]);auto Point2 = std::dynamic_pointer_cast<Point3D>(InputParams[1]);if (Point1 == nullptr || Point2 == nullptr)throw std::runtime_error("Line3DModel - InputParams type mismatch. It is not a Point3D.");std::copy(InputParams.begin(), InputParams.end(), m_MinModelParams.begin());m_P = Point1->m_Point3D;m_Q = Point2->m_Point3D;// Compute the line parametersdouble Q[3]{ m_Q[0],m_Q[1],m_Q[2] };double P[3]{ m_P[0],m_P[1],m_P[2] };double temp[3];vtkMath::Subtract(Q, P, temp);m_DistDenominator = vtkMath::Norm(temp);};virtual std::pair<GRANSAC::VPFloat, std::vector<std::shared_ptr<GRANSAC::AbstractParameter>>> Evaluate(const std::vector<std::shared_ptr<GRANSAC::AbstractParameter>>& EvaluateParams, GRANSAC::VPFloat Threshold){std::vector<std::shared_ptr<GRANSAC::AbstractParameter>> Inliers;int nTotalParams = EvaluateParams.size();int nInliers = 0;for (auto& Param : EvaluateParams){if (ComputeDistanceMeasure(Param) < Threshold){Inliers.push_back(Param);nInliers++;}}GRANSAC::VPFloat InlierFraction = GRANSAC::VPFloat(nInliers) / GRANSAC::VPFloat(nTotalParams); // This is the inlier fractionreturn std::make_pair(InlierFraction, Inliers);};
};

实验

#include <cmath>
#include <cstdint>
#include <iostream>
#include <random>
#include <vtkActor.h>
#include <vtkGlyph3DMapper.h>
#include <vtkLine.h>
#include <vtkLineSource.h>
#include <vtkNamedColors.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSphereSource.h>
#include <vtkRenderer.h>
#include <vtkMath.h>
#include "GRANSAC.hpp"
#include "LineModel.hpp"vtkSmartPointer<vtkActor> PointToGlyph(vtkPoints* points, double const& scale)
{auto bounds = points->GetBounds();double maxLen = 0;for (int i = 1; i < 3; ++i){maxLen = std::max(bounds[i + 1] - bounds[i], maxLen);}vtkNew<vtkSphereSource> sphereSource;sphereSource->SetRadius(scale * maxLen);vtkNew<vtkPolyData> pd;pd->SetPoints(points);vtkNew<vtkGlyph3DMapper> mapper;mapper->SetInputData(pd);mapper->SetSourceConnection(sphereSource->GetOutputPort());mapper->ScalarVisibilityOff();mapper->ScalingOff();vtkNew<vtkActor> actor;actor->SetMapper(mapper);return actor;
}int main(int argc, char * argv[])
{int Side = 1000;int nPoints = 500;vtkNew<vtkPoints> points;vtkNew<vtkPoints> inlierPoints;vtkNew<vtkPoints> outlierPoints;// Randomly generate points in a 2D plane roughly aligned in a line for testingstd::random_device SeedDevice;std::mt19937 RNG = std::mt19937(SeedDevice());std::uniform_int_distribution<int> UniDist(0, Side - 1); // [Incl, Incl]int Perturb = 25;std::normal_distribution<GRANSAC::VPFloat> PerturbDist(0, Perturb);std::vector<std::shared_ptr<GRANSAC::AbstractParameter>> CandPoints;for (int i = 0; i < nPoints; ++i){int Diag = UniDist(RNG);//cv::Point Pt(floor(Diag + PerturbDist(RNG)), floor(Diag + PerturbDist(RNG)));//cv::circle(Canvas, Pt, floor(Side / 100) + 3, cv::Scalar(0, 0, 0), 2, cv::LINE_AA);auto x = floor(Diag + PerturbDist(RNG));auto y = floor(Diag + PerturbDist(RNG));auto z = floor(Diag + PerturbDist(RNG));points->InsertNextPoint(x, y, z);std::shared_ptr<GRANSAC::AbstractParameter> CandPt = std::make_shared<Point3D>(x, y, z);CandPoints.push_back(CandPt);}GRANSAC::RANSAC<Line3DModel, 2> Estimator;Estimator.Initialize(20, 100); // Threshold, iterationsint64_t start = clock();Estimator.Estimate(CandPoints);int64_t end = clock();std::cout << "RANSAC took: " << GRANSAC::VPFloat(end - start) << " ms." << std::endl;auto BestInliers = Estimator.GetBestInliers();if (BestInliers.size() > 0){for (auto& Inlier : BestInliers){auto RPt = std::dynamic_pointer_cast<Point3D>(Inlier);inlierPoints->InsertNextPoint(floor(RPt->m_Point3D[0]), floor(RPt->m_Point3D[1]), floor(RPt->m_Point3D[2]));}}vtkNew<vtkLineSource> vector1Source;auto BestLine = Estimator.GetBestModel();if (BestLine){auto BestLinePt1 = std::dynamic_pointer_cast<Point3D>(BestLine->GetModelParams()[0]);auto BestLinePt2 = std::dynamic_pointer_cast<Point3D>(BestLine->GetModelParams()[1]);if (BestLinePt1 && BestLinePt2){vector1Source->SetPoint1(BestLinePt1->m_Point3D[0], BestLinePt1->m_Point3D[1], BestLinePt1->m_Point3D[2]);vector1Source->SetPoint1(BestLinePt2->m_Point3D[0], BestLinePt2->m_Point3D[1], BestLinePt2->m_Point3D[2]);}}vtkNew<vtkPolyDataMapper> vec1Mapper;vec1Mapper->SetInputConnection(vector1Source->GetOutputPort());vtkNew<vtkNamedColors> colors;vtkNew<vtkActor> vector1Actor;vector1Actor->SetMapper(vec1Mapper);vector1Actor->GetProperty()->SetColor(colors->GetColor3d("LimeGreen").GetData());vector1Actor->GetProperty()->SetLineWidth(3);// There will be one render windowvtkNew<vtkRenderWindow> renderWindow;renderWindow->SetSize(600, 300);// And one interactorvtkNew<vtkRenderWindowInteractor> interactor;interactor->SetRenderWindow(renderWindow);// Setup both renderersvtkNew<vtkRenderer> leftRenderer;renderWindow->AddRenderer(leftRenderer);// Map the points to spheresauto sphereActor = PointToGlyph(points, 0.007);sphereActor->GetProperty()->SetColor(colors->GetColor3d("Blue").GetData());auto sphereActor1 = PointToGlyph(inlierPoints, 0.01);sphereActor1->GetProperty()->SetColor(colors->GetColor3d("Red").GetData());leftRenderer->AddActor(sphereActor);leftRenderer->AddActor(sphereActor1);leftRenderer->AddActor(vector1Actor);leftRenderer->ResetCamera();renderWindow->Render();interactor->Start();return 0;
}

结果

在这里插入图片描述

引用

[1]. https://github.com/drsrinathsridhar/GRANSAC


http://chatgpt.dhexx.cn/article/O3k020Gn.shtml

相关文章

二维直线拟合--opencv实现

二维/三维直线拟合–opencv实现 ​ Hough变换可以提取图像中的直线&#xff0c;但是提取线的精度不高&#xff0c;然而我们的很多场合下需要精确估计直线的参数&#xff0c;这时就需要进行直线拟合。 ​ 直线拟合的方法有很多&#xff0c;比如&#xff1a;一元线性回归就是一…

直线拟合最小二乘法

数据 x(1,2,3,4,5) y (1,1.5,3,4.5,5) 算法结果 R语言运行结果 算法原理 x的均值&#xff1a; xpsum&#xff08;x1x2x3…xn&#xff09;/n y的均值 : ypsum&#xff08;y1y2y3…yn&#xff09;/n x的平方差之和&#xff1a; lxxsum( (xi-xp) ^ 2 ) 协方差之和 lxy…

直线生成以及pcl直线拟合

目录 写在前面codecompile&run result参考 写在前面 1、本文内容 pcl直线拟合&#xff0c;生成带噪声的直线并进行直线拟合的demo 2、平台 windows, pcl1.10.0 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/details/127147223 code line_…

opencv 直线拟合

一、话说直线拟合 霍夫直线检测容易受到线段形状与噪声的干扰而失真&#xff0c;这个时候我们需要另辟蹊径&#xff0c;通过对图像进行二值分析&#xff0c;提取骨架&#xff0c;对骨架像素点拟合生成直线&#xff0c;这种做法在一些场景下非常有效&#xff0c;而且效果还比较…

拟合算法之一 直线拟合

直线拟合 很早就想学习拟合了&#xff0c;经常听同事用到拟合&#xff0c;当时尚且一窍不通&#xff0c;必须快递加急紧追此处才是&#xff0c;也参考了网上大佬的一些宝贵经验&#xff0c;先将拟合方法总结如下&#xff1a; 最小二乘法 1.原理 2.举例实现 void fitline3(…

直线拟合问题(Python实现)

程序如下&#xff1a; import matplotlib.pyplot as plt import numpy as npx_list [466, 741, 950, 1422, 1634] y_list [7.04, 4.28, 3.40, 2.54, 2.13] # x_list [0, 1, 3, 5] # y_list [1, 2, 4, 8]l_mat11 len(x_list) l_mat12 l_mat21 sum(x for x in x_list) l_…

OpenCV直线拟合检测

点击上方“小白学视觉”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达本文转自&#xff1a;opencv学堂 OpenCV直线拟合检测 霍夫直线检测容易受到线段形状与噪声的干扰而失真&#xff0c;这个时候我们需要另辟蹊径&#xff0c;通过对图像进行…

RANSAC 直线拟合

result&#xff1a; code&#xff1a; #include "stdafx.h". #include <opencv2\opencv.hpp> #include <iostream> #include <ctime>using namespace std; using namespace cv;//生成[0,1]之间符合均匀分布的数 double uniformRandom(void) {ret…

OpenCV——直线拟合

相比于直线检测&#xff0c;直线拟合的最大特点是将所有数据只拟合出一条直线 void fitLine( InputArray points, OutputArray line, int distType,double param, double reps, double aeps ); points&#xff1a;输入待拟合直线的2D或者3D点集。line&#xff1a;输出描述直线…

直线拟合

在进行直线拟合算法中&#xff0c;一直使用最小二乘法&#xff0c;使用时间长了&#xff0c;也比较熟练了&#xff0c;但是在最近一次使用中&#xff0c;最小二乘法在拟合垂直或者接近垂直的直线时&#xff0c;效果不好&#xff1b;斜率很大&#xff0c;使用稳定性不好。查阅《…

Android 冷启动 热启动 测试

一、应用的启动 启动方式 通常来说&#xff0c;在安卓中应用的启动方式分为两种&#xff1a;冷启动和热启动。 1、冷启动&#xff1a;当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动…

Spring Boot 热启动

目的&#xff1a;修改类文件可以马上编译发布&#xff0c;提高了工作效率 步骤&#xff1a; 第一步&#xff1a; 修改pom.xml <!-- 热启动 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools&l…

APP冷热启动专项测试

一、冷热启动的概念 冷启动&#xff1a;当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动。 热启动&#xff1a;当启动应用时&#xff0c;后台已有该应用的进程&#xff08;例&#…

嵌入式linux热启动和冷启动,使用keil判断ARM的冷启动和热启动的方法

微处理器:LPC2114 编译环境:Keil MDK V4.10 思路: 常把单片机系统的复位分为冷启动和热启动。所谓冷启动&#xff0c;也就是一般所说的上电复位&#xff0c;冷启动后片内外RAM的内容是随机的&#xff0c;通常是0x00或0xFF&#xff1b;单片机的热启动是通过外部电路给运行中的单…

Android冷启动和热启动以及冷启动优化方案

1、什么是冷启动和热启动 冷启动&#xff1a; 当启动应用时&#xff0c;后台没有该应用的进程&#xff0c;这时系统会重新创建一个新的进程分配给该应用&#xff0c;这个启动方式就是冷启动&#xff0c;也就是先实例化Application。热启动&#xff1a; 当启动应用时&#xff0…

冷启动和热启动的区别android,app冷启动和热启动的区别(详解两者定义及区别)...

介绍一下 app 冷启动和热启动方式来实现 app 秒开的效果。那么,先来看看什么叫冷启动和热启动。 冷启动:指 app 被后台杀死后,在这个状态打开 app,这种启动方式叫做冷启动。 热启动:指 app 没有被后台杀死,仍然在后台运行,通常我们再次去打开这个 app,这种启动方式叫热…

热启动和冷启动以及复位启动的小知识

热启动和冷启动的区别&#xff1a; 1、重启是热启动。开机是冷启动。 2、热启动是通过开始菜单、任务管理器或者快捷键&#xff0c;重新启动计算机&#xff0c;叫热启动。冷启动是在关机状态下按POWER启动计算机&#xff0c;叫做冷启动 。 3、热启动是在计算机已经开启的状态…

dimen属性报错

<TextViewandroid:id"id/tvQuote1"android:layout_width"fill_parent"android:layout_height"wrap_content"android:textColor"#FFFFFF"android:textSize"dimen/text_size" /> 发现问题是dimen属性出现错误 别的手机上…

解决android:padding=“@dimen/activity_vertical_margin“> 标红问题

解决android:padding“dimen/activity_vertical_margin”> 标红问题 如果有这个问题&#xff0c;可以在values中新建一个dimen.xml文件 在dimen.xml里增加如下代码&#xff1a; <?xml version"1.0" encoding"utf-8"?><resources><…

android:paddingTop=“@dimen/activity_horizontal_margin“报错(报红)解决方法

android:paddingTop"dimen/activity_horizontal_margin"报错&#xff08;报红&#xff09;解决方法 在app----->res----->values------>dimens.xml中添加如下代码&#xff1a; <dimen name“activity_horizontal_margin” / (后面还有一个尖括号)&#…