本文将梳理一种单目摄像头标定和矫正的方法,在梳理的过程中,首先使用网上离线的图片数据跑通流程,然后接入自己的camera,手动采集标定图像,实时矫正相机的畸变,然后输出矫正后的图像。全文基于Opencv使用C++实现,文末附带相应的python代码。

1. 基本概念

1.1 什么是畸变

下面两张示意图可以让大家直观的感受摄像头的畸变效果,简单的讲摄像头的畸变会让人们看到的图像出现“拉伸”或“扭曲”的直观感受,出现“横不平,竖不直”的现象。虽然畸变现象改变了图像原有的面貌,但在日常生活中也有很多应用之处,比如转弯路口的凸透镜,汽车的左右后视镜等,利用畸变效果扩大视野。但自动驾驶中,往往需要利用图像进行测量或者识别,为了保证精度,在这种场景下,就需要尽量还原图像,也就是所说的“畸变矫正”。畸变效果示意图一畸变效果示意图二

1.2 为什么会产生畸变

在进行畸变矫正之前,我们需要简单的理解产生畸变的原因。通常畸变可以分为两种,一种是径向畸变,一种是切向畸变,如下面两张图所示。

径向畸变有两种形态,即桶形畸变和枕形畸变,从效果上看一个突出,一个内凹。产生径像畸变的原因是光学镜头在生产制造的过程中,很难保证厚度的均匀,离透镜中心越远的地方光线弯曲越大,从而产生径向畸变。径向畸变(图片来源https://blog.csdn.net/waeceo/article/details/50580808)

切向畸变如下图所示,从效果上看,一个平直的物体在照片中看上去会有“倾斜”,“大小不一”的现象。出现切向畸变的原因是由于镜头与图像传感器不完全平行造成的(可理解为投影仪与影布不平行)。切向畸变(图片来源https://blog.csdn.net/waeceo/article/details/50580808)

1.3 什么是摄像头参数

1)相机矩阵:包括焦距(fx,fy),光学中心(Cx,Cy),完全取决于相机本身,是相机的固有属性,只需要计算一次,可用矩阵表示如下:[fx, 0, Cx; 0, fy, cy; 0,0,1];

2) 畸变系数:畸变数学模型的5个参数 D = (k1,k2, P1, P2, k3);

3)相机内参:相机矩阵和畸变系数统称为相机内参,在不考虑畸变的时候,相机矩阵也会被称为相机内参;

4) 相机外参:通过旋转和平移变换将3D的坐标转换为相机2维的坐标,其中的旋转矩阵和平移矩阵就被称为相机的外参;描述的是将世界坐标系转换成相机坐标系的过程。

1.4 摄像头标定的流程

相机的标定过程实际上就是在4个坐标系转化的过程中求出相机的内参和外参的过程。这4个坐标系分别是:世界坐标系(描述物体真实位置),相机坐标系(摄像头镜头中心),图像坐标系(图像传感器成像中心,图片中心,影布中心,单位mm),像素坐标系(图像左上角为原点,描述像素的位置,单位是多少行,多少列)。

(1)世界坐标系

相机坐标系:求解摄像头外参(旋转和平移矩阵);

(2)相机坐标系

图像坐标系:求解相机内参(摄像头矩阵和畸变系数);

(3) 图像坐标系

像素坐标系:求解像素转化矩阵(可简单理解为原点从图片中心到左上角,单位厘米变行列)

2. 摄像头标定与矫正实践

2.1 离线图片实现摄像头标定和矫正

1)Cmakelist 配置Opencv

//要求cmake最低版本

cmake_minimum_required(VERSION 3.1)

// 工程名

project(camera_calibration)

set(CMAKE_CXX_STANDARD 11)

add_executable(camera_calibration main.cpp)

// 配置opencv

find_package(OpenCV REQUIRED)

target_link_libraries(camera_calibration ${OpenCV_LIBS}))

2)导入棋盘格图片;

在标定过程中,需要使用棋盘格,拍摄棋盘格在多个角度的图片,这里省去了拍摄的过程,直接使用网上下载的棋盘格图片。

#include

#include

#include

#include "opencv2/imgproc.hpp"

#include "opencv2/calib3d.hpp"

#include

#include

#include

#include

using namespace std;

using namespace cv;

#define PATH "/camera_calibration/image_my/"

#define NUM 30

int main() {

// 定义用来保存导入的图片

Mat image_in;

// 定义用来保存文件路径的容器

vector filelist;

// 定义用来保存旋转和平移矩阵的容器

vector rvecs, tvecs;

// 定义相机矩阵,畸变矩阵

Mat cameraMatrix;

Mat distCoeffs;

int flags = 0;

// 定义保存图像二维角点的容器

vector corners;

// 定义保存图像三维角点的容器

vector > corners2;

// 定义保存图像二维和三维角点的容器

vector worldPoints;

vector > worldPoints2;

//***************读取一个文件夹中的所有图片(所有标定图片)**********************

for(int i=1; i

stringstream str;

str << PATH << setw(2) << setfill('0') << i << ".jpg";

// 保存所有图片的路径,放入容器filelist中

filelist.push_back(str.str());

image_in = imread(str.str());

}棋盘格标定示意图一棋盘格标定示意图二

3)找角点

标定前需要找到棋盘格中黑白框结合的角点,opencv提供了findChessboardCorners函数来完成这个工作。这个函数的输入参数为:输入图片,图片的内角点数,输出角点,求解方式;

//***************************找角点×××××××××××××××××××××××××××××××× for(int i=0;i

//cout <

// 找图片的角点,参数分别为: // 输入图片,图片内角点数(不算棋盘格最外层的角点),输出角点,求解方式 bool found = findChessboardCorners(image_in, Size(8,6),corners,CALIB_CB_ADAPTIVE_THRESH|CALIB_CB_NORMALIZE_IMAGE);

// 将找到的角点放入容器中; corners2.push_back(corners);

//画出角点 drawChessboardCorners(image_in,Size(9,6),corners, found);

//显示图像 imshow("test",image_in);

// 图像刷新等待时间,单位ms waitKey(100);

// 世界坐标系的二维vector 放入三维vector worldPoints2.push_back(worldPoints);

}角点检测示意图一角点检测示意图二

4)生成世界坐标系下三维空间点

畸变矫正的本质是通过寻找棋盘格上角点,在图像中和真实世界中的对应关系,来计算相机参数。因此我们需要生成真实世界中的棋盘格坐标点。由于矫正的过程与标定过程的比例尺一样,实际是等比例缩放,因此这些点可以不与真实的尺寸对应,只要成比例就行。

//***********************生成一组object_points*************************

for(int j = 0;j<6;j++){

for(int k = 0; k<8;k++){

worldPoints.push_back(Point3f(j*1.0 ,k*1.0 ,0.0f));

}

}

5)标定

采用calibrateCamera函数能够计算出相应的相机参数,实现相机的标定,这个函数的输入参数依次为:世界坐标系内角点, 图像的角点, 图像的尺寸,相机矩阵,畸变矩阵,旋转矩阵,平移矩阵,求解方式。 其中需要注意的是,世界坐标系内的角点和图像的角点 二者的维度一定要对应,要么全是二维Vector,要么全是三维Vector 即Vector> 或vector

calibrateCamera(worldPoints2,corners2,image_in.size(),cameraMatrix,distCoeffs,

rvecs,tvecs,CV_CALIB_FIX_PRINCIPAL_POINT);

查看对应的相机参数:

//*************************************查看参数*****************************************

cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << endl;

cout << cameraMatrix.at(0,0) << " " << cameraMatrix.at(0,1) << " " << cameraMatrix.at(0,2) << endl;

cout << cameraMatrix.at(1,0) << " " << cameraMatrix.at(1,1) << " " << cameraMatrix.at(1,2) << endl;

cout << cameraMatrix.at(2,0) << " " << cameraMatrix.at(2,1) << " " << cameraMatrix.at(2,2) << endl;

cout << distCoeffs.rows << "x" <

cout << distCoeffs << endl;

for(int i = 0;i < distCoeffs.cols;i++)

{

cout << distCoeffs.at(0,i) << " " ;

}

cout <

相机参数

6)矫正

opencv提供了多种畸变矫正的函数,这里使用最基本的undistort, 输入参数分别为:输入图像,矫正后图像,相机矩阵,畸变矩阵。

//*********************畸变矫正**************************

// 导入要矫正的图片

Mat test_image2 = imread("/camera_calibration/test_image.jpg");

Mat show_image;

undistort(test_image2, show_image, cameraMatrix, distCoeffs);矫正前的图片矫正后的图片

2.2 在线Camera数据采集

打印一张棋盘格,固定在木板上,使用摄像头从各个角度拍摄棋盘格图像。本文使用一个外接Usb罗技摄像头,以下是采集摄像头图像时所使用的程序,按下S键保存当帧的图像。这里需要注意的是waitKey()这个函数,其作用对象是显示的图像窗口,不能对控制台起作用,也就是说在使用waitKey这个函数时,必须在前面显示图片,然后才起作用。

#include

#include

#include

#include

using namespace std;

using namespace cv;

int main() {

VideoCapture capture(1);

Mat img,img_flip;

char filename[200];

int i =0;

int key = 0;

while (capture.isOpened()) {

capture >> img;

flip(img, img_flip, 0);

// imshow("test0",img);

imshow("test1",img_flip);

//char key_board = waitKey(10);

key = waitKey(30);

cout <

if (key == 's') {

sprintf(filename, "%s%d%s", "../image/", i++, ".jpg");

imwrite(filename, img_flip);

}

}

return 0;

}

2.3 在线摄像头畸变矫正

// 当摄像头打开时,实时矫正图片,并实时输出;

while(capture.isOpened()){

capture >> frame;

flip(frame,fz,-1);//1代表水平方向旋转180度

undistort(fz,show, cameraMatrix,distCoeffs);

imshow("raw",fz);

waitKey(30);

imshow("corrected2",show);

waitKey(30);

3. 其他

3.1 python 代码

import numpy as np

import cv2

import glob

import matplotlib.pyplot as plt

%matplotlib

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)

objp = np.zeros((6*8,3), np.float32)

objp[:,:2] = np.mgrid[0:8, 0:6].T.reshape(-1,2)

# Arrays to store object points and image points from all the images.

objpoints = [] # 3d points in real world space

imgpoints = [] # 2d points in image plane.

# Make a list of calibration images

images = glob.glob('calibration_wide/GO*.jpg')

# Step through the list and search for chessboard corners

for idx, fname in enumerate(images):

img = cv2.imread(fname)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Find the chessboard corners

ret, corners = cv2.findChessboardCorners(gray, (8,6), None)

# If found, add object points, image points

if ret == True:

objpoints.append(objp)

imgpoints.append(corners)

# Draw and display the corners

cv2.drawChessboardCorners(img, (8,6), corners, ret)

#write_name = 'corners_found'+str(idx)+'.jpg'

#cv2.imwrite(write_name, img)

cv2.imshow('img', img)

cv2.waitKey(500)

cv2.destroyAllWindows()

import pickle

%matplotlib inline

# Test undistortion on an image

img = cv2.imread('calibration_wide/test_image.jpg')

img_size = (img.shape[1], img.shape[0])

# Do camera calibration given object points and image points

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size,None,None)

dst = cv2.undistort(img, mtx, dist, None, mtx)

cv2.imwrite('calibration_wide/test_undist.jpg',dst)

# Save the camera calibration result for later use (we won't worry about rvecs / tvecs)

dist_pickle = {}

dist_pickle["mtx"] = mtx

dist_pickle["dist"] = dist

pickle.dump( dist_pickle, open( "calibration_wide/wide_dist_pickle.p", "wb" ) )

#dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)

# Visualize undistortion

f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))

ax1.imshow(img)

ax1.set_title('Original Image', fontsize=30)

ax2.imshow(dst)

ax2.set_title('Undistorted Image', fontsize=30)

3.2 参考资料

3)OpenCv camera calibration with C++ (Unsupported format or combination of formats error)​stackoverflow.com

python opencv 摄像头标定_(五)单目摄像头标定与畸变矫正(C++,opencv)相关推荐

  1. 基于matlab的摄像头,基于Matlab的单目摄像头标定

    实验环境:matlab2016 准备材料:单目摄像头.定标板.摄像头拍下定标板的图片 本博客带大家实际动手,再讲单目摄像头标定原理. 准备标定板 标定的开始阶段最需要用到的标定板,可以直接从openc ...

  2. 3.OpenCV可视化(Viz)——单目相机标定模拟

    单目相机标定模拟 基于OpenCV中的Viz模块,虚拟一个相机,设置相机的内参数.然后在相机视野下放置标定板,通过相机标定算法,最终再获取相机内参数. 当然最终相机标定还是存在误差,我猜测主要原因是标 ...

  3. python绘制黑白棋盘_生成黑白棋盘标定图和单目相机标定(python+opencv实现)

    学习记录. 事实上很早就接触过视觉定位这东西,但是到现在才返回头学习一下相机的标定,真是可耻啊!我把想法和过程记录一下. 相机成像 相机的成像原理--小孔成像 然而,在实际由于设计工艺问题.相机安装环 ...

  4. 生成黑白棋盘标定图和单目相机标定(一)(python+opencv实现)

    学习记录. 事实上很早就接触过视觉定位这东西,但是到现在才返回头学习一下相机的标定,真是可耻啊!我把想法和过程记录一下. 相机成像 相机的成像原理--小孔成像 然而,在实际由于设计工艺问题.相机安装环 ...

  5. Halcon例程(基于多个标定图的单目相机标定)详解—— Camera_calibration_multi_image.hdev

    一.前言 在我的工业相机专栏里已经将相机标定涉及到的理论部分讲解完毕,为什么要标定以及标定要求出什么参数呢,用一个Halcon 例程来帮助理解. 这个例程是比较经典的标定程序,基本将标定过程讲的比较清 ...

  6. 【计算机视觉】OpenCV实现单目相机标定

    文章目录 单目相机标定(基于Python OpenCV) 1.上期填坑 2.单目相机标定 2.1 数据采集 2.2 角点提取 2.3 参数求解 2.4 参数评估(重投影误差) 2.5 相机位姿(棋盘位 ...

  7. 一文图解单目相机标定算法

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 有一天,蟹老板找底下的员工川建国同学: 等蟹老板走后,然后转头问旁边的学霸李雷同学: 李雷同学整理了下 ...

  8. 3D视觉(二):单目摄像头的标定与校正

    3D视觉(二):单目摄像头的标定与校正 文章目录 3D视觉(二):单目摄像头的标定与校正 一.相机模型 1.机器车坐标系到相机坐标系 2.相机坐标系到归一化平面坐标系 3.归一化平面坐标畸变 4.归一 ...

  9. 单目摄像头标定与测距

    单目摄像头标定与测距 一. 标定 首先要对摄像头做标定,具体的公式推导在learning opencv中有详细的解释,这里顺带提一句,这本书虽然确实老,但有些理论.算法类的东西里面还是讲的很不错的,必 ...

最新文章

  1. JAVA设计模式:代理模式
  2. 世界读书日 阿里人是这样看书的?
  3. oracle over函数 去重,oracle over结合row_number分区进行数据去重处理
  4. OnDraw与OnPaint有什么区别
  5. Cent os常见操作命令
  6. Mysql 1030 Got error -1 from storage engine 错误解决
  7. linux mii,Linux mii-tool 命令用法详解-Linux命令大全(手册)
  8. 有没有轻便又好用的CRM管理应用?
  9. 从业余挖洞到微软漏洞研究员,我的遗憾、惊喜和建议
  10. React-Native开发App,修改图标和名字
  11. python3 open打开文件_Python3基础 file open 打开txt文件并打印出全文
  12. 清理系统垃圾缓存BAT
  13. gatk过滤_vcf文件过滤
  14. C语言开辟空间和C++ 开辟空间
  15. jquery判断日期格式
  16. 如何用Word制作流程图(一)
  17. 便利贴--14{GIF录制工具}
  18. 虚拟机CentOS7启动报错:Entering emergency mode
  19. 上蔡一高2021高考成绩查询,上蔡一高高考录取名单1
  20. 微信小程序不同分享效果的实现

热门文章

  1. python--pdb
  2. 18.mysql优化(三)–explain分析sql语句执行效率
  3. iOS中如何添加自定义的字体库
  4. PHP - 验证用户名
  5. java初级学习04
  6. TFS的站点中无法找到Report服务器
  7. springboot2源码2-SpringApplication运行
  8. angular2.0中为什么初始化的时候就把全部路由下的模板文件加载出来的原因。
  9. Thunder团队第二周 - Scrum会议3
  10. mormot json操作