跳到正文
格致开物MATHWIKI

射影变换

内容维护:格致开物 · 编写与核验 · 修订记录 · 纠错

射影变换(projective transformation)是在射影空间中由可逆线性映射诱导的变换。对实射影平面,取可逆 3×3 矩阵 H,它把齐次点 [p] 送到 [Hp]。平面上的这种变换也称为单应变换(homography)。它保持点、线及相交关系,但一般不保持平行、长度、角度或中点。

一个具体例子是 H=(100010011),(X′Y′W′)=H(xy1)=(xyy+1). 只要 y≠−1,普通坐标中的关系就是 (x′,y′)=(x/(y+1),y/(y+1))。例如 (1,1) 被送到 (1/2,1/2),而 (1,0) 保持不动。同一幅图中不同位置的缩放比例不同,这正是它与一般仿射变换的区别之一。

可逆矩阵在齐次点上的作用

齐次坐标把非零向量的非零倍数视为同一点。若将代表 p 换为 λp,则 H(λp)=λHp,所以输出点不变。可逆性保证 Hp≠0,输出总是合法的齐次点;逆矩阵 H−1 则给出逆变换。这证明了该规则在整个射影平面上定义良好且为双射。[1]

矩阵 H 与 μH(μ≠0)表示同一射影变换。九个矩阵元素模掉一个共同尺度,使一般平面单应变换具有八个自由度。可以把某个非零元素归一化为一,但不能预先断定右下角 h33 非零。两次变换的复合按矩阵乘法表示:先 H1、后 H2,结果是 H2H1。

写 H=(hij),有限点的像在 W′≠0 时为 x′=h11x+h12y+h13h31x+h32y+h33,y′=h21x+h22y+h23h31x+h32y+h33. 分母为零表示像落在目标坐标片的无穷远直线上,并不表示射影变换本身在那里没有定义。若 h31=h32=0,可逆性迫使 h33≠0,归一化后便得到可逆仿射变换;一般情形的分母随位置变化。

直线按逆转置变换

设源直线的非零系数向量为 ℓ,其点满足 ℓ𝖳p=0。由于 p′=Hp,可以代入 p=H−1p′: 0=ℓ𝖳H−1p′=(H−𝖳ℓ)𝖳p′. 因此像直线的坐标可取 ℓ′=H−𝖳ℓ,H−𝖳:=(H−1)𝖳. 可逆性保证 ℓ′≠0。反过来,若一个目标点满足 ℓ'𝖳p′=0,其逆像必满足 ℓ𝖳H−1p′=0,故像恰好是整条直线,而不仅是它的一部分。这个双向推导证明了直线与点线关联的保持。[2]

不同点的像仍不同,否则用 H−1 会得到两个原点相同。于是两点的连线被送到像点的连线,两条不同线的交点被送到像线的交点。这个结论同时适用于有限交点与无穷远交点。直接把线系数乘以 H 一般是错的;点和线虽然都写成三元列向量,变换法则却不同。

在开篇算例中, H−1=(1000100−11),H−𝖳=(10001−1001). 例如直线 x=1 的系数为 ℓ=(1,0,−1)𝖳,因此 ℓ′=(1,1,−1)𝖳,像直线为 x′+y′=1。把像点 (1,0) 和 (1/2,1/2) 代入,均满足该式。

正方形、消失点与消失线

取单位正方形顶点 A=(0,0)、B=(1,0)、C=(1,1)、D=(0,1)。按上述矩阵逐点计算, A′=(0,0),B′=(1,0),C′=(1/2,1/2),D′=(0,1/2). 四条边仍在直线上,像围成一个梯形。正方形内部也映到该梯形内部:由 y′=y/(y+1) 得 y=y′/(1−y′);再由 x=x′/(1−y′),条件 0≤x,y≤1 恰好变为 0≤y′≤1/2、0≤x′≤1−y′。

虚线单位正方形与实线梯形叠画在同一坐标系中,C映为二分之一二分之一,D映为零二分之一,A和B不动
虚线表示原正方形,着色梯形表示像;A′=A、B′=B。图中C到C′的箭头只标示这一个点的对应。

源平面中的竖直平行线 x=a,齐次系数为 (1,0,−a)𝖳,像线为 x′+ay′−a=0. 所有这些线都经过 V=(0,1)。原来竖直方向的无穷远点 v∞=(0,1,0)𝖳 被送到 Hv∞=(0,1,1)𝖳,这就是该方向的消失点。在普通坐标中,也可固定 x=a 并令 y→+∞,得到 x′→0、y′→1。没有一个源有限点被送到这个消失点,因为它的逆像是无穷远点。

所有源无穷远点组成源无穷远线 ℓ∞=(0,0,1)𝖳。其像为 ℓv=H−𝖳ℓ∞=(0,−1,1)𝖳, 即目标平面的直线 y′=1。这是源仿射平面的消失线;当源平面表示地面时,通常称为地平线。它不必是图像中的水平线,这取决于坐标与成像姿态;本例恰好水平。某些方向的消失点也可能仍在目标无穷远处,例如 H(1,0,0)𝖳=(1,0,0)𝖳,所以水平平行线在本例中仍保持平行。[3]

源竖直线x等于零一二的像线,在目标平面交于V零一,V位于消失线y撇等于一上
三条像线分别为x′=0、x′+y′=1、x′+2y′=2。延长线交于V;金色虚线是整个源无穷远直线的像。

另有一条容易混淆的线:哪些源有限点被送到目标无穷远处由 W′=0 决定。它的系数是 H𝖳ℓ∞=(0,1,1)𝖳,即源直线 y=−1。例如 H(1,−1,1)𝖳=(1,−1,0)𝖳. 源中的 y=−1 与目标中的消失线 y′=1 属于不同平面、承担相反的像与逆像关系,不能混成同一条“分母为零的地平线”。

沿源直线x等于零观察,普通坐标函数u等于t除以t加一在t等于负一处不能除法,曲线分成两支并趋近水平线u等于一
这里t是源纵坐标y,u是像纵坐标y′。竖虚线t=−1对应像落在无穷远处;横虚线u=1是源无穷远点的像,两者不是同一个坐标值。

精确有理数实现

下面代码实现已知矩阵的点变换、线变换和逆变换。输入为整数或 Fraction,矩阵必须为可逆的 3×3 矩阵,点和线必须是非零三元组。它不修改输入;输出按“第一个非零分量归一化为一”选取代表,因此也适用于末坐标为零的点。affine 单独进行有限坐标的除法,像在无穷远处时明确拒绝这一步。

Python 3
from fractions import Fraction


def rational(value):
    if isinstance(value, bool) or not isinstance(value, (int, Fraction)):
        raise TypeError("use int or Fraction, not floating-point input")
    return Fraction(value)


def vector3(values):
    v = tuple(rational(x) for x in values)
    if len(v) != 3 or not any(v):
        raise ValueError("a nonzero homogeneous triple is required")
    return v


def canonical(values):
    v = vector3(values)
    scale = next(x for x in v if x)
    return tuple(x / scale for x in v)


def affine(values):
    x, y, w = vector3(values)
    if w == 0:
        raise ValueError("point is at infinity in the W=1 chart")
    return x / w, y / w


def cross(u, v):
    return (u[1]*v[2] - u[2]*v[1],
            u[2]*v[0] - u[0]*v[2],
            u[0]*v[1] - u[1]*v[0])


def matvec(matrix, vector):
    return tuple(sum((a*b for a, b in zip(row, vector)), Fraction(0))
                 for row in matrix)


class Homography:
    def __init__(self, matrix):
        rows = tuple(tuple(rational(x) for x in row) for row in matrix)
        if len(rows) != 3 or any(len(row) != 3 for row in rows):
            raise ValueError("matrix must be 3 by 3")
        self.H = rows
        self.C = (cross(rows[1], rows[2]), cross(rows[2], rows[0]),
                  cross(rows[0], rows[1]))
        self.det = sum((rows[0][j] * self.C[0][j] for j in range(3)),
                       Fraction(0))
        if self.det == 0:
            raise ValueError("singular matrix is not a projectivity")

    def point(self, values):
        return canonical(matvec(self.H, vector3(values)))

    def line(self, values):
        # C = det(H) * H^(-T); the common nonzero scale cancels.
        return canonical(matvec(self.C, vector3(values)))

    def inverse(self):
        # C^T = det(H) * H^(-1), equally valid projectively.
        return Homography(tuple(zip(*self.C)))


H = Homography(((1, 0, 0), (0, 1, 0), (0, 1, 1)))
q = H.point((1, 1, 1))
print(tuple(map(str, affine(q))))                # ('1/2', '1/2')
print(tuple(map(str, H.line((1, 0, -1)))))        # ('1', '1', '-1')
print(H.inverse().point(q) == canonical((1, 1, 1)))  # True
J = Homography(((1, 0, 0), (0, 0, 1), (0, 1, 0)))
print(tuple(map(str, affine(J.point((2, 3, 1))))))  # ('2/3', '1/3')

代码中的 C 是代数余子式矩阵,三行依次由另两行的叉积构成。记 H 的行为 ri、C 的行为 ci,则 ri⋅ci=det⁡H,因为三重积的循环排列不变;当 j≠i 时,rj 正是构成 ci 的一个叉积因子,故 rj⋅ci=0。逐项相乘便得到 HC𝖳=(det⁡H)I,于是 C𝖳=(det⁡H)H−1、C=(det⁡H)H−𝖳。共同的非零因子不改变齐次结果。矩阵非奇异保证两种乘法都不会把非零输入变为零;归一化只去掉一个公共尺度。这给出了点变换、线变换及逆变换的正确性理由,也说明代码无需固定 h33=1。

对固定的三维齐次表示,建立矩阵、变换一个点或一条线以及构造逆变换,各需要常数次有理数运算和常数个有理数存储。处理 N 个对象需要 O(N) 次这样的运算;若保留所有结果,输出占用 O(N) 个三元组。精确有理数的分子、分母可能很长,实际位运算成本还随其长度增长,不能把任意大整数运算视为固定时间。实现拒绝普通浮点数,包括 NaN 和无穷;用精确整数或分数可避免数值溢出和“接近零”判定的歧义,但不能消除输入观测误差。

边界、变式与矩阵估计的条件

代码最后的矩阵 J 有 j33=0,但 det⁡J=−1,所以它是合法射影变换。它把 [X:Y:W] 送到 [X:W:Y],因此 (2,3) 的像为 (2/3,1/3)。把它强行除以右下角来“归一化”会错误地丢掉这个变换。若将主例点改为 (2,−1),H 的像为 [2:−1:0];point 可以返回它,但 affine 会拒绝除以零。

可逆性也有实际作用。奇异矩阵 diag⁡(1,1,0) 把 [0:0:1] 送到零向量,其余点的像又都落在同一条无穷远直线上,不能定义整个射影平面的双射。代码因此在构造时就拒绝奇异矩阵。

射影变换虽然保持共线,一般不保持中点。主例中 A=(0,0)、D=(0,1) 的中点为 M=(0,1/2),其像是 M′=(0,1/3);像端点的普通中点却是 (0,1/4)。两者不同,所以不能在透视变换后直接沿用原来的仿射比例。

若 H 未知,点对应 pi↔pi′ 给出约束 pi′×Hpi=0,对每一对非零齐次点只有两条独立线性约束。直接线性变换法(DLT)把这些约束堆叠为 Ah=0,其中 h 排列矩阵九个元素。在无噪声情况下,唯一到尺度的候选要求 rank⁡A=8,且重排后的候选矩阵仍须可逆;至少需要四对点,四对点的常用非退化配置要求源、目标两边都无三点共线。仅有“四对”这个数量并不能保证可解。带噪声时常求最小奇异值对应的方向,但代数残差不等于几何重投影误差。本篇的精确代码只应用已知变换,没有实现或证明完整 DLT 估计流程。[4]

单应变换用于平面图像校正与拼接时,还需检查场景模型。理想透视相机拍摄同一平面、投影中心不在该平面上,或两幅图像具有相同投影中心,是产生单应关系的典型条件;相机平移且场景具有一般深度变化时,不能把全部点强行套进同一个平面单应模型。[5]

参考资料

  1. ↑ Tomas Pajdla. Elements of Geometry for Computer Vision,2017年2月27日版,§8.4,第73—75页,式(8.78)—(8.89)及满秩矩阵的比例表示。
  2. ↑ Pajdla,同上,§9.3,第96—97页,式(9.27)—(9.31);§9.3.1—9.3.3,第97—98页讨论连线与交点在单应下的行为。
  3. ↑ Pajdla,同上,§9.4—9.5,第98—99页,消失点、消失线及地面对应的 horizon;本篇矩阵和坐标为独立构造的算例。
  4. ↑ Carnegie Mellon University, 16-385 Computer Vision. Image homographies, Spring 2020, Lecture 9,PDF第39—42页:应用矩阵与八个自由度;第43—50、53—54页:点对应、齐次线性系统与SVD。另见 Pajdla,同上,§8.6.1,第76—78页,式(8.111)—(8.124)及秩八的非退化条件。
  5. ↑ Pajdla,同上,§8.4,第73页,列出相同投影中心、平面及平面两幅像三种情况与满秩条件。