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

















