RANSAC(RANdom SAmple Consensus)算法是一种可以去除噪声影响,估计模型的算法。它比类似的最小二乘法更robust。 RANSAC使用数据的子集(inlier)来估计模型,然后通过现有模型参数,尽可能扩大模型在样本集合中的影响,拟合较多的点。
对于 N N N个点构成的集合 P P P,我们假定最少通过 n n n个点可以拟合出正确的模型,也就是说样本集合中有 N − n N-n N−n个噪声点。具体的执行下列操作:
从数据集中随机选择一组样本构建成内点集合,拟合基础模型 H H H使用剩余数据对 H H H进行测试,将落在预定误差( σ \sigma σ)范围内的点划到内点集合中。使用全部的内点集合拟合模型 H H H使用内点集合来估计模型的误差如果模型的性能达到了设定的阈值,或者达到了预定的迭代次数,则算法终止,否则跳转到第2步继续执行。算法第1步中选择内点时,通常选择能确定模型的最少点,比如两点确定一条直线,3点确定一个平面。在求解单应性矩阵的时候,就是4个点对8个点确定一个矩阵模型。
通常来说 N N N的值比较大,那么随机选择点的时候,点与点的组合就很多,如果不对迭代次数进行限制,运算量就会很大。因为算法第2步要用剩余点对模型进行测试,如果点的数量较多,这一步的运算量很大。包括后面的第3,4步。其实我们只要保证我们估计模型的时候使用的都是内点即可。 假设是内点的概率为 t t t: t = n i n i + n o t=\frac{n_i}{n_i + n_o} t=ni+noni n i n_i ni为内点数量, n o n_o no为外点数量。那么我们每次计算模型使用 n n n个点的情况下,选取的点集中至少有一个外点的概率就是: 1 − t n 1-t^n 1−tn。那么在迭代 k k k次的情况下, ( 1 − t n ) k (1-t^n)^k (1−tn)k就是 k k k次迭代都至少有个为外点的概率。 那么能够得到 n n n个正确的点来估计模型的概率就是: P = 1 − ( 1 − t n ) k P = 1-(1-t^n)^k P=1−(1−tn)k 两边取对数,就可以得到最少迭代次数: k = l o g ( 1 − P ) l o g ( 1 − t n ) k=\frac{log(1-P)}{log(1-t^n)} k=log(1−tn)log(1−P) 这里 P P P是希望通过算法得到正确模型的最少概率, k k k是关于 P P P单调递增的, k k k就是在 P P P确定情况下的最少迭代次数。 t t t通常未知,那么我们可以采用自适应的办法,在开始的时候设置一个较大的迭代次数 k k k,然后通过每次迭代中更新 t t t来更新迭代次数。
这里使用RANSAC算法来拟合直线,代码如下:
import numpy as np import matplotlib.pyplot as plt import random import math class ransacMatchingTest(object): def __init__(self, X, Y, sigma=0.2, prob=0.95): self.X = X self.Y = Y self.sigma = sigma self.prob = prob def runMatch(self): preInlier = 0 iters = 10000 best_a = 0 best_b = 0 for i in range(iters): # sample points sampleIndex = random.sample(range(self.X.shape[0]), 2) x1 = self.X[sampleIndex[0]] y1 = self.Y[sampleIndex[0]] x2 = self.X[sampleIndex[1]] y2 = self.Y[sampleIndex[1]] # Compute mode a = (y2 - y1) / (x2 - x1) b = y1 - a * x1 # Get number of inlier inlier = 0 for index in range(self.X.shape[0]): y_estimate = a * self.X[index] + b if abs(y_estimate - self.Y[index]) < self.sigma: inlier = inlier + 1 if inlier > preInlier: # Update iteration times iters = math.log(1-self.prob) / math.log(1 - pow(inlier / self.X.shape[0], 2)) preInlier = inlier best_a = a best_b = b # While inliers over half of input set then break if inlier > (self.X.shape[0] / 2): break return best_a, best_b if __name__ == '__main__': X = np.linspace(0, 10, 50) Y = 4 * X + 6 randomX = [] randomY = [] # Add mode noise for i in range(50): randomX.append(X[i] + random.uniform(-0.5, 0.5)) randomY.append(Y[i] + random.uniform(-0.5, 0.5)) # Add random noise for _ in range(50): randomX.append(random.uniform(0, 6)) randomY.append(random.uniform(6, 30)) RANDOMX = np.array(randomX) RANDOMY = np.array(randomY) ransac = ransacMatchingTest(RANDOMX, RANDOMY, sigma=0.2, prob=0.95).runMatch() preY = ransac[0] * RANDOMX + ransac[1] fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) ax1.scatter(RANDOMX, RANDOMY) ax1.plot(RANDOMX, preY) plt.show()