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。
验证方法:
- 程序正常结束且无报错,输出三条保存信息;
- 打开
chessboard_distorted.png,可见棋盘格呈明显梯形(上窄下宽或左右倾斜); - 打开
chessboard_corrected.png,观察棋盘格是否恢复为矩形:横线水平、竖线垂直,且各格大小均匀; - 用图像查看工具测量校正图中任意相邻两行间距,应基本相等(误差小于2像素即视为通过)。
判定标准
- 校正图中棋盘格外边框为矩形,四角接近90°(偏差小于2°);
- 任意相邻横线间距差小于2像素,任意相邻竖线间距差小于2像素;
- 程序不依赖人工选点,全自动完成检测与校正。