4.1 针孔照相机模型
图像坐标轴和三维坐标系中的x轴、y轴对齐平行,光学坐标轴和z轴一致,在投影之前通过旋转和平移变换,在坐标系中加入三维点,会出现完整的投影变换。
针孔照相机中,三维点X投影为图像点x(齐次坐标表示),也就是把一个三维立体图形投影为二维图像,关系可由如下:
4.1.1 投影矩阵
分解为:
R为旋转矩阵,t为三维平移向量,内标定矩阵K描述照相机的投影性质,大多数情况下,K可表示为:
4.1.2 三维点的投影
#导入线性代数部分
from scipy import linalg
import camera
class Camera(object):
""" Class for representing pin-hole cameras. """
def __init__(self,P):
""" Initialize P = K[R|t] camera model. """
self.P = P
# 标定矩阵
self.K = None # calibration matrix
self.R = None # rotation
self.t = None # translation
# 照相机中心
self.c = None # camera center
def project(self,X):
""" Project points in X (4*n array) and normalize coordinates. """
#定义和坐标归一化
# (3,n)
x = dot(self.P,X)
for i in range(3):
x[i] /= x[2]
return x
# 载入点
points = loadtxt('house.p3d').T
points = vstack((points,ones(points.shape[1])))
# 设置照相机参数
# eye(3)是三阶单位矩阵
P = hstack((eye(3),array([[0],[0],[-10]])))
# 使用类Camera
cam = camera.Camera(P)
# 投影点
x = cam.project(points)
# 绘制投影
figure()
plot(x[0],x[1],'k.')
axis('off')
show()
4.1.3 照相机矩阵的分解
def factor(self):
""" Factorize the camera matrix into K,R,t as P = K[R|t]. """
# 分解前(3,3)
K,R = linalg.rq(self.P[:,:3])
# make K 对角为正值
T = diag(sign(diag(K)))
if linalg.det(T) < 0:
T[1,1] *= -1
self.K = dot(K,T)
# 令R为正定矩阵
self.R = dot(T,R) # T is its own inverse
self.t = dot(linalg.inv(self.K),self.P[:,3])
return self.K, self.R, self.t
from Camera import * from numpy import * import camera K = array([[5000,0,800],[0,800,500],[0,0,1]]) tmp = Camera.rotation_matrix([0,0,1])[:3,:3] Rt = hstack((tmp,array([[50],[40],[30]]))) cam = Camera(dot(K, Rt)) print(K, Rt) print(cam.factor())
4.1.4 计算机相机中心
给定照相机投影矩阵 P,我们可以计算出空间上照相机的所在位置。照相机的中心 C,是一个三维点,满足约束 PC=0。对于投影矩阵为P = K [ R ∣ t ] P=K\left [ R\mid t \right ]P=K[R∣t]的照相机,有:
4.2 照相机标定
4.2 照相机标定
代码:
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:5, 0:7].T.reshape(-1, 2) # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y obj_points = [] # 存储3D点 img_points = [] # 存储2D点 images = glob.glob(r"D:\JSJSJ\images\camerea\*.jpg") i=0; for fname in images: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) size = gray.shape[::-1] ret, corners = cv2.findChessboardCorners(gray, (5, 7), None) #print(corners) if ret: obj_points.append(objp) corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角点的基础上寻找亚像素角点 #print(corners2) if [corners2]: img_points.append(corners2) else: img_points.append(corners) cv2.drawChessboardCorners(img, (5, 7), corners, ret) # 记住,OpenCV的绘制函数一般无返回值 i+=1; cv2.imwrite('conimg'+str(i)+'.jpg', img) cv2.waitKey(1500) print(len(img_points)) cv2.destroyAllWindows() # 标定 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None) print("内参数矩阵:\n", mtx) # 内参数矩阵 print("畸变系数:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3) print("旋转向量:\n", rvecs) # 旋转向量 # 外参数 print("平移向量:\n", tvecs ) # 平移向量 # 外参数
需要注意的是我使用的棋盘格子是6*8,所以内角点为5*7,手机型号为iPhone 13 (蓝)
结果:
图像校正
img = cv2.imread(images[2]) h, w = img.shape[:2] # 获取新的相机矩阵和ROI newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h)) # 进行畸变校正 dst = cv2.undistort(img, mtx, dist, None, newcameramtx) # 裁剪校正后的图片,去掉黑边 x, y, w, h = roi dst = dst[y:y+h, x:x+w] # 保存校正后的图片 cv2.imwrite('undistorted.jpg', dst) print("校正后的图片已保存到文件 'undistorted.jpg'")
左边为校正后的图像。