1.定义
首先,我们来理解一下怎么从相机的角度去看一张图片,就好比如你用眼睛作为相机来进行摄影,但是比摄影机强的是,你是怎么摄影图片之后再将它矫正出现在你眼前,将歪歪扭扭的图片变成一张在你眼前是一张直的图片
为了轻松理解问题,假设您在房间中部署了一个摄像头。
给定这个房间中的一个 3D 点 P,我们希望在相机拍摄的图像中找到这个 3D 点的像素坐标 (u, v),即你眼前对应的二位平面的坐标
现在我们一步步进行推理变换
1.世界坐标系
2.相机坐标系
在射影几何中,使用齐次坐标来表示点,它在坐标上添加了一个额外的维度。
世界坐标在等式中使用齐次坐标,这允许在有限数内表示无限量,例如表示无穷远处的点
3.图像坐标系
现在则需要将相机坐标系中的3D坐标投影成投影面的2D坐标
特殊情况:如果相机不是正方形的,则x方向和y方向的焦距不相等,
因此我们可能有两种不同的焦距 fx 和 fy 相机的光学中心 (Cx,Cy) 可能与图像坐标系的中心不重合
总结
将世界坐标系中的 3D 点投影到相机像素坐标分两步完成。
- 使用外部矩阵将 3D 点从世界坐标转换为相机坐标,该矩阵由两个坐标系之间的旋转和平移组成。
- 相机坐标系中的新 3D 点使用内在矩阵投影到图像平面上,该矩阵由内部相机参数(如焦距、光学中心等)组成。
2.相关的函数方法
3.代码演示
import numpy as np
import cv2 as cv
import glob
# 终止标准
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像对象点与图像点的矩阵
objpoints = [] # 在真实世界中的 3d 点
imgpoints = [] # 在图像平面中的 2d 点
images = glob.glob('*.jpg')
for fname in images:
img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盘上所有的角点
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# 如果找到了,便添加对象点和图像点(在细化后)
if ret == True:
objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# 绘制角点
cv.drawChessboardCorners(img, (7,6), corners2, ret)
cv.imshow('img', img)
cv.waitKey(500)
cv.destroyAllWindows()
矫正
import numpy as np
import cv2 as cv
import glob
# 终止标准
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点, 如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像对象点与图像点的矩阵
objpoints = [] # 在真实世界中的 3d 点
imgpoints = [] # 在图像平面中的 2d 点
images = glob.glob('*.jpg')
for fname in images:
img = cv.imread(fname)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 找到棋盘上所有的角点
ret, corners = cv.findChessboardCorners(gray, (7,6), None)
# 如果找到了,便添加对象点和图像点(在细化后)
if ret == True:
objpoints.append(objp)
corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# 绘制角点
cv.drawChessboardCorners(img, (7,6), corners2, ret)
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
img2 = cv.imread(fname)
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# 矫正
dst = cv.undistort(img2, mtx, dist, None, newcameramtx)
# 裁切图像
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv.imwrite('calibresult.png', dst)
cv.destroyAllWindows()
4.总结
总结一下上面的叙述,首先是理解整个相机校准的原理,然后是通过代码进行进一步叙述,首先还是检测角点,如果是棋盘检测可以用上面的代码,也有很多其他检测角点的方法在上面简单提了一下,然后是通过角点位置,包括3d点(世界坐标系中的三维点)和2d点位置(图像检测出的角点坐标),接着利用calibrateCamera
计算相机参数(旋转,平移等),最后通过计算出的相机参数来矫正图片,使得图片尽量在平面上是以平面的形式展示。注意:检测角点和计算相机参数都需要通过额外的方法来修正。
下面简单演示一下用Harris检测角点的效果
import numpy as np
import cv2 as cv
# 终止标准
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 准备对象点,如 (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# 用于存储所有图像对象点与图像点的矩阵
objpoints = [] # 在真实世界中的 3D 点
imgpoints = [] # 在图像平面中的 2D 点
# 图像路径
image_path = r'C:\Users\xiaoou\Desktop\picture\chess.jpg'
# 读取图像
img = cv.imread(image_path)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 使用 cornerHarris 找到角点
dst = cv.cornerHarris(gray, 2, 3, 0.04)
print(dst.shape)
# 通过阈值筛选角点
img[dst > 0.01 * dst.max()] = [0, 0, 255]
# 寻找非零像素的坐标
y, x = np.nonzero(dst > 0.01 * dst.max())
# 将坐标转换为图像点格式
corners = np.vstack([x, y]).T.reshape(-1, 1, 2).astype(np.float32)
# 如果找到了,便添加对象点和图像点(在细化后)
if corners.shape[0] >= 42:
# print(objp.shape)
# print(corners.shape)
objpoints.append(objp)
imgpoints.append(corners[:42])
# 绘制角点
cv.drawChessboardCorners(img, (7, 6), corners, True)
cv.imshow('img', img)
cv.waitKey(0)
本次实验主要演示了三维重建中的相机校准,这对于要学自动驾驶或者三维重建的小伙伴都是要学的一门基础,主要函数方法怎么实现这里也不再一一介绍了。
如有错误或遗漏,希望小伙伴批评指正!!!!
希望这篇博客对你有帮助!!!!
实验七:Opencv实验合集——实验七:二维码和条形码匹配-CSDN博客