原文发表于我的公众号:ADAS之眼,链接原文连接
“ CIS可见光相机是ADAS的眼睛,而相机标定则是其应用的基础,本文为初学者讲述了相机标定的模型以及逻辑,最后使用opencv中自带的例程演示了相机标定的demo。”
初中物理我们便学习了基础的小孔成像原理,现实生活中的蜡烛经过小孔之后,在小孔背面形成了物体倒立的投影,如下所示:
本质上,相机模型与小孔成像原理一样,不同的是,在真实的相机应用中:
真实的物理世界中一点Pw,我们可通过(Xw,Yw,Zw)进行具体定位。而相对于真实物理世界而言,相机世界中有三个坐标系,相机世界坐标系、物理成像坐标系以及像素坐标系,而相机标定的本质是找到一个数据模型,完成下图的转变:
在不考虑畸变的理想情况下,P点以及P’点分别在世界坐标系以及物理成像坐标系形成了相似三角形:根据相似三角形我们可以获取如下关系:
将上式整理可以得到P’点X’以及Y’的坐标值为:
这里完成了相机世界坐标系到物理成像坐标系的转换。现在在物理成像平面中有一个像素平面o-u-v,其中o是图像左上角原点,u、v分别与是物理成像坐标系的x、y轴平行。因此可以看出,o-u-v坐标系是经过物理成像坐标系X’-Y’缩放加平移过来的。
现在假设P’在像素平面的坐标为(u,v),u、v轴的缩放系数分别为α和β,原点平移了(cx,cy)的话,那么物理成像坐标系中P’点转化成像素坐标系的关系如下:
把X’和Y’带入,并把αf以及βf统一成fx以及fy可以得到:
将上式转换成矩阵的形式如下:
其中K是内参、T是外参,T的一般形式为:
[R, t]
至此,理想相机模型已经推导出来了。
在1.1中我们提到了真实相机存在各种各样的畸变以及安装误差。首先我们看由于镜头的不完美导致的径向畸变,它主要分为两种:桶形畸变以及枕形畸变,如下图所示:
在研究生课程数值分析中我们学习过非线性多项式拟合,而径向畸变刚好可以用多阶多项式进行拟合,经过校正的Xcorrect以及ycorrect拟合多项式可以如下表示:
可以看出,对于畸变比较小的中心区域,k1起到了主要作用,而对于畸变比较大的边缘区域,主要是k2在起作用。普通相机k1、k2便可以很好地校准畸变,对于畸变较大的鱼眼相机,往往还需要k3进行校正。
切向畸变来源于镜头、sensor安装的偏差,最直观来源的如下图所示:
对于切向畸变,我们可以使用另外两个参数p1以及p2进行校准,如下所示:
结合径向畸变以及切向畸变,可以得到最终的校准公式如下:
最终,我们将纠正的坐标通过内参矩阵进行计算,便可得到其在图像正确的位置:
至此,相机内参模型已经讲清楚。下面,我们使用opencv自带的历程,真实的跑一下相机标定的demo。
软件开发环境:
ubuntu20.04
vs code
opencv
cmake
g++
make
软件安装:
sudo apt-get install make cmake gcc g++ git
opencv源码编译安装:
git clone https://github.com/opencv/opencv.git #拉最新源码
cd opencv
mkdir build
cd build/
cmake .. #等待漫长的下载以及配置
make -j12 #具体编译线程数按照自己电脑情况来,然后等待执行完毕
sudo make install
将./opencv/samples/cpp/tutorial_code/calib3d/camera_calibration/目录下面的所有文件复制到你的工作目录下面:
camera_calibration.cpp in_VID5.xml out_camera_data.yml VID5.xml
在工作目录下新建build目录,然后复制./opencv/samples/data/left*.jpg图像到工作目录的build下。
在工作目录下新建CMakeLists.txt,内容参考如下:
# cmake needs this line
cmake_minimum_required(VERSION 3.1)
# Define project name
project(opencv_example_project)
# Find OpenCV, you may need to set OpenCV_DIR variable
# to the absolute path to the directory containing OpenCVConfig.cmake file
# via the command line or GUI
find_package(OpenCV REQUIRED)
# If the package has been found, several variables will
# be set, you can find the full list with descriptions
# in the OpenCVConfig.cmake file.
# Print some message showing some of them
message(STATUS "OpenCV library status:")
message(STATUS " config: ${OpenCV_DIR}")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
# Declare the executable target built from your sources
add_executable(calibration camera_calibration.cpp)
# Link your application with OpenCV libraries
target_link_libraries(calibration PRIVATE ${OpenCV_LIBS})
然后在build目录下执行:
cmake ..
make
编译完毕之后,因为文件路径变动,因此需要修改VID5.xml以及in_VID5.xml文件,VID5.xml存储了需要标定哪些图像:
<?xml version="1.0"?>
<opencv_storage>
<images>
left01.jpg
left02.jpg
left03.jpg
left04.jpg
left05.jpg
left06.jpg
left07.jpg
left08.jpg
</images>
</opencv_storage>
in_VID5.xml主要修改点在于VID5.xml的位置
<Calibrate_Pattern>"CHESSBOARD"</Calibrate_Pattern>
<!-- The input to use for calibration.
To use an input camera -> give the ID of the camera, like "1"
To use an input video -> give the path of the input video, like "/tmp/x.avi"
To use an image list -> give the path to the XML or YAML file containing the list of the images, like "/tmp/circles_list.xml"
-->
<Input>"VID5.xml"</Input>
<!-- If true (non-zero) we flip the input images around the horizontal axis.-->
<Input_FlipAroundHorizontalAxis>0</Input_FlipAroundHorizontalAxis>
<!-- Time delay between frames in case of camera. -->
<Input_Delay>100</Input_Delay>
修改之后,运行demo如下:
./cabraition ./in_VID5.xml
此时运行便会提取图像中的角点进行校准,标定完毕之后会显示畸变校准后的图像:
在app目录下会生成out_camera_data.xml,其中相机参数如下:
<camera_matrix type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
5.4340333589622401e+02 0. 3.1950000000000000e+02 0.
5.4340333589622401e+02 2.3950000000000000e+02 0. 0. 1.</data></camera_matrix>
<distortion_coefficients type_id="opencv-matrix">
<rows>5</rows>
<cols>1</cols>
<dt>d</dt>
<data>
-3.0200616231670130e-01 1.5646925098754344e-01 0. 0.
-6.5880224677954125e-02</data></distortion_coefficients>
其中,camera_matrix 33即相机内参,distortion_coeffiecients 51即为相机畸变参数,至此相机demo演示完毕。
本文介绍了相机模型以及内外参数的含义,并使用opencv的demo做了一次标定的流程,如果想要直接可运行的源码可以后台私信,希望对使用相机的初学者有一定的帮助。
本文介绍了相机模型以及内外参数的含义,并使用opencv的demo做了一次标定的流程,如果想要直接可运行的源码可以后台私信,希望对使用相机的初学者有一定的帮助。