计算机视觉--相机标定

一、背景

  本次实验的目标,主要是理解相机标定,而说到相机标定,还得从立体视觉的三维重建了解起。关于三维重建,简单点说,就是根据多张图片,从二维像素空间中还原出三维的场景。根据双目视差原理,建立双目立体视觉系统 ,即由一个或两个摄像头在不同位置拍摄某个物体的若干幅图像,通过特定的算法求取待测点在两幅图像中的视差,最后算出此点的空间坐标值。---包括:摄像机参数标定、立体标定与图像校正、立体匹配、三维恢复等几个部分。所以,相机标定是三维重建中的一个重点内容,理解三维重建的大背景,也能促进理解相机标定的意义和应用。

  三维重建的步骤:

   1. 图像采集

      2. 摄像机标定求取内参数与镜头的畸变量

      3. 特征提取, 即物体的点、线段、边缘、区域等 特征信息

      4. 立体匹配(最重要的步骤), 即建立空间中同一个点在不同图像中的对应关系

      5. 三维恢复, 即在立体匹配的基础上计算出视差后再用三角原则进行

      6. 后期处理
  三维重建的应用: 恢复战场的三维信息;医学立体成像;移动机器人导航(搭载两个摄像机恢复三维场景完成对目标的检测、定位和识别)

  相机标定:根据给定的摄像机成像模型求取摄像机内外参数和镜头的畸变量的过程,其实就是求解参数的过程

二、相机标定原理

  摄像机标定(Camera calibration)简单来说是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵 P 的过程。

  一般来说,标定的过程分为两个部分:

    第一步是从世界坐标系转换为相机坐标系,这一步是三维点到三维点的转换,包括 R,t (相机外参)等参数;
    第二部是从相机坐标系转为图像坐标系,这一步是三维点到二维点的转换,包括 K(相机内参)等参数;

  世界坐标:也称为测量坐标系,是一个三维直角坐标系,以其为基准可以描述相机和待测物体的空间位置。世界坐标系的位置可以根据实际情况自由确定。

  相机坐标:也是一个三维直角坐标系,原点位于镜头光心处,x、y轴分别与相面的两边平行,z轴为镜头光轴,与像平面垂直。

  世界坐标转化为相机坐标:

                             

 

 

   像素坐标与图像坐标的关系:

              

  像素坐标系uovuov是一个二维直角坐标系,反映了相机CCD/CMOS芯片中像素的排列情况。原点oo位于图像的左上角,uu轴、vv轴分别于像面的两边平行。像素坐标系中坐标轴的单位是像素(整数)。

  像素坐标系不利于坐标变换,因此需要建立图像坐标系XOYXOY,其坐标轴的单位通常为毫米(mm),原点是相机光轴与相面的交点(称为主点),即图像的中心点,XX轴、YY轴分别与uu轴、vv轴平行。故两个坐标系实际是平移关系,即可以通过平移就可得到。

  图像坐标转为像素坐标:

            

 

 

 三、实验代码

import cv2
import numpy as np
import glob

# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 6   #内角点个数,内角点是和其他格子连着的点
h = 4

# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
objp = np.zeros((w*h,3), np.float32)
objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
# 储存棋盘格角点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平面的二维点

images = glob.glob('picture/*.jpg')
for fname in images:
    img = cv2.imread(fname)
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # 找到棋盘格角点
    # 棋盘图像(8位灰度或彩色图像)  棋盘尺寸  存放角点的位置
    ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    # 如果找到足够点对,将其存储起来
    if ret == True:
        # 角点精确检测
        # 输入图像 角点初始坐标 搜索窗口为2*winsize+1 死区 求角点的迭代终止条件
        cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将角点在图像上显示
        cv2.drawChessboardCorners(img, (w,h), corners, ret)
        cv2.imshow('findCorners',img)
        cv2.waitKey(1000)
cv2.destroyAllWindows()
#标定、去畸变
# 输入:世界坐标系里的位置 像素坐标 图像的像素尺寸大小 3*3矩阵,相机内参数矩阵 畸变矩阵
# 输出:标定结果 相机的内参数矩阵 畸变系数 旋转矩阵 平移向量
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# mtx:内参数矩阵
# dist:畸变系数
# rvecs:旋转向量 (外参数)
# tvecs :平移向量 (外参数)
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)    # 平移向量  # 外参数
# 去畸变
img2 = cv2.imread('picture/5_d.jpg')
h,w = img2.shape[:2]
# 我们已经得到了相机内参和畸变系数,在将图像去畸变之前,
# 我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,
# 通过设定自由自由比例因子alpha。当alpha设为0的时候,
# 将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;
# 当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉
newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例参数

dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 根据前面ROI区域裁剪图片
x,y,w,h = roi
dst = dst[y:y+h, x:x+w]
cv2.imwrite('calibresult.jpg',dst)

# 反投影误差
# 通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。
# 通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,
# 然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。
total_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)
    total_error += error
print (("total error: "), total_error/len(objpoints))

  1.提取角点:findChessboardCorners(image,(w,h),None);第一个参数为图片,第二个为图片横纵角点的个数。

  2.找寻亚像素角点:cornerSubPix(gray, corners, (winsize, winsize), (-1, -1), criteria);

  winsize为搜索窗口边长的一半。

  zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。

  criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。
  3.
把角点画出来:drawChessboardCorners(image, (w, h), corners, ret)

  其中corners和ret为第一个函数的输出值。

四、实验结果

                                

 

   参数矩阵求解结果: 

      

 

       

 

 

 

附录-参考连接:

https://blog.csdn.net/weixin_42054950/article/details/80811656  三维重建介绍及原理

https://blog.csdn.net/betrapped/article/details/89247561   相机标定原理和过程

https://blog.csdn.net/lql0716/article/details/71973318?locationNum=8&fps=1    相机标定原理详解,真的很详细

posted @ 2021-05-22 23:32  予醉伴花眠  阅读(548)  评论(0)    收藏  举报