Structure Light做法如其名,在物體表面上投射出預先設計的圖面,,由於物體的立體表面曲面的變化,會使投射出去的圖案在被測物體上形成高低起伏的圖案,而在偵測器中形成一平移量。透過擷取畫面上線條(或點)的曲折狀況,利用簡易的三角幾何運算,就可以計算出曲面的起伏,也就是物體的立體深度。這並非新技術,早有類似產品如(連結),KINECT或IPHONE X的識別技術。
其中又因為光線形狀可再區分為陣列線,phase shift以及光斑技術(Projected speckle),網格。連結 。
各式結構光源(連結) |
陣列線
是一組逐漸變細且持續垂直正反互換的線條,分析亮暗順序即能回推出該點的原座標位置,入門者會參考Brown大學專案(連結),但我實在駑鈍無法掌握,退而求其次找到更精簡範例(連結)如下。
Capture Images
conda選用3denv,建立structuredlight目錄,建立src目錄,python Capture.py 啟動視窗,輸入要儲存的目錄,例如aaa(必須在captures目錄下先建立aaa目錄,之後都會放在這邊)。接著視窗(已經被拉到投影機上)開始依序輪播四十多個pattern圖片(GrayImage依序產生),這些光線照射在物體上產生曲折紋路,同時會啟動webcam拍攝紋路畫面,存入../captures/下指定目錄。記得,要關燈。
Pattern
基本三角測量時,僅需要單條雷射光即能計算空間位移,若要取得全平面資料,直覺想法是,為何不能直接打出三十條雷射,直接計算這三十條曲線的位置呢?先前我也被這問題困擾很久,直到看到下圖說明。打出的光線分別是1~10號,但是相機接收到的分別是1 7 8 9 3 4,其中好幾個號碼消失或無法區分,這些都會導致錯誤的判斷。
我們無法直接追蹤三十條線的去處,所以得設計出一組編碼過程,類似二進位的方式,為平面上每個點定義出獨特編碼,結果就是亮暗亮暗亮亮暗的概念。
編碼規則,b(全黑) h0~9(垂直線條逐漸變細) ih0~9(黑白反轉垂直線條逐漸變細) iv0~9 v0~9 w(全白)。
self.num_bits = int(ceil(np.log2(max_dim)))
grayCodes = np.arange(max_dim, dtype=np.uint16)
grayCodes = (grayCodes >> 1) ^ grayCodes
grayCodes.byteswap(inplace=True)
self.grayCodes = np.unpackbits(grayCodes.view(dtype=np.uint8)).reshape((-1, 16))[:, 16-self.num_bits:]*255
H : self.imageOut[:] = self.grayCodes[:, i]
V : self.imageOut[:] = self.grayCodes[:self.height, i, None]
其他打光寫法(連結)
Decode
保留Makefile make clean, make,使用C語言是為了加速。DecodeGrayImages 逐個image cell計算,取得每個點的亮和暗資訊,產生五種檔案。iHigh=挑出6~9層各種模式中最亮的數值。iLow=挑出6~9層各種模式中最暗的數值。
b_inv = (float)pw[px] / (pw[px] + pb[px]);
out_DirectImage = Ld = (iHigh - iLow) * b_inv;
out_indirectImage = Lg = 2.0 * (iHigh - Ld) * b_inv;
out_InvalidImage H/V out_DirectImage 小於15,Invalid直接成為255 並且阻擋後面 過程中若不合理也會直接打斷後面計算
out_GrayImage H/V 會紀錄各個level中,是否需要紀錄進去
out_BinImage H/V 由GrayImage得到最後這個點的位置,也就是每個pixel被"看到"的理論位置。
Camera Projector Calibration
產生圖紙python GenerateAurcoAndChaurco.py, python BoardInfo.py 兩個檔案中實際使用為charucoBoard.png。將板子放在投影機前,比照3D重建作法,拍攝一組結構光照片,並且繼續移動板子位置,拍攝更多組照片。
python CameraCalibration.py ,選用了白光就可以產生DetectedMarkers.png和DetectedCorners.png。影響all_charco_corners_camera和all_charco_ids_camera。
第二階段是取出out_InvalidImageV/H out_BinImageH/V ,利用GetSecondViewPoints.py 再確認,若通過測試,影響all_charco_corners_camera_2 all_real_points.append
all_charco_corners_projector all_charco_ids_projector。
計算所有的板子後,用所有參數,產生計算相機和投影機角度的參數 ../camera_calibration_out/calculated_cams_matrix_less_distortion.npz(沒用到) calculated_cams_matrix.npz,可用在後續轉換照片回立體空間之用。其他calibration範例(PYTHON 連結)。
Reproject
python Reproject.py,讀入前面Decode的結果Invalid H/V BinImage H/V ,取得物體理論位置,再透過相機和投影機的相對矩陣資料校正資料(npz),利用cv2.convertPointsToHomogeneous取得原始空間"實際"被偏移的位置Pts。
open3d儲存成ply(詳細bin),及downsample(詳細bin),並且讀入原始color檔案,成為新的filtered ply(詳細bin)。其他範例專案(連結)。
Phase Shift
相位移動則是形狀固定(正弦光束)但會隨時間平移的波形,雖然複雜但速度快,解析度也更高。對我來說這裡論實在艱澀,只能邊看程式邊猜測(沒辦法,小時候數學沒學好),參考專案(連結 c++)及論文(連結)。