From 9484c1f1f38b8b08917da782f71492e41dd316c4 Mon Sep 17 00:00:00 2001 From: fuly Date: Tue, 5 Nov 2024 21:17:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=AC=AC=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=9A=84=E5=86=85=E5=8F=82=E6=A0=87=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- calibrate_intri.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 calibrate_intri.py diff --git a/calibrate_intri.py b/calibrate_intri.py new file mode 100644 index 0000000..8998478 --- /dev/null +++ b/calibrate_intri.py @@ -0,0 +1,91 @@ +import os +import os.path as osp +import glob +import cv2 as cv +import numpy as np +import json +import argparse + +def calibrate_camera(imgFolder, chessboardSize, squareSize): + # 设置输出目录 + outputFolder = osp.join(imgFolder, "output") + if not osp.exists(outputFolder): + os.makedirs(outputFolder) + + # 图片路径 + imgPaths = [] + for extension in ["jpg", "png", "jpeg"]: + imgPaths += glob.glob(osp.join(imgFolder, "*.{}".format(extension))) + if len(imgPaths) == 0: + print("No images found!") + return + + # 存储世界坐标和像素坐标 + # 计算出棋盘格中每个网格角点的坐标,之后当成世界坐标 + board_w, board_h = chessboardSize + board_grid = np.zeros((board_w * board_h, 3), np.float32) + board_grid[:, :2] = np.mgrid[0:board_w, 0:board_h].T.reshape(-1, 2) * squareSize + + pointsWorld = [] + pointsPixel = [] + + # 遍历图片 + for imgPath in imgPaths: + img = cv.imread(imgPath) + gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) + + # 查找角点 + ret, corners = cv.findChessboardCorners(gray, (board_w, board_h), None) + if ret: + cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)) + pointsWorld.append(board_grid) + pointsPixel.append(corners) + + cv.drawChessboardCorners(img, (board_w, board_h), corners, ret) + cv.imwrite(osp.join(outputFolder, osp.basename(imgPath)), img) + + # 标定相机 + ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(pointsWorld, pointsPixel, gray.shape[::-1], None, None) + print("Intrinsic matrix:\n", mtx.astype(np.float32)) + print("Distortion coefficients:\n", dist.astype(np.float32)) + + # 计算重投影误差 + nimg = len(pointsWorld) + img_error = np.zeros(nimg) + for i in range(nimg): + imgpoints2, _ = cv.projectPoints(pointsWorld[i], rvecs[i], tvecs[i], mtx, dist) + error = cv.norm(pointsPixel[i], imgpoints2, cv.NORM_L2) / len(imgpoints2) + img_error[i] = error + + good_img = np.where(img_error < 0.5)[0] + mean_error = np.mean(img_error[good_img]) + print("Reprojection error: ", mean_error) + + # 挑选出重投影误差小于1.0的图片,重新标定相机 + if len(good_img) == 0: + print("No images with error < 0.5") + elif len(good_img) == nimg: + print("All images have error < 0.5") + pass + else : + pointsWorld2 = [pointsWorld[i] for i in good_img] + pointsPixel2 = [pointsPixel[i] for i in good_img] + ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(pointsWorld2, pointsPixel2, gray.shape[::-1], None, None) + print("Intrinsic matrix:\n", mtx.astype(np.float32)) + print("Distortion coefficients:\n", dist.astype(np.float32)) + + return mtx, dist + + +def write_json_data(mtx, dist, outputFolder): + data = { + "intrinsic_matrix": mtx.tolist(), + "distortion_coefficients": dist.tolist() + } + with open(osp.join(outputFolder, "calibration.json"), "w") as f: + json.dump(data, f, indent=4) + +if __name__ == "__main__": + mtx, dist = calibrate_camera("./test/intri_imgs", (11, 8), 60) + write_json_data(mtx, dist, "./test/intri_imgs/output") +