照⽚畸变校正python_(五)单⽬摄像头标定与畸变矫正
(C++,opencv)
本⽂将梳理⼀种单⽬摄像头标定和矫正的⽅法,在梳理的过程中,⾸先使⽤⽹上离线的图⽚数据跑通流程,然后接⼊⾃⼰的camera,⼿动采集标定图像,实时矫正相机的畸变,然后输出矫正后的图像。全⽂基于Opencv使⽤C++实现,⽂末附带相应的python代码。
1. 基本概念
1.1 什么是畸变
下⾯两张⽰意图可以让⼤家直观的感受摄像头的畸变效果,简单的讲摄像头的畸变会让⼈们看到的图像出现“拉伸”或“扭曲”的直观感受,出现“横不平,竖不直”的现象。虽然畸变现象改变了图像原有的⾯貌,但在⽇常⽣活中也有很多应⽤之处,⽐如转弯路⼝的凸透镜,汽车的左右后视镜等,利⽤畸变效果扩⼤视野。但⾃动驾驶中,往往需要利⽤图像进⾏测量或者识别,为了保证精度,在这种场景下,就需要尽量还原图像,也就是所说的“畸变矫正”。畸变效果⽰意图⼀畸变效果⽰意图⼆
1.2 为什么会产⽣畸变
在进⾏畸变矫正之前,我们需要简单的理解产⽣畸变的原因。通常畸变可以分为两种,⼀种是径向畸变,⼀种是切向畸变,如下⾯两张图所⽰。
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: " << ws << "x" << ls << 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 << ws << "x" <
cout << distCoeffs << endl;
for(int i = 0;i < ls;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);
import picklewaitKey(30);
imshow("corrected2",show);
waitKey(30);
3. 其他
3.1 python 代码
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论