4. 透视畸变校正(梯形校正)

任务说明

拍摄文档或屏幕时,因相机与目标平面不垂直,画面常呈现梯形畸变(透视畸变)。本案例实现:给定一张发生透视畸变的图像(以棋盘格模拟),通过检测棋盘格角点获得四个顶点坐标,计算透视变换矩阵,将图像校正为正面视角。校正后棋盘格应恢复为规则矩形网格,横竖线条平直且间距均匀。

实现方案

使用OpenCV的findChessboardCorners自动检测棋盘格内角点,取最外层四角作为源点;目标点设为固定尺寸矩形的四角。通过getPerspectiveTransform求单应矩阵,再用warpPerspective完成校正。代码包含完整的图像生成、畸变模拟、校正与结果保存流程,可直接运行验证。

代码实现


import cv2
import numpy as np

# ---------- 1. 生成标准棋盘格图像(9x6内角点) ----------
pattern_size = (9, 6)          # 内角点数 (宽, 高)
square_size = 50               # 每格边长(像素)
img_w, img_h = 640, 480

# 创建棋盘格(黑白交替)
board = np.zeros((pattern_size[1] * square_size, pattern_size[0] * square_size), dtype=np.uint8)
for i in range(pattern_size[1]):
    for j in range(pattern_size[0]):
        if (i + j) % 2 == 0:
            y1, y2 = i * square_size, (i + 1) * square_size
            x1, x2 = j * square_size, (j + 1) * square_size
            board[y1:y2, x1:x2] = 255

# 扩展为三通道并加白边
board_color = cv2.cvtColor(board, cv2.COLOR_GRAY2BGR)
canvas = np.full((img_h, img_w, 3), 255, dtype=np.uint8)
x_off, y_off = (img_w - board.shape[1]) // 2, (img_h - board.shape[0]) // 2
canvas[y_off:y_off + board.shape[0], x_off:x_off + board.shape[1]] = board_color
cv2.imwrite("chessboard_original.png", canvas)

# ---------- 2. 模拟透视畸变(梯形效果) ----------
src_pts = np.float32([[x_off, y_off],
                      [x_off + board.shape[1], y_off],
                      [x_off, y_off + board.shape[0]],
                      [x_off + board.shape[1], y_off + board.shape[0]]])

# 目标:将左上角下移、右上角上移,形成梯形
dst_pts = np.float32([[x_off + 60, y_off + 80],
                      [x_off + board.shape[1] - 60, y_off - 40],
                      [x_off, y_off + board.shape[0]],
                      [x_off + board.shape[1], y_off + board.shape[0]]])

M_distort = cv2.getPerspectiveTransform(src_pts, dst_pts)
distorted = cv2.warpPerspective(canvas, M_distort, (img_w, img_h))
cv2.imwrite("chessboard_distorted.png", distorted)

# ---------- 3. 自动检测棋盘格角点 ----------
found, corners = cv2.findChessboardCorners(distorted, pattern_size, None)
if not found:
    raise RuntimeError("未能检测到棋盘格角点,请调整畸变程度或图像质量")

# ---------- 4. 取最外层四角作为源点 ----------
# corners 形状: (54, 1, 2),按行优先排列:左上角为corners[0],右下角为corners[-1]
# 需手动确定四角:第一行第一个、第一行最后一个、最后一行第一个、最后一行最后一个
n_cols, n_rows = pattern_size
top_left     = corners[0][0]
top_right    = corners[n_cols - 1][0]
bottom_left  = corners[(n_rows - 1) * n_cols][0]
bottom_right = corners[-1][0]

src_quad = np.float32([top_left, top_right, bottom_left, bottom_right])

# 目标矩形:以检测到的四边形宽度/高度为基准
width  = int(max(np.linalg.norm(top_right - top_left), np.linalg.norm(bottom_right - bottom_left)))
height = int(max(np.linalg.norm(bottom_left - top_left), np.linalg.norm(bottom_right - top_right)))
dst_quad = np.float32([[0, 0], [width - 1, 0], [0, height - 1], [width - 1, height - 1]])

# ---------- 5. 计算校正矩阵并执行 ----------
M_correct = cv2.getPerspectiveTransform(src_quad, dst_quad)
corrected = cv2.warpPerspective(distorted, M_correct, (width, height))
cv2.imwrite("chessboard_corrected.png", corrected)

print("校正完成,结果已保存:")
print("  原始图像: chessboard_original.png")
print("  畸变图像: chessboard_distorted.png")
print("  校正图像: chessboard_corrected.png")

运行与验证

依赖:Python 3 + OpenCV(pip install opencv-python numpy)。

运行:将代码保存为perspective_correction.py并执行python perspective_correction.py。

验证方法:

  1. 程序正常结束且无报错,输出三条保存信息;
  2. 打开chessboard_distorted.png,可见棋盘格呈明显梯形(上窄下宽或左右倾斜);
  3. 打开chessboard_corrected.png,观察棋盘格是否恢复为矩形:横线水平、竖线垂直,且各格大小均匀;
  4. 用图像查看工具测量校正图中任意相邻两行间距,应基本相等(误差小于2像素即视为通过)。
透视畸变校正流程与数据流 ① 标准棋盘格 9×6 内角点 640×480 画布 透视变换 ② 畸变图像 梯形效果 warpPerspective 角点检测 ③ 检测四角 findChessboardCorners 取最外层四角 单应矩阵 ④ 校正结果 getPerspective Transform 关键数据流 源点 src_quad:由棋盘格最外层四角给出(自动检测,无需手动选取) 目标 dst_quad:以检测四边形宽高构建的矩形 [0,0] → [w-1,0] → [0,h-1] → [w-1,h-1] 矩阵 M_correct = getPerspectiveTransform(src_quad, dst_quad) → 校正 = warpPerspective(畸变图, M_correct, (w,h)) 验证:校正图网格平直均匀

判定标准

  • 校正图中棋盘格外边框为矩形,四角接近90°(偏差小于2°);
  • 任意相邻横线间距差小于2像素,任意相邻竖线间距差小于2像素;
  • 程序不依赖人工选点,全自动完成检测与校正。