9

我有一张图像的高度图,它告诉我每个像素在 Z 方向上的偏移量。我的目标是仅使用它的高度图来展平扭曲的图像。

我该怎么做呢?我知道相机的位置,如果有帮助的话。


为此,我正在考虑假设每个像素是平面上的一个点,然后根据我从高度图和从该平移中获得的 Z 值垂直平移每个点(假设您正在寻找在上面的点上;移动会导致点从你的角度移动)。

从那个预计的偏移中,我可以提取每个像素的 X 和 Y 偏移,我可以将其输入到cv.Remap().

但我不知道如何使用 OpenCV 获得一个点的投影 3D 偏移,更不用说从中构建偏移图了。


这是我正在做的参考图片:

校准图像 扭曲的图像

我知道激光的角度(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

我希望这有帮助 ;)


现在,这就是我必须对图像进行扭曲。考虑到它的位置(以及相机的位置、旋转等),中间的所有奇怪的东西都会将 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

有任何想法吗?

4

3 回答 3

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,这似乎有点工作......

于 2011-03-07T21:11:15.307 回答
0

基于与相机距离的失真仅发生在透视投影中。如果您有像素的 (x,y,z) 位置,则可以使用相机的投影矩阵将像素取消投影回世界空间。使用该信息,您可以以正交方式渲染像素。但是,由于原始透视投影,您可能会丢失数据。

于 2011-03-02T19:59:47.310 回答
0

将您的场景分开如下:

  • 你有一个未知的位图图像I (x,y) -> (r,g,b)
  • 你有一个已知的高度场H (x,y) -> h
  • 你有一个相机变换C (x,y,z) -> (u,v) 将场景投影到屏幕平面

请注意,相机变换会丢弃信息(您不会获得每个屏幕像素的深度值)。您可能还会在屏幕上出现一些场景重叠,在这种情况下,只会显示最重要的部分 - 其余部分会被丢弃。所以总的来说,这不是完全可逆的。

  • 你有一个截图S (u,v) 这是C (x,y, H (x,y)) for x,y in I的结果
  • 你想生成一个屏幕截图S' (u',v') 这是C (x,y,0) for x,y in I的结果

有两种明显的方法可以解决这个问题;两者都依赖于相机变换的准确值。

  1. 光线投射:对于S中的每个像素,将光线投射回场景中。找出它击中高度场的位置;这会在原始图像I中为您提供 (x,y) ,并且屏幕像素会为您提供该点的颜色。一旦你有尽可能多的I可以恢复,重新转换它以找到S'

  2. 双重渲染:对于I中的每个 x,y ,投影以查找 (u,v) 和 (u',v')。从S (u,v)中获取像素颜色并将其复制到S' (u',v')。

两种方法都会有采样问题,这可以通过超级采样或插值来解决;方法 1 将在图像的遮挡区域留下空白空间,方法 2 将从第一个表面“投影”。

编辑:

我假设您的意思是 CG 风格的高度场,其中 S 中的每个像素都直接位于 S' 中相应位置的上方;但这不是页面覆盖在表面上的方式。页面固定在书脊并且没有弹性 - 抬起页面的中心会将自由边缘拉向书脊。

根据您的示例图像,您必须反转这种累积拉动 - 检测书脊中心线的位置和方向,并逐渐左右工作,找到每个垂直页面顶部和底部的高度变化,计算结果方面缩小和倾斜,并将其反转以重新创建原始平面页面。

于 2011-03-02T21:15:14.153 回答