如何用Python实现批量图像自动裁剪与透视矫正(仿CamScanner效果)
棋盘格图像批量裁剪与俯视矫正方案
相机标定主要用于消除镜头畸变,你需要的是透视变换实现鸟瞰视角矫正,以下是基于OpenCV的可行方案,无需依赖标定数据:
核心步骤
- 图像预处理:灰度化+高斯模糊+Canny边缘检测,突出棋盘格轮廓
- 轮廓检测与筛选:提取图像中最大的四边形轮廓(对应棋盘格边界)
- 顶点排序:将检测到的四边形顶点按左上、右上、右下、左下顺序排列
- 透视变换:将不规则四边形映射为矩形,得到俯视视角的棋盘格图像
- 批量处理:遍历指定目录下的所有图像,自动执行上述流程
完整代码实现
import cv2 import numpy as np import os def get_chessboard_corners(img): # 预处理 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blur = cv2.GaussianBlur(gray, (5,5), 0) edges = cv2.Canny(blur, 50, 150) # 检测轮廓 contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 筛选面积最大的轮廓 contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5] target_contour = None for cnt in contours: perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * perimeter, True) # 筛选四边形轮廓 if len(approx) == 4: target_contour = approx break if target_contour is None: return None # 顶点排序:左上、右上、右下、左下 pts = target_contour.reshape(4, 2) rect = np.zeros((4, 2), dtype="float32") # 按x+y排序,最小的是左上,最大的是右下 s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # 按x-y排序,最小的是右上,最大的是左下 diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect def warp_chessboard(img, rect): (tl, tr, br, bl) = rect # 计算目标矩形的宽度和高度 widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) maxWidth = max(int(widthA), int(widthB)) heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) maxHeight = max(int(heightA), int(heightB)) # 目标透视变换的顶点 dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32") # 计算变换矩阵并执行透视变换 M = cv2.getPerspectiveTransform(rect, dst) warped = cv2.warpPerspective(img, M, (maxWidth, maxHeight)) return warped def batch_process_images(input_dir, output_dir): # 创建输出目录 os.makedirs(output_dir, exist_ok=True) for filename in os.listdir(input_dir): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): img_path = os.path.join(input_dir, filename) img = cv2.imread(img_path) if img is None: print(f"无法读取图像: {filename}") continue rect = get_chessboard_corners(img) if rect is None: print(f"未检测到棋盘格轮廓: {filename}") continue warped_img = warp_chessboard(img, rect) output_path = os.path.join(output_dir, f"corrected_{filename}") cv2.imwrite(output_path, warped_img) print(f"处理完成: {filename}") # 示例调用 if __name__ == "__main__": INPUT_DIR = "input_images" # 替换为你的输入目录 OUTPUT_DIR = "output_images" # 替换为你的输出目录 batch_process_images(INPUT_DIR, OUTPUT_DIR)
关键说明
- 轮廓筛选逻辑:优先选择面积最大的四边形,确保是棋盘格的外边界,避免干扰轮廓
- 顶点排序:通过坐标的和与差排序,保证透视变换的顶点顺序正确,避免图像颠倒
- 自适应尺寸:根据检测到的棋盘格实际尺寸计算目标矩形大小,保证矫正后的图像比例正确
- 批量处理:自动遍历指定目录下的所有图像,跳过无法处理的文件并给出提示
如果部分图像检测效果不佳,可以调整以下参数:
- Canny边缘检测的阈值(
cv2.Canny(blur, 50, 150)中的50和150) - 轮廓近似的精度(
0.02 * perimeter中的0.02,可在0.01-0.05之间微调)
内容的提问来源于stack exchange,提问作者shainis
相关产品推荐
相关产品推荐

