问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

摄像头畸变矫正详解:Matlab与OpenCV两种实现方法

创作时间:
作者:
@小白创作中心

摄像头畸变矫正详解:Matlab与OpenCV两种实现方法

引用
CSDN
1.
https://blog.csdn.net/zs3194068129/article/details/145626280

摄像头畸变矫正是一种常见的图像处理技术,主要用于解决广角镜头拍摄时出现的图像失真问题。本文将介绍两种常用的畸变矫正方法:使用Matlab的Camera Calibration Toolbox和使用OpenCV进行标定。

简单介绍

所谓畸变其实就是由摄像头引起的图片失真,一般在广角摄像头表现明显,原本平整的桌面通过镜头看像个球面,直观的解释直线被拍成了曲线

去畸变的办法

首先我们需要一个标准棋盘(印有特定的标定图案),如图:

把它摊平放在桌子上,然后用需要去畸变的相机对它进行拍摄,拍几张(10~20张差不多)不同角度的棋盘图片,保存好。然后我们有两种方法进行畸变矫正。

1. 借助matlab的Camera Calibration Toolbox工具

打开matlab,在应用程序中找到 Camera Calibration,然后点击打开。

然后点击Add Images添加图片,这里会弹出一个弹框,提示选择棋盘格子的大小,根据实际情况选择即可。

添加后会显示角点,如果识别不清楚或有错误,可以用红色边框标记,然后删除并换一张图片。

导入多张图片后,直接点击Calibrate 开始标定

完成后会显示各个参数:

  • 重投影误差(Reprojection Error):理想情况下应 < 0.5 像素。
  • 相机内参:
  • 焦距(Focal Length)
  • 主点(Principal Point)
  • 畸变系数(Radial, Tangential)

新版工具箱还提供了可视化功能:

  • Show Undistorted Images 查看校正效果。
  • Plot Reprojection Errors 分析误差分布。

确认无误后可以点击 Export Camera Parameters导出数据,生成mat文件。也可以点击第二个按钮生成matlab代码进行后续操作。

2. 使用OpenCV进行标定

OpenCV是计算机视觉库,可以用来进行图像处理,里面有很多函数,我们可以借助它来进行畸变矫正。

首先将拍摄的照片全部保存到一个文件夹中,然后需要手动指定实际拍摄图片中有多少个棋盘角点。例如,一张9 X 5的棋盘图片。

对于所有的图片,都需要进行同样的操作,然后保存到列表中。

根据指定的大小获取三维(np.zeros((nx*ny, 3), np.float32)) 理论坐标:

# 每张图片的棋盘格参数, 9 X 6 格大小
objp_dict = {
    1: (9, 5),
    2: (9, 6),
    3: (9, 6),
    4: (9, 6),
    5: (9, 6),
    6: (9, 6),
    7: (9, 6),
    8: (9, 6),
    9: (9, 6),
    10: (9, 6),
    11: (9, 6),
    12: (9, 6),
    13: (9, 6),
    14: (9, 6),
    15: (9, 6),
    16: (9, 6),
    17: (9, 6),
    18: (9, 6),
    19: (9, 6),
    20: (9, 6),
}

# 遍历objp_dict的每个参数
for k in objp_dict:
    nx, ny = objp_dict[k]	# 取出长宽
    # (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
    objp = np.zeros((nx*ny, 3), np.float32)	              # 生成三维坐标,即每个点理论坐标应该是多少
    objp[:, : 2] = np.mgrid[0:nx, 0:ny].T.reshape(-1, 2)  # 生成x和y的坐标,z坐标都是0

读取图片通过 cv2.cvtColor(灰度化) 和 cv2.findChessboardCorners(棋盘角点) 寻找真实的情况:

# 对于识别的点应该的坐标序号初始化好后, 开始来读取图片中每个点的真实位置
fname = 'camera_cal/calibration%s.jpg' % str(k)
img = cv2.imread(fname)	# 读取一张图片
# 转成灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 检测角点
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)

将返回值保存到列表中:

if ret == True:	 # 成功检测到了
    # Save object points and corresponding corners
    objp_list.append(objp)	#
    corners_list.append(corners)

将列表带入 cv2.calibrateCamera 中即可得到参数 内参矩阵(mtx)和畸变系数(dist):

img = cv2.imread('test_images/straight_lines1.jpg')		# 这里得保证所有图像的大小一致
img_size = (img.shape[1], img.shape[0])		# 获取测试图片的尺寸
#  获取相关参数
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objp_list, corners_list, img_size,None,None)

最后使用 cv2.undistort 将图片进行去畸变即可:

# 去畸变
img = mpimg.imread('camera_cal/calibration5.jpg')	 #  读取图像
dst = cv2.undistort(img, mtx, dist, None, mtx)

下面来康康完整代码:

import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import pickle

def calibrate_camera():
    # Mapping each calibration image to number of checkerboard corners
    # Everything is (9,6) for now
    # 每张图片的棋盘格参数, 9 X 6 格大小
    objp_dict = {
        1: (9, 5),
        2: (9, 6),
        3: (9, 6),
        4: (9, 6),
        5: (9, 6),
        6: (9, 6),
        7: (9, 6),
        8: (9, 6),
        9: (9, 6),
        10: (9, 6),
        11: (9, 6),
        12: (9, 6),
        13: (9, 6),
        14: (9, 6),
        15: (9, 6),
        16: (9, 6),
        17: (9, 6),
        18: (9, 6),
        19: (9, 6),
        20: (9, 6),
    }
    # List of object points and corners for calibration
    objp_list = []		 # 存储对象点
    corners_list = []	 # 存储角点
    # Go through all images and find corners
    # 遍历objp_dict的每个参数
    for k in objp_dict:
        nx, ny = objp_dict[k]	# 取出长宽
        # Prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
        objp = np.zeros((nx*ny, 3), np.float32)	              # 生成三维坐标,即每个点理论坐标应该是多少
        objp[:, : 2] = np.mgrid[0:nx, 0:ny].T.reshape(-1, 2)  # 生成x和y的坐标,z坐标都是0
        # 对于识别的点应该的坐标序号初始化好后, 开始来读取图片中每个点的真实位置
        # Make a list of calibration images
        fname = 'camera_cal/calibration%s.jpg' % str(k)
        img = cv2.imread(fname)	# 读取一张图片
        # Convert to grayscale	转成灰度图
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # Find the chessboard corners
        # 检测角点
        ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)
        # If found, save & draw corners
        if ret == True:	 # 成功检测到了
            # Save object points and corresponding corners
            objp_list.append(objp)	#
            corners_list.append(corners)
            # Draw and display the corners
            #cv2.drawChessboardCorners(img, (nx, ny), corners, ret)
            #plt.imshow(img)
            #plt.show()
            #print('Found corners for %s' % fname)
        else:	# 否则没有检测到
            print('Warning: ret = %s for %s' % (ret, fname))
    # Calibrate camera and undistort a test image
    img = cv2.imread('test_images/straight_lines1.jpg')		# 这里得保证所有图像的大小一致
    img_size = (img.shape[1], img.shape[0])		# 获取测试图片的尺寸
    #  获取相关参数
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objp_list, corners_list, img_size,None,None)
    return mtx, dist

if __name__ == '__main__':
    mtx, dist = calibrate_camera()
    save_dict = {'mtx': mtx, 'dist': dist}		# 得到数据
    #  将mtx和dist通过pickle保存至calibrate_camera.p中
    #  pickle库将此序列转化成二进制字节流存入.p文件中, wb:w--写入, b--以二进制形式
    with open('calibrate_camera.p', 'wb') as f:
        pickle.dump(save_dict, f)
    # Undistort example calibration image
    # 去畸变
    img = mpimg.imread('camera_cal/calibration5.jpg')	 #  读取图像
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    plt.imshow(dst)	# 显示矫正结果
    plt.savefig('example_images/undistort_calibration.png')		# 然后保存图片

参考资料:

  • 代码来自github的开源项目 Lane Detection with OpenCV
  • 上海交通大学AuTop战队开源算法讲解(三)标定与透视变换
  • 摄像头校准之白平衡&畸变&坏点
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号