近來(lái)在做三維姿態(tài)恐怕的東西,用到了上的深度攝像頭(),我們都曉得深度攝像頭可以獲得某個(gè)點(diǎn)的三維信息(基于此可以做好多有趣的東西,例如三維重建,三維關(guān)鍵點(diǎn)跟蹤與檢查,后續(xù)有空漸漸填坑),但具體怎么獲得網(wǎng)上能找到的資料也不多,我在這兒整理了一下我近來(lái)收集到的資料并提供了基于Swift的示例代碼,該文章分成下邊幾小節(jié)來(lái)探討。
雖然最后估算獲得三維座標(biāo)系的方式很簡(jiǎn)單凸透鏡成像原理應(yīng)用,但要了解為何要這樣估算須要清楚單反成像的原理。
單反大致工作原理
X采用的是結(jié)構(gòu)光的方案,這兒以的工作流程來(lái)解釋下它是怎樣獲取深度值的:
點(diǎn)陣投影器和泛光照明器都可以投射紅外線光點(diǎn),不同之處在于后者幀率高前者幀率低,且后者投射結(jié)構(gòu)光,前者投射非結(jié)構(gòu)光。
這兒并不對(duì)原理展開(kāi)講,感盛行的可以移步閱讀尾部的參考鏈接
凸透鏡成像中的焦距和光心
不同角度出發(fā)的光線經(jīng)過(guò)透鏡,跟透鏡表面產(chǎn)生不同的傾角,形成不同程度的折射。
從一個(gè)真實(shí)世界w的一點(diǎn)出發(fā)的光,經(jīng)過(guò)透鏡,又重新匯集到一點(diǎn),最終產(chǎn)生了點(diǎn)對(duì)點(diǎn)的成像關(guān)系,從上圖我們可以得出以下幾個(gè)名詞的定義。
光心:凸透鏡的中心
焦點(diǎn):一束光以凸透鏡的主軸穿過(guò)凸透鏡時(shí),在凸透鏡的另外兩側(cè)會(huì)被凸透鏡凝聚成一點(diǎn),這一點(diǎn)稱作焦點(diǎn)。
焦距:焦點(diǎn)到凸透鏡光心的距離就稱作這個(gè)凸透鏡的焦距,一個(gè)凸透鏡的一側(cè)各自有一個(gè)焦點(diǎn)。
清楚這幾個(gè)基本概念后理解下邊幾個(gè)座標(biāo)系就愈加容易了。
單反成像中所用到的世界,單反凸透鏡成像原理應(yīng)用,圖象,象素座標(biāo)系
我們換一張成像的原理圖
成像過(guò)程中須要經(jīng)過(guò)幾個(gè)座標(biāo)系的轉(zhuǎn)換,最后顯示在我們的屏幕上。
通常來(lái)說(shuō),須要經(jīng)過(guò)四個(gè)座標(biāo)系的轉(zhuǎn)換。
世界座標(biāo)系
描述現(xiàn)實(shí)世界中物體所處的三維座標(biāo)。
單反座標(biāo)系
以單反的光心為座標(biāo)原點(diǎn),x軸和y軸分別平行于圖象座標(biāo)系的x軸和y軸,單反的光軸為z軸。
圖象座標(biāo)系
以圖象平面(通常指?jìng)鞲校┑闹行臑樽鶚?biāo)原點(diǎn),x軸和y軸分別平行于圖象平面的兩條垂直邊,用(x,y)表示其座標(biāo)值,圖象座標(biāo)系是用化學(xué)單位(比如毫米)表示象素在圖象中的位置。
象素座標(biāo)系
以圖象平面左上角的頂點(diǎn)為原點(diǎn),x軸和y軸分別平行于圖象坐標(biāo)的x軸和y軸,用(u,v)表示其座標(biāo)值。這個(gè)座標(biāo)系也就是最終在我們手機(jī)上顯示的座標(biāo)系。
所以,假若我們假如我們想獲得象素點(diǎn)對(duì)應(yīng)的三維坐標(biāo)的話,就要按照象素座標(biāo)系反推回單反座標(biāo)系中。而怎樣反推就涉及到幾個(gè)座標(biāo)系之間的轉(zhuǎn)換方式。
已知一個(gè)現(xiàn)實(shí)世界中的物體點(diǎn)在世界座標(biāo)系中的座標(biāo)為(X,Y,Z),單反座標(biāo)系為(Xc,Yc,Zc),圖象座標(biāo)系中的座標(biāo)為(x,y),象素座標(biāo)系上的座標(biāo)為(u,v)
象素座標(biāo)系與圖象座標(biāo)系之間的轉(zhuǎn)換為:
其中u0,v0是圖象座標(biāo)系原點(diǎn)在象素座標(biāo)系中的座標(biāo),dx和dy分別是每位象素在圖象平面上x和y方向上的規(guī)格,這種值也被稱為圖象的內(nèi)參矩陣,是可以通過(guò)API領(lǐng)到的。
圖象座標(biāo)系與單反座標(biāo)系之間的轉(zhuǎn)換為:
其中f為焦距,為何如此轉(zhuǎn)換是按照相像三角形定律得到的,如右圖所示:
最后則是單反座標(biāo)系與世界座標(biāo)系的轉(zhuǎn)換關(guān)系:
其中R為3x3的正交旋轉(zhuǎn)矩陣,t為三維平移向量,這幾個(gè)參數(shù)也被稱為單反的外參矩陣,也是可以領(lǐng)到的。
在通常的應(yīng)用中,我們只須要從象素座標(biāo)系轉(zhuǎn)換到單反座標(biāo)系就夠用了。
基本知識(shí)都打算完畢,接下來(lái)看怎樣在上獲取象素點(diǎn)的三維座標(biāo)。
在Swift按照象素點(diǎn)估算出它基于單反的三維座標(biāo)
在Swift中啟動(dòng)單反主要有以下三個(gè)步驟
// 1. 發(fā)現(xiàn) TruthDepth 相機(jī)
let videoDeviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInTrueDepthCamera], mediaType: .video, position: .front)
// 2. 初始化輸入和輸出
let videoDeviceInput = try AVCaptureDeviceInput(device: videoDeviceDiscoverySession.devices.first!)
// 3. 給 session 添加輸出
let depthDataOutput = AVCaptureDepthDataOutput() session.addOutput(depthDataOutput) depthDataOutput.setDelegate(self, callbackQueue: dataOutputQueue)
由于我們?cè)O(shè)置了,所以單反只要捕捉到一幀深度圖都會(huì)反彈下邊這個(gè)函數(shù)
func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) {
...
// 獲得相機(jī)內(nèi)參數(shù)和對(duì)應(yīng)的分辨率
let intrinsicMartix = syncedDepthData.depthData.cameraCalibrationData?.intrinsicMatrix
let refenceDimension = syncedDepthData.depthData.cameraCalibrationData?.intrinsicMatrixReferenceDimensions
self.camFx = intrinsicMartix![0][0]
self.camFy = intrinsicMartix![1][1]
self.camOx = intrinsicMartix![0][2]
self.camOy = intrinsicMartix![1][2]
self.refWidth = Float(refenceDimension!.width)
self.refHeight = Float(refenceDimension!.height)
...
}
在這個(gè)反彈函數(shù)里,我們可以獲得攝像頭的內(nèi)參數(shù),示例的程序中,只要觸摸預(yù)覽圖中某一個(gè)象素點(diǎn),程序會(huì)調(diào)用下邊代碼塊輸出該象素點(diǎn)在單反座標(biāo)系下的X,Y和Z的值。
override func touchesBegan(_ touches: Set, with event: UIEvent?) {
let touchPoint = (touches as NSSet).allObjects[0] as! UITouch
// 獲得像素坐標(biāo)系的坐標(biāo)
let coord = touchPoint.location(in: self.preview)
let viewContent = self.preview.bounds
let xRatio = Float(coord.x / viewContent.size.width)
let yRatio = Float(coord.y / viewContent.size.height)
// 獲得觸摸像素點(diǎn)的深度值 Z,單位為 cm
let realZ = getDepth(from: depthPixelBuffer!, atXRatio: xRatio, atYRatio: yRatio)
// 獲得對(duì)應(yīng)的 X 和 Y 值,計(jì)算公式其實(shí)就是兩個(gè)坐標(biāo)轉(zhuǎn)換矩陣之間相乘后的結(jié)果
// 像素 -> 圖像 -> 相機(jī)坐標(biāo)系
let realX = (xRatio * refWidth! - camOx!) * realZ / camFx!
let realY = (yRatio * refHeight! - camOy!) * realZ / camFy!
DispatchQueue.main.async {
self.touchCoord.text = String.localizedStringWithFormat("X = %.2f cm, Y = %.2f cm, Z = %.2f cm", realX, realY, realZ)
}
}
示例程序的療效圖如下:
image.png
這兒輸出的是黑色點(diǎn)對(duì)應(yīng)的X,Y,Z值,完整代碼戳這兒。
參考鏈接
[1]手臂辨識(shí)技術(shù)解析
[2]Quoar:WhatisthefloodinXfor?
[3]世界,單反,圖象,象素座標(biāo)系之間的關(guān)系
[4]Guide-Apple
[5]PhotoandVideoUsingDepth