RANSAC(Random sample consensus)随机抽样一致性

文章目录

    • 算法介绍
    • 实现过程
    • 以直线拟合为例
    • 直线的描述
    • 源码
    • 参考连接

算法介绍

RANSAC(Random Sample Consensus)是一种迭代的参数估计算法,用于从包含噪声和异常值的数据中拟合数学模型。它最初由Fischler和Bolles于1981年提出,被广泛应用于计算机视觉和计算机图形学等领域。

RANSAC的核心思想是随机选择数据中的一小部分样本,并根据这些样本拟合一个模型。然后,通过计算其他数据点到该模型的距离,并将距离小于一定阈值的数据点划分为内点,而距离大于阈值的数据点则划分为外点。重复此过程多次,并选择具有最多内点的模型作为最终的估计结果。

RANSAC算法的优点在于它对于大量异常值和噪声的数据有较好的鲁棒性。它可以估计出包含异常值的数据集中的准确模型,并且不要求事先知道异常值的数量,这种算法常被用于处理具有离群点或噪声的数据。

与最小二乘法相比,RANSAC的主要优点是对异常值的鲁棒性。最小二乘法试图最小化所有数据点的误差平方和,因此对异常值非常敏感。只要有一个数据点远离真实模型,就可能严重影响最小二乘法的结果。而RANSAC通过随机抽样和内点检测机制,能够有效地抵抗异常值的影响。

应用场景:

  • 特征匹配:在图像配准和目标识别中,我们需要匹配两幅图像中的特征点。由于噪声和异常值的存在,直接匹配可能会得到错误的结果。RANSAC可以用于鲁棒地估计特征点之间的映射关系,从而提高匹配的准确性。

  • 三维重建:在立体视觉和结构光扫描中,我们需要从多个视角的图像中重建三维结构。RANSAC可以用于估计相机的运动和场景的几何结构,从而实现鲁棒的三维重建。

  • 运动估计:在视频分析和机器人导航中,我们需要估计物体或相机的运动。RANSAC可以用于从时间序列的图像数据中鲁棒地估计运动参数。

  • 平面检测:在场景理解和物体检测中,我们需要从图像中检测平面。RANSAC可以用于从点云数据中鲁棒地检测平面。

  • 校准:在相机校准和图像拼接中,我们需要估计图像之间的几何变换。RANSAC可以用于鲁棒地估计这些变换,从而实现准确的校准和拼接。

实现过程

下面的伪代码来源于维基百科,仔细读一下这份伪代码,它已经把算法的实现过程说的很明确了。

Given:
    data – A set of observations.
    model – A model to explain the observed data points.
    n – The minimum number of data points required to estimate the model parameters.
    k – The maximum number of iterations allowed in the algorithm.
    t – A threshold value to determine data points that are fit well by the model (inlier).
    d – The number of close data points (inliers) required to assert that the model fits well to the data.

Return:
    bestFit – The model parameters which may best fit the data (or null if no good model is found).


iterations = 0
bestFit = null
bestErr = something really large // This parameter is used to sharpen the model parameters to the best data fitting as iterations go on.

while iterations < k do
    maybeInliers := n randomly selected values from data
    maybeModel := model parameters fitted to maybeInliers
    confirmedInliers := empty set
    for every point in data do
        if point fits maybeModel with an error smaller than t then
             add point to confirmedInliers
        end if
    end for
    if the number of elements in confirmedInliers is > d then
        // This implies that we may have found a good model.
        // Now test how good it is.
        betterModel := model parameters fitted to all the points in confirmedInliers
        thisErr := a measure of how well betterModel fits these points
        if thisErr < bestErr then
            bestFit := betterModel
            bestErr := thisErr
        end if
    end if
    increment iterations
end while

return bestFit

以直线拟合为例

随机构造一些散点,添加一部分噪声,用RANSAC的算法来拟合这些散点,结果示例如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

直线的描述

假设我们有一个点 P ( x 0 , y 0 ) P(x_0, y_0) P(x0,y0)和一个法向量 N ( a , b ) N(a, b) N(a,b),那么直线的点法式方程可以表示为:

a ( x − x 0 ) + b ( y − y 0 ) = 0 a(x - x_0) + b(y - y_0) = 0 a(xx0)+b(yy0)=0

这个方程的含义是,对于直线上的任意一点 ( x , y ) (x, y) (x,y),它与点 P P P的向量 < P , ( x , y ) > <P, (x, y)> <P,(x,y)>与法向量 N N N是垂直的。因为两个垂直的向量的点积为0,所以有这个等式。
给定两个点 P 1 ( x 1 , y 1 ) , P 2 ( x 2 , y 2 ) P_1(x_1,y_1),P_2(x2,y_2) P1(x1,y1),P2(x2,y2),

  • 直线的方向向量 D D D D = ( x 2 − x 1 , y 2 − y 1 ) D=(x_2-x_1, y_2-y_1) D=(x2x1,y2y1)

  • 直线的法向量 N N N N = ( − ( y 2 − y 1 ) , x 2 − x 1 ) N=(-(y_2-y_1), x_2-x_1) N=((y2y1),x2x1)
    将法向量 N N N和已知 P 1 P1 P1代入上面的方程,得到:

− ( y 2 − y 1 ) ( x − x 1 ) + ( x 2 − x 1 ) ( y − y 1 ) = 0 -(y_2 - y_1)(x - x_1) + (x_2 - x_1)(y - y_1) = 0 (y2y1)(xx1)+(x2x1)(yy1)=0

然后,我们可以将这个方程进一步整理为一般式方程ax + by + c = 0的形式:

( y 1 − y 2 ) x + ( x 2 − x 1 ) y + ( x 1 y 2 − x 2 y 1 ) = 0 (y_1 - y_2)x + (x_2 - x_1)y + (x_1y_2 - x_2y_1) = 0 (y1y2)x+(x2x1)y+(x1y2x2y1)=0

所以,一般式方程的系数 a = y 1 − y 2 , b = x 2 − x 1 , c = x 1 y 2 − x 2 y 1 a = y_1 - y_2,b = x_2 - x_1,c = x_1y_2 - x_2y_1 a=y1y2b=x2x1c=x1y2x2y1
点到直线的距离公式如下:

d = ∣ a x 0 + b y 0 + c ∣ ( a 2 + b 2 ) d = \frac{|ax0 + by0 + c|}{ \sqrt{(a^2 + b^2)}} d=(a2+b2) ax0+by0+c
有时候为了方便计算点到直线的距离,会对直线的一般式方程做归一化使得 a 2 + b 2 = 1 a^2+b^2=1 a2+b2=1这样上面的式子中分母项消失,变为 d = ∣ a x 0 + b y 0 + c ∣ d = |ax0 + by0 + c| d=ax0+by0+c

源码

#include <iostream>
#include <vector>
#include <cmath>

#include "opencv2/opencv.hpp"

// define point
struct Point
{
    float x;
    float y;
};

// define line
struct Line
{
    float a;
    float b;
    float c;
    Line(){};
    Line(Point p1, Point p2)
    {
        a = p2.y - p1.y;
        b = p1.x - p2.x;
        c = p2.x * p1.y - p1.x * p2.y;
        reciprocal_sqrt_asq_plus_bsq = 1.0 / std::sqrt(a * a + b * b);
    }

    Line(float a, float b, float c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        reciprocal_sqrt_asq_plus_bsq = 1.0 / std::sqrt(a * a + b * b);
    }

    // calculate the distance from a point to a line
    float Distance(const Point &p)
    {
        if (reciprocal_sqrt_asq_plus_bsq < 0.0)
            reciprocal_sqrt_asq_plus_bsq = 1.0 / std::sqrt(a * a + b * b);
        return std::abs(a * p.x + b * p.y + c) * reciprocal_sqrt_asq_plus_bsq;
    }

private:
    float reciprocal_sqrt_asq_plus_bsq = -1.0; // 1 / sqrt(a^2 + b^2)
    // calculate the point to line distance
    // d = |ax + by + c| / sqrt(a^2 + b^2)
};

class MyRansac
{
public:
    MyRansac(){};
    ~MyRansac(){};
    Line FitLine(std::vector<Point> &points, int max_iterations, float distance_threshold)
    {
        Line best_line;
        int best_line_num_inliers = 0;
        for (int i = 0; i < max_iterations; i++)
        {
            // randomly select two points
            int idx1 = rand() % points.size();
            int idx2 = rand() % points.size();
            while (idx1 == idx2)
                idx2 = rand() % points.size();
            Point p1 = points[idx1];
            Point p2 = points[idx2];

            // fit line
            Line line(p1, p2);

            // count inliers
            int num_inliers = 0;
            for (int j = 0; j < points.size(); j++)
            {
                Point p = points[j];
                float distance = line.Distance(p);
                if (distance < distance_threshold)
                    num_inliers++;
            }

            // update best line
            if (num_inliers > best_line_num_inliers)
            {
                best_line_num_inliers = num_inliers;
                best_line = line;
                std::cout << "best_line_num_inliers: " << best_line_num_inliers << std::endl;
            }
        }
        return best_line;
    }
};

class PointFactory
{
public:
    std::vector<Point> CreatePoints(float slope, float intercept, int num_points, float x_min, float x_max, float noise)
    {
        std::vector<Point> points;
        for (int i = 0; i < num_points; i++)
        {
            float x = x_min + (x_max - x_min) * rand() / RAND_MAX;
            // float y = y_min + (y_max - y_min) * rand() / RAND_MAX;
            float y_line = slope * x + intercept;
            float y_noise = y_line + noise * rand() / RAND_MAX;
            Point p;
            p.x = x;
            p.y = y_noise;
            points.push_back(p);
        }

        // add outlier
        int num_outliers = num_points / 10;
        for (int i = 0; i < num_outliers; i++)
        {
            float x = x_min + (x_max - x_min) * rand() / RAND_MAX;
            float y = x_min + (x_max - x_min) * rand() / RAND_MAX;
            Point p;
            p.x = x;
            p.y = y;
            points.push_back(p);
        }
        return points;
    }
};

// visualize points and line
void Visualize(std::vector<Point> &points, Line &line, float distance_threshold = 0.0)
{
    cv::Mat img(500, 500, CV_8UC3, cv::Scalar(255, 255, 255));
    for (int i = 0; i < points.size(); i++)
    {
        Point p = points[i];
        float distance = line.Distance(p);
        if (distance < distance_threshold)
            cv::circle(img, cv::Point(p.x, p.y), 2, cv::Scalar(255, 0, 0), -1);
        else
            cv::circle(img, cv::Point(p.x, p.y), 2, cv::Scalar(0, 0, 255), -1);
    }
    if (std::abs(line.b) > 1e-6)
        cv::line(img, cv::Point(0, -line.c / line.b), cv::Point(img.cols, -(line.a * img.cols + line.c) / line.b), cv::Scalar(0, 255, 0), 2);

    cv::imshow("RANSAC", img);
    cv::waitKey(0);
}

int main(int argc, char *argv[])
{
    std::srand(std::time(nullptr));
    // create points
    PointFactory point_factory;
    float slope = 1;
    float intercept = 10.0;
    int num_points = 100;
    float x_min = 0.0;
    float x_max = 500.0;
    float noise = 50.0;
    std::vector<Point> points = point_factory.CreatePoints(slope, intercept, num_points, x_min, x_max, noise);

    // fit line
    MyRansac ransac;
    float distance_threshold = 20.0;
    int max_iterations = 1000;
    Line line = ransac.FitLine(points, max_iterations, distance_threshold);

    // visualize
    Visualize(points, line, distance_threshold);

    return 0;
}

参考连接

[1] Random sample consensus

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/325467.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

soso移动营业大厅(纯后端+MySQL数据库+JDBC)

一、项目需求 中国移动,中国联通,中国电信是国内3大通信运营商,每个运营商都提供了不同的品牌套餐来应对不同的用户群,比如北京移动主要有全球通,神州行,动感地带等3大品牌套餐,每种套餐的内容和费用不同,嗖嗖移动是一个假定的通信运营商,提供了话痨套餐,网虫套餐,超人套餐,各…

[HNCTF 2022 Week1]What is Web

flag 在源码里 <! 是 HTML 文档的注释格式&#xff0c;在源码里按 Ctrl F 搜索 <! 能帮你更快地找到提示。 将这个字符串 base64 解码得到 flag &#xff1a;

React Native 环境安装

Notion – The all-in-one workspace for your notes, tasks, wikis, and databases. 搭建开发环境 React Native 中文网 Homebrew&#xff08;包管理器&#xff09; → rvm&#xff08;ruby版本管理&#xff09; → ruby → cocoapods 安装 Homebrew Homebrew /bin/ba…

shell 循环 判断

for 循环 Shell 脚本里最简单的循环当属 for 循环。最简单的 for 循环如下所示&#xff0c;你只需将变量值依次写在 in 后面即可&#xff1a; #!/bin/bashfor num in 1 2 3 4 doecho $num done 如果要循环的内容是字母表里的连续字母或连续数字&#xff0c;那么就可以按以下语…

Matlab 使用 DH table 建立的 robot 和实际不符

机器人仿真 想借助 matlab robotics toolbox 来仿真机器人&#xff0c;但是直接输入自己的 DH table 显示出来的 robot 和实际不情况不符。 DH table 建立 robot Build Manipulator Robot Using Kinematic DH Parameters 主要使用 setFixedTransform&#xff0c;DH table 中…

智云谷再获资本市场青睐,完成数千万元A+轮融资

近日&#xff0c;深圳前海智云谷科技有限公司&#xff08;以下简称“智云谷”&#xff09;完成数千万元A轮融资&#xff0c;本轮融资由青松基金独家投资&#xff0c;多维资本担任独家融资财务顾问。本轮融资资金将用于扩大新技术研发投入、智能工厂扩产、加速产品交付&#xff…

K8S----YAML

kubernetes中资源可以使用YAML描述&#xff08;如果您对YAML格式不了解&#xff0c;可以参考YAML语法&#xff09;&#xff0c;也可以使用JSON。其内容可以分为如下四个部分&#xff1a; typeMeta&#xff1a;对象类型的元信息&#xff0c;声明对象使用哪个API版本&#xff0c…

Vue-20、Vue监测数组改变

1、数组调用以下方法Vue可以监测到。 arr.push(); 向数组的末尾追加元素 const array [1,2,3] const result array.push(4) // array [1,2,3,4] // result 4arr.pop(); 删除末尾的元素 const array [a, b] array.pop() // b array.pop() // a array.pop() // undefi…

【Shell编程练习】编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机状态

系列文章目录 输出Hello World 通过位置变量创建 Linux 系统账户及密码 监控内存和磁盘容量&#xff0c;小于给定值时报警 猜大小 输入三个数并进行升序排序 系列文章目录编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机状态 编写脚本测试 192.…

opencv_角点检测

文章内容 一个opencv检测角点的程序 运行效果 #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <iostream>using namespace cv; using namespace std;void detectCorners(M…

AI数字人短视频变现项目:打造短视频运营变现新模式

如今&#xff0c;随着科技的快速发展和人们对数字内容的增长需求&#xff0c;短视频成为了互联网用户最喜爱的娱乐方式之一。然而&#xff0c;在这个竞争激烈的市场中&#xff0c;如何为短视频创作者提供更多的变现机会成为了一个重要的问题。AI数字人短视频变现项目的出现&…

【RT-DETR改进涨点】MPDIoU、InnerMPDIoU损失函数中的No.1(包含二次创新)

前言 大家好&#xff0c;我是Snu77&#xff0c;这里是RT-DETR有效涨点专栏。 本专栏的内容为根据ultralytics版本的RT-DETR进行改进&#xff0c;内容持续更新&#xff0c;每周更新文章数量3-10篇。 专栏以ResNet18、ResNet50为基础修改版本&#xff0c;同时修改内容也支持Re…

深入解析多目标优化技术:理论、实践与优化

本文深入探讨了多目标优化技术及其在机器学习和深度学习中的应用&#xff0c;特别聚焦于遗传算法的原理和实践应用。我们从多目标优化的基础概念、常见算法、以及面临的挑战入手&#xff0c;进而详细介绍遗传算法的工作原理、Python代码实现&#xff0c;以及如何应用于实际的机…

【React源码 - Diff算法】

介绍 在React学习中&#xff0c;Diff算法(协调算法)&#xff0c;想必我们并不陌生&#xff0c;简单来说就是一个对比新老节点寻找差异&#xff0c;然后找出最小的一个变化集&#xff0c;最后对这个最小变化集进行最小的DOM操作&#xff0c;本文将从源码来分析在React(17.0.2)中…

四、任意文件读取漏洞

一、介绍 解释&#xff1a;任意文件读取漏洞就其本身来说就是&#xff0c;攻击者绕过网站防御者设置的防御&#xff0c;读取到了正常使用者不应该读取到的内容。网站开发者使用不同的语言&#xff0c;任意文件读取漏洞利用方式就不同。 二、不同开发语言的不同漏洞点 1.PHP …

韶音、南卡、Oladance开放式耳机值得买吗?超强机型对比环节!

​虽然很多耳机音频爱好者最近都爱上了使用开放式耳机&#xff0c;但是作为一个7年音频数码测评的老司机&#xff0c;我还是要提醒一下&#xff0c;目前有很多的开放式耳机过分强调外观颜值设计&#xff0c;在音质体验和佩戴舒适性上的效果极差&#xff0c;还会有很多漏音、破音…

cesium设置近地天空盒 多种效果(附天空盒原图)

效果&#xff08;天空盒原图已放云盘在文章最后&#xff09;&#xff1a; 为了效果逼真设置了当达到一定高度时就恢复系统默认星空天空盒所&#xff0c;以设置了两个变量 一个是近地的天空盒子一个是当超过一定高度时用系统默认的 let currSkyBox; // 当前生效的Skybox let de…

OpenCV——图像按位运算

目录 一、算法概述1、逻辑运算2、函数解析3、用途 二、代码实现三、结果展示 OpenCV——图像按位运算由CSDN点云侠原创&#xff0c;爬虫自重。如果你不是在点云侠的博客中看到该文章&#xff0c;那么此处便是不要脸的爬虫。 一、算法概述 1、逻辑运算 OpenCV4 针对两个图像之…

Android studio 简单登录APP设计

一、登录界面: 二、xml布局设计: <LinearLayoutandroid:id="@+id/linearLayout"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:layout_editor_absoluteX="…

二、基础篇 vue计算属性和侦听器

计算属性 模板内的表达式非常便利&#xff0c;但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如&#xff1a; <div id"example">{{ message.split().reverse().join() }} </div> 在这个地方&#xff0c;模板不…
最新文章