如何计算透视下矩形的宽高比?求最优方案与可用库实现
求解透视照片中矩形的真实宽高比问题
问题描述
我们拥有一张未裁剪的矩形物体透视照片,矩形真实尺寸及拍摄参数均未知。目标是仅通过该照片计算矩形的真实宽高比(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
相关产品推荐
相关产品推荐

