【摄像头标定】单目摄像头标定及矫正-opencv(python)

单目摄像头标定及矫正

①标定

既然是标定,就需要准备使用待标定单目摄像头拍摄的标定板图片。

这里使用棋盘格标定板,可以到网上下载,需要知道棋盘格的角点。

由于我的摄像头是嵌入于开发板底板中,并且摄像头数据通过ros话题发布,所以拍照方式比较不一样,使用ros作为工具,编写节点代码进行拍照,不了解ros的可以自己搜索一下如何对连接到开发板上的usb摄像头拍照,一般开发板连接USB摄像头的话,可以直接通过opencv的videocapture打开摄像头获取数据,比较方便的拍照。拍摄不同角度和位置的图片10-20张即可。

标定代码如下:可以在各种能运行python代码的IDE中运行,比如pycharm、vscode等等。

import cv2
import numpy as np
import glob
import os
import yaml
# 相机标定
# 设置棋盘格w和h方向的角点数量
w_corners = 8               #改!数棋盘格宽有多少个格子,然后减一
h_corners = 5               #改!数棋盘格高有多少个格子,然后减一
# 设置图像路径
images = glob.glob(r"C:\new_pycharm_project\yolov10-main\daijiaozheng\*.png")         #改!改成自己存放图片的路径

criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)

# 获取标定板角点的位置
objp = np.zeros((w_corners * h_corners, 3), np.float32)
objp[:, :2] = np.mgrid[0:w_corners, 0:h_corners].T.reshape(-1, 2)
objp = objp * 21            #改!这里的21是一个格子的长度,单位是mm

obj_points = []  # 存储3D点
img_points = []  # 存储2D点

i = 0

def save_calibration_to_yaml(file_path, cameraMatrix_l, distCoeffs_l):
    data = {
        'camera_matrix': {
            'rows': 3,
            'cols': 3,
            'dt': 'd',
            'data': cameraMatrix_l.flatten().tolist()
        },
        'dist_coeff': {
            'rows': 1,
            'cols': 5,
            'dt': 'd',
            'data': distCoeffs_l.flatten().tolist()
        }
    }

    with open(file_path, 'w') as file:
        yaml.dump(data, file, default_flow_style=False)
    print(f"Calibration parameters saved to {file_path}")

for fname in images:
    if not os.path.exists(fname):
        print(f"文件不存在: {fname}")
        continue

    try:
        with open(fname, 'rb') as f:
            print(f"文件正常读取: {fname}")
    except Exception as e:
        print(f"无法读取文件: {fname}, 错误: {e}")
        continue

    img = cv2.imread(fname)
    if img is None:
        print(f"OpenCV 无法读取文件: {fname}")
        continue

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    size = gray.shape[::-1]
    ret, corners = cv2.findChessboardCorners(gray, (w_corners, h_corners), None)

    if ret:
        obj_points.append(objp)
        corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria)
        if [corners2]:
            img_points.append(corners2)
        else:
            img_points.append(corners)

        cv2.drawChessboardCorners(img, (w_corners, h_corners), corners, ret)
        i += 1

        new_size = (1280, 800)
        resized_img = cv2.resize(img, new_size)
        cv2.imshow('img', resized_img)
        cv2.waitKey(150)

print(len(img_points))
cv2.destroyAllWindows()

if len(img_points) > 0:
    # 标定
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
    save_calibration_to_yaml('calibration_danmu.yaml', mtx, dist)         #改!换成自己yaml文件想要的路径和名字

    print("ret:", ret)
    print("mtx:\n", mtx)
    print("dist:\n", dist)
    print("rvecs:\n", rvecs)
    print("tvecs:\n", tvecs)

else:
    print("没有检测到角点,无法进行相机标定。")

需要更改的地方已在代码中标出。

标定过程中会可视化画了角点连线的图,标定结果会保存到一个yaml文件中:

camera_matrix:
  cols: 3
  data:
    - 538.5703097550794
    - 0
    - 638.7847675804244
    - 0
    - 542.6025156738463
    - 419.4305002874007
    - 0
    - 0
    - 1
  dt: d
  rows: 3
dist_coeff_left:
  cols: 5
  data:
    - -0.1879080034212516
    - 0.06154713313351551
    - 4.605878792222752e-05
    - 0.002483271337902348
    - -0.003210439918482585
  dt: d
  rows: 1

其中camera_matrix为相机的内参矩阵,dist_coeff_left为畸变系数,接下来就可以使用这两个参数对图像进行矫正。

②矫正:

import cv2
import numpy as np
import glob
import os
import yaml


def load_camera_params_from_yaml(yaml_file):
    with open(yaml_file, 'r') as f:
        calib_data = yaml.safe_load(f)

    camera_matrix = np.array(calib_data['camera_matrix']['data']).reshape(3, 3)
    dist_coeffs = np.array(calib_data['dist_coeff_left']['data'])

    return camera_matrix, dist_coeffs


def cam_calib_correct_img(distort_img_dir, crct_img_dir, cameraMatrix, distCoeffs):
    imgs = glob.glob(os.path.join(distort_img_dir, "*.png"))
    imgs.extend(glob.glob(os.path.join(distort_img_dir, "*.png")))
    imgs.extend(glob.glob(os.path.join(distort_img_dir, "*.bmp")))

    for img_ in imgs:
        print("已读取待校正图像:", img_)
        img = cv2.imread(img_)
        (h1, w1) = img.shape[:2]

        newcameramtx, roi = cv2.getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, (w1, h1), 1, (w1, h1))

        dst = cv2.undistort(img, cameraMatrix, distCoeffs, None, newcameramtx)

        x, y, w, h = roi
        dst = dst[y:y + h, x:x + w]
        resized_dst = cv2.resize(dst, (w1, h1))

        imgname = os.path.basename(img_).split('.')[0]
        rlt_path = os.path.join(crct_img_dir, imgname + "_crct.png")
        cv2.imwrite(rlt_path, resized_dst)

        print("已保存校正图像:", rlt_path)


if __name__ == "__main__":
    distort_img_dir = 'daijiaozheng'  # 待校正图像的路径
    crct_img_dir = 'jiaozheng_result'  # 保存校正图像的路径
    yaml_file = 'calibration_danmu.yaml'  # YAML文件路径

    cameraMatrix, distCoeffs = load_camera_params_from_yaml(yaml_file)
    cam_calib_correct_img(distort_img_dir, crct_img_dir, cameraMatrix, distCoeffs)

完成后可以在jiaozheng_result文件夹中看到矫正完的图像,现在给出两张照片进行对照:

明显看出畸变减小,可以注意看头顶的线条。

相关推荐
RobotsRuning9 分钟前
OpenCV cv::Mat到 Eigen 的正确转换——cv2eigen
opencv·cv2eigen
木木阳2 小时前
ICCV2023单目摄像头相关论文速览
论文阅读·iccv·深度估计·单目摄像头
朝阳眯眼6 小时前
Android 集成OpenCV
android·人工智能·opencv
mq白8 小时前
zxing-cpp+OpenCV根据字符串生成条形码
c++·人工智能·opencv·计算机视觉
bryant_meng8 小时前
【python】OpenCV—Feature Detection and Matching
开发语言·python·opencv·特征匹配·特征检测
王大锤439110 小时前
OpenCV 用mediapipe做一个虚拟鼠标
人工智能·opencv·计算机外设
新兴AI民工1 天前
OPENCV清晰度判断(二)
人工智能·opencv·计算机视觉·灰度共轭矩阵·lbp·图像清晰度
花想云1 天前
【OpenCV 图像处理 Python版】OpenCV 简介及安装
图像处理·python·opencv
zhqh1001 天前
ffmpeg + opencv 把摄像头画面保存为mp4文件(Ubuntu24.04)
人工智能·opencv·ffmpeg
jndingxin1 天前
cv::Mat类的矩阵内容输出的各种格式的例子
opencv