2011-03-02 144 views
7

我有一個圖像的高度圖,它告訴我在Z方向上每個像素的偏移量。我的目標是隻使用它的高度圖平整扭曲的圖像。使用高度圖扭曲圖像?

我該怎麼做呢?如果有幫助,我知道相機的位置。


要做到這一點,我在想假設每個像素是在一個平面上的點,然後翻譯垂直根據Z值我從高度圖得到的每個點,並從那翻譯(想象你正在看從上面的點;轉變將導致點從您的角度移動)。

從這個預測的轉變中,我可以提取每個像素的X和Y偏移,我可以將它提供給cv.Remap()

但是我不知道如何通過OpenCV獲得一個點的投影3D偏移量,更不用說構建一個偏移量圖。


這裏是我的參考圖像的我在做什麼:

Calibration Image Warped Image

我知道激光器(45度)的角度,以及從校正圖像,我可以真的很容易計算書的高度:

h(x) = sin(theta) * abs(calibration(x) - actual(x)) 

我這樣做的兩條線和線性內插兩林使用這種方法生成曲面(Python代碼。這是一個循環內):

height_grid[x][y] = heights_top[x] * (cv.GetSize(image)[1] - y) + heights_bottom[x] * y 

我希望這有助於;)


眼下,這就是我要dewarp圖像。中間項目所有的怪東西3D座標到相機平面,因爲它的位置(與相機的位置,旋轉等):這是變成圖像和代碼的龐大線程現在

class Point: 
    def __init__(self, x = 0, y = 0, z = 0): 
    self.x = x 
    self.y = y 
    self.z = z 

mapX = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1) 
mapY = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1) 

c = Point(CAMERA_POSITION[0], CAMERA_POSITION[1], CAMERA_POSITION[2]) 
theta = Point(CAMERA_ROTATION[0], CAMERA_ROTATION[1], CAMERA_ROTATION[2]) 
d = Point() 
e = Point(0, 0, CAMERA_POSITION[2] + SENSOR_OFFSET) 

costx = cos(theta.x) 
costy = cos(theta.y) 
costz = cos(theta.z) 

sintx = sin(theta.x) 
sinty = sin(theta.y) 
sintz = sin(theta.z) 


for x in xrange(cv.GetSize(image)[0]): 
    for y in xrange(cv.GetSize(image)[1]): 

    a = Point(x, y, heights_top[x/2] * (cv.GetSize(image)[1] - y) + heights_bottom[x/2] * y) 
    b = Point() 

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z) 
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 

    mapX[y, x] = x + (d.x - e.x) * (e.z/d.z) 
    mapY[y, x] = y + (d.y - e.y) * (e.z/d.z) 


print 
print 'Remapping original image using map...' 

remapped = cv.CreateImage(cv.GetSize(image), 8, 3) 
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR) 

...無論如何,這個代碼塊需要我7分鐘才能在18MP相機圖像上運行;這是方式太長了,最後,這種方法對圖像沒有任何影響(每個像素的偏移量爲<< 1)。

任何想法?

回答

3

我最終實現自己的解決方案:

for x in xrange(cv.GetSize(image)[0]): 
    for y in xrange(cv.GetSize(image)[1]): 

    a = Point(x, y, heights_top[x/2] * (cv.GetSize(image)[1] - y) + heights_bottom[x/2] * y) 
    b = Point() 

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z) 
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x)) 

    mapX[y, x] = x + 100.0 * (d.x - e.x) * (e.z/d.z) 
    mapY[y, x] = y + 100.0 * (d.y - e.y) * (e.z/d.z) 


print 
print 'Remapping original image using map...' 

remapped = cv.CreateImage(cv.GetSize(image), 8, 3) 
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR) 

這(慢)重新映射使用cv.Remap功能的每個像素,這似乎樣的工作?

0

基於距相機距離的失真僅在透視投影時發生。如果具有像素的(x,y,z)位置,則可以使用攝像機的投影矩陣將像素重新投影到世界空間中。利用這些信息,您可以以正交方式呈現像素。但是,由於原始透視投影,您可能會丟失數據。

+0

能OpenCV的地圖3D到2D ?或者我必須爲此提出自己的公式?不過,我會嘗試實施這個,所以謝謝! – Blender

0

獨立場景列如下:

  • 你有一個未知的位圖圖像(X,Y) - >(R,G,B)
  • 你有一個已知的高度字段ħ(X,Y) - >ħ
  • 你有一個照相機變換ç(X,Y,Z) - >(U,v),其突出的場景到屏幕平面

請注意,相機變換會丟棄信息(您無法獲得每個屏幕像素的深度值)。你也可以在屏幕上重疊一些場景,在這種情況下只顯示最前面的部分 - 其餘部分將被丟棄。所以一般來說這不是完全可逆的。

  • 你有一個屏幕截圖小號(U,V),這是Ç結果(X,Y,ħ(X,Y))爲X,Y在
  • 要生成的屏幕截圖S '(U',v'),這是ç(X,Y,0)爲X,Y在
結果

有兩種明顯的方法可以解決這個問題。兩者都依賴於具有相機變換的準確值。

  1. 光線投射:用於小號每個像素,抹上射線回到場景中。找出它到達高度場的地方;這給你在原始圖像你(x,y),屏幕像素給你在那一點的顏色。一旦您擁有儘可能多的I您可以恢復,重新轉換它以找到S'

  2. 雙渲染:每個X,Y在,項目找到(U,V)和(U 'V')。以S(u,v)的像素顏色並將其複製到S'(u',v')。

這兩種方法都會有抽樣問題,這些問題可以通過超級採樣或插值來獲得;方法1將在圖像的遮擋區域留下空白空間,方法2將從第一個表面「投影」。

編輯:

我曾推測你指CG式灰階高度,其中S中的每個像素的正上方在S'的相應位置;但這不是頁面如何在表面上打印。頁面固定在脊柱上,無彈力 - 擡起頁面的中心會將自由邊緣拉向脊柱。

根據您的示例圖片,您必須撤銷此累計拉動 - 檢測脊椎中線位置和方向,並逐步向左和向右移動,查找每個垂直條帶頁面頂部和底部的高度變化,計算得到的方面縮小和傾斜,並將其反轉以重新創建原始平面頁面。

+0

我編輯了我的答案。我也會包含參考圖片,以便您能看到我的意思。 – Blender

+0

是的,示例圖像是一個很好的幫助。有幾個想法:首先,通過使用長焦鏡頭和儘可能早的拍攝,您可以使圖像幾乎垂直。其次,這些頁面與它們垂直偏斜地放置 - 將底部邊緣對準平面表面可以減少或消除這種情況。然後,圖像校正結果只是通過頁面入射角的反餘弦進行寬度校正(即非常簡單)。 –

+0

我被卡住了只有一個3倍變焦鏡頭,所以我不得不忍受手動糾正切向和徑向翹曲。你能詳細說一下'arccos()'方法嗎?我不太明白。 – Blender