欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

摄像机标定

程序员文章站 2023-12-27 10:40:57
...

坐标转换

摄像机标定

世界坐标系(3D)——>相机坐标系(3D)——>图像物理坐标系(2D)——>图像像素坐标系(2D)

摄像机标定

R代表旋转矩阵,T代表平移矩阵
f为摄像机的焦距
每个像素点在图像坐标系x轴、y轴方向的尺寸为:dx、dy

除了在四个坐标系之间的转化外,仍要考虑镜头畸变的影响
透镜的畸变主要分为径向畸变和切向畸变(还有薄透镜畸变等等,但都没有径向和切向畸变影响显著,所以我们在这里只考虑径向和切向畸变)。

径向畸变

是由于透镜形状的制造工艺导致。且越向透镜边缘移动径向畸变越严重。实际情况中我们常用r=0处的泰勒级数展开的前几项来近似描述径向畸变。矫正径向畸变前后的坐标关系为:

•xcorrected = x(1+k1r2+k2r4+k3r6)

•ycorrected = y(1+k1r2+k2r4+k3r6)

由此可知对于径向畸变,我们有3个畸变参数需要求解。

切向畸变

是由于透镜和成像平面不平行。切向畸变需要两个额外的畸变参数来描述,矫正前后的坐标关系为:

•xcorrected = x + [ 2p1y + p2 (r2 + 2x2) ]

•ycorrected = y + [ 2p2x + p1 (r2 + 2y2) ]

由此可知对于切向畸变,我们有2个畸变参数需要求解。

综上,我们一共需要5个畸变参数(k1、k2、k3、p1和p2 )来描述透镜畸变。

相机内参和外参

摄像机标定
我们将之前的坐标变换合并之后能够得到如上的式子,左边的矩阵代表了摄像机的内参,右边的矩阵代表了摄像机的外参。

标定方法

传统相机标定法需要使用尺寸已知的标定物,通过建立标定物上坐标已知的点与其图像点之间的对应,利用一定的算法获得相机模型的内外参数
有了以上的理论知识,我们至少需要 10张 图案模式来进行摄像机标定。为了便于理解,我们可以认为仅有一张棋盘图像。重要的是在进行摄 像机标定时我们要输入一组 3D 真实世界中的点以及与它们对应 2D 图像中的点。2D 图像的点可以在图像中很容易的找到。(这些点在图像中的位置是棋盘上两个黑色方块相互接触的地方)

那么真实世界中的 3D 的点呢?这些图像来源与静态摄像机和棋盘不同的摆放位置和朝向。所以我们需要知道(X,Y,Z)的值。但是为了简单,我 们可以说棋盘在 XY 平面是静止的,(所以 Z 总是等于 0)摄像机在围着棋 盘移动。这种假设让我们只需要知道 X,Y 的值就可以了

于是我用手机围绕电脑拍了13张不同角度的棋盘照片,作为标定物

代码

计算相机内外参数

1.1、寻找标定物

我们要使用函数cv2.findChessboardCorners()。我们还需要传入图案的类型,我的棋盘图是5*7的格式。它会返 回角点,如果得到图像的话返回值类型(Retl)就会是 True。这些角点会按顺序排列(从左到右,从上到下)。

ret, corners = cv2.findChessboardCorners(image, patternSize, corners, flags)

1.2、提高准确度

corners2 = cv2.cornerSubPix(gray, corners, (5,5), (-1,-1), criteria)

1.3、绘制角点

image = cv2.drawChessboardCorners(image, patternSize, corners, patternWasFound)

1.4、标定

在得到了这些对象点和图像点之后,我们已经准备好来做摄像机标定了。我们要使用的函数是 cv2.calibrateCamera()。它会返回摄像机矩阵,畸变系数,旋转和变换向量等。

ret, cameraMatrix, distCoeffs, rvecs, tvecs  =  cv2.calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, flags, criteria)

畸变矫正

现在我们找到我们想要的东西了,我们可以找到一幅图像来对他进行校正了。不过在那之前我们可以使用 从函数 cv2.getOptimalNewCameraMatrix() 得到的*缩放系数对摄 像机矩阵进行优化。如果缩放系数 alpha = 0,返回的非畸变图像会带有最少量 的不想要的像素。它甚至有可能在图像角点去除一些像素。如果 alpha = 1,所 有的像素都会被返回,还有一些黑图像。它还会返回一个 ROI 图像,我们可以 用来对结果进行裁剪。

img = cv2.imread(fname)
h,  w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# undistort
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# crop the image
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.png', dst)

完整代码

import cv2
import numpy as np
import glob
 
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
 
# 获取标定板角点的位置
objp = np.zeros((5*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:5].T.reshape(-1,2)  # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
 
obj_points = []    # 存储3D点
img_points = []    # 存储2D点
 
images = glob.glob('D:/sxjbd/*')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    size = gray.shape[::-1]
    ret, corners = cv2.findChessboardCorners(gray, (7,5), 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, (7,5), corners, ret)   # 记住,OpenCV的绘制函数一般无返回值
        cv2.imwrite(fname, img)
        cv2.waitKey(50)
 
print(len(img_points))
cv2.destroyAllWindows()
 
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points,size, None, None)
 
print("ret:",ret)
print("mtx:\n",mtx)    # 内参数矩阵
print("dist:\n",dist)     # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:\n",rvecs)    # 旋转向量  # 外参数
print("tvecs:\n",tvecs)    # 平移向量  # 外参数
 
print("-----------------------------------------------------")
# 畸变校正
l = len(images)
for i in range(l):
    img = cv2.imread(images[i])
    h, w = img.shape[:2]
    newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
    print(newcameramtx)
    dst = cv2.undistort(img,mtx,dist,None,newcameramtx)
    x,y,w,h = roi
    dst1 = dst[y:y+h,x:x+w]
    imgName="targetPic"+str(i)+".jpg"
    cv2.imwrite(imgName, dst1)
    print("dst的大小为:", dst1.shape)

原始图像
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定

标定后图像
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定
摄像机标定

上一篇:

下一篇: