OpenCV相机标定- camera_calibration_python

引言

想要研究机视觉引导机械臂抓取,不知道如何进行相机标定:

①网上看了一些文章,代码没有能复现成功的。

②尝试用大模型生成,效果也不理想。

③尝试啃一下官网的教程,实在没啃动。暂时放一下,等有时间再研究。

OpenCV: Camera calibration With OpenCVicon-default.png?t=N7T8https://docs.opencv.org/4.x/d4/d94/tutorial_camera_calibration.html④折腾了几天,突然想到github和开源,终于找到了。关键词 calibration camera +python

下面详细记录一下

1、步骤及代码:

1)代码根据github一个土耳其大神mesutpiskin的开源,替换成中文注释

mesutpiskin/opencv-fisheye-undistortion: OpenCV camera calibration and image undistortion. (github.com)

2)非常重要的地方——内角点与棋盘格的方块的行列数不同

# 棋盘格的行和列数(按方块数的行和列减去1)
rows = 9
cols = 6

如果行和列设置不对,标定是摄像头画面是没有反应的。

如果摄像头画面没有反应,可以从以下方面进行排查,chatgpt4总结的还是不错的。

代码中的相机标定流程基本上是正确的,但是如果无法显示角点,可能有几个原因:

1. **棋盘格尺寸不匹配**:代码中设置的棋盘格行数和列数必须与实际使用的棋盘格完全匹配。如果实际棋盘格的行数或列数与代码中的设置不同,`cv2.findChessboardCorners`函数将无法正确找到角点。

2. **图像质量问题**:如果摄像头拍摄的图像模糊或光线条件不佳,可能会导致角点检测失败。确保棋盘格清晰可见,且光线均匀。

3. **棋盘格未被完整拍摄**:棋盘格必须完整地出现在摄像头视野中,否则角点检测也会失败。

4. **摄像头索引错误**:在代码中,`capture = cv2.VideoCapture(1)`尝试打开索引为1的摄像头。如果系统中只有一个摄像头或者索引不正确,可能无法获取图像。尝试将索引改为0或其他正确的摄像头索引。

5. **显示窗口的等待时间**:`cv2.waitKey(500)`设置了一个500毫秒的等待时间,这可能对于观察角点检测结果来说太短。可以尝试增加这个值,以便有足够的时间观察结果。

6. **参数设置问题**:`cv2.cornerSubPix`函数的参数设置可能不适合所有情况,可能需要根据实际情况调整搜索窗口的大小和停止条件。

7. **摄像头未正确初始化**:如果摄像头没有正确初始化,可能无法捕获图像。检查`retCam`的值以确保图像被正确读取。

8. **循环退出条件**:代码中的循环将在找到10次角点后退出。如果在循环内部发生错误,可能永远不会达到这个条件。

请检查以上可能的问题点,并确保使用的棋盘格图像和实际的棋盘格尺寸相匹配,摄像头正确初始化,且图像质量良好。如果问题仍然存在,可能需要进一步调试,例如通过打印中间变量的值或使用断点来检查程序的执行流程。

继续问,而且第一个就中了

Q:标定板的行和列如何计算的?

A:在相机标定中使用的棋盘格标定板,其行和列的计算是基于棋盘格内角点的数量,而不是棋盘格的方格数量。内角点是指棋盘格中黑白方块相交的角落点。

例如,如果你有一个8x8的棋盘格(就像国际象棋一样),那么它将有7x7的内角点,因为每一行和每一列的边缘方格没有相邻的方格来形成一个内角点。因此,对于一个8x8的棋盘格,你应该在相机标定代码中设置行数为7,列数也为7。

当你准备棋盘格用于相机标定时,你需要数一数棋盘格内部的角点,而不是方格的数量。确保在代码中正确设置这些值,否则`cv2.findChessboardCorners`函数将无法正确识别角点。

Q:方格是10x7,那内角点就是9x6对吗?

A:是的,如果棋盘格的方格是10x7,那么内角点的数量就是9x6。这是因为内角点是由方格的四个角相交形成的,所以在每个方向上,内角点的数量都会比方格的数量少一个。因此,对于一个10x7的棋盘格,你应该在相机标定代码中设置行数为9,列数为6。这样设置后,相机标定函数就能正确地识别和使用这些内角点进行标定了。

问题是有时一不容易就掉坑里了,下面是另一个大模型回答的,差点被它带到沟里了,选对工具很重要。

3)cv2.waitKey(5000)原来是500,改成了5000,避免操作跟不上。

4)详细代码如下

# -*- coding: utf-8 -*-
"""
Created on Wed Jun 13 18:48:48 2018
@author: mesutpiskin
"""

import cv2
import numpy as np

# 棋盘格的行和列数(按方块数的行和列减去1)
rows = 9
cols = 6

# 用于迭代算法的停止/决策标准
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 30, 0.001)

# 所需变量
objectPoints = np.zeros((rows * cols, 3), np.float32)
objectPoints[:, :2] = np.mgrid[0:rows, 0:cols].T.reshape(-1, 2)
objectPointsArray = []
imgPointsArray = []

capture = cv2.VideoCapture(1) # 连接到默认摄像头
found = 0 
# 重复直到成功校准10次
while(found < 10):
    # 从摄像头获取一帧并转换为灰度图像
    retCam, img = capture.read()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 查找棋盘格角点
    isSucces, corners = cv2.findChessboardCorners(gray, (rows, cols), None)

    # 是否找到角点?
    if isSucces:
        '''
        如果正确检测到与指定行和列数相符的角点
        使用cornerSubPix()方法来迭代地找到角点或径向背景点的亚像素位置。
        '''
        corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        # 存储这些点
        objectPointsArray.append(objectPoints)
        imgPointsArray.append(corners)

        # 绘制检测到的角点
        cv2.drawChessboardCorners(img, (rows, cols), corners, isSucces)
        found += 1
    
    cv2.imshow('校准', img)
    cv2.waitKey(5000)
cv2.destroyAllWindows()

# 将获得的K和D值保存为npz文件(也可以使用txt、xml、yaml)
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objectPointsArray, imgPointsArray, gray.shape[::-1], None, None)
np.savez('calibrationdata.npz', mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)

# 使用校准数据校正图像
cap = cv2.VideoCapture(1)
while True:
    # 读取灰度图像
    ret3, img = cap.read()
    h, w = img.shape[:2]
    
    '''
    使用我们校准得到的K和D值以及OpenCV的undistort*方法
    可以校正失真的图像。这些方法需要K和D值作为参数,
    并将返回校正后的图像。
    '''
    newCameraMtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
    undistortedImg = cv2.undistort(img, mtx, dist, None, newCameraMtx)
    
    cv2.imshow('校正后图像', undistortedImg)
    cv2.waitKey(0)
    
cv2.destroyAllWindows()

5)执行后效果如下,一直循环10次

得到保存的相机参数文件calibrationdata.npz,保存的位置没有与python文件同一路径

,在C:\users\XXX下面。

2、.npz文件及思考的问题

Q:详细介绍一下.npz文件

A:.npz文件是一种用于存储多个numpy数组的文件格式,通常用于在Python中进行科学计算和数据处理时保存和加载数据。这种文件格式是经过压缩的,可以有效地减小文件大小。在一个.npz文件中,可以包含多个数组,每个数组都有一个对应的名称。这种文件格式非常适合用于保存模型参数、训练数据、特征向量等多维数组数据。要读取.npz文件,可以使用numpy.load()函数加载文件内容。

Q:相机标定结果存储,.npz文件是最适合的格式吗?

A:对于相机标定结果的存储,.npz文件是一个非常适合的格式。相机标定通常涉及到多个参数,如相机内参、畸变参数、旋转矩阵、平移向量等,这些参数可以被整理成多个numpy数组,并以.npz格式进行存储。通过将这些参数存储在一个.npz文件中,可以方便地保持参数之间的对应关系,并且可以有效地压缩数据以节省存储空间。此外,使用numpy库加载.npz文件也非常方便,可以轻松地获取和操作存储的标定结果数据。因此,.npz文件是一个非常适合存储相机标定结果的格式。

Q:使用同一摄像头+同一标定板进行两次标定,光线和位置不同,得到的.npz文件一致吗?或者换一种说法,在一固定位置完成了用于机械臂引导抓取用普通摄像头的标定,位置移动后,是否需要重新进行标定?

A:在同一摄像头和同一标定板进行两次标定,但光线和位置不同的情况下,得到的.npz文件通常不会完全一致。由于光线和位置的变化可能导致标定结果中的参数值发生微小变化,例如相机内参、畸变参数等。因此,即使是同一摄像头和标定板,不同的光线和位置条件下进行标定,得到的标定结果可能会有细微差异。

当完成了用于机械臂引导抓取的摄像头标定后,如果位置发生移动,通常需要重新进行标定。因为摄像头的位置变化会影响到标定结果中的旋转矩阵和平移向量等参数,从而影响到后续的机械臂引导抓取的准确性。重新进行标定可以确保标定结果与摄像头实际位置的对应关系是准确的,从而保证机械臂引导抓取的精度和稳定性。

得到的.npz文件想要看看,用记事本打开乱码,可以用脚本打印到控制台。

import numpy as np

# 加载npz文件
data = np.load('calibrationdata.npz')

# 查看文件中的所有数组名称
print(data.files)

# 访问每个数组并查看其内容
mtx = data['mtx']
dist = data['dist']
rvecs = data['rvecs']
tvecs = data['tvecs']

print("相机内参矩阵 mtx:\n", mtx)
print("畸变系数 dist:\n", dist)
print("旋转向量 rvecs:\n", rvecs)
print("平移向量 tvecs:\n", tvecs)

# 关闭npz文件
data.close()
import numpy as np

# 加载npz文件
data = np.load('calibrationdata.npz')

# 查看文件中的所有数组名称
print(data.files)

# 访问每个数组并查看其内容
mtx = data['mtx']
dist = data['dist']
rvecs = data['rvecs']
tvecs = data['tvecs']

print("相机内参矩阵 mtx:\n", mtx)
print("畸变系数 dist:\n", dist)
print("旋转向量 rvecs:\n", rvecs)
print("平移向量 tvecs:\n", tvecs)

# 关闭npz文件
data.close()

执行结果大概这个样子

至此,完成了相机标定及文件的输出。

2024.4.11

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用OpenCV进行相机标定的示例代码: ```python import cv2 import numpy as np # 设置棋盘格尺寸 pattern_size = (9, 6) # 准备棋盘格角点的坐标 pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32) pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) # 存储棋盘格角点的世界坐标和图像坐标 obj_points = [] # 世界坐标 img_points = [] # 图像坐标 # 获取图像列表 images = [cv2.imread(f) for f in glob.glob('images/*.jpg')] # 查找棋盘格角点,并进行标定 for img in images: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, pattern_size, None) if ret: obj_points.append(pattern_points) img_points.append(corners) # 可视化棋盘格角点 cv2.drawChessboardCorners(img, pattern_size, corners, ret) cv2.imshow('Chessboard', img) cv2.waitKey(500) # 相机标定 ret, camera_matrix, distortion_coeffs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None) # 打印标定结果 print("相机内参数 (Camera Matrix):\n", camera_matrix) print("\n畸变系数 (Distortion Coefficients):\n", distortion_coeffs) # 保存标定结果 np.savez('calibration.npz', camera_matrix=camera_matrix, distortion_coeffs=distortion_coeffs) ``` 这个示例代码假设你已经拍摄了一些包含棋盘格的图像,并将它们保存在`images`文件夹中。代码使用`cv2.findChessboardCorners`函数查找棋盘格角点,然后使用`cv2.calibrateCamera`函数进行相机标定。最后,代码打印出相机内参数和畸变系数,并将它们保存到`calibration.npz`文件中。 请注意,这只是一个简单的示例代码,实际应用中可能需要根据具体情况进行适当的修改和调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值