ransac拟合
链接:https://zhuanlan.zhihu.com/p/62238520
RANSAC简介
RANSAC(Random Sample Consensus,随机采样一致)算法是从一组含有“外点”(outliers)的数据中正确估计数学模型参数的迭代算法。“外点”一般指的的数据中的噪声,比如说匹配中的误匹配和估计曲线中的离群点。所以,RANSAC也是一种“外点”检测算法。RANSAC算法是一种不确定算法,它只能在一种概率下产生结果,并且这个概率会随着迭代次数的增加而加大(之后会解释为什么这个算法是这样的)。RANSAC算最早是由Fischler和Bolles在SRI上提出用来解决LDP(Location Determination Proble)问题的。
对于RANSAC算法来说一个基本的假设就是数据是由“内点”和“外点”组成的。“内点”就是组成模型参数的数据,“外点”就是不适合模型的数据。同时RANSAC假设:在给定一组含有少部分“内点”的数据,存在一个程序可以估计出符合“内点”的模型。
算法基本思想和流程
RANSAC是通过反复选择数据集去估计出模型,一直迭代到估计出认为比较好的模型。
具体的实现步骤可以分为以下几步:
- 选择出可以估计出模型的最小数据集;(对于直线拟合来说就是两个点,对于计算Homography矩阵就是4个点)
- 使用这个数据集来计算出数据模型;
- 将所有数据带入这个模型,计算出“内点”的数目;(累加在一定误差范围内的适合当前迭代推出模型的数据)
- 比较当前模型和之前推出的最好的模型的“内点“的数量,记录最大“内点”数的模型参数和“内点”数;
- 重复1-4步,直到迭代结束或者当前模型已经足够好了(“内点数目大于一定数量”)。
迭代次数推导
这里有一点就是迭代的次数我们应该选择多大呢?这个值是否可以事先知道应该设为多少呢?还是只能凭经验决定呢? 这个值其实是可以估算出来的。下面我们就来推算一下。
假设“内点”在数据中的占比为
那么我们每次计算模型使用个点的情况下,选取的点至少有一个外点的情况就是
也就是说,在迭代 次的情况下,就是次迭代计算模型都至少采样到一个“外点”去计算模型的概率。那么能采样到正确的 个点去计算出正确模型的概率就是
通过上式,可以求得
“内点”的概率 通常是一个先验值。然后是我们希望RANSAC得到正确模型的概率。如果事先不知道的值,可以使用自适应迭代次数的方法。也就是一开始设定一个无穷大的迭代次数,然后每次更新模型参数估计的时候,用当前的“内点”比值当成来估算出迭代次数。
用Python实现直线/平面拟合
import numpy as np import random import matplotlib import matplotlib.pyplot as plt from mpl_toolkits import mplot3d # 将数据增加一个维度,最后一位是1 def augment(xys): axy = np.ones((len(xys), len(xys[0]) + 1)) axy[:, :len(xys[0])] = xys return axy # 计算方程组的解 def estimate(xys): axy = augment(xys) return np.linalg.svd(axy)[-1][-1, :] # 判断是否是inlier点, 方程:ax + by + c < threshold def is_inlier(coeffs, xy, threshold = 0.01): return np.abs(coeffs.dot(augment([xy]).T)) < threshold def run_ransac(data, sample_size, goal_inliers, max_iterations, stop_at_goal=True, random_seed=None): """ :param data: 待拟合数据 :param sample_size: 2:两个点确定一条线 3:三个点确定一个平面。 :param goal_inliers: inliers点的个数 :param max_iterations: 最大迭代次数 :param stop_at_goal: 是否在满足goal_inliers条件时候结束迭代 :param random_seed: 随机初始化种子 :return: 1:返回拟合参数(a, b, c ...) 2: 拟合数据量 """ best_ic = 0 best_model = None random.seed(random_seed) # random.sample cannot deal with "data" being a numpy array data = list(data) for i in range(max_iterations): s = random.sample(data, int(sample_size)) m = estimate(s) ic = 0 for j in range(len(data)): if is_inlier(m, data[j]): ic += 1 if ic > best_ic: best_ic = ic best_model = m if ic > goal_inliers and stop_at_goal: break print('took iterations:', i+1, 'best model:', best_model, 'explains:', best_ic) return best_model, best_ic def line_fit(): n = 100 max_iterations = 100 goal_inliers = n * 0.3 # test data xys = np.random.random((n, 2)) * 10 xys[:50, 1:] = xys[:50, :1] plt.scatter(xys.T[0], xys.T[1]) # RANSAC m, b = run_ransac(xys, 2, goal_inliers, max_iterations) a, b, c = m plt.plot([0, 10], [-c / b, -(c + 10 * a) / b], color=(0, 1, 0)) plt.show() def plane_fit(): fig = plt.figure() ax = mplot3d.Axes3D(fig) def plot_plane(a, b, c, d): xx, yy = np.mgrid[:10, :10] return xx, yy, (-d - a * xx - b * yy) / c n = 100 max_iterations = 100 goal_inliers = n * 0.3 # test data xyzs = np.random.random((n, 3)) * 10 xyzs[:50, 2:] = xyzs[:50, :1] ax.scatter3D(xyzs.T[0], xyzs.T[1], xyzs.T[2]) # RANSAC m, b = run_ransac(xyzs, 3, goal_inliers, max_iterations) a, b, c, d = m xx, yy, zz = plot_plane(a, b, c, d) ax.plot_surface(xx, yy, zz, color=(0, 1, 0, 0.5)) plt.show() if __name__ == '__main__': # line_fit() # 拟合直线 plane_fit() # 拟合平面
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)