Calibrate Monocular Vision Cameras With Narrow-Angle Lens ========================================================= Basic Camera Calibration ------------------------ Cannonical theories and methods of `pinhole camera calibration `_ have been deeply investigated for about two centuries. Camera calibration has already been widely used in `photogremmetry `_, `surveying `_ and `mapping `_, etc. Basically, the `pinhole camera model `_ of imaging from the 3D `world frame `_ to the 2D `image plane `_ is simply denoted by the following ill-conditioned equation: .. math:: \begin{pmatrix} \vec{u} \\ \vec{v} \end{pmatrix} =P_{2*3} \begin{pmatrix} \vec{X} \\ \vec{Y} \\ \vec{Z} \end{pmatrix} , where .. math:: \begin{pmatrix} \vec{u} \\ \vec{v} \end{pmatrix} = \begin{pmatrix} u_1 & u_2 & \cdots & u_n \\ v_1 & v_2 & \cdots & v_n \end{pmatrix} represents the projected 2D points' coordinates on the image plane, .. math:: \begin{pmatrix} \vec{X} \\ \vec{Y} \\ \vec{Z} \end{pmatrix} = \begin{pmatrix} X_1 & X_2 & \cdots & X_n \\ Y_1 & Y_2 & \cdots & Y_n \\ Z_1 & Z_2 & \cdots & Z_n \end{pmatrix} represents the 3D points' coordinates in the world `coordinate system `_. :math:`P_{2*3}` is a :math:`2*3` projection matrix, :math:`n` represents the number of points under concern. Normally, :math:`n>=4`. By using `Homogeneous Coordinates `_ for both the 2D image point, as well as the 3D world point, the above equation can be re-denoted as: .. math:: \begin{pmatrix} \vec{u} \\ \vec{v} \\ \vec{w} \end{pmatrix} =P_{3*4} \begin{pmatrix} \vec{X} \\ \vec{Y} \\ \vec{Z} \\ \vec{1} \end{pmatrix} - References * `Camera Matrix slides from CMU 16-385 Computer Vision (Kris Kitani) `_ * `Camera Models slides from Stanford CS231A Course Notes 1 (Kenji Hata and Silvio Savarese) `_ * `Camera Calib Course Contents from Professor Aditi Majumder, UCI `_ * `Camera Models and Parameters slides from UToronto `_ * `Camera Calibration and 3D Reconstruction of OpenCV 2.4 `_ * etc. By refering to the above references, :math:`P_{3*4}` in the above pinhole camera model can be decomposed to: .. math:: P_{3*4}&=A_{3*3}[R_{3*3}|t_{3*1}] \\ &=\begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} r_{11} & r_{12} & r_{13} & t_1 \\ r_{21} & r_{22} & r_{23} & t_2 \\ r_{31} & r_{32} & r_{33} & t_3 \end{pmatrix} , where .. math:: A_{3*3}=\begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} is the `camera matrix <./02_calibrate_monocular_vision_cameras_with_narrow_angle_lens.html#camera-matrix>`_, which is also called camera's **intrinsic parameters**; .. math:: R_{3*3}=\begin{pmatrix} r_{11} & r_{12} & r_{13} \\ r_{21} & r_{22} & r_{23} \\ r_{31} & r_{32} & r_{33} \end{pmatrix} is the `rotation matrix `_; .. math:: t_{3*1}=\begin{pmatrix} t_1 \\ t_2 \\ t_3 \end{pmatrix} is the `translation `_. The combination of **rotation matrix** and **translation** .. math:: [R_{3*3}|t_{3*1}]= \begin{pmatrix} r_{11} & r_{12} & r_{13} & t_1 \\ r_{21} & r_{22} & r_{23} & t_2 \\ r_{31} & r_{32} & r_{33} & t_3 \end{pmatrix} is just called camera's **extrinsic parameters**, which **also** defines the camera's **relative posture**. Camera Matrix ^^^^^^^^^^^^^ Clearly, there are several obvious systematic errors: - the process of camera assembling cannot guarantee the principal `optic axis `_ pass through the center of `CMOS (complementary metal oxide semiconductor) `_. In such, the center we expect to be at the center of the image will not be exactly the real scenary center. - the manufacturing of camera lens cannot guarantee a perfect isotropic lens surface, therefore the `focal length `_ in every direction are not perfectly equal. And the lens assembling process cannot guarantee the `focal length `_ to be always of the same length. The `focal length(s) `_ are even adjustable sometimes. For simplicity, `focal lengths `_ in two directions :math:`x` and :math:`y` are assumed to be fixed and used to represent the camera's `focal lengths `_ in all directions. Therefore, the following 4 parameters :math:`f_x, f_y, c_x, c_y` are to be calculated, where :math:`(f_x, f_y)` are two `focal lengths `_ in the direction of :math:`x` and :math:`y`, and :math:`(c_x, c_y)` represent the image center. Clearly, if we write these 4 parameters in the form of the following **camera matrix** .. math:: \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} , the above `projection `_ function will turn into: .. math:: \begin{pmatrix} \vec{x} \\ \vec{y} \\ \vec{z} \end{pmatrix} = \begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} \vec{X} \\ \vec{Y} \\ \vec{Z} \end{pmatrix} Distortion ^^^^^^^^^^ Besides the above systematic errors, there are two other image imperfections caused by `distortion `_, respectively, `radial distortion and decentering distortion (namely, tangential distortion) `_. Please refer to the following academic papers for the detailed deductions. - `Decentred Lens-Systems `_ - `Tsai Camera Calibration `_ - `A Flexible New Technique for Camera Calibration `_ - `Laboratory calibration of star sensor with installation error using a nonlinear distortion model `_ Both `Camera calibration With OpenCV `_ and `Matlab Camera Calibration `_ cope with `radial distortion and tangential distortion `_ by using the following formulas respectively: - Radial: .. math:: x_{distorted} = x(1+k_1r^2+k_2r^4+k_3r^6) \\ y_{distorted} = y(1+k_1r^2+k_2r^4+k_3r^6) - Tangential: .. math:: x_{distorted} = x+[2p_1xy+p_2(r^2+2x^2)] \\ y_{distorted} = y+[p_1(r^2+2y^2)+2p_2xy] In such, we have 5 distortion parameters to calculate: - 3 parameters :math:`k_1, k_2, k_3` for radial distortion; - 2 parameters :math:`p_1, p_2` for tangential distortion. Distortion parameters are normally used for `monocular vision `_ cameras with `wide-angle lens `_ or `fisheye lens `_. Calibration Pattern ------------------- As shown above, :math:`n` points are used to compute the 4 parameters in camera matrix in the `projection `_ function. That means, at least 4 independent equations are required. If 5 additional distortion parameters are also to be computed, at least 4+5=9 independent equations are required. What's more, in order to avoid the manual annotation, a well-designed calibration pattern is used for auto detection of such :math:`n` points. In fact, in each adopted frame, a number of key points will be localized. Supposing 10 frames are captured, and in each frame 35 key points are accurately localized, the projection function is going to be composed of 10*35=350 independent equations, which is surely adequate. `Camera calibration With OpenCV `_ discussed three popular calibration patterns, including: Classical Black-white Chessboard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: https://docs.opencv.org/master/pattern.png :align: center :target: https://docs.opencv.org/master/pattern.png :alt: Classical black-white chessboard Classical black-white chessboards Symmetrical Circle Pattern ^^^^^^^^^^^^^^^^^^^^^^^^^^ Clearly, since a circle itself is symmetrical, if the circle pattern is designed as symmetrical at the same time, this will cause the ambiguity when the calibration board is turned around 180 degree. That's possibly why a symmetrical circle pattern is **NOT** provided in `OpenCV `_, which is also **NOT** recommended from us. Asymmetrical circle pattern ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. figure:: https://docs.opencv.org/master/acircles_pattern.png :align: center :target: https://docs.opencv.org/master/acircles_pattern.png :alt: Asymmetrical circle pattern Asymmetrical circle pattern The **BEST** low-cost way to design such a pattern is **NOT** to have it printed on a hardboard, but directly have it displayed on an extra monitor. In such a way, - you not only ensure the perfect flatness of the calibration board, - but also move a portable handy camera in front of the monitor rather than hold the calibration board and move it around the fixed camera. Demonstrations -------------- Preparation ^^^^^^^^^^^ Now, let's calibrate a popular `monocular vision `_ camera `Logitech C930e `_ with its default narrow-angle lens. We'll use a classical black-white chessboard in `Demo 1 <./02_calibrate_monocular_vision_cameras_with_narrow_angle_lens.html#demo-1-calibration-based-on-classical-black-white-chessboard>`_ and an asymmetrical circle pattern in `Demo 2 <./02_calibrate_monocular_vision_cameras_with_narrow_angle_lens.html#demo-2-calibration-based-on-asymmetrical-circle-pattern>`_ respectively. Before running the demo code, guarantee `Logitech C930e `_ is correctly connected. 1. Plug in `Logitech C930e `_ to your host computer via USB. 2. Open up a terminal, and type in ``lsusb``. You'll see device info about `Logitech C930e `_. .. code-block:: bash ➜ ~ lsusb ...... Bus 001 Device 006: ID 046d:0843 Logitech, Inc. Webcam C930e ...... 3. Make sure python packages ``numpy`` and ``cv2`` have been installed on your system. .. code-block:: bash ➜ ~ pip show numpy Name: numpy Version: 1.18.3 Summary: NumPy is the fundamental package for array computing with Python. Home-page: https://www.numpy.org Author: Travis E. Oliphant et al. Author-email: None License: BSD Location: /home/longervision/.local/lib/python3.6/site-packages Requires: Required-by: ...... .. code-block:: bash ➜ ~ python Python 3.6.9 (default, Nov 7 2019, 10:44:02) [GCC 8.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>> cv2.__version__ 'master-dev' >>> cv2.__file__ '/usr/local/lib/python3.6/dist-packages/cv2/python-3.6/cv2.cpython-36m-x86_64-linux-gnu.so' >>> exit() ➜ ~ Demo 1: Calibration Based On Classical Black-white Chessboard ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Code Snippet: chessboard.py """"""""""""""""""""""""""" .. literalinclude:: ../resource/chapter2/code/chessboard.py :language: python3 :emphasize-lines: 63,69,98 :linenos: Intermediate Images: Chessboard """"""""""""""""""""""""""""""" .. list-table:: * - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/00.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/00.jpg :alt: 00.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/01.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/01.jpg :alt: 01.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/02.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/02.jpg :alt: 02.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/03.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/03.jpg :alt: 03.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/04.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/04.jpg :alt: 04.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/05.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/05.jpg :alt: 05.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/06.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/06.jpg :alt: 06.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/07.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/07.jpg :alt: 07.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/08.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/08.jpg :alt: 08.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/09.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/09.jpg :alt: 09.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/10.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/10.jpg :alt: 10.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/11.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/11.jpg :alt: 11.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/12.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/12.jpg :alt: 12.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/13.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/13.jpg :alt: 13.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/14.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/14.jpg :alt: 14.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/15.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/15.jpg :alt: 15.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/16.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/16.jpg :alt: 16.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/17.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/17.jpg :alt: 17.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/18.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/18.jpg :alt: 18.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/chessboard/19.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/chessboard/19.jpg :alt: 19.jpg Results: calibration_chessboard.yml """"""""""""""""""""""""""""""""""" .. literalinclude:: ../resource/chapter2/code/calibration_chessboard.yml :language: xml :linenos: Clearly, .. math:: &\begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} = \\ &\begin{pmatrix} 1.3791275848728299e+03 & 0. & 1.1424618419580618e+03 \\ 0. & 1.3814642193483867e+03 & 7.5694652936749662e+02 \\ 0. & 0. & 1. \end{pmatrix} .. math:: \begin{pmatrix} k_1 & k_2 & p_1 & p_2 & k_3 \end{pmatrix} = \begin{pmatrix} 8.0913920339187262e-02 \\ -2.8105574608293149e-01 \\ 1.7830770264422226e-03 \\ 4.2064577323684092e-03 \\ 2.9991508240001869e-02 \end{pmatrix}^T Demo 2: Calibration Based On Asymmetrical Circle Pattern ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Code Snippet: circle_grid.py """""""""""""""""""""""""""" .. literalinclude:: ../resource/chapter2/code/circle_grid.py :language: python3 :emphasize-lines: 143,148,153,183 :linenos: Intermediate Images: Circle Grid """""""""""""""""""""""""""""""" .. list-table:: * - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/00.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/00.jpg :alt: 00.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/01.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/01.jpg :alt: 01.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/02.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/02.jpg :alt: 02.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/03.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/03.jpg :alt: 03.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/04.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/04.jpg :alt: 04.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/05.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/05.jpg :alt: 05.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/06.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/06.jpg :alt: 06.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/07.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/07.jpg :alt: 07.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/08.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/08.jpg :alt: 08.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/09.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/09.jpg :alt: 09.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/10.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/10.jpg :alt: 10.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/11.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/11.jpg :alt: 11.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/12.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/12.jpg :alt: 12.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/13.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/13.jpg :alt: 13.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/14.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/14.jpg :alt: 14.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/15.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/15.jpg :alt: 15.jpg * - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/16.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/16.jpg :alt: 16.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/17.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/17.jpg :alt: 17.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/18.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/18.jpg :alt: 18.jpg - .. image:: ../resource/chapter2/images/Logitech_e930c/circlegrid/19.jpg :align: center :target: ../resource/chapter2/images/Logitech_e930c/circlegrid/19.jpg :alt: 19.jpg Results: calibration_circle_grid.yml """""""""""""""""""""""""""""""""""" .. literalinclude:: ../resource/chapter2/code/calibration_circle_grid.yml :language: xml :linenos: Clearly, .. math:: &\begin{pmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{pmatrix} =\\ &\begin{pmatrix} 1.4167295372250414e+03 & 0. & 1.0987014294575711e+03 \\ 0. & 1.4143477979365002e+03 & 7.8219583855213511e+02 \\ 0. & 0. & 1. \end{pmatrix} .. math:: \begin{pmatrix} k_1 & k_2 & p_1 & p_2 & k_3 \end{pmatrix} = \begin{pmatrix} 6.9131941945763054e-02 \\ -5.8538182599906861e-02 \\ 1.6830698943192594e-03 \\ -9.4239470722430434e-03 \\ -2.6261143127238118e-01 \end{pmatrix}^T **Clearly**, result ``calibration_chessboard.yml`` from `Demo 1 <./02_calibrate_monocular_vision_cameras_with_narrow_angle_lens.html#demo-1-calibration-based-on-classical-black-white-chessboard>`_ and result ``calibration_circle_grid.yml`` from `Demo 2 <./02_calibrate_monocular_vision_cameras_with_narrow_angle_lens.html#demo-2-calibration-based-on-asymmetrical-circle-pattern>`_ are of some difference. Assignments ----------- There are much more different calibration patterns to try. Due to different applications, calibration board varies from factors including: - pattern sizes: such as: * How many rows and columns of squares or circles? * How big is a single square or circle? For example, telephoto lens requires a more precise calibration when the target view is far away. In such cases, the calibration board needs to be big enough to be detected at a very long distance from the camera. - pattern types: * Besides traditional chessboard and circle grids, `OpenCV `_ also provides `ChArUco `_. There are a number of online calibration pattern generators free to use. `Calib.io `_ is one recommendation. In this activity, please generate any calibration pattern as you wish, and calibrate your camera with it. Different calibration patterns are of different performance due to different applications. Please compare the calibration performance of various calibration patterns for various applications with various camera lenses.