(Python 3)opencv相机标定

前言

  • 相机将三维世界中的坐标点(单位为米)映射到二维图像平面(单位为像素)的过程能够用一个几何模型进行描述。这个模型有很多种,其中最简单的称为针孔模型
    (Python 3)opencv相机标定_第1张图片
  • 为了获得好的成像效果,我们在相机的前方加了透镜。透镜的加入对成像过程中光线的传播会产生新的影响: 一是透镜自身的形状对光线传播的影响;二是在机械组装过程中,透镜和成像平面不可能完全平行,这也会使得光线穿过透镜投影到成像面时的位置发生变化。
  • 由透镜形状引起的畸变称之为径向畸变。在针孔模型中,一条直线投影到像素平面上还是一条直线。可是,在实际拍摄的照片中,摄像机的透镜往往使得真实环境中的一条直线在图片中变成了曲线 。越靠近图像的边缘,这种现象越明显。由于实际加工制作的透镜往往是中心对称的,这使得不规则的畸变通常径向对称。它们主要分为两大类,桶形畸变枕形畸变
    (Python 3)opencv相机标定_第2张图片
  • 桶形畸变是由于图像放大率随着离光轴的距离增加而减小,而枕形畸变却恰好相反。在这两种畸变中,穿过图像中心和光轴有交点的直线还能保持形状不变。除了透镜的形状会引入径向畸变外,在相机的组装过程中由于不能使得透镜和成像面严格平行也会引入切向畸变。

(Python 3)opencv相机标定_第3张图片
(Python 3)opencv相机标定_第4张图片
实际应用中可以灵活选择纠正模型,比如只选择 k 1 , p 1 , p 2 这三项等。

上述概述均采用高翔视觉SLAM14讲 从理论到实践》书本里面的说法。

  • 一言以蔽之,由于相机存在畸变,因此要通过标定,来矫正图片,来减小误差。

参考书籍:

  • 高翔《视觉SLAM14讲 从理论到实践》

参考链接:

  • https://blog.csdn.net/AdamShan/article/details/78712120

环境

  • Ubuntu16.04
  • pycharm
  • opencv
  • python3.5

相机标定

  • 先考虑一张棋盘图片的情况

原图

可以看到图片边缘的直线扭曲的比较厉害。

(Python 3)opencv相机标定_第5张图片
代码

# 相机标定

import cv2

# 首先读取图像并转为灰度图
img = cv2.imread('./camera_cal/calibration1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# cv2.imshow("img",img)
# cv2.imshow("gray",gray)

# 使用OpenCV的cv2.findChessboardCorners()函数找出棋盘图中的对角(即图片中黑白相对的点的坐标),
# 同时使用cv2.drawChessboardCorners()将之画出来

# cv2.findChessboardCorners参数patternSize取(9,5)--棋盘图中每行和每列交点的个数
# 取(9,5)下面打印corners才会有输出,不然是None,
# 其原因在于导入的图片./camera_cal/calibration1.jpg数一下交点的数目,一行有9个,一列有5个
# Adam博客当中取(9,6)原因在于他的图和我的图不一样,认真数一下可以发现他的图确实是一行9个一列6个角点
# 事实证明,(9,4)也可以,只要size小于图片中的交点数即可

# 函数解析参见官网https://docs.opencv.org/3.3.0/dc/dbb/tutorial_py_calibration.html
# It returns the corner points and retval which will be True if pattern is obtained.
# These corners will be placed in an order (from left-to-right, top-to-bottom)
ret, corners = cv2.findChessboardCorners(gray, (9, 5),None)
print(ret)
# print(corners)  # 交点坐标

if ret == True:
    img = cv2.drawChessboardCorners(img, (9, 5), corners, ret)

cv2.imshow("final",img)

cv2.waitKey()
cv2.destroyAllWindows()
  • 打印输出
True
  • 结果图

(Python 3)opencv相机标定_第6张图片
到此为止也就只是完成了找出交点并且标注出来,接下来应该还要对其进行矫正。构造这些对角点在在现实世界中的相对位置,将这些位置简化成整数值。
完整代码如下:

# 相机标定

import cv2
import numpy as np

# 首先读取图像并转为灰度图
img = cv2.imread('./camera_cal/calibration1.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# cv2.imshow("img",img)
# cv2.imshow("gray",gray)

# 使用OpenCV的cv2.findChessboardCorners()函数找出棋盘图中的对角(即图片中黑白相对的点的坐标),
# 同时使用cv2.drawChessboardCorners()将之画出来

# cv2.findChessboardCorners参数patternSize取(9,5)--棋盘图中每行和每列交点的个数
# 取(9,5)下面打印corners才会有输出,不然是None,
# 其原因在于导入的图片./camera_cal/calibration1.jpg数一下交点的数目,一行有9个,一列有5个
# Adam博客当中取(9,6)原因在于他的图和我的图不一样,认真数一下可以发现他的图确实是一行9个一列6个角点
# 事实证明,(9,4)也可以,只要size小于图片中的交点数即可

# 函数解析参见官网https://docs.opencv.org/3.3.0/dc/dbb/tutorial_py_calibration.html
# It returns the corner points and retval which will be True if pattern is obtained.
# These corners will be placed in an order (from left-to-right, top-to-bottom)
ret, corners = cv2.findChessboardCorners(gray, (9, 5),None)
# print(ret)
# print(corners)  # 交点坐标

if ret == True:
    img = cv2.drawChessboardCorners(img, (9, 5), corners, ret)

cv2.imshow("result",img)

# 构造这些对角点在在现实世界中的相对位置,我们将这些位置简化成整数值
objp = np.zeros((5*9, 3), np.float32)
objp[:, :2] = np.mgrid[0:9, 0:5].T.reshape(-1, 2)

img_points = []
obj_points = []

img_points.append(corners)
obj_points.append(objp)

# 最后我们使用OpenCV中的 cv2.calibrateCamera() 即可求得这个相机的畸变系数,在后面的所有图像的矫正都可以使用这一组系数来完成
image_size = (img.shape[1], img.shape[0])
#  It returns the camera matrix, distortion coefficients, rotation and translation vectors etc.
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points,image_size, None, None)

test_img = cv2.imread("./camera_cal/calibration3.jpg")
# 这里cv2.undistort的最后一个参数依旧选用原来的相机矩阵mtx
# 也可以尝试做opencv官网举例的使用cv2.getOptimalNewCameraMatrix()基于自由缩放参数来优化相机矩阵,之后再带入
undist = cv2.undistort(test_img, mtx, dist, None, mtx)

cv2.imshow("test_img",test_img)
cv2.imshow("undist",undist)

cv2.waitKey()
cv2.destroyAllWindows()
  • 实验效果大致如下,图中undist为矫正后的结果,最底下的图片为test_img

不足之处:

  • 正如前文提到的这里只考虑了用一张棋盘图片来求得相机的相关畸变参数,实际上应当有10张以上效果较好,之后再改进一下。正如代码中写的,可以试着用cv2.getOptimalNewCameraMatrix()基于自由缩放参数来优化相机矩阵。

你可能感兴趣的:(学习笔记)