1. cv2.calibrateCamera
作用:
通过要标定相机拍摄的不同方位的棋盘图,获取相机的内参矩阵,畸变系数,以及每幅图的相应旋转,平移矩阵
函数定义:
def calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, rvecs=None, tvecs=None, flags=None, criteria=None)
参数说明:
>objectPoints:棋盘格角点在棋盘格坐标系的三维坐标
>imagePoints:棋盘格对应的角点
>imageSize:拍摄的图像分辨率(w,h)
返回值:
>retval:标志位(是否成功找到对应参数)
> cameraMatrix:内参矩阵
> distCoeffs:畸变参数
> rvecs
> tvecs
ps:
内参矩阵:
A = [ f x 0 c x 0 f y c y 0 0 1 ] A = \begin{bmatrix} f_x&0&c_x\\ 0&f_y&c_y\\ 0&0&1\\ \end{bmatrix} A=⎣⎡fx000fy0cxcy1⎦⎤
畸变系数:
( k 1 , k 2 , p 1 , p 2 [ , k 3 [ , k 4 , k 5 , k 6 [ , s 1 , s 2 , s 3 , s 4 [ , τ x , τ y ] ] ] ] ) (k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6[, s_1, s_2, s_3, s_4[, \tau_x, \tau_y]]]]) (k1,k2,p1,p2[,k3[,k4,k5,k6[,s1,s2,s3,s4[,τx,τy]]]]),其按照需求可以有4,5,8,12,14个元素
2. cv2.getOptimalNewCameraMatrix
作用:
当使用cv2.calibrateCamera
获得相机内参矩阵后,若直接进行去畸变操作,产生的图片会带有部分黑色的无效值,可通过调用本函数来优化相机内参矩阵,去除黑边。
函数定义:
def getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, alpha, newImgSize=None, centerPrincipalPoint=None)
参数说明:
>cameraMatrix:优化前的内参矩阵
>distCoeffs:畸变系数
>imageSize:原先图像尺寸
>alpha:值位于[0,1]之间的缩放因子,如果alpha=0,去畸变后的图像中的所有像素都是有效的(不会产生黑色无效像素),
但原有图片中的部分像素也会被丢弃。如果alpha=1,则未去畸变图片中的所有像素均被保留至去畸变后的图像中,
但也会产生相应黑色无效像素
>newImgSize:矫正后的图像尺寸
>centerPrincipalPoint:可选标志,表示在新的相机内参矩阵中,主点(the principal point光轴与相面的交点)是否应该位于图像中心。默认情况下,主点被设置为最佳拟合原图片子集的位置,以使其更好修正图片
返回值:
新的内参矩阵,validPixROI(outlines all-good-pixels region in the undistorted image.)
其中新的内参矩阵代表不对图像进行ROI截取对应的相机内参。(截取后,图像分辨率发生变化,相机内参再次变化)
3. cv2.initUndistortRectifyMap
作用:
获得从畸变图像到去畸变图像的像素映射函数
函数定义
def initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, m1type, map1=None, map2=None)
4. cv2.remap
作用:
借助映射函数将畸变图像映射回去畸变图像
5.cv2.undistort
该函数实际上是cv2.initUndistortRectifyMap
(其中R=单位矩阵)和cv2.remap
(使用双线性插值)的结合体。
作用:
将畸变图像通过相机内参矩阵和畸变系数矩阵转换为无畸变图像
函数定义:
def undistort(src, cameraMatrix, distCoeffs, dst=None, newCameraMatrix=None):
参数说明:
>src:输入的原始畸变图像
>cameraMatrix:通过cv2.calibrateCamera获得的相机内参A
>distCoeffs:畸变系数
>dst: 畸变矫正后的图像,其格式与尺寸和输入图像一致(默认设置为None)
>newCameraMatrix:默认情况下等于cameraMatrix,但当你相对输出图像进行缩放平移时,可输入该图像
返回值:
代表矫正后无畸变的图像矩阵,其格式与尺寸和输入图像一致。若在函数调用时应用newCameraMatrix,则还要对返
回图像进行相应的截取等操作以得到最终的图像。
6.综合代码
# encoding: utf-8
# by HoveXb
# 2021-06-12
####
import cv2
import numpy as np
import glob
import argparse
def calib(opt):
# 导入配置信息
width, height, square_size = opt.width, opt.height, opt.square_size
imgs_source = opt.imgs_source
images_format = opt.image_format
imgs_source = imgs_source + f'*.{images_format}'
drawChessboardCorners = opt.drawChessboardCorners
# 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
# 获取标定板角点的位置
objp = np.zeros((width * height, 3), np.float32)
# 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
objp[:, :2] = (np.mgrid[0:height, 0:width] * square_size).T.reshape(-1, 2)
obj_points = [] # 存储3D点
img_points = [] # 存储2D点
images = glob.glob(imgs_source)
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, (height, width), None)
if ret:
obj_points.append(objp)
corners2 = cv2.cornerSubPix(gray, corners, (5, 5), (-1, -1), criteria) # 在原角点的基础上寻找亚像素角点
if [corners2]:
img_points.append(corners2)
else:
img_points.append(corners)
# 是否画出角点并保存
if drawChessboardCorners:
cv2.drawChessboardCorners(img, (height, width), corners, ret) # 记住,OpenCV的绘制函数一般无返回值
i += 1
cv2.imwrite('conimg' + str(i) + f'.{images_format}', img)
cv2.waitKey(150)
else:
print("can't find cornet!")
print("there are total ", len(img_points), "images.\n")
cv2.destroyAllWindows()
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, size, None, None)
print("mtx:\n", mtx) # 内参数矩阵
print("dist:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
# 保存数据
mtx_save = np.array(mtx)
dist_save = np.array(dist)
np.save("mtx_LK15.npy", mtx_save)
np.save("dist_LK15.npy", dist_save)
# undistort_img = opt.undistort
# if undistort_img:
img = cv2.imread(images[0])
h0, w0 = img.shape[:2]
# If the scaling parameter alpha=0, it returns undistorted image with minimum unwanted pixels.
# So it may even remove some pixels at image corners.
# If alpha=1, all pixels are retained with some extra black images.
# This function also returns an image ROI which can be used to crop the result.
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w0, h0), 1, (w0, h0)) # 图片中的像素都是有效信息
print("------------------使用undistort函数(newcameramtx)-------------------")
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
x, y, w, h = roi
dst1 = dst[y:y + h, x:x + w]
cv2.imwrite('calibresult1' + f'.{images_format}', dst1)
print("Using cv.undistort_img(),dst的大小为:", dst1.shape)
print("------------------使用remapping-------------------")
mapx, mapy = cv2.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w0, h0), 5)
print('111111:',mapx.shape)
dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR)
# crop the image
dst2 = dst[y:y + h, x:x + w]
print("Using remapping", dst2.shape)
cv2.imwrite('calibresult2' + f'.{images_format}', dst2)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--imgs-source', type=str, default='./123/', help='imgs source')
parser.add_argument('--image-format', type=str, default='jpg', help='imgs source')
parser.add_argument('--width', type=int, default=8, help='how many inner squares there are in the chessboard')
parser.add_argument('--height', type=int, default=6, help='how many inner squares there are in the chessboard')
parser.add_argument('--square-size', type=int, default=1, help='square size of chess board(mm)')
parser.add_argument('--drawChessboardCorners', action='store_true', help='drawChessboardCorners or not')
parser.add_argument('--undistort', action='store_true', help='undistort')
opt = parser.parse_args()
calib(opt)
源码分析见:
https://blog.csdn.net/chengde6896383/article/details/88583653
https://zhuanlan.zhihu.com/p/66863619