RANSAC平面擬合python實現(xiàn)

畢設(shè)中有一個小模塊是要在點(diǎn)云中做平面的擬合乎串,翻閱了一些資料后覺得用最簡單的最小二乘法加上AX+BY+CZ=D的平面方程就可以實現(xiàn)(使用SVD進(jìn)行最小二乘法擬合)绎谦,但是當(dāng)實現(xiàn)過后發(fā)現(xiàn)效果并不是很好,考慮原因應(yīng)該是因為最小二乘法受誤差值當(dāng)影響比較大(代碼也放在文末)。繼續(xù)查閱資料決定選擇RANSAC實現(xiàn)平面擬合。

原理解釋

這里參考維基百科對于RANSAC算法原理進(jìn)行簡要解釋:
這里要先解釋兩個名詞

內(nèi)點(diǎn):擬合模型用到的點(diǎn)
外點(diǎn):誤差點(diǎn)

(個人的理解感覺很像隨機(jī)森林幼东,投票制)(直線擬合為例)

1 隨機(jī)選出兩個點(diǎn)。(選擇出可以估計出模型的最小數(shù)據(jù)集,如果是平面就選擇3個點(diǎn))
2 兩點(diǎn)確定一條直線根蟹,擬合出這條直線
3 將所有數(shù)據(jù)帶入這個模型脓杉,計算出“內(nèi)點(diǎn)”的數(shù)目;(累加在一定誤差范圍內(nèi)的適合當(dāng)前迭代推出模型的數(shù)據(jù))
4 比較當(dāng)前模型和之前推出的最好的模型的“內(nèi)點(diǎn)“的數(shù)量简逮,記錄最大“內(nèi)點(diǎn)”數(shù)的模型參數(shù)和“內(nèi)點(diǎn)”數(shù)球散;
5 重復(fù)1-4步,直到迭代結(jié)束或者當(dāng)前模型已經(jīng)足夠好了(“內(nèi)點(diǎn)數(shù)目大于一定數(shù)量”)散庶。
(PS:這一部分沒有提及對于迭代次數(shù)設(shè)置的推導(dǎo)蕉堰,感興趣的可以去查一查)

python代碼實現(xiàn)

廢話不多說,上代碼

import numpy as np
from ransac import *
import random

def augment(xyzs):
    axyz = np.ones((len(xyzs), 4))
    axyz[:, :3] = xyzs
    return axyz

def estimate(xyzs):
    axyz = augment(xyzs[:3])
    return np.linalg.svd(axyz)[-1][-1, :]

def is_inlier(coeffs, xyz, threshold):
    return np.abs(coeffs.dot(augment([xyz]).T)) < threshold

def run_ransac(data, estimate, is_inlier, sample_size, goal_inliers, max_iterations, stop_at_goal=True, random_seed=None):
    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

        print(s)
        print('estimate:', m,)
        print('# inliers:', ic)

        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

if __name__ == '__main__':
    import matplotlib
    import matplotlib.pyplot as plt
    from mpl_toolkits import mplot3d
    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, estimate, lambda x, y: is_inlier(x, y, 0.01), 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()
image.png

最小二乘法平面擬合

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np

N_POINTS = 10
TARGET_X_SLOPE = 2
TARGET_y_SLOPE = 3
TARGET_OFFSET  = 5
EXTENTS = 5
NOISE = 5

# create random data
xs = [np.random.uniform(2*EXTENTS)-EXTENTS for i in range(N_POINTS)]
ys = [np.random.uniform(2*EXTENTS)-EXTENTS for i in range(N_POINTS)]
zs = []
for i in range(N_POINTS):
    zs.append(xs[i]*TARGET_X_SLOPE + \
              ys[i]*TARGET_y_SLOPE + \
              TARGET_OFFSET + np.random.normal(scale=NOISE))

# plot raw data
plt.figure()
ax = plt.subplot(111, projection='3d')
ax.scatter(xs, ys, zs, color='b')

# do fit
tmp_A = []
tmp_b = []
for i in range(len(XS)):
    tmp_A.append([xs[i], ys[i], 1])
    tmp_b.append(zs[i])
b = np.matrix(tmp_b).T
A = np.matrix(tmp_A)

# Manual solution
fit = (A.T * A).I * A.T * b
errors = b - A * fit
residual = np.linalg.norm(errors)

# Or use Scipy
# from scipy.linalg import lstsq
# fit, residual, rnk, s = lstsq(A, b)

print("solution:")
print("%f x + %f y + %f = z" % (fit[0], fit[1], fit[2]))
print("errors:")
print(errors)
print("residual:")
print(residual)

# plot plane
xlim = ax.get_xlim()
ylim = ax.get_ylim()
X,Y = np.meshgrid(np.arange(xlim[0], xlim[1]),
                  np.arange(ylim[0], ylim[1]))
Z = np.zeros(X.shape)
for r in range(X.shape[0]):
    for c in range(X.shape[1]):
        Z[r,c] = fit[0] * X[r,c] + fit[1] * Y[r,c] + fit[2]
ax.plot_wireframe(X,Y,Z, color='k')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悲龟,一起剝皮案震驚了整個濱河市屋讶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌须教,老刑警劉巖皿渗,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異轻腺,居然都是意外死亡乐疆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門贬养,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挤土,“玉大人,你說我怎么就攤上這事误算⊙雒溃” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵尉桩,是天一觀的道長筒占。 經(jīng)常有香客問我,道長蜘犁,這世上最難降的妖魔是什么翰苫? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮这橙,結(jié)果婚禮上奏窑,老公的妹妹穿的比我還像新娘。我一直安慰自己屈扎,他們只是感情好埃唯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹰晨,像睡著了一般墨叛。 火紅的嫁衣襯著肌膚如雪止毕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天漠趁,我揣著相機(jī)與錄音扁凛,去河邊找鬼。 笑死闯传,一個胖子當(dāng)著我的面吹牛谨朝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播甥绿,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼字币,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了共缕?” 一聲冷哼從身側(cè)響起洗出,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎图谷,沒想到半個月后共苛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜓萄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了澄峰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫉沽。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俏竞,靈堂內(nèi)的尸體忽然破棺而出绸硕,到底是詐尸還是另有隱情,我是刑警寧澤魂毁,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布玻佩,位于F島的核電站,受9級特大地震影響席楚,放射性物質(zhì)發(fā)生泄漏咬崔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一烦秩、第九天 我趴在偏房一處隱蔽的房頂上張望垮斯。 院中可真熱鬧,春花似錦只祠、人聲如沸兜蠕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熊杨。三九已至曙旭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晶府,已是汗流浹背桂躏。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郊霎,地道東北人沼头。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像书劝,于是被迫代替她去往敵國和親进倍。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348