使用Matlab、Opencv、Ros三种方法完成相机标定

一、前置知识总结

1、相机标定的意义

        在机器视觉领域,相机的标定是一个关键的环节,它决定了机器视觉系统能否有效的定位,能否有效的计算目标物。相机标定意义在于将现实世界中的三维物体与相机图像对应的二维物体映射起来,实际上就是透视投影。

2、相机标定原理

        针对针孔相机模型, 相机标定,标定的是指内参矩阵和外参矩阵,就可以确定为一的相机模型。

  • 相机的内参: 主要包含 焦距,相机主点坐标,以及畸变参数即 fx fy  cx cy k1 k2 k3 p1 p2
  • 相机的外参: 包含 旋转矩阵和平移矩阵 即 R  t     

 2、相机成像几何模型

  • 世界坐标系:是客观三维世界的绝对坐标系,也称客观坐标系。
  • 相机坐标系(光心坐标系):以相机的光心为坐标原点, X 轴和 Y 轴分别平行于图像坐标系的 X 轴和 Y 轴,相机的光轴为 Z 轴。
  • 图像坐标系:以图像平面的中心为坐标原点,X 轴和 Y 轴分别平行于图像平面的两条垂直边, 用( x , y )表示其坐标值。 图像坐标系是用物理单位(例如毫米)表示像素在图像中的位置。
  • 像素坐标系:以图像平面的左上角顶点为原点,X 轴和 Y 轴分别平行于图像坐标系的 X 轴和 Y 轴,用(u , v )表示其坐标值,单位是像素。

3、相机内外参基本原理    

        相机坐标系世界坐标系之间的坐标转换关系称为相机外参,一般由平移旋转两部分参数组成。求取外参的过程称之为外参标定,外参标定的结果关系到如何引导机器人进行抓取。
        相机内部参数(简称内参)只与相机内部属性(如焦距、分辨率、像素尺寸、镜头畸变等)
有关。利用相机内参可以将相机坐标系中的三维空间点变换到图像平面坐标系中,经过镜头畸变等校正过程之后可进一步变换至图像像素坐标系中的二维像素点。求取这一关系的
过程称为相机内参标定。

4、相机内参矩阵推导   

4.1 相机坐标系–>图像坐标系

        真实世界中的某点会投影在相机的成像平面上,利用针孔成像原理,空间任意一点Pc与图像点p之间的关系,Pc与相机光心Oc的连线为OcPc,与像面的交点p即为空间点Pc在图像平面上的投影。

        该过程为透视投影,利用相似三角形关系可得:

        其中f为焦距,z轴方向上s为Pc点到光心的距离,一般称为比例因子。投影关系为写成矩阵形式:

        其中,s为比例因子(s不为0),f为有效焦距(光心到图像平面的距离),(x,y,z,1)T是空间点P在坐标系oxyz中的齐次坐标,(x,y,1)T是像点p在图像坐标系OXY中的齐次坐标。

        注意:此时投影点p的单位还是mm,并不是pixel,需要进一步转换到像素坐标系。

4.2、图像坐标系–>像素平面坐标系

       像素坐标系和图像坐标系都在成像平面上,只是各自的原点和度量单位不一样。图像坐标系的原点为相机光轴与成像平面的交点,通常情况下是成像平面的中点或者叫principal point。图像坐标系的单位是mm,属于物理单位,而像素坐标系的单位是pixel,我们平常描述一个像素点都是几行几列。所以这二者之间的转换如下:

dx和dy表示每一列和每一行分别代表多少mm,即1pixel=dx mm。

5、相机透镜畸变与校正

5.1、径向畸变

 光线经过透镜中心会发生弯曲,这种现象称为径向畸变,径向畸变分为枕型畸变与筒形畸变。

  径向畸变围绕像素坐标及其泰勒展开式进行矫正。     

 其中(\widehat{x}\widehat{y})为矫正之后的坐标, (x,y)代表畸变像素坐标, r 代表成像中心距离,

5.2、切向畸变

相机畸变除了会产生径向畸变之外而且会产生切向畸变,透镜与成像平面不完全平行造成切向畸变的发生。

      切向畸变的矫正公式:

6、棋盘格图片采集

        分享一个可以生成各种标定板的网站: https://calib.io/pages/camera-calibration-pattern-generatorSingle Camera Calibrator App​​​​​​https://calib.io/pages/camera-calibration-pattern-generatorSingle Camera Calibrator App​​​​​​

和自定义检测器图案。有关这些图案的细节和包含可打印图案的PDF文件,请参见校准图案。

二、使用Opencv 获得图片

import cv2 as cv

print(cv.__version__)

def videoDemo():
    capture = cv.VideoCapture(0)
    n = 0
    while True:
        ret, frame = capture.read()
        n = n + 1
        print(f"num {n} {ret}")
        cv.imshow("Video", frame)
        c = cv.waitKey(50)
        if c == 32:
            cv.destroyAllWindows()
            cv.imwrite("/home/dofbot/camera/pic/borad.png", frame)
            cv.imshow("Video", frame)
            break

if __name__ == '__main__':
    videoDemo()

三、Matlab 

        支持棋盘格、圆圈格在APP中选择Camera Calibrator,如下:

        点击 Add Images,导入拍照图片。标定20张左右就够了,然后角度变一下,但不需要变太大,太大了会影响标定效果。标定板最好在视场中心,且占据较大面积。 

        修改棋盘格大小为27*27mm(我的A4纸测量是这样)

         对于标准相机,菜单栏的option里选择三阶径向畸变和斜切:

        点击Calibrate,进行相机标定:
        右上角是重建平均误差,只要平均误差小于0.5,就可以认为这是相机标定的结果是可靠的。

        把相机参数导出来,点击 Export Camera Parameters。点击确定,就可以看到matlab工作区出现了相机参数。点开这个参数,就可以得到相机的各个参数: 

四、Opencv

        为了找到棋盘的图案,使⽤函数 cv2.findChessboardCorners()。还需要传⼊图案的类型⽐如说 8x8 的格⼦或 5x5 的格⼦等。在本例中我们使⽤的9×6 的格⼦。(通常情况下棋盘都是 8x8 或者7x7)。它会返回⻆点,如果得到图像的话返回值类型(Retval)就会是 True。这些⻆点会按顺序排列(从左到右,从上到下)。 在找到这些⻆点之后我们可以使⽤函数 cv2.cornerSubPix() 增加准确度。我们使⽤函数cv2.drawChessboardCorners() 绘制图案。

        在得到了这些对象点和图像点之后,我们已经准备好来做摄像机标定了。我们要使⽤的函数是cv2.calibrateCamera()。它会返回摄像机矩阵,畸变系数,旋转和变换向量等。

所有的这些步骤都被包含在下⾯的代码中了:

import cv2
import numpy as np
import glob

# 找棋盘格⻆点
# 设置寻找亚像素⻆点的参数,采⽤的停⽌准则是最⼤循环次数30和最⼤误差容限0.001
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)  # 阈值
#棋盘格模板规格
w = 9   # 10 - 1
h = 6   # 7 - 1
# 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为⼆维矩阵
objp = np.zeros((w*h, 3), np.float32)
objp[:, :2] = np.mgrid[0:w,0:h].T.reshape(-1, 2)
objp = objp*18.1  # 18.1 mm

# 储存棋盘格⻆点的世界坐标和图像坐标对
objpoints = [] # 在世界坐标系中的三维点
imgpoints = [] # 在图像平⾯的⼆维点
#加载pic⽂件夹下所有的jpg图像
images = glob.glob('D:\pic\*.png') # 拍摄的⼗⼏张棋盘图⽚所在⽬录

i = 0
for fname in images:
    img = cv2.imread(fname)
    # 获取画⾯中⼼点
    #获取图像的⻓宽
    h1, w1 = img.shape[0], img.shape[1]
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    u, v = img.shape[:2]
    # 找到棋盘格⻆点
    ret, corners = cv2.findChessboardCorners(gray, (w, h), None)
    # 如果找到⾜够点对,将其存储起来
    if ret == True:
        print("i:", i)
        i = i+1
        # 在原⻆点的基础上寻找亚像素⻆点
        cv2.cornerSubPix(gray,corners, (11, 11), (-1, -1), criteria)
        #追加进⼊世界三维点和平⾯⼆维点中
        objpoints.append(objp)
        imgpoints.append(corners)
        # 将⻆点在图像上显⽰
        cv2.drawChessboardCorners(img, (w, h), corners, ret)
        cv2.namedWindow('findCorners', cv2.WINDOW_NORMAL)
        cv2.resizeWindow('findCorners', 640, 480)
        cv2.imshow('findCorners', img)
        cv2.waitKey(200)
cv2.destroyAllWindows()
#%% 标定
print('正在计算')
#标定
ret, mtx, dist, rvecs, tvecs = \
    cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

print("ret:", ret)
print("mtx:\n", mtx)  # 内参数矩阵
print("dist畸变值:\n", dist)  # 畸变系数 distortion cofficients = (k_©,k_ª,p_©,p_ª,k_«)
print("rvecs旋转(向量)外参:\n", rvecs)  # 旋转向量 # 外参数
print("tvecs平移(向量)外参:\n", tvecs)  # 平移向量 # 外参数
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v), 0, (u, v))
print('newcameramtx外参', newcameramtx)

五、Ros 

5.1 使用ROS usb_cam驱动相机

5.1.1 安装usbcam

Kinetic:

sudo apt-get install ros-kinetic-usb-cam

Melodic:

sudo apt-get install ros-melodic-usb-cam

其他版本

sudo apt-get install ros-版本名称-usb-cam
4.1.2 修改launch文件

进入目录:

roscd usb_cam
cd launch
sudo gedit usb_cam-test.launch 

目前主要修改device和width两个参数,可以使用ls /dev/video*查看系统视频设备。

<launch>
  <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
    <!-- modify the video device to your device -->
    <param name="video_device" value="/dev/video0" />
    <!-- modify the size of your device -->
    <param name="image_width" value="1280" />
    <param name="image_height" value="720" />
    <param name="pixel_format" value="yuyv" />
    <param name="camera_frame_id" value="usb_cam" />
    <param name="io_method" value="mmap"/>
  </node>
  <node name="image_view" pkg="image_view" type="image_view" respawn="false" ou$
    <remap from="image" to="/usb_cam/image_raw"/>
    <param name="autosize" value="true" />
  </node>
</launch>
c. 启动相机
roslaunch usb_cam usb_cam-test.launch

5.2 使用ROS进行相机标定

5.2.1 ros使用相机usb_cam标定内参。
  • 标定完成后点击Save可以保存标定所用的图片和参数矩阵。在终端里会输出标定产生的压缩包,默认放在/tmp目录下。
5.2.2 查看摄像头设备名
ls /dev/video*

选择自己需要显示的设备,再修改launch文件,就可以使用。

查看相机接口信息

v4l2-ctl -d 0 --all

参考相机接口信息修改usb_cam-test.launch文件,文件原内容如下

<launch>
  <node name="usb_cam" pkg="usb_cam" type="usb_cam_node" output="screen" >
    <param name="video_device" value="/dev/video0" />
    <param name="image_width" value="640" />
    <param name="image_height" value="480" />
    <param name="pixel_format" value="yuyv" />
    <param name="camera_frame_id" value="usb_cam" />
    <param name="io_method" value="mmap"/>
  </node>
  <node name="image_view" pkg="image_view" type="image_view" respawn="false" output="screen">
    <remap from="image" to="/usb_cam/image_raw"/>
    <param name="autosize" value="true" />
  </node>
</launch>

 运行修改后的launch文件即可

roslaunch usb_cam usb_cam-test.launch

打开标定窗口

一般来说正常安装ros都是包含了camera_calibration,输入下面命令检查一下

rosdep install camera_calibration #All required rosdeps installed successfully

运行前需要根据你的棋盘格修改参数

  • 一个是size参数为棋盘格内角点数量比如8x9=72个格子的棋盘格,角点个数为7x8=63个,size参数就要写7x8
  • 另外一个参数为square,传入的参数为棋盘格一个小格子的宽度(注意单位为m
  • image,图像话题的原始数据默认为camera:=/usb_cam
rosrun camera_calibration cameracalibrator.py --size 10x7 --square 0.015 image:=/usb_cam/image_raw camera:=/usb_cam
5.2.3 生成标定文件

标定完成后点击Calculate会稍微有点卡顿,不要担心后台正在进行标定,完成后下面的SAVE和COMMIT按钮变为可用状态,点击SAVE即可保存标定完成后的文件。

点击commit即可把标定文件存储到系统的~/.ros/camera_info/xxx.yaml目录(Ctrl+H 显示隐藏的目)

5.2.4 在ROS中使用该参数

可以在usb_cam的launch文件中增加以下参数,重新启动usb_cam节点,即可使用该标定参数。

参数值小鱼这里写的是file:///home/dev/.ros/camera_info/ost.yaml,可打开目录~/.ros/camera_info/进行查看

<param name="camera_info_url" type="string" value="file:///home/dev/.ros/camera_info/ost.yaml"/>

5.3.3 开始运行

source devel/setup.bash
roslaunch handeye-calib aruco_start_usb_cam.launch

六、 相机信息中的内参相关内容

以Intel Realsense彩色相机为例,以下是彩色相机的信息。

# rostopic echo /right_arm_camera/color/camera_info
---
header: 
  seq: 32308
  stamp: 
    secs: 1694683940
    nsecs: 530882359
  frame_id: "right_arm_camera_color_optical_frame"
height: 720
width: 1280
distortion_model: "plumb_bob"
D: [0.0, 0.0, 0.0, 0.0, 0.0]
K: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 1.0]
R: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
P: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 0.0, 1.0, 0.0]
binning_x: 0
binning_y: 0
roi: 
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: False
---
header: 
  seq: 32309
  stamp: 
    secs: 1694683940
    nsecs: 564413071
  frame_id: "right_arm_camera_color_optical_frame"
height: 720
width: 1280
distortion_model: "plumb_bob"
D: [0.0, 0.0, 0.0, 0.0, 0.0]
K: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 1.0]
R: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
P: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 0.0, 1.0, 0.0]
binning_x: 0
binning_y: 0
roi: 
  x_offset: 0
  y_offset: 0
  height: 0
  width: 0
  do_rectify: False
---

其中包括相机的内部参数有关的参数:

当处理相机内参时,常见的三个参数是 D、K 和 P,此外相关的还有畸变模型。

6.1、D畸变系数(distortion_coefficients):

D 是一个通常包含五个元素的数组 [k1, k2, p1, p2, k3],用于描述相机图像中的径向和切向畸变。

  • k1 和 k2 是径向畸变系数,它们用于描述镜头的弯曲形状,通常是负数。
  • p1 和 p2 是切向畸变系数,它们用于描述镜头的畸变不对称性,通常是很小的值。
  • k3 是一个更高阶的径向畸变系数,通常情况下很小,有时甚至可以忽略。
D: [0.0, 0.0, 0.0, 0.0, 0.0]

6.2、K相机内参矩阵(camera_matrix):

K 是一个3x3的矩阵,它包括了相机的内部参数,用于将相机坐标系中的三维点映射到图像平面上的二维坐标。

K 的主要元素包括:

  • K[0] 和 K[4] 是焦距在图像 x 和 y 轴上的分量,通常以像素为单位。
  • K[2] 和 K[5] 是光心在图像 x 和 y 轴上的坐标,也以像素为单位。
  • K[8] 通常是1,是一个归一化参数。
  • K 包含了焦距和主点的信息。
K: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 1.0]

6.3、P投影矩阵(projection_matrix):

P 是一个3x4的矩阵,包含了相机的投影参数,用于将相机坐标系中的三维点映射到图像坐标系。

P 的元素包括了内参和外参信息。

  • 其中 P[0] 和 P[5] 分别对应于焦距 fx 和 fy,用于将相机坐标系的 x 和 y 坐标映射到图像坐标系上。
  • P[2] 和 P[6] 对应于光心的 x 和 y 坐标 cx 和 cy。
  • P[3] 和 P[7] 通常为0,用于表示没有视角的旋转或平移。
  • P[10] 通常是1,是一个归一化参数。
  • 投影矩阵 P包含了相机的内部参数和畸变参数。
P: [911.4010620117188, 0.0, 645.8338623046875, 0.0, 0.0, 910.145263671875, 347.9687194824219, 0.0, 0.0, 0.0, 1.0, 0.0]

6.4、distortion_model"(畸变模型):

distortion_model 是相机标定信息中的一个字段,用于描述相机的畸变模型。
畸变模型是用来描述相机镜头产生的畸变效应的数学模型。
在计算机视觉中,畸变通常分为径向畸变和切向畸变两种主要类型,而畸变模型用于对这些畸变进行建模和校正。
Plumb Bob (钉形畸变模型):
“plumb_bob” 畸变模型是最常见的畸变模型之一,也是默认的畸变模型。它通常用于描述径向畸变(Radial Distortion)和切向畸变(Tangential Distortion)。
径向畸变主要是由于镜头形状不完全圆形引起的,导致图像中心附近的像素与图像边缘的像素之间存在畸变。这种畸变通常用多项式函数建模。
切向畸变则是由于相机镜头不完全平行于图像平面引起的,导致图像中心附近的像素存在畸变。切向畸变通常也用多项式函数建模。
“plumb_bob” 畸变模型使用径向和切向畸变系数(D)来描述这两种畸变。

matlab 相机标定代码 摄像机标定(Camera calibration)简单来说是从世界坐标系换到图像坐标系的过程,也就是求最终的投影矩阵的过程。 [1]基本的坐标系: 世界坐标系; 相机坐标系; 成像平面坐标系; 像素坐标系 [2]一般来说,标定的过程分为两个部分: 第一步是从世界坐标系转为相机坐标系,这一步是三维点到三维点的转换,包括R,t(相机外参,确定了相机在某个三维空间中的位置和朝向)等参数; 第二部是从相机坐标系转为成像平面坐标系(像素坐标系),这一步是三维点到二维点的转换,包括K(相机内参,是对相机物理特性的近似)等参数; 投影矩阵 : P=K [ R | t ] 是一个3×4矩阵,混合了内参和外参而成。 P=K[Rt] 二.基本知识介绍及 1、摄像机模型 Pinhole Camera模型如下图所示: 摄像机模型与标定 - 小企鹅 - 企鹅的博客 是一个小孔成像的模型,其中: [1]O点表示camera centre,即相机的中心点,也是相机坐标系的中心点; [2]z轴表示principal axis,即相机的主轴; [3]q点所在的平面表示image plane,即相机的像平面,也就是图片坐标系所在的二维平面; [4]O1点表示principal point,即主点,主轴与像平面相交的点; [5]O点到O1点的距离,也就是右边图中的f,即相机的焦距; [6]像平面上的x和y坐标轴是与相机坐标系上的X和Y坐标轴互相平行的; [7]相机坐标系是以X,Y,Z(大写)三个轴组成的且原点在O点,度量值为米(m); [8]像平面坐标系是以x,y(小写)两个轴组成的且原点在O1点,度量值为米(m); [9]像素坐标系一般指图片相对坐标系,在这里可以认为和像平面坐标系在一个平面上,不过原点是在图片的角上,而且度量值为像素的个数(pixel); 2、相机坐标系→成像平面坐标系 [1]以O点为原点建立摄像机坐标系。点Q(X,Y,Z)为摄像机坐标系空间中的一点,该点被光线投影到图像平面上的q(x,y,f)点。 图像平面与光轴z轴垂直,和投影中心距离为f (f是相机的焦距)。按照三角比例关系可以得出: x/f = X/Z y/f = Y/Z ,即 x = fX/Z y = fY/Z 以图像平面的左上角或左下角为原点建立坐标系。假设像平面坐标系原点位于图像左下角,水平向右为u轴,垂直向上为v轴,均以像素为单位。 以图像平面与光轴的交点O1 为原点建立坐标系,水平向右为x轴,垂直向上为y轴。原点O1一般位于图像中心处,O1在以像素为单位的图像坐标系中的坐标为(u0, v0)。 像平面坐标系和像素坐标系虽然在同一个平面上,但是原点并不是同一个。 摄像机模型与标定 - 小企鹅 - 企鹅的博客 设每个像素的物理尺寸大小为 dx * dy (mm) ( 由于单个像素点投影在图像平面上是矩形而不是正方形,因此可能dx != dy), 图像平面上某点在成像平面坐标系中的坐标为(x, y),在像素坐标系中的坐标为(u, v),则二者满足如下关系:[即(x, y)→(u, v)] u = x / dx + u0 v = y / dy + v0 用齐次坐标与矩阵形式表示为: 摄像机模型与标定 - 小企鹅 - 企鹅的博客 将等式两边都乘以点Q(X,Y,Z)坐标中的Z可得: 摄像机模型与标定 - 小企鹅 - 企鹅的博客 将摄像机坐标系中的(1)式代入上式可得: 则右边第一个矩阵和第二个矩阵的乘积亦为摄像机的内参数矩阵(单位为像素),相乘后可得: (2) 和(1)式相比,此内参数矩阵中f/dx, f/dy, cx/dx+u0, cy/dy+v0 的单位均为像素。令内参数矩阵为K,则上式可写成: 摄像机模型与标定 - 小企鹅 - 企鹅的博客 (3) 三.相机内参K(与棋盘所在空间的3D几何相关) 在计算机视觉中,摄像机内参数矩阵 其中 f 为摄像机的焦距,单位一般是mm;dx,dy 为像元尺寸;u0,v0 为图像中心。 fx = f/dx, fy = f/dy,分别称为x轴和y轴上的归一化焦距. 为更好的理解,举个实例: 现以NiKon D700相机为例进行求解其内参数矩阵: 就算大家身边没有这款相机也无所谓,可以在网上百度一下,很方便的就知道其一些参数—— 焦距 f = 35mm 最高分辨率:4256×2832 传感器尺寸:36.0×23.9 mm 根据以上定义可以有: u0= 4256/2 = 2128 v0= 2832/2 = 1416 dx = 36.0/4256 dy = 23.9/2832 fx = f/dx = 4137.8 fy = f/dy = 4147.3 分辨率可以从显示分辨率与图像分辨率两个方向来分类。 [1]显示分辨率(屏幕分辨率)是屏幕图像的精密度,是指显示器所能显示的像素有多少。由于屏幕上的点、线和面都是由像素组成的, 显示器可显示的像素越多,画面就越精细,同样的屏幕区域内能显示的信息也越多,所以分辨率是个非常重要的性能指标之一。 可以把整个图像想象成是一个大型的棋盘,而分辨率的表示方式就是所有经线和纬线交叉点的数目。 显示分辨率一定的情况下,显示屏越小图像越清晰,反之,显示屏大小固定时,显示分辨率越高图像越清晰。 [2]图像分辨率则是单位英寸中所包含的像素点数,其定义更趋近于分辨率本身的定义。 四.畸变参数(与点集如何畸变的2D几何相关。) 采用理想针孔模型,由于通过针孔的光线少,摄像机曝光太慢,在实际使用中均采用透镜,可以使图像生成迅速,但代价是引入了畸变。 有两种畸变对投影图像影响较大: 径向畸变和切向畸变。 1、径向畸变 对某些透镜,光线在远离透镜中心的地方比靠近中心的地方更加弯曲,产生“筒形”或“鱼眼”现象,称为径向畸变。 一般来讲,成像仪中心的径向畸变为0,越向边缘移动,畸变越严重。不过径向畸变可以通过下面的泰勒级数展开式来校正: xcorrected = x(1+k1r2+k2r4+k3r6) ycorrected = y(1+k1r2+k2r4+k3r6) 这里(x, y)是畸变点在成像仪上的原始位置,r为该点距离成像仪中心的距离,(xcorrected ,ycorrected )是校正后的新位置。 对于一般的摄像机校正,通常使用泰勒级数中的前两项k1和k2就够了;对畸变很大的摄像机,比如鱼眼透镜,可以使用第三径向畸变项k3 2、切向畸变 当成像仪被粘贴在摄像机的时候,会存在一定的误差,使得图像平面和透镜不完全平行,从而产生切向畸变。也就是说,如果一个矩形被投影到成像仪上时, 可能会变成一个梯形。切向畸变可以通过如下公式来校正: xcorrected = x + [ 2p1y + p2 (r2 + 2x2) ] ycorrected = y + [ 2p2x + p1 (r2 + 2y2) ] 这里(x, y)是畸变点在成像仪上的原始位置,r为该点距离成像仪中心的距离,(xcorrected ,ycorrected )是校正后的新位置。 五.摄像机的外参数 旋转向量(大小为1×3的矢量或旋转矩阵3×3)和平移向量(tx,ty,tz)。 旋转向量:旋转向量是旋转矩阵紧凑的变现形式,旋转向量为1×3的行矢量。 r就是旋转向量,旋转向量的方向是旋转轴 ,旋转向量的模为围绕旋转轴旋转的角度。 通过上面的公式,我们就可以求解出旋转矩阵R。同样的已知旋转矩阵,我们也可以通过下面的公式求解得到旋转向量: 。
OpenCV可以使用Intel的TBB(Threading Building Blocks)库进行多线程加速,从而提高相机标定的速度。以下是使用TBB库进行相机标定的基本步骤: 1. 导入OpenCV和TBB库 ```c++ #include <opencv2/opencv.hpp> #include <tbb/tbb.h> ``` 2. 定义并行任务 使用TBB库的主要目的是将相机标定中的一些计算密集型任务分配到多个线程中并行计算。在这里,我们可以将每个图像的角点检测和角点坐标的提取作为一个任务。 ```c++ class FindCornersTask { public: FindCornersTask(const cv::Mat& image, std::vector<cv::Point2f>& corners, cv::Size pattern_size, int flags) : image_(image), corners_(corners), pattern_size_(pattern_size), flags_(flags) {} void operator()() const { bool found = cv::findChessboardCorners(image_, pattern_size_, corners_, flags_); if (found) cv::cornerSubPix(image_, corners_, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1)); } private: cv::Mat image_; std::vector<cv::Point2f>& corners_; cv::Size pattern_size_; int flags_; }; ``` 3. 创建并行任务列表 我们可以使用TBB的并行算法`parallel_for`来运行一组任务。下面的代码创建一个任务列表,每个任务都是使用上一步定义的`FindCornersTask`类。 ```c++ std::vector<FindCornersTask> tasks; for (int i = 0; i < image_list.size(); i++) { tasks.push_back(FindCornersTask(image_list[i], corners[i], pattern_size, flags)); } ``` 4. 运行并行任务 我们可以使用`parallel_for`算法来运行一组任务。这将自动将任务分配到可用的线程上并行执行。 ```c++ tbb::parallel_for(tbb::blocked_range<size_t>(0, tasks.size()), [&](const tbb::blocked_range<size_t>& r) { for (size_t i = r.begin(); i != r.end(); ++i) { tasks[i](); } }); ``` 完整的示例代码如下: ```c++ #include <opencv2/opencv.hpp> #include <tbb/tbb.h> class FindCornersTask { public: FindCornersTask(const cv::Mat& image, std::vector<cv::Point2f>& corners, cv::Size pattern_size, int flags) : image_(image), corners_(corners), pattern_size_(pattern_size), flags_(flags) {} void operator()() const { bool found = cv::findChessboardCorners(image_, pattern_size_, corners_, flags_); if (found) cv::cornerSubPix(image_, corners_, cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.1)); } private: cv::Mat image_; std::vector<cv::Point2f>& corners_; cv::Size pattern_size_; int flags_; }; int main(int argc, char** argv) { std::vector<cv::Mat> image_list; // load images ... int flags = cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + cv::CALIB_CB_FAST_CHECK; cv::Size pattern_size(9, 6); std::vector<std::vector<cv::Point2f>> corners(image_list.size()); std::vector<FindCornersTask> tasks; for (int i = 0; i < image_list.size(); i++) { tasks.push_back(FindCornersTask(image_list[i], corners[i], pattern_size, flags)); } tbb::parallel_for(tbb::blocked_range<size_t>(0, tasks.size()), [&](const tbb::blocked_range<size_t>& r) { for (size_t i = r.begin(); i != r.end(); ++i) { tasks[i](); } }); // run camera calibration ... return 0; } ``` 这样,我们就可以使用TBB库加速相机标定的过程了。需要注意的是,TBB库需要在编译时与OpenCV一起链接。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值