基于MATLAB和python的简易相机标定

准备工具:MATLAB2021A,python,opencv3.4.2.16

图像处理一系列工作往往都需要用到不同、特殊的相机,这些相机不同于日常生活中的普通相机,我们往往需要对其进行标定,以及利用标定参数对所得到的图片进行矫正。

目前业界已有成熟的标定方法——张正友棋盘标定法,这也是我们所常用的。相机标定原理和畸变来源在此就不过多赘述,此篇文章只给出相关软件在相机标定中的使用。

原理部分请自行阅读《视觉SLAM十四讲从理论到实践 第二版》118页至128页 

MATLAB部分

提前准备一块标定板,或者使用命令行生成棋盘格

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');

基于MATLAB和python的简易相机标定_第1张图片

 如果使用标定版则如下:基于MATLAB和python的简易相机标定_第2张图片

基于MATLAB和python的简易相机标定_第3张图片 此标定板是12格*9格*40mm的标定板

接下来在不同位姿和角度拍摄完整的标定板图片,图片数量最好在15至20张

打开matlab,顶部工具栏APP部分,找到Camera Calibrator点击进入基于MATLAB和python的简易相机标定_第4张图片 

 进入新的页面后,Add Images 

基于MATLAB和python的简易相机标定_第5张图片

 顶部栏选择Options,可选择标定参数个数,以及是否测量切向畸变,完成后点击Calibrate进行标定,成功后界面如下,可点击绿色对勾键将数据表示在命令行窗口方便查看各种具体参数基于MATLAB和python的简易相机标定_第6张图片

基于MATLAB和python的简易相机标定_第7张图片

以下参数依次为:

K值,标定时选择三参数则此时有三个值,否则默认两个(径向畸变参数)

P值,两个 (切向畸变参数)

世界坐标

平移矩阵T

相机内参(矩阵)

旋转矩阵R

python opencv部分


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相机标定方法及主要参数含义,坐标变换过程

你可能感兴趣的:(日志,matlab,python)