准备工具:MATLAB2021A,python,opencv3.4.2.16
图像处理一系列工作往往都需要用到不同、特殊的相机,这些相机不同于日常生活中的普通相机,我们往往需要对其进行标定,以及利用标定参数对所得到的图片进行矫正。
目前业界已有成熟的标定方法——张正友棋盘标定法,这也是我们所常用的。相机标定原理和畸变来源在此就不过多赘述,此篇文章只给出相关软件在相机标定中的使用。
原理部分请自行阅读《视觉SLAM十四讲从理论到实践 第二版》118页至128页
提前准备一块标定板,或者使用命令行生成棋盘格
close all;
clear all;
clc;
width=1024 ; %pattern的宽
height=768 ; %pattern的高
img_final=zeros(height,width);
reinforceconner=0 ;%是否加强角点
row=8; %pattern中棋盘格的行数
col=12 ; %pattern中棋盘格的列数
length=60; %pattern中棋盘格的大小
org_X=(height-row*length)/2; %pattern关于纵轴方向的位置,默认放在中间
org_Y=(width-col*length)/2; %pattern关于横轴方向的位置,默认放在中间
color1=1;
color2=color1;
img=zeros(row*length,col*length);
for i=0:(row-1)
color2=color1;
for j=0:(col-1)
if color2==1
img(i*length+1:(i+1)*length-1,j*length+1:(j+1)*length-1)=color2;
end
%不加的话,可以注释掉
%
color2=~color2;
end
color1=~color1;
end
img_final(org_X:org_X+row*length-1,org_Y:org_Y+col*length-1)=img;
img_final=~img_final;
figure;imshow(img_final);
imwrite(img_final, 'cheesBoard.bmp','bmp');
接下来在不同位姿和角度拍摄完整的标定板图片,图片数量最好在15至20张
打开matlab,顶部工具栏APP部分,找到Camera Calibrator点击进入
进入新的页面后,Add Images
顶部栏选择Options,可选择标定参数个数,以及是否测量切向畸变,完成后点击Calibrate进行标定,成功后界面如下,可点击绿色对勾键将数据表示在命令行窗口方便查看各种具体参数
以下参数依次为:
K值,标定时选择三参数则此时有三个值,否则默认两个(径向畸变参数)
P值,两个 (切向畸变参数)
世界坐标
平移矩阵T
相机内参(矩阵)
旋转矩阵R
import numpy as np
import cv2
import glob
'''
在这里,我的棋盘格是8*8的,所以角点个数为7*7,当然棋盘格的行列个数可以不一样;
如果想方便代码改变棋盘格数,是以定义两个变量w(列角点数)和h(行角点数),注意如果角点维数超出的话,标定的时候会报错。
'''
# 终止标准
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)......,(6,5,0)
# objp = np.zeros((w*h,3), np.float32)
objp = np.zeros((6 * 6, 3), np.float32)
objp[:, :2] = np.mgrid[0:6, 0:6].T.reshape(-1, 2)
# 用于存储所有图像中的对象点和图像点的数组。
objpoints = [] # 在现实世界空间的3d点
imgpoints = [] # 图像平面中的2d点。
# glob是个文件名管理工具
images = glob.glob(r"C:/Users/ZJH/Desktop/image/*.png")
print('...loading')
for fname in images:
# 对每张图片,识别出角点,记录世界物体坐标和图像坐标
print(f'processing img:{fname}')
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转灰度
print('grayed')
# 寻找角点,存入corners,ret是找到角点的flag
# ret, corners = cv2.findChessboardCorners(gray, (w, h),None)
ret, corners = cv2.findChessboardCorners(gray, (6, 6), None)
# 如果找到,添加对象点,图像点(精炼后)
if ret == True:
print('chessboard detected')
objpoints.append(objp)
# 执行亚像素级角点检测
corners2 = cv2.cornerSubPix(gray, corners, (9, 9), (-1, -1), criteria)
imgpoints.append(corners2)
# 绘制并显示角点
# img = cv2.drawChessboardCorners(img, (w,h), corners2,ret)
img = cv2.drawChessboardCorners(img, (6, 6), corners2, ret)
cv2.namedWindow('img', 0)
cv2.resizeWindow('img', 500, 500)
cv2.imshow('img', img)
cv2.waitKey(5000)
cv2.destroyAllWindows()
'''
传入所有图片各自角点的三维、二维坐标,相机标定。
每张图片都有自己的旋转和平移矩阵,但是相机内参和畸变系数只有一组。
mtx,相机内参;dist,畸变系数;revcs,旋转矩阵;tvecs,平移矩阵。
'''
img2 = cv2.imread(r"C:/Users/ZJH/Desktop/image/Image_20220802101955958.png")
#print(f"type objpoints:{objpoints[0].shape}")
#print(f"type imgpoints:{imgpoints[0].shape}")
ret,mtx,dist,rvecs,tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
h, w = img2.shape[:2]
'''
优化相机内参(camera matrix),这一步可选。
参数1表示保留所有像素点,同时可能引入黑色像素,
设为0表示尽可能裁剪不想要的像素,这是个scale,0-1都可以取。
'''
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# 纠正畸变
dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
# 裁剪图像,输出纠正畸变以后的图片
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv2.imwrite('calibresult.png', dst)
# 打印我们要求的两个矩阵参数
print("newcameramtx内参:\n", newcameramtx)
print("dist畸变值:\n", dist)
print("newcameramtx旋转(向量)外参:\n", rvecs)
print("dist平移(向量)外参:\n", tvecs)
print("roi:",roi)
# 计算误差
tot_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)
tot_error += error
print("total error: ", tot_error / len(objpoints))
使用时角点检测、存储3D和2D点等部分若图片质量不够高时可将7*7的区域下调为6*6、5*5,此时标定效果会下降,若使用图片质量足够好且标定板大小足够可上调至9*9,效果精度上升。
接下来提供批量矫正代码
import os
import cv2
import numpy as np
import glob
def Correct(path, outpath):
mtx = [[1.469676421803936e+04,0.00000000e+00 ,2.037223864686618e+03],
[0.00000000e+00 ,1.469588493956052e+04,1.378055336391949e+03],
[0.00000000e+00 ,0.00000000e+00 ,1.00000000e+00]]#内参
dist = [[-0.011893710738358, 4.904839042705877 ,-0.002652573042541,-2.067947202844048e-04
,-1.154190361442721e+02]]#畸变参数--顺序很重要!!!:k1,k2,p1,p2,k3
image_path = path
img = cv2.imread(image_path)
img_distort = cv2.undistort(img, np.array(mtx), np.array(dist))
# ---------------------------------------------------------------------------------
cv2.imwrite(outpath, img_distort)
return img_distort
if __name__ == "__main__":
count=os.listdir(r"C:/Users/ZJH/Desktop/image")
countt=os.listdir(r"C:/Users/ZJH/Desktop/image_1")
for i in range(0,len(count)):
path=os.path.join("C:/Users/ZJH/Desktop/image",count[i])
outpath=os.path.join("C:/Users/ZJH/Desktop/image_1/"+count[i].split(".")[0]+".png")
Correct(path, outpath)
print(path)
print(outpath+"\n")
# path = "C:/Users/ZJH/Desktop/image/31.png"
# outpath = "C:/Users/ZJH/Desktop/image_1/31.png"
# Correct(path, outpath)
修改路径,导入参数,参数可使用MATLAB进行标定,快捷且精度较高
参考 计算机视觉——python3实现张正友棋盘相机标定图像
Matlab相机标定方法及主要参数含义,坐标变换过程