You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算透视下矩形的宽高比?求最优方案与可用库实现

求解透视照片中矩形的真实宽高比问题

问题描述

我们拥有一张未裁剪的矩形物体透视照片,矩形真实尺寸及拍摄参数均未知。目标是仅通过该照片计算矩形的真实宽高比(aspect ratio),计划使用Python结合NumPy实现,针对A4纸的测试示例应得出1.4142 : 1的比例。

核心疑问

  • 哪种方案最优且能处理边缘情况?是否有未提及的其他可行方案?
  • OpenCV、SciPy等维护中的库是否有现成实现(支持C++/Python等语言)?

调研现状

已调研相关学术论文、博客及StackOverflow问题,相关答案存在差异且不完整。目前已基于资料实现两种Python+OpenCV+NumPy的方法:

Method #1

def compute_aspect_ratio(image, corners):
    # 参考:透视矩形宽高比计算方法

    # 步骤1:获取图像中心作为原点
    h, w = image.shape[:2]
    origin = (w * .5, h * .5)
    
    # 步骤2:计算各点相对于图像原点的坐标
    # 注意:点需按锯齿顺序排列(A, B, D, C)
    a = corners[0] - origin
    b = corners[1] - origin
    c = corners[3] - origin
    d = corners[2] - origin
    
    # 步骤3:检查相机是否与矩形共面
    # 若三点共线则共面,此时无法计算宽高比
    M = numpy.array([[b[0], c[0], d[0]], [b[1], c[1], d[1]], [1., 1., 1.]])
    det = numpy.linalg.det(M)
    if math.isclose(det, 0., abs_tol=.001):
        # 无法计算宽高比,调用方需检查返回值是否为0
        return 0.

    # 步骤4:构建矩阵
    A = numpy.array([[1., 0., -b[0], 0., 0., 0.],
                     [0., 1., -b[1], 0., 0., 0.],
                     [0., 0.,    0., 1., 0., -c[0]],
                     [0., 0.,    0., 0., 1., -c[1]],
                     [1., 0., -d[0], 1., 0., -d[0]],
                     [0., 1., -d[1], 0., 1., -d[1]]], dtype=float)
    B = numpy.array([[b[0]-a[0]],
                     [b[1]-a[1]],
                     [c[0]-a[0]],
                     [c[1]-a[1]],
                     [d[0]-a[0]],
                     [d[1]-a[1]]], dtype=float)

    # 步骤5:求解线性方程组,得到 [Ux, Uy, (Uz / λ), Vx, Vy, (Vz / λ)]
    s = numpy.linalg.solve(A, B)

    # 步骤6:计算λ(焦距)
    l = 0.
    l_sq = ((-(s[0] * s[3]) - (s[1] * s[4])) / (s[2] * s[5]))
    if l_sq > 0.:
        l = numpy.sqrt(l_sq)
    # 若l_sq <= 0,说明矩形的两条对边在图像中平行,无法计算λ,保持l=0

    # 步骤7:计算U和V向量的模长
    u = numpy.linalg.norm([s[0], s[1], (s[2] * l)])
    v = numpy.linalg.norm([s[3], s[4], (s[5] * l)])

    return (v / u)

Method #2

def compute_aspect_ratio(image, corners):
    # 参考:微软研究院相关论文

    # 步骤1:计算图像中心(即原点)
    h, w = image.shape[:2]
    origin = (w * .5, h * .5)

    # 步骤2:转换为齐次坐标
    # 注意:点需按锯齿顺序排列(A, B, D, C)
    p1 = numpy.array([*corners[0], 1.])
    p2 = numpy.array([*corners[1], 1.])
    p3 = numpy.array([*corners[3], 1.])
    p4 = numpy.array([*corners[2], 1.])

    k2 = numpy.dot(numpy.cross(p1, p4), p3) / numpy.dot(numpy.cross(p2, p4), p3)
    k3 = numpy.dot(numpy.cross(p1, p4), p2) / numpy.dot(numpy.cross(p3, p4), p2)

    # 步骤3:计算U和V向量,此时向量的z分量形式为 z/f(f为焦距)
    u = (k2 * p2) - p1
    v = (k3 * p3) - p1

    # 步骤4:拆分向量分量以简化访问
    uX, uY, uZ = u
    vX, vY, vZ = v

    # 步骤5:检查矩形的两条对边是否近似平行
    if math.isclose(uZ, .0, abs_tol=.01) or math.isclose(vZ, .0, abs_tol=.01):
        aspect_ratio = numpy.sqrt((vX ** 2 + vY ** 2) / (uX ** 2 + uY ** 2))
        return aspect_ratio

    # 步骤6:计算焦距
    f = numpy.sqrt(numpy.abs((1. / (uZ * vZ)) * ((uX * vX - (uX * vZ + uZ * vX) * origin[0] + uZ * vZ * origin[0] * origin[0]) + (uY * vY - (uY * vZ + uZ * vY) * origin[1] + uZ * vZ * origin[1] * origin[1]))))

    A = numpy.array([[f, 0., origin[0]], [0., f, origin[1]], [0., 0., 1.]]).astype('float32')
    Ati = numpy.linalg.inv(numpy.transpose(A))
    Ai = numpy.linalg.inv(A)

    # 步骤7:计算真实宽高比
    aspect_ratio = numpy.sqrt(numpy.dot(numpy.dot(numpy.dot(v, Ati), Ai), v) / numpy.dot(numpy.dot(numpy.dot(u, Ati), Ai), u))

    return aspect_ratio

内容的提问来源于stack exchange,提问作者Ben Souchet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 11:46:05