一、相机锁定
1 、相机成像原理
相机成像原理分为透镜成像原理和小孔成像原理。
现代相机有很多分类,且分类标准不统一。在这里简单分类为可更换镜头相机和不可更换镜头相机两种。对于可更换镜头而言。例如单反,单镜头反光照相机,镜头只是整个系统的一个部件,他的作用是获得我们想要的成像效果,但是镜头并不是成像的必要部件,把镜头取下,依然可以获得成像,此时就是利用了小孔成像的原理。对于没有反光镜设计的微单而言也一样。但是对于不可更换镜头的相机而言,那就是透镜成像原理。(其实也可以拆卸,只是你不能随意拆卸更换。)所以,严谨正确的表述应该是,在不拆掉现代相机的镜头时,现代相机运用的是透镜成像原理,在拆掉镜头之后,现代相机的机身本身利用的是小孔成像原理。
从中幅到手机镜头,统统都是透镜成像。
透镜成像原理
凸透镜的成像规律是1/u+1/v=1/f(即:物距的倒数与像距的倒数之和等于焦距的倒数。
△ABO∽△A’B’O与△COF∽△A’B’F推到出
小孔成像原理
小孔成像,基于光线是直线传播:当小孔只能让一根光线通过时,从光源上的一点发出的直线传播的光线中,只有一根能通过小孔照射到屏上,光源上每一个点都只有一根光线通过小孔在自己照射到对应位置上,在屏上组成了光源的像;光源发出的光线在成像过程中没有改变方向,没有汇聚或发散,所以不存在焦点,也就没有焦距;
但成像中,光源到小孔的距离(物距)是存在的,屏到小孔的距离也同样是存在的,屏到小孔的距离实质上就是像距,改变像距可以改变像的大小。
当小孔的直径较大时,光源上一点发出的光线在屏上得到较大的光斑,光源每点的像都是大光斑,使得整个光源的像由相互重叠的大光斑构成,无法得到清晰的光源的像,孔太小,图象清晰但亮度较低,适当的选择小孔直径大小,是得到清晰图象的关键
2、坐标映射
1、设备坐标系都是以像素为单位,水平(x轴)从左到右递增,垂直(y轴)从上到下递增
2、原点(0, 0)坐标
屏幕坐标:以显示器左上角为原点
窗口坐标:以窗口左上角为原点
客户区域坐标:以客户区坐标窗口为原点
3、映射方式用于定义从“窗口”(逻辑坐标)到“视口”(设备坐标)的映射,“窗口”和“视口”这两个词用得并不恰当,因为视口通常包含裁剪区域的意思,况且在windows中又已经用窗口来指程序在屏幕上占住的区域。
4、逻辑坐标转换为设备坐标
xViewport = (xWindow - xWinOrg) * xViewExt/xWinExt + xViewOrg
yViewport = (yWindow - yWinOrg) * yViewExt/yWinExt + yViewOrg
公式中使用了“范围”:xWinExt、yWinExt、xViewExt、yViewExt,每个范围自身没有什么意义,但是视口范围和窗口范围的比是逻辑单位转换为设备单位的换算因子,windows提供2个函数来进行坐标转换,DPtoLP,LPtoDP
5、SetViewportOrg是以2中描述的坐标进行参考
3、相机锁定
1、相机标定就是求解上面的 K[R | t] 矩阵前提是我们已知了物体在世界中的三维坐标和在图像上的二维坐标。二维坐标的位置比较好确定,问题是如何确定三维坐标的位置,采用传统的办法就是构造一个标定板,标定板的3个平面相互垂直,每个小孔的距离相等,通过标定板中的小孔可以确定物体在三维空间中的坐标。板上每个小孔的距离数值的精确度要非常高。
相机锁定原理
摄像机标定(Camera calibration)简单来说是从世界坐标系转换为相机坐标系,再由相机坐标系转换为图像坐标系的过程,也就是求最终的投影矩阵P的过程。
世界坐标系(world coordinate system):用户定义的三维世界的坐标系,为了描述目标物在真实世界里的位置而被引入。单位为m。
相机坐标系(camera coordinate system):在相机上建立的坐标系,为了从相机的角度描述物体位置而定义,作为沟通世界坐标系和图像/像素坐标系的中间一环。单位为m。
图像坐标系(image coordinate system):为了描述成像过程中物体从相机坐标系到图像坐标系的投影透射关系而引入,方便进一步得到像素坐标系下的坐标。 单位为m。
从世界坐标系到相机坐标系:
这一步是三维点到三维点的转换,包括R,t(相机外参)等参数;
相机坐标系转换为图像坐标系:
这一步是三维点到二维点的转换,包括K(相机内参)等参数;
根据上述的关系图可以推导出下面的变换公式:
像主点的偏移:
可以推出:
内参矩阵K:
外参矩阵[R丨t]:
投影矩阵P
总结一下公式大致如下:
4、相机锁定代码
import cv2
import numpy as np
import glob
# 找棋盘格角点
# 阈值
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
#棋盘格模板规格
w = 7 #内角点个数,内角点是和其他格子连着的点
h = 7
# 世界坐标系中的棋盘格点,例如(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))
5、测试图片
6、运行结果