結構光相移法-多頻外差原理+實踐(上篇)


作者:曹博

原文:微信公眾號|3D視覺工坊(系投稿)

3D視覺精品文章匯總:https://github.com/qxiaofan/awesome-3D-Vision-Papers/

 

01 相移法原理
02 雙頻外差原理
03 多頻率外差原理
04 代碼實踐

01 相移法原理

結構光法原理其實是跟雙目視覺一樣的,都是要確定對應“匹配點”,利用“視差”三角關系計算距離,所不同的是:

  • 雙目視覺通過“被動”匹配唯一特征點
  • 相移法作為結構光法中的一種,通過主動投影多副相移圖案來標記唯一位置。

說明:雖然大多數結構光系統是單目的,但我們可以將其“雙目”的,因為投影儀可以看做是一個“逆向”的相機,明白了這點,對於結構光系統一些公式推導就容易很多。

對於“雙目”系統來說,最重要的工作是通過唯一標記來標記某一點,假設我們只投射一個周期的數據,我們從投影儀投出去的光柵公式如下:

結構光相移法-多頻外差原理+實踐(上篇)

 

其中:

結構光相移法-多頻外差原理+實踐(上篇)

 

比如說四步相移公式:

結構光相移法-多頻外差原理+實踐(上篇)

 

我們主要關心的是求解出相位主值,因為它對每個像素點是唯一的,假設我們從相機中獲取了這四副圖像,那怎么反過來求解相位主值?

需要說明的是,雖然這個公式對整副相移圖像的,但是這公式對每個像素都是獨立的,所以即使我們拿從相機拍攝到經過調制變形的圖像來求解,依然可以得到單個像素點唯一的相位主值。

聯立4個方程,得到:

結構光相移法-多頻外差原理+實踐(上篇)

 

無論:

  • 哪台相機
  • 拍攝到什么圖像

我們要得到某個像素點的唯一“標記”,也就是這個相位主值,代回這個公式即可,都可以得到唯一值。得到了唯一值,建立匹配關系,就可以利用三角公式進行重建。

結構光相移法-多頻外差原理+實踐(上篇)

 

結構光相移法-多頻外差原理+實踐(上篇)

 

其中:橫坐標為任意一行的像素,這張圖中使用周期為11的像素條紋作為正弦光柵。

02 雙頻外差原理

解決的方法有很多,分為空域和時域展開兩種:

  • 空域展開:依靠空間相鄰像素點之間的相位值恢復絕對相位,如果重建表面不連續,則出現解碼錯誤。
  • 時域展開:將每個像素點的相位值進行獨立計算,有格雷碼和多頻外差兩種,其中格雷碼方法對物理表面問題敏感,並且多投影的圖並不能用來提升精度,多頻外差精度更高。

當然目前還有更多精度更高、效率更快的相位展開方法,在這里暫時不予討論,這里主要討論多頻外差原理。

多頻外差原理:通過多個不同頻率(周期)正弦光柵的相位做差,將小周期的相位主值轉化為大周期的相位差,從而使得相位差信號覆蓋整個視場,然后再根據相位差來得到整副圖像的絕對相位分布。

這里以雙頻外差為例,原理如圖1所示:

結構光相移法-多頻外差原理+實踐(上篇)

 

結構光相移法-多頻外差原理+實踐(上篇)

 

注:通常我們說的相位函數的周期,代表的是一個周期正弦函數所占的像素單位個數。

03 多頻率外差原理

結構光相移法-多頻外差原理+實踐(上篇)

 

結構光相移法-多頻外差原理+實踐(上篇)

 

其可以完成整個視場的無歧義標記。

04 代碼實踐

結構光相移法-多頻外差原理+實踐(上篇)

 

依據相移法得到的包裹相位圖如下圖所示,不同顏色代表不同頻率的相位主值:

結構光相移法-多頻外差原理+實踐(上篇)

 

我們進行疊加后的效果:

結構光相移法-多頻外差原理+實踐(上篇)

 

在這里,我們可以看到,由兩個周期小的相位可以合成一個周期更大的編碼圖案。

結構光相移法-多頻外差原理+實踐(上篇)

 

結構光相移法-多頻外差原理+實踐(上篇)

 

其中:

結構光相移法-多頻外差原理+實踐(上篇)

 

明白了原理,我們來代碼實踐一下,需要注意的是,求解出來的相位我們要進行歸一化到區間操作:

import numpy as np import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽 plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負號 def phase_simulation(WIDTH, T1, T2, T3): pha1, pha2, pha3 = np.zeros(shape=WIDTH), np.zeros(shape=WIDTH), np.zeros(shape=WIDTH) t1, t2, t3 = 1, 1, 1 for idx in range(WIDTH): if t1 > T1: t1 = 1 # 重置一下 pha1[idx] = (t1 / T1) * 2 * np.pi if t2 > T2: t2 = 1 pha2[idx] = (t2 / T2) * 2 * np.pi if t3 > T3: t3 = 1 pha3[idx] = (t3 / T3) * 2 * np.pi t1 += 1; t2 += 1; t3 += 1 return pha1, pha2, pha3 def parse_phase(pha1, pha2, T1, T2): pha12 = np.zeros_like(pha1) # 計算Delta(如果滿足條件,輸出左側,否則右側) pha12 = np.where(pha1 >= pha2, pha1 - pha2, pha1 - pha2 + 2 * np.pi) # # 跟下面這段代碼等價 # for idx in range(0, pha12.shape[0]): # if pha1[idx] >= pha2[idx]: # pha12[idx] = pha1[idx] - pha2[idx] # else: # pha12[idx] = pha1[idx] - pha2[idx] + 2 * np.pi T12 = T1 * T2 / (T2 - T1) # 方法1 pha12 = T2 / (T2 - T1) * pha12 # # 方法2 # m = np.round((T2 / (T2 - T1) * pha12 - pha1) / (2 * np.pi)) # pha12 = 2 * np.pi * m + pha12 # 歸一化到[0,2π] min_value, max_value = np.min(pha12), np.max(pha12) pha12 = (pha12 - min_value) * (2 * np.pi / (max_value - min_value)) return pha12, T12 if __name__ == '__main__': # 視場寬度 WIDTH = 854 # 條紋周期 T1 = 11 T2 = 12 T3 = 13 pha1, pha2, pha3 = phase_simulation(WIDTH, T1, T2, T3) X = np.arange(0, WIDTH) plt.plot(X, pha1, label="pha1") plt.plot(X, pha2, label="pha2:") plt.plot(X, pha3, label="pha3") plt.title("相移主值圖(仿真)") plt.xlabel("像素") plt.ylabel("w/rad") plt.legend() plt.show() # 解相位 pha12, T12 = parse_phase(pha1, pha2, T1, T2) pha23, T23 = parse_phase(pha2, pha3, T2, T3) pha123, T123 = parse_phase(pha12, pha23, T12, T23) plt.plot(X, pha12, label="pha12") plt.plot(X, pha23, label="pha23") plt.plot(X, pha123, label="pha123") plt.title("解出絕對相位") plt.xlabel("像素") plt.ylabel("w/rad") plt.legend() plt.show()
結構光相移法-多頻外差原理+實踐(上篇)

 

結構光相移法-多頻外差原理+實踐(上篇)

 

可以看出,最終解出的絕對相位線單調遞增,每個相位值時唯一的,雖然在一些交界處會有些許誤差。

結構光多頻外差的原理很簡單,而精度這塊,其實很大程度依賴於標定、高反處理這些地方。這一期內容將分為上下兩期,為了便於理解,不再講述更多內容,更多我們下一期再講!怎么拿實際投影拍攝到的光柵圖片來還原絕對相位!

備注:作者也是我們「3D視覺從入門到精通」特邀嘉賓:一個超干貨的3D視覺學習社區

本文僅做學術分享,如有侵權,請聯系刪文。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM