import numpy as np
import cv2
import os
from matplotlib import pyplot as plt
import matplotlib
# 设置中文字体支持
def setup_chinese_font():
"""设置中文字体支持,避免显示方块"""
try:
# 方法1: 使用系统自带的中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 方法2: 如果上述方法不行,尝试使用matplotlib的字体管理器
from matplotlib.font_manager import FontProperties
chinese_font = None
# 尝试常见的中文字体路径
font_paths = [
'C:/Windows/Fonts/simhei.ttf', # 黑体
'C:/Windows/Fonts/msyh.ttc', # 微软雅黑
'C:/Windows/Fonts/simsun.ttc', # 宋体
]
for font_path in font_paths:
if os.path.exists(font_path):
chinese_font = FontProperties(fname=font_path)
matplotlib.rcParams['font.family'] = chinese_font.get_name()
break
# 如果找不到中文字体,使用默认字体但避免中文
if chinese_font is None:
print("警告: 未找到中文字体,将使用英文显示")
return False
return True
except Exception as e:
print(f"字体设置错误: {e}")
return False
# 在程序开始处调用字体设置
chinese_supported = setup_chinese_font()
class ImprovedCameraCalibration:
def __init__(self, pattern_size=(6, 4), square_size=0.025): # 核心修改:内角点改为6×4
"""
改进的相机标定类,包含多种角点检测方法
"""
self.pattern_size = pattern_size # 6列(x方向)×4行(y方向)内角点
self.square_size = square_size
self.objpoints = [] # 3D世界坐标点
self.imgpoints = [] # 2D图像坐标点
self.camera_matrix = None # 相机内参矩阵
self.dist_coeffs = None # 畸变系数
self.rvecs = None # 旋转向量
self.tvecs = None # 平移向量
self.reprojection_error = None # 重投影误差
self.errors_per_image = [] # 每张图像的重投影误差
self.detection_methods_used = [] # 记录每张图像使用的检测方法
def prepare_object_points(self):
"""准备世界坐标系中的棋盘格角点坐标"""
# 核心逻辑不变:生成(6*4, 3)的3D坐标,x方向6个点,y方向4个点
objp = np.zeros((self.pattern_size[0] * self.pattern_size[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:self.pattern_size[0], 0:self.pattern_size[1]].T.reshape(-1, 2)
objp *= self.square_size
return objp
def improved_find_chessboard_corners(self, image_path, show_corners=False):
"""
改进的角点检测方法,尝试多种预处理技术
返回: (是否成功, 角点坐标, 使用的方法名称)
"""
# 读取图像
img = cv2.imread(image_path)
if img is None:
return False, None, "Cannot read image"
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
height, width = gray.shape
# 定义不同的预处理方法和参数
methods = [
{
"name": "Standard detection",
"image": gray,
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
},
{
"name": "Gaussian blur (3x3)",
"image": cv2.GaussianBlur(gray, (3, 3), 0),
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
},
{
"name": "Gaussian blur (5x5)",
"image": cv2.GaussianBlur(gray, (5, 5), 0),
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
},
{
"name": "Histogram equalization",
"image": cv2.equalizeHist(gray),
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
},
{
"name": "Median filter",
"image": cv2.medianBlur(gray, 3),
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
},
{
"name": "Bilateral filter",
"image": cv2.bilateralFilter(gray, 9, 75, 75),
"flags": cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE
}
]
# 尝试不同的检测标志组合
flag_combinations = [
cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE,
cv2.CALIB_CB_FAST_CHECK,
cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FILTER_QUADS
]
# 首先尝试标准方法(使用修改后的pattern_size=6×4)
for method in methods:
for flags in flag_combinations:
ret, corners = cv2.findChessboardCorners(method["image"], self.pattern_size, flags=flags)
if ret:
# 亚像素级优化
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
# 验证角点质量:检查角点是否在合理范围内
corners_flat = corners_refined.reshape(-1, 2)
x_coords = corners_flat[:, 0]
y_coords = corners_flat[:, 1]
# 确保角点不会太靠近图像边缘
margin = 20
if (np.min(x_coords) > margin and np.max(x_coords) < width - margin and
np.min(y_coords) > margin and np.max(y_coords) < height - margin):
if show_corners:
img_draw = cv2.drawChessboardCorners(img.copy(), self.pattern_size, corners_refined, ret)
plt.figure(figsize=(10, 8))
plt.imshow(cv2.cvtColor(img_draw, cv2.COLOR_BGR2RGB))
# 根据字体支持情况设置标题
if chinese_supported:
plt.title(f'检测方法: {method["name"]} - {os.path.basename(image_path)}')
else:
plt.title(f'Method: {method["name"]} - {os.path.basename(image_path)}')
plt.axis('off')
plt.show()
return True, corners_refined, method["name"]
# 如果标准方法都失败,尝试更宽松的条件(使用修改后的pattern_size=6×4)
for method in methods:
ret, corners = cv2.findChessboardCorners(method["image"], self.pattern_size,
flags=cv2.CALIB_CB_ADAPTIVE_THRESH)
if ret:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
corners_refined = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
if show_corners:
img_draw = cv2.drawChessboardCorners(img.copy(), self.pattern_size, corners_refined, ret)
plt.figure(figsize=(10, 8))
plt.imshow(cv2.cvtColor(img_draw, cv2.COLOR_BGR2RGB))
# 根据字体支持情况设置标题
if chinese_supported:
plt.title(f'检测方法: {method["name"]} (宽松条件) - {os.path.basename(image_path)}')
else:
plt.title(f'Method: {method["name"]} (Loose condition) - {os.path.basename(image_path)}')
plt.axis('off')
plt.show()
return True, corners_refined, method["name"] + " (Loose condition)"
return False, None, "All methods failed"
def find_chessboard_corners(self, image_paths, show_corners=False):
"""检测所有图像中的棋盘格角点(使用改进的方法)"""
objp = self.prepare_object_points()
if chinese_supported:
print("开始检测棋盘格角点(使用改进方法)...")
print(f"使用内角点配置: {self.pattern_size[0]} × {self.pattern_size[1]}") # 显示修改后的6×4
else:
print("Starting chessboard corner detection (improved method)...")
print(f"Using pattern size: {self.pattern_size[0]} × {self.pattern_size[1]}") # 显示修改后的6×4
valid_images = 0
for i, image_path in enumerate(image_paths):
print(f"Processing image {i+1}/{len(image_paths)}: {os.path.basename(image_path)}")
# 使用改进的角点检测方法(依赖修改后的pattern_size)
success, corners, method_used = self.improved_find_chessboard_corners(image_path, show_corners)
if success:
self.objpoints.append(objp)
self.imgpoints.append(corners)
self.detection_methods_used.append(method_used)
valid_images += 1
print(f" ✓ Successfully found corners (Method: {method_used})")
else:
print(f" ✗ Failed to find corners")
if chinese_supported:
print(f"成功检测到 {valid_images}/{len(image_paths)} 张图像的角点")
else:
print(f"Successfully detected corners in {valid_images}/{len(image_paths)} images")
# 打印使用的检测方法统计
if valid_images > 0:
if chinese_supported:
print("\n检测方法统计:")
else:
print("\nDetection method statistics:")
method_counts = {}
for method in self.detection_methods_used:
method_counts[method] = method_counts.get(method, 0) + 1
for method, count in method_counts.items():
print(f" {method}: {count} images")
return valid_images > 0
def calibrate_camera(self, image_size):
"""进行相机标定"""
if len(self.objpoints) < 3:
if chinese_supported:
print("错误: 需要至少3张有效图像进行标定")
else:
print("Error: Need at least 3 valid images for calibration")
return False
if chinese_supported:
print("开始相机标定...")
else:
print("Starting camera calibration...")
# 相机标定(使用修改后的objpoints/imgpoints,对应6×4角点)
ret, self.camera_matrix, self.dist_coeffs, self.rvecs, self.tvecs = cv2.calibrateCamera(
self.objpoints, self.imgpoints, image_size, None, None)
if ret:
self.calculate_reprojection_error()
return True
else:
if chinese_supported:
print("错误: 相机标定失败")
else:
print("Error: Camera calibration failed")
return False
def calculate_reprojection_error(self):
"""计算重投影误差(逻辑不变,自动适配6×4角点数量)"""
total_error = 0
self.errors_per_image = []
for i in range(len(self.objpoints)):
# 将3D点投影到2D图像平面
imgpoints2, _ = cv2.projectPoints(self.objpoints[i], self.rvecs[i], self.tvecs[i],
self.camera_matrix, self.dist_coeffs)
# 计算误差(6×4=24个角点,自动按实际数量计算)
error = cv2.norm(self.imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
self.errors_per_image.append(error)
total_error += error
self.reprojection_error = total_error / len(self.objpoints)
return self.reprojection_error
def evaluate_calibration_quality(self):
"""评估标定质量(逻辑不变)"""
error = self.reprojection_error
if chinese_supported:
print(f"\n标定质量评估:")
print(f"重投影误差: {error:.6f} 像素")
if error < 0.1:
print("质量评级: 优秀 - 标定结果非常精确")
elif error < 0.3:
print("质量评级: 良好 - 标定结果可用于大多数应用")
elif error < 0.5:
print("质量评级: 可接受 - 标定结果基本可用")
else:
print("质量评级: 需要改进 - 建议检查图像质量或增加标定图像数量")
else:
print(f"\nCalibration quality assessment:")
print(f"Reprojection error: {error:.6f} pixels")
if error < 0.1:
print("Quality rating: Excellent - Very accurate calibration")
elif error < 0.3:
print("Quality rating: Good - Suitable for most applications")
elif error < 0.5:
print("Quality rating: Acceptable - Basically usable")
else:
print("Quality rating: Needs improvement - Check image quality or add more images")
return error
def print_results(self):
"""打印标定结果(逻辑不变)"""
if self.camera_matrix is None:
if chinese_supported:
print("错误: 请先进行相机标定")
else:
print("Error: Please calibrate the camera first")
return
print("\n" + "="*60)
if chinese_supported:
print("相机标定结果分析")
else:
print("Camera Calibration Results Analysis")
print("="*60)
if chinese_supported:
print(f"\n1. 相机内参矩阵 (Camera Matrix):")
print("[[fx, 0, cx],")
print(" [ 0, fy, cy],")
print(" [ 0, 0, 1]]")
print(f"fx (x轴焦距): {self.camera_matrix[0,0]:.2f} 像素")
print(f"fy (y轴焦距): {self.camera_matrix[1,1]:.2f} 像素")
print(f"cx (主点x坐标): {self.camera_matrix[0,2]:.2f} 像素")
print(f"cy (主点y坐标): {self.camera_matrix[1,2]:.2f} 像素")
print(f"\n完整矩阵:")
else:
print(f"\n1. Camera Intrinsic Matrix:")
print("[[fx, 0, cx],")
print(" [ 0, fy, cy],")
print(" [ 0, 0, 1]]")
print(f"fx (focal length x): {self.camera_matrix[0,0]:.2f} pixels")
print(f"fy (focal length y): {self.camera_matrix[1,1]:.2f} pixels")
print(f"cx (principal point x): {self.camera_matrix[0,2]:.2f} pixels")
print(f"cy (principal point y): {self.camera_matrix[1,2]:.2f} pixels")
print(f"\nFull matrix:")
print(self.camera_matrix)
if chinese_supported:
print(f"\n2. 畸变系数 (Distortion Coefficients):")
print(f"k1 (径向畸变1): {self.dist_coeffs[0,0]:.6f}")
print(f"k2 (径向畸变2): {self.dist_coeffs[0,1]:.6f}")
print(f"p1 (切向畸变1): {self.dist_coeffs[0,2]:.6f}")
print(f"p2 (切向畸变2): {self.dist_coeffs[0,3]:.6f}")
else:
print(f"\n2. Distortion Coefficients:")
print(f"k1 (radial distortion 1): {self.dist_coeffs[0,0]:.6f}")
print(f"k2 (radial distortion 2): {self.dist_coeffs[0,1]:.6f}")
print(f"p1 (tangential distortion 1): {self.dist_coeffs[0,2]:.6f}")
print(f"p2 (tangential distortion 2): {self.dist_coeffs[0,3]:.6f}")
if len(self.dist_coeffs[0]) > 4:
if chinese_supported:
print(f"k3 (径向畸变3): {self.dist_coeffs[0,4]:.6f}")
else:
print(f"k3 (radial distortion 3): {self.dist_coeffs[0,4]:.6f}")
print(f"\n3. 重投影误差分析:" if chinese_supported else "\n3. Reprojection Error Analysis:")
self.evaluate_calibration_quality()
if chinese_supported:
print(f"\n4. 各图像重投影误差 (前10张):")
else:
print(f"\n4. Reprojection Error per Image (first 10):")
# 只显示前10张图像的误差,避免输出过长
for i, error in enumerate(self.errors_per_image[:10]):
method = self.detection_methods_used[i]
if chinese_supported:
print(f" 图像 {i+1}: {error:.6f} 像素 (检测方法: {method})")
else:
print(f" Image {i+1}: {error:.6f} pixels (Method: {method})")
# 显示误差统计信息
if len(self.errors_per_image) > 10:
if chinese_supported:
print(f" ... (共{len(self.errors_per_image)}张图像)")
print(f" 最大误差: {max(self.errors_per_image):.6f} 像素")
print(f" 最小误差: {min(self.errors_per_image):.6f} 像素")
print(f" 误差标准差: {np.std(self.errors_per_image):.6f} 像素")
else:
print(f" ... (total {len(self.errors_per_image)} images)")
print(f" Max error: {max(self.errors_per_image):.6f} pixels")
print(f" Min error: {min(self.errors_per_image):.6f} pixels")
print(f" Error std: {np.std(self.errors_per_image):.6f} pixels")
if chinese_supported:
print(f"\n5. 外参示例 (第一张图像):")
else:
print(f"\n5. Extrinsic Parameters Example (First Image):")
# 将旋转向量转换为旋转矩阵
rmat, _ = cv2.Rodrigues(self.rvecs[0])
if chinese_supported:
print(f"旋转矩阵:")
else:
print(f"Rotation Matrix:")
print(rmat)
if chinese_supported:
print(f"平移向量 (米): {self.tvecs[0].flatten()}")
else:
print(f"Translation Vector (meters): {self.tvecs[0].flatten()}")
def visualize_results(self, image_paths):
"""可视化标定结果(使用修改后的pattern_size绘制角点)"""
if self.camera_matrix is None:
if chinese_supported:
print("错误: 请先进行相机标定")
else:
print("Error: Please calibrate the camera first")
return
# 选择前3张图像进行可视化
num_show = min(3, len(image_paths))
fig, axes = plt.subplots(2, num_show, figsize=(5*num_show, 10))
if num_show == 1:
axes = axes.reshape(2, 1)
for i in range(num_show):
img = cv2.imread(image_paths[i])
if img is None:
continue
# 去畸变
undistorted_img = cv2.undistort(img, self.camera_matrix, self.dist_coeffs)
# 绘制角点重投影(使用修改后的pattern_size)
imgpoints2, _ = cv2.projectPoints(self.objpoints[i], self.rvecs[i], self.tvecs[i],
self.camera_matrix, self.dist_coeffs)
# 原始图像(绘制6×4角点)
img_with_corners = cv2.drawChessboardCorners(img.copy(), self.pattern_size,
self.imgpoints[i], True)
axes[0, i].imshow(cv2.cvtColor(img_with_corners, cv2.COLOR_BGR2RGB))
if chinese_supported:
axes[0, i].set_title(f'原始图像 {i+1}\n误差: {self.errors_per_image[i]:.4f}px\n方法: {self.detection_methods_used[i]}')
else:
axes[0, i].set_title(f'Original Image {i+1}\nError: {self.errors_per_image[i]:.4f}px\nMethod: {self.detection_methods_used[i]}')
axes[0, i].axis('off')
# 去畸变图像
axes[1, i].imshow(cv2.cvtColor(undistorted_img, cv2.COLOR_BGR2RGB))
if chinese_supported:
axes[1, i].set_title(f'去畸变后 {i+1}')
else:
axes[1, i].set_title(f'Undistorted {i+1}')
axes[1, i].axis('off')
plt.tight_layout()
plt.show()
# 绘制误差分布图(逻辑不变)
plt.figure(figsize=(12, 6))
x_pos = range(1, len(self.errors_per_image)+1)
bars = plt.bar(x_pos, self.errors_per_image)
# 为不同检测方法使用不同颜色
method_colors = {}
colors = ['blue', 'green', 'red', 'orange', 'purple', 'brown']
for i, method in enumerate(self.detection_methods_used):
if method not in method_colors:
method_colors[method] = colors[len(method_colors) % len(colors)]
bars[i].set_color(method_colors[method])
plt.axhline(y=self.reprojection_error, color='r', linestyle='--',
label=f'Average error: {self.reprojection_error:.4f}px')
plt.xlabel('Image Number')
plt.ylabel('Reprojection Error (pixels)')
if chinese_supported:
plt.title('各图像重投影误差分布 (颜色表示检测方法)')
else:
plt.title('Reprojection Error Distribution (Color indicates detection method)')
# 添加图例
legend_handles = []
for method, color in method_colors.items():
legend_handles.append(plt.Rectangle((0,0),1,1, color=color, label=method))
legend_handles.append(plt.Line2D([0],[0], color='r', linestyle='--', label=f'Average error'))
plt.legend(handles=legend_handles)
plt.grid(True, alpha=0.3)
plt.show()
def main():
# 修改为新的数据集路径:从1.jpg到60.jpg
image_dir = r"F:\JIQISHIJUE\13"
image_paths = []
# 生成从1到60的图像路径
for i in range(1, 61):
filename = f"{i}.jpg"
full_path = os.path.join(image_dir, filename)
image_paths.append(full_path)
# 检查图像文件是否存在
existing_paths = []
for path in image_paths:
if os.path.exists(path):
existing_paths.append(path)
else:
print(f"Warning: Image file does not exist: {path}")
if not existing_paths:
print(f"Error: No image files found")
return
print(f"Found {len(existing_paths)} images:")
# 只显示前10个文件路径,避免输出过长
for path in existing_paths[:10]:
print(f" {os.path.basename(path)}")
if len(existing_paths) > 10:
print(f" ... (and {len(existing_paths)-10} more images)")
# 创建改进的标定对象,核心修改:内角点设置为6×4
calibrator = ImprovedCameraCalibration(pattern_size=(6, 4), square_size=0.025)
# 检测角点(设置show_corners=False避免显示过多图像)
success = calibrator.find_chessboard_corners(existing_paths, show_corners=False)
if not success:
if chinese_supported:
print("错误: 角点检测失败")
print("可能的原因:")
print("1. 棋盘格内角点数量设置不正确")
print("2. 图像质量不佳")
print("3. 棋盘格在图像中不完整或被遮挡")
print("4. 棋盘格太小或太大")
else:
print("Error: Corner detection failed")
print("Possible reasons:")
print("1. Incorrect pattern size setting")
print("2. Poor image quality")
print("3. Chessboard not fully visible or occluded")
print("4. Chessboard too small or too large")
return
# 获取图像尺寸
sample_img = cv2.imread(existing_paths[0])
image_size = (sample_img.shape[1], sample_img.shape[0])
print(f"Image size: {image_size}")
# 进行相机标定
success = calibrator.calibrate_camera(image_size)
if success:
# 打印详细结果
calibrator.print_results()
# 可视化结果
calibrator.visualize_results(existing_paths)
# 保存标定结果(文件名添加6x4标识,避免与原结果混淆)
np.savez('camera_calibration_6x4_results.npz',
camera_matrix=calibrator.camera_matrix,
dist_coeffs=calibrator.dist_coeffs,
reprojection_error=calibrator.reprojection_error,
image_size=image_size,
detection_methods=calibrator.detection_methods_used)
if chinese_supported:
print(f"\n标定结果已保存到 'camera_calibration_6x4_results.npz'")
else:
print(f"\nCalibration results saved to 'camera_calibration_6x4_results.npz'")
# 保存可用于OpenCV的YAML文件(文件名添加6x4标识)
fs = cv2.FileStorage("camera_calibration_6x4.yml", cv2.FILE_STORAGE_WRITE)
fs.write("camera_matrix", calibrator.camera_matrix)
fs.write("distortion_coefficients", calibrator.dist_coeffs)
fs.write("reprojection_error", calibrator.reprojection_error)
fs.write("image_width", image_size[0])
fs.write("image_height", image_size[1])
fs.release()
if chinese_supported:
print(f"标定结果已保存到 'camera_calibration_6x4.yml'")
else:
print(f"Calibration results saved to 'camera_calibration_6x4.yml'")
else:
if chinese_supported:
print("相机标定失败")
else:
print("Camera calibration failed")
if __name__ == "__main__":
main()把这里面的文件地址修改为"D:\jupyter notebook\homework3\images\13"
最新发布