MOVE_AI/calibration/test/chessboard_detector.py

222 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
棋盘格检测程序
用于检测视频中的11x8内角点棋盘格并用于相机校准
"""
import cv2
import numpy as np
import os
import json
from datetime import datetime
class ChessboardDetector:
def __init__(self, pattern_size=(11, 8), square_size=1.0):
"""
初始化棋盘格检测器
Args:
pattern_size: 棋盘格内角点数量 (列, 行)
square_size: 棋盘格方格实际尺寸(单位:mm或其他)
"""
self.pattern_size = pattern_size
self.square_size = square_size
# 准备棋盘格的3D坐标点
self.objp = np.zeros((pattern_size[0] * pattern_size[1], 3), np.float32)
self.objp[:, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
self.objp *= square_size
# 存储所有图像的角点
self.obj_points = [] # 3D点
self.img_points = [] # 2D点
# 角点检测参数
self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
def detect_chessboard(self, image):
"""
检测单张图像中的棋盘格
Args:
image: 输入图像
Returns:
ret: 是否检测成功
corners: 角点坐标
gray: 灰度图
"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 查找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, self.pattern_size, None)
if ret:
# 亚像素精度优化
corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), self.criteria)
return ret, corners, gray
def process_video(self, video_path, output_dir='output', sample_interval=30):
"""
处理视频文件,检测棋盘格
Args:
video_path: 视频文件路径
output_dir: 输出目录
sample_interval: 采样间隔(帧数)
"""
if not os.path.exists(output_dir):
os.makedirs(output_dir)
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
print(f"无法打开视频: {video_path}")
return False
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
print(f"视频信息: 总帧数={total_frames}, FPS={fps}")
print(f"开始检测棋盘格 (内角点: {self.pattern_size[0]}x{self.pattern_size[1]})...")
frame_count = 0
detected_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
# 按间隔采样
if frame_count % sample_interval != 0:
continue
# 检测棋盘格
success, corners, gray = self.detect_chessboard(frame)
if success:
detected_count += 1
self.obj_points.append(self.objp)
self.img_points.append(corners)
# 绘制角点
vis_img = frame.copy()
cv2.drawChessboardCorners(vis_img, self.pattern_size, corners, success)
# 保存结果图像
output_path = os.path.join(output_dir, f'detected_{detected_count:03d}_frame{frame_count}.jpg')
cv2.imwrite(output_path, vis_img)
print(f"✓ 帧 {frame_count}: 检测成功 (已保存 {detected_count} 张)")
else:
print(f"✗ 帧 {frame_count}: 未检测到棋盘格")
cap.release()
print(f"\n检测完成: 共处理 {frame_count} 帧, 成功检测 {detected_count} 张图像")
return detected_count > 0
def calibrate_camera(self, image_size):
"""
执行相机校准
Args:
image_size: 图像尺寸 (width, height)
Returns:
ret: 标定误差
camera_matrix: 相机内参矩阵
dist_coeffs: 畸变系数
rvecs: 旋转向量
tvecs: 平移向量
"""
if len(self.obj_points) < 3:
print("错误: 需要至少3张成功检测的图像进行校准")
return None
print(f"\n开始相机校准 (使用 {len(self.obj_points)} 张图像)...")
ret, camera_matrix, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(
self.obj_points, self.img_points, image_size, None, None
)
print(f"标定完成! 重投影误差: {ret:.4f} 像素")
return ret, camera_matrix, dist_coeffs, rvecs, tvecs
def save_calibration_results(self, camera_matrix, dist_coeffs, ret, output_path='calibration_result.json'):
"""
保存校准结果到JSON文件
"""
result = {
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'pattern_size': self.pattern_size,
'square_size': self.square_size,
'num_images': len(self.obj_points),
'reprojection_error': float(ret),
'camera_matrix': camera_matrix.tolist(),
'distortion_coefficients': dist_coeffs.tolist()
}
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(result, f, indent=4, ensure_ascii=False)
print(f"\n校准结果已保存到: {output_path}")
# 打印结果
print("\n=== 相机校准结果 ===")
print(f"重投影误差: {ret:.4f} 像素")
print(f"\n相机内参矩阵:")
print(camera_matrix)
print(f"\n畸变系数:")
print(dist_coeffs.ravel())
def main():
# 配置参数
VIDEO_PATH = 'Video_20260303114232727.avi'
OUTPUT_DIR = 'chessboard_detection_output'
PATTERN_SIZE = (11, 8) # 11x8 内角点
SQUARE_SIZE = 25.0 # 假设每个方格25mm根据实际情况调整
SAMPLE_INTERVAL = 30 # 每30帧采样一次
# 创建检测器
detector = ChessboardDetector(pattern_size=PATTERN_SIZE, square_size=SQUARE_SIZE)
# 处理视频
success = detector.process_video(VIDEO_PATH, OUTPUT_DIR, SAMPLE_INTERVAL)
if not success:
print("未能检测到任何棋盘格,程序退出")
return
# 获取图像尺寸
cap = cv2.VideoCapture(VIDEO_PATH)
ret, frame = cap.read()
if ret:
image_size = (frame.shape[1], frame.shape[0])
cap.release()
# 执行相机校准
calib_result = detector.calibrate_camera(image_size)
if calib_result:
ret, camera_matrix, dist_coeffs, rvecs, tvecs = calib_result
# 保存校准结果
detector.save_calibration_results(
camera_matrix, dist_coeffs, ret,
os.path.join(OUTPUT_DIR, 'calibration_result.json')
)
print("\n✓ 校准完成!可以使用生成的校准参数进行图像矫正")
if __name__ == '__main__':
main()