12

给定两个图像缓冲区(假设它是一个大小为宽度 * 高度的整数数组,每个元素都有一个颜色值),我如何将一个由四边形定义的区域从一个图像缓冲区映射到另一个(始终为正方形)图像缓冲区?我被引导理解这被称为“投影变换”。

我也在寻找一种通用的(不是特定于语言或库的)方法,以便它可以合理地应用于任何语言,而不依赖于“为我完成所有工作的魔法函数 X”。

一个例子:我使用处理库 (processing.org) 用 Ja​​va 编写了一个简短的程序,该程序从相机捕获视频。在初始“校准”步骤中,捕获的视频直接输出到窗口中。然后用户点击四个点来定义将要转换的视频区域,然后在程序的后续操作期间映射到方形窗口。如果用户在摄像机输出中单击定义以某个角度可见的门角的四个点,则此转换将导致后续视频将转换后的门的图像映射到窗口的整个区域,尽管有点扭曲。

4

6 回答 6

8

使用线性代数比所有几何都容易得多!另外,您不需要使用正弦、余弦等,因此您可以将每个数字存储为有理分数,并在需要时获得准确的数值结果。

您想要的是从旧 (x,y) 坐标到新 (x',y') 坐标的映射。你可以用矩阵来做。您需要找到 2×4 投影矩阵 P 使得 P 乘以旧坐标等于新坐标。我们假设您将线映射到线(例如,不是直线到抛物线)。因为您有投影(平行线不保持平行)和平移(滑动),所以您也需要 (xy) 和 (1) 的因子。绘制为矩阵:

          [x  ]
[a b c d]*[y  ] = [x']
[e f g h] [x*y]   [y']
          [1  ]

你需要知道 a 到 h 所以求解这些方程:

a*x_0 + b*y_0 + c*x_0*y_0 + d = i_0
a*x_1 + b*y_1 + c*x_1*y_1 + d = i_1
a*x_2 + b*y_2 + c*x_2*y_2 + d = i_2
a*x_3 + b*y_3 + c*x_3*y_3 + d = i_3

e*x_0 + f*y_0 + g*x_0*y_0 + h = j_0
e*x_1 + f*y_1 + g*x_1*y_1 + h = j_1
e*x_2 + f*y_2 + g*x_2*y_2 + h = j_2
e*x_3 + f*y_3 + g*x_3*y_3 + h = j_3

同样,您可以使用线性代数:

[x_0 y_0 x_0*y_0 1]   [a e]   [i_0 j_0]
[x_1 y_1 x_1*y_1 1] * [b f] = [i_1 j_1]
[x_2 y_2 x_2*y_2 1]   [c g]   [i_2 j_2]
[x_3 y_3 x_3*y_3 1]   [d h]   [i_3 j_3]

为 x_n、y_n、i_n、j_n 插入你的角。(角落的效果最好,因为如果你从用户点击中挑选点,它们相距很远可以减少错误。)取 4x4 矩阵的倒数并将其乘以等式的右侧。该矩阵的转置是 P。您应该能够找到计算矩阵逆和在线乘法的函数。

您可能会遇到错误的地方:

  • 计算时,记得检查除以零。这表明您的矩阵不可逆。如果您尝试将一个 (x,y) 坐标映射到两个不同的点,则可能会发生这种情况。
  • 如果您编写自己的矩阵数学,请记住矩阵通常指定为行、列(垂直、水平),屏幕图形为 x,y(水平、垂直)。你第一次肯定会出错。
于 2010-03-31T09:29:36.447 回答
5

编辑

下面关于角度比不变性的假设是不正确的。相反,投影变换保留了交叉比率和发生率。那么解决方案是:

  1. 在由段 AD 和 CP 定义的线的交点处找到点 C'。
  2. 在由线段 AD 和 BP 定义的线的交点处找到点 B'。
  3. 确定B'DAC'的交叉比,即r = (BA' * DC') / (DA * B'C')。
  4. 构造投影线 F'HEG'。这些点的交叉比等于r,即r = (F'E * HG') / (HE * F'G')。
  5. F'F 和 G'G 将在投影点 Q 处相交,因此使交叉比率相等并知道正方形边的长度,您可以通过一些算术体操来确定 Q 的位置。

嗯....我会试一试这个。该解决方案依赖于在转换中保留角度比率的假设。请参阅图像以获取指导(抱歉图像质量差……真的太晚了)。该算法仅提供四边形中的点到正方形中的点的映射。您仍然需要实现处理映射到同一个正方形点的多个四边形点。

设 ABCD 为四边形,其中 A 为左上顶点,B 为右上顶点,C 为右下顶点,D 为左下顶点。对 (xA, yA) 表示顶点 A 的 x 和 y 坐标。我们将这个四边形中的点映射到边长等于 m 的正方形 EFGH。

替代文字

计算长度 AD、CD、AC、BD 和 BC:

AD = sqrt((xA-xD)^2 + (yA-yD)^2)
CD = sqrt((xC-xD)^2 + (yC-yD)^2)
AC = sqrt((xA-xC)^2 + (yA-yC)^2)
BD = sqrt((xB-xD)^2 + (yB-yD)^2)
BC = sqrt((xB-xC)^2 + (yB-yC)^2)

令 thetaD 为顶点 D 处的角度,thetaC 为顶点 C 处的角度。使用余弦定律计算这些角度:

thetaD = arccos((AD^2 + CD^2 - AC^2) / (2*AD*CD))
thetaC = arccos((BC^2 + CD^2 - BD^2) / (2*BC*CD))

我们将四边形中的每个点 P 映射到正方形中的点 Q。对于四边形中的每个点 P,请执行以下操作:

  • 求距离 DP:

    DP = sqrt((xP-xD)^2 + (yP-yD)^2)
    
  • 求距离 CP:

    CP = sqrt((xP-xC)^2 + (yP-yC)^2)
    
  • 求 CD 和 DP 之间的角度 thetaP1:

    thetaP1 = arccos((DP^2 + CD^2 - CP^2) / (2*DP*CD))
    
  • 求 CD 和 CP 之间的角度 thetaP2:

    thetaP2 = arccos((CP^2 + CD^2 - DP^2) / (2*CP*CD))
    
  • thetaP1 与 thetaD 的比值应该是 thetaQ1 与 90 的比值。因此,计算 thetaQ1:

    thetaQ1 = thetaP1 * 90 / thetaD
    
  • 同样,计算 thetaQ2:

    thetaQ2 = thetaP2 * 90 / thetaC
    
  • 求距离总部:

    HQ = m * sin(thetaQ2) / sin(180-thetaQ1-thetaQ2)
    
  • 最后,Q 相对于 EFGH 左下角的 x 和 y 位置为:

    x = HQ * cos(thetaQ1)
    y = HQ * sin(thetaQ1)
    

您必须跟踪有多少颜色值映射到正方形中的每个点,以便您可以计算每个点的平均颜色。

于 2008-10-04T10:59:46.327 回答
4

我认为你所追求的是平面单应性,看看这些讲义:

http://www.cs.utoronto.ca/~strider/vis-notes/tutHomography04.pdf

如果你向下滚动到最后,你会看到一个你所描述的例子。我希望英特尔 OpenCV 库中有一个函数可以做到这一点。

于 2008-10-04T10:02:45.210 回答
2

如果此转换必须看起来不错(与在 Paint 中调整位图大小相反),您不能只创建一个将目标像素映射到源像素的公式。目标缓冲区中的值必须基于附近源像素的复杂平均,否则结果将高度像素化。

因此,除非您想进行一些复杂的编码,否则请使用其他人的魔法函数,正如 smacl 和 Ian 所建议的那样。

于 2008-10-04T16:28:46.333 回答
2

CodeProject 上有一个C++ 项目,其中包括位图投影变换的源代码。数学在 Wikipedia上请注意,据我所知,投影变换不会将任意四边形映射到另一个四边形,但会为三角形映射,您可能还想查找倾斜变换。

于 2008-10-04T08:35:54.433 回答
0

原则上是这样的:

  • 通过平移向量将 A 的原点映射到 B 的原点t
  • 取 A (1,0) 和 (0,1) 的单位向量并计算它们如何映射到 B 的单位向量上。
  • 这给了你一个变换矩阵M以便aA 中的每个向量都映射到M a +t
  • 反转矩阵并否定平移向量,因此对于bB 中的每个向量,您都有逆映射b-> M -1 ( b- t)
  • 一旦你有了这个转换,对于B中目标区域的每个点,在A中找到对应的并复制。

这种映射的优点是你只计算你需要的点,即你在目标点上循环,而不是点。这是几年前“演示编码”场景中广泛使用的技术。

于 2008-10-04T10:45:07.417 回答