赞
踩
随着科技和经济的蓬勃发展,机器人作业、汽车自动导航等技术已经得到广泛应用,在很大程度上推动了社会生产力的发展。不论是主动光学视觉传感或者是被动光学视觉传感,要从图像推知物空间的三维信息,或反之,从空间三维信息推知二维图像坐标,都必须确定相机在参考坐标系中的空间位置和取向,以及相机本身的几何和光学参数,这个求解参数的过程就称之为相机标定。
【1】进行摄像机标定的目的:求出相机的内、外参数,以及畸变参数。
【2】标定相机后通常是想做两件事:一个是由于每个镜头的畸变程度各不相同,通过相机标定可以校正这种镜头畸变矫正畸变,生成矫正后的图像;另一个是根据获得的图像重构三维场景。
传统相机标定法需要使用尺寸已知的标定物,通过建立标定物上坐标已知的点与其图像点之间的对应,利用一定的算法获得相机模型的内外参数。
主动视觉的相机标定法是指已知相机的某些运动信息对相机进行标定。该方法不需要标定物,但需要控制相机做某些特殊运动,利用这种运动的特殊性可以计算出相机内部参数。基于主动视觉的相机标定法的优点是算法简单,往往能够获得线性解,故鲁棒性较高,缺点是系统的成本高、实验设备昂贵、实验条件要求高,而且不适合于运动参数未知或无法控制的场合。
自标定算法中主要是利用相机运动的约束。相机的运动约束条件太强,因此使得其在实际中并不实用。
相机标定指建立相机图像像素位置与场景点位置之间的关系,根据相机成像模型,由特征点在图像中坐标与世界坐标的对应关系,求解相机模型的参数。
相机需要标定的模型参数包括内部参数和外部参数。
针孔相机成像原理其实就是利用投影将真实的三维世界坐标转换到二维的相机坐标上去,其模型示意图如下图所示
从图中我们可以看出,在世界坐标中的一条直线上的点在相机上只呈现出了一个点,其中发生了非常大的变化,同时也损失和很多重要的信息,这正是我们3D重建、目标检测与识别领域的重点和难点。
实际中,镜头并非理想的透视成像,带有不同程度的畸变。理论上镜头的畸变包括径向畸变和切向畸变,切向畸变影响较小,通常只考虑径向畸变。
径向畸变
径向畸变主要由镜头径向曲率产生(光线在远离透镜中心的地方比靠近中心的地方更加弯曲)。导致真实成像点向内或向外偏离理想成像点。
枕形畸变
其中畸变像点相对于理想像点沿径向向外偏移,远离中心的,称为枕形畸变;
桶状畸变
径向畸点相对于理想点沿径向向中心靠拢,称为桶状畸变。
传统标定板与张正友标定法标定板
张正友标定法标定相机的内外参数的思路如下:
1)、求解内参矩阵与外参矩阵的积;
2)、求解内参矩阵;
3)、求解外参矩阵。
具体的求解过程可以参考下面这篇文章
https://zhuanlan.zhihu.com/p/94244568
import cv2 import numpy as np import glob # 找棋盘格角点 # 阈值 criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) #棋盘格模板规格 w = 14 #内角点个数,内角点是和其他格子连着的点 h = 9 # 世界坐标系中的棋盘格点,例如(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('./pc/*.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('pc/009.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))
mtx为内参数矩阵,dist为畸变系数
rvecs为旋转向量
rvecs为旋转向量
畸变矫正
通过标定得到该手机的后置摄像头的内置参数矩阵为:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。