【问题标题】:How do you triangulate the location of a pixel in an image using it's location in other images?如何使用其他图像中的位置对图像中像素的位置进行三角测量?
【发布时间】:2017-05-17 00:46:59
【问题描述】:

我有四个来自略有不同位置的场景的航拍图像。每个图像都有一个在每个图像中可见的相同十字。我知道 3 张图像中十字的确切位置(像素 x 和 y)以及所有图像对之间的单应性和基本矩阵。

是否可以使用此信息精确三角测量第四张图像中十字架的位置?不幸的是,如果我只是将十字架的位置从任何图像映射到带有单应性的第四个图像,就会出现一些错误。它在附近但不准确,我希望有一种方法可以限制搜索,也许使用极线?

【问题讨论】:

  • 你知道其中一个图像和第四个图像之间的单应性吗?如果是,则使用它通过将交叉点与单应性相乘来将交叉点从一张图像转换为第四张图像。有这样的假设,即两个相机作为相同的内在相机参数。
  • 不幸的是,就像我说的,如果我将点与单应性映射,则会出现错误。我希望利用我知道三张图像中的确切位置这一事实。单应性并不完全同意这一点,但有没有办法更新它们......

标签: python opencv computer-vision


【解决方案1】:

假设img0 包含您要定位的目标,而img1img2img3 的目标位置分别为(x1, y1)(x2, y2)(x3, y3)。如果您知道所有图像对之间的单应性(例如,H1H2H3 是从 img1img2img3img0 的单应性),那么您可以只需将这些像素位置乘以单应性即可获得img0 中的估计坐标。您不能精确地进行三角测量,但是您可以从所有三个图像和单应性中找到估计的变形点,然后取平均值,或者根据您的喜好组合它们,这应该会给您一个很好的估计,只要您的单应性是准确的够了。

单应矩阵是 3x3 矩阵,您的点是 2 向量。为了应用您的变​​换(或将矩阵和向量相乘),您需要齐次坐标;您的积分格式为(x, y, 1)。然后,要从img1 中获取像素在img0 中的位置,乘法如下:

[s*x0]        [x1]   [h00 h01 h02]   [x1]
[s*y0] = H1 * [y1] = [h10 h11 h12] * [y1]
[s   ]        [ 1]   [h20 h21 h22]   [ 1]

您的输出将是一个向量,但不是同质的。结果点将有一个比例因子s;除以s 以获得您的最终坐标(x0, y0)。只需对您拥有的三个目标位置和那些相应的单应性执行此操作,您最终将得到三个估计位置,然后您可以对它们进行平均。


完整示例。

这是使用带有地面实况数据的一些图像,可用here

Here's my results.

通过了解其他三个中的位置以及数据集中给出的地面实况单应性,在第一张图像中估计出鸡眼中间的十字位置。我输出了每个单应性的估计值、它们的平均值和四舍五入的像素值,结果证明四舍五入的估计像素值是准确的(因为单应性非常准确)。

Estimations:
2 -> 1:    [ 527.15670903  222.57196904] 
3 -> 1:    [ 527.21339222  221.86819147] 
4 -> 1:    [ 527.63122722  222.30614892] 
Avg loc:   [ 527.33377616  222.24876981] 
Est loc:   [527 222] 
True loc:  [527 222]

这是一个完整的编码示例,只需从该数据集中下载图像并将其弹出到该文件夹​​中的脚本中并运行。

import numpy as np
import cv2

# read images, taken from http://kahlan.eps.surrey.ac.uk/featurespace/web/
img1 = cv2.imread("img1.png", 1)
img2 = cv2.imread("img2.png", 1)
img3 = cv2.imread("img3.png", 1)
img4 = cv2.imread("img4.png", 1)

# true locations of the chicken's crossed eye (labeled myself)
loc1 = np.array([527, 222, 1])
loc2 = np.array([449, 241, 1])
loc3 = np.array([476, 275, 1])
loc4 = np.array([385, 236, 1])

# define ground truth homographies, also from http://kahlan.eps.surrey.ac.uk/featurespace/web/
H12 = np.array([
    [8.7976964e-01,   3.1245438e-01,  -3.9430589e+01],
    [-1.8389418e-01,   9.3847198e-01,   1.5315784e+02],
    [1.9641425e-04,  -1.6015275e-05,   1.0000000e+00]])

H13 = np.array([
   [7.6285898e-01,  -2.9922929e-01,   2.2567123e+02],
   [3.3443473e-01,   1.0143901e+00,  -7.6999973e+01],
   [3.4663091e-04,  -1.4364524e-05,   1.0000000e+00]])

H14 = np.array([
   [6.6378505e-01,   6.8003334e-01,  -3.1230335e+01],
   [-1.4495500e-01,   9.7128304e-01,   1.4877420e+02],
   [4.2518504e-04,  -1.3930359e-05,   1.0000000e+00]])

# need the homographies going the other direction
H21 = np.linalg.inv(H12)
H31 = np.linalg.inv(H13)
H41 = np.linalg.inv(H14)

# ensure they are homogeneous by dividing by the last entry
H21 = H21/H21[-1,-1]
H31 = H31/H31[-1,-1]
H41 = H41/H41[-1,-1]

# warp the locations loc2, loc3, loc4 to the coordinates of img1
est21 = np.matmul(H21, loc2)
est31 = np.matmul(H31, loc3)
est41 = np.matmul(H41, loc4)

# make homogeneous, toss the final 1
est21 = est21[:-1]/est21[-1]
est31 = est31[:-1]/est31[-1]
est41 = est41[:-1]/est41[-1]

# remove the last coordinate, take an average
avgest = (est21 + est31 + est41)/3
estloc = np.around(avgest).astype(int)

# output
print("Estimations:"
    "\n2 -> 1:   ", est21,
    "\n3 -> 1:   ", est31,
    "\n4 -> 1:   ", est41,
    "\nAvg loc:  ", avgest, 
    "\nEst loc:  ", estloc,
    "\nTrue loc: ", loc1[:-1])

# show images
cv2.circle(img1, (estloc[0], estloc[1]), 2, (0,0,255), -1) # filled
cv2.circle(img1, (estloc[0], estloc[1]), 20, (255,255,255)) # outline
cv2.imshow('img1-est', img1)
cv2.waitKey(0)

cv2.circle(img2, (loc2[0], loc2[1]), 2, (0,0,255), -1) # filled
cv2.circle(img2, (loc2[0], loc2[1]), 20, (255,255,255)) # outline
cv2.imshow('img2-loc', img2)
cv2.waitKey(0)

cv2.circle(img3, (loc3[0], loc3[1]), 2, (0,0,255), -1) # filled
cv2.circle(img3, (loc3[0], loc3[1]), 20, (255,255,255)) # outline
cv2.imshow('img3-log', img3)
cv2.waitKey(0)

cv2.circle(img4, (loc4[0], loc4[1]), 2, (0,0,255), -1) # filled
cv2.circle(img4, (loc4[0], loc4[1]), 20, (255,255,255)) # outline
cv2.imshow('img4-log', img4)
cv2.waitKey(0)

cv2.destroyAllWindows()

【讨论】:

  • 不幸的是,就像我说的,如果我将点与单应性映射,则会出现错误。我希望利用我知道三张图像中的确切位置这一事实。单应性并不完全同意这一点,但有没有办法更新它们......
  • 在单应性估计中,有很多需要纠正的错误,但如果你像你展示的那样偏离了相当大的数量,你的主要问题是你的单应性估计。您是否有基本事实单应性或正在计算它们?如果是后者,怎么做?这里的主要问题是单应性直接告诉你像素已经在哪里,不需要三角测量。所以如果你关闭了,你的单应性就关闭了。您可以围绕估计的目标位置定义 ROI,并为该区域创建更好的单应性。或者你甚至可以使用模板匹配。
  • 感谢您的努力,我很感激。这里的问题是你有基本事实单应性。我正在使用 SIFT 和 RANSAC 自己计算单应性,并且可能会引入错误,因为场景不是完全平面的。在这种情况下,使用基本矩阵而不是使用单应矩阵会更有意义吗?
  • 在数学上,基本矩阵和单应性可以直接平等地相互映射,所以我看不出一个更容易出错的地方。我可能会建议在这里通过 Lukas-Kanade 使用 direct 方法,而不是基于特征的方法,因为场景非常缺乏兴趣点。特别是由于目标在平面上,我认为围绕img1中的估计位置以及img2img3img4的真实位置周围进行ROI,并估计新的单应性从那里开始工作,特别是因为你已经有了一个近似的单应性开始。
  • @nickponline 我有一个自定义 LKIC 例程,我不久前编写了程序,如果你能提供你正在使用的图像,我很想在你的场景中尝试这种方法。有一篇关于单应性纠错的好帖子here TBH 我不确定极线是否可以帮助您;不过可能。我找到了一个paper,它使用特征的形状约束来提供更好的结果,也许这会对你有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-16
  • 1970-01-01
  • 2016-04-02
  • 2022-12-11
  • 2017-03-18
  • 2018-07-07
  • 1970-01-01
相关资源
最近更新 更多