cv2摄像头标定

本篇博客主要介绍cv2模块中的摄像头标定。

摄像头会发生径向畸变和切向畸变。

摄像头的内部参数和外部参数。内部参数是摄像头特异的,它包括的信息有x方向和y方向的焦距(fx, fy),光圈中心(cx, cy),相机的内部参数也称为摄像头矩阵。

在进行摄像头标定时至少需要使用10张图案模式,3D点称为对象点,2D点称为图像点,可以使用棋盘或者环形格子来进行摄像头标定,使用函数cv2.findCircleGrid()来找图案。

代码示例:

# encoding:utf-8
import numpy as np
import cv2
import glob

# 设置终止条件,迭代30次或移动0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点,类似(0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6 * 7, 3), np.float32)
objp[:, : 2] = np.mgrid[0: 7, 0: 6].T.reshape(-1, 2)  # np.mgrid()返回多维结构

# 从所有图像中存储对象点和图像点的数组
objpoints = []  # 真实世界的3D点
imgpoints = []  # 图像的2D点
# glob.globglob.glob函数的参数是字符串。这个字符串的书写和我们使用
# linux的shell命令相似,或者说基本一样。也就是说,只要我们按照平常使
# 用cd命令时的参数就能够找到我们所需要的文件的路径。字符串中可以包括“*”、
# “?”和"["、"]",其中“*”表示匹配任意字符串,“?”匹配任意单个字符,
# [0-9]与[a-z]表示匹配0-9的单个数字与a-z的单个字符。
images = glob.glob('../data/left*.jpg')
images += glob.glob('../data/right*.jpg')

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 找到棋盘边界,角点检测
    ret, corners = cv2.findChessboardCorners(gray, (7, 6), None)
    # 如果找到,则添加对象点和图像点
    if ret == True:
        objpoints.append(objp)
        # 亚像素级角点检测,在角点检测中精确化角点位置
        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners)
        # 绘制并展示边界
        cv2.drawChessboardCorners(img, (7, 6), corners2, ret)
        cv2.imshow('img', img)
        cv2.waitKey(500)
cv2.destroyAllWindows()

注:由于使用的数据有点多,就不一一上传了,需要的可以私信我邮箱

输出结果(截取其中的两张):

代码2:

# encoding:utf-8
import cv2
import numpy as np

creteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
cap = cv2.VideoCapture(0)

# 等比缩放
frame_height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
frame_width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
frame_height = int(480 / frame_height * frame_width)
ret = cap.set(cv2.CAP_PROP_FRAME_HEIGHT, frame_height)
ret = cap.set(cv2.CAP_PROP_FRAME_WIDTH, 480)

while cap.isOpened():
    ret, img = cap.read()
    cv2.imshow('img0', img)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    '''
    第一个参数Image,传入拍摄的棋盘图Mat图像,必须是8位的灰度或者彩色图像;
    第二个参数patternSize,每个棋盘图上内角点的行列数,一般情况下,行列数不要相同,便于后续标定程序识别标定板的方向;
    第三个参数corners,用于存储检测到的内角点图像坐标位置,一般用元素是Point2f的向量来表示:vector<Point2f> image_points_buf;
    第四个参数flage:用于定义棋盘图上内角点查找的不同处理方式,有默认值。
    '''
    ret, corners = cv2.findChessboardCorners(image=gray, patternSize=(6, 4), corners=None)

    print(corners)
    print('---------')
    if ret == True:
        # objpoints.append(objp)
        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), creteria)
        # imgpoints.append(corners)
        # Draw and display the corners
        cv2.drawChessboardCorners(img, (6, 4), corners2, ret)

    cv2.imshow('img', img)

    key = cv2.waitKey(delay=10)
    if key == ord("q"):
        break

cv2.destroyAllWindows()

结果:

标定:

代码:

# encoding:utf-8
import numpy as np
import cv2
import glob

# 设置终止条件,迭代30次或移动0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点,类似(0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6 * 7, 3), np.float32)
objp[:, : 2] = np.mgrid[0: 7, 0: 6].T.reshape(-1, 2)  # np.mgrid()返回多维结构

# 从所有图像中存储对象点和图像点的数组
objpoints = []  # 真实世界的3D点
imgpoints = []  # 图像的2D点
# glob.globglob.glob函数的参数是字符串。这个字符串的书写和我们使用
# linux的shell命令相似,或者说基本一样。也就是说,只要我们按照平常使
# 用cd命令时的参数就能够找到我们所需要的文件的路径。字符串中可以包括“*”、
# “?”和"["、"]",其中“*”表示匹配任意字符串,“?”匹配任意单个字符,
# [0-9]与[a-z]表示匹配0-9的单个数字与a-z的单个字符。
images = glob.glob('../data/left*.jpg')
images += glob.glob('../data/right*.jpg')

for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 找到棋盘边界,角点检测
    ret, corners = cv2.findChessboardCorners(gray, (7, 6), None)
    # 如果找到,则添加对象点和图像点
    if ret == True:
        objpoints.append(objp)
        # 亚像素级角点检测,在角点检测中精确化角点位置
        corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners)
        # 绘制并展示边界
#         cv2.drawChessboardCorners(img, (7, 6), corners2, ret)
#         print(objpoints)
#         print(imgpoints)
#         cv2.imshow('img', img)
#         cv2.waitKey(500)
# cv2.destroyAllWindows()
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[:: -1], None, None)

# 畸形校正
img = cv2.imread('../data/left12.jpg')
cv2.imshow('source', img)
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))

# 使用cv2.undistort(),和上面得到的ROI对结果进行剪裁
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# 裁剪图像
x, y, w, h = roi
dst = dst[y: y + h, x: x + w]
cv2.imshow('re1', dst)

# 找到畸变图像到畸变图像的映射方程,在使用重映射方程
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)
dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)

# 裁剪图像
x, y, w, h = roi
dst = dst[y: y + h, x: x + w]
cv2.imshow('re2', dst)
'''
反向投影误差,我们可以利用反向投影误差对我们找到的参数的准确性评估,
得到的结果越接近0越好,有了内部参数、畸变参数和旋转变化矩阵,
就可以使用cv2.projectPoints()将对象转换到图像点
然后就可以计算变换得到的图像与角点检测算法的绝对差了
最后计算所有标定图像的误差平均值
'''
mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv2.norm(imgpoints[i], imgpoints2, cv2.NORM_L2) / len(imgpoints2)
    mean_error += error

print("total error: ", mean_error / len(objpoints))

cv2.waitKey(0)
cv2.destroyAllWindows()

原图:

运行结果:

猜你喜欢

转载自blog.csdn.net/github_39611196/article/details/81189286