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

获取度量测量用图像单应性及解决矫正图像过小问题

问题描述

我有一张带标记线的便利贴图像,想计算标记线在现实世界中的水平位置(单位:cm)。图像尺寸为3456×3456像素,便利贴实际尺寸是9×9cm。我手动选取了便利贴四个角的像素坐标[[1769, 353], [3199, 835], [2683, 2145], [983,1409]],并尝试用OpenCV的getPerspectiveTransform函数估计单应性矩阵,代码如下:

import cv2 as cv
import numpy as np

src_bbox = [[0,0], [9, 0], [9, 9], [0, 9]] # [cm]
dst_bbox = [[1769, 353], [3199, 835], [2683, 2145], [983,1409]] # [px]

H = cv.getPerspectiveTransform(np.array(src_bbox).astype(np.float32),
                               np.array(dst_bbox).astype(np.float32))

H_inv = np.linalg.inv(H)

image = cv.imread("sheet.jpg")
if image is None:
    exit("Could not read the image.")

mod = cv.warpPerspective(image, H_inv, image.shape[:2])

cv.namedWindow("sheet", cv.WINDOW_NORMAL)
cv.resizeWindow("sheet", 400, 400)

cv.namedWindow("sheet_warped", cv.WINDOW_NORMAL)
cv.resizeWindow("sheet_warped", 400, 400)

cv.imshow("sheet", image)
cv.imshow("sheet_warped", mod)
cv.waitKey(0)
cv.destroyAllWindows()

运行后得到的矫正图像过小,没法识别标记线来获取其厘米单位的位置。请问这是不是因为对应点用了不同单位(像素vs厘米)、量级差异导致的缩放问题?该怎么选合适的缩放因子?

解决方案
  • 问题根源:确实是单位量级差异导致的缩放问题。你用厘米(最大数值9)作为源坐标,像素(最大数值超过3000)作为目标坐标,单应性矩阵会把厘米空间的小尺寸映射到像素空间,逆变换时就会把像素空间的内容压缩到极小的厘米空间范围内,所以矫正后的图像才会过小。

  • 调整思路:统一坐标单位的量级,要么把现实世界的厘米坐标按比例放大到像素量级,要么把像素坐标缩小到厘米量级,核心是让源和目标坐标的尺度匹配。

  • 具体实现:

    1. 计算缩放因子:先算便利贴在图像中的像素宽度/高度,比如取两个对角的像素距离,或者直接用图像尺寸和实际尺寸的比例。这里便利贴实际9cm,对应图像中我们可以先算出它的像素尺寸,比如从[1769,353]到[3199,835]的水平像素距离是3199-1769=1430像素,对应9cm,所以缩放因子为1430/9 ≈ 158.89像素/cm(也可以用图像整体尺寸3456像素对应9cm,得到3456/9=384像素/cm,两种都可行,前者更贴合实际检测到的便利贴尺寸)。
    2. 修改源坐标:把现实世界的厘米坐标乘以缩放因子,转换成像素量级的虚拟坐标。比如src_bbox改成[[0,0], [9*scale, 0], [9*scale, 9*scale], [0, 9*scale]],这样源和目标坐标的尺度就一致了。
    3. 调整逆变换后的输出尺寸:warpPerspective的第三个参数是输出图像的尺寸,应该设为我们期望的矫正后尺寸,比如(int(9*scale), int(9*scale)),这样矫正后的图像大小合适,方便识别标记线。
  • 修改后的代码示例:

import cv2 as cv
import numpy as np

# 实际尺寸转像素量级,用便利贴实际宽度对应的像素距离计算缩放因子
# 取右上角和左上角的水平像素差:3199-1769=1430像素对应9cm
scale = (3199 - 1769) / 9  # 约158.89像素/cm
# 也可以用图像整体尺寸:scale = 3456 / 9 = 384像素/cm,按需选择

src_bbox = [[0,0], [9*scale, 0], [9*scale, 9*scale], [0, 9*scale]] # 像素量级的虚拟坐标
dst_bbox = [[1769, 353], [3199, 835], [2683, 2145], [983,1409]] # 实际像素坐标

H = cv.getPerspectiveTransform(np.array(src_bbox).astype(np.float32),
                               np.array(dst_bbox).astype(np.float32))

H_inv = np.linalg.inv(H)

image = cv.imread("sheet.jpg")
if image is None:
    exit("Could not read the image.")

# 设置输出尺寸为矫正后的便利贴像素尺寸
output_size = (int(9*scale), int(9*scale))
mod = cv.warpPerspective(image, H_inv, output_size)

cv.namedWindow("sheet", cv.WINDOW_NORMAL)
cv.resizeWindow("sheet", 400, 400)

cv.namedWindow("sheet_warped", cv.WINDOW_NORMAL)
cv.resizeWindow("sheet_warped", 400, 400)

cv.imshow("sheet", image)
cv.imshow("sheet_warped", mod)
cv.waitKey(0)
cv.destroyAllWindows()
  • 获取标记线的实际位置:矫正后,图像中每个像素对应1/scale厘米。比如标记线的水平像素坐标是x_px,那么实际水平位置就是x_px / scale cm。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 04:37:02