目录
1.4大坐标系
2.相机成像原理
3.光圈(控制进光量)
4.现实生活中的相机系统
5.坐标系的关系
图像坐标系和相机坐标的关系
像素坐标系和图像坐标系的关系
相机坐标系到世界坐标系的关系
6.相机畸变
1.径向畸变(Radial Distortion)
2.切向畸变(Tangential Distortion)
7.图像插值
8.标定
理论:
DLT求解相机标定原理:
张正友标定法:
实现过程:
实验代码:
函数讲解:
FindChessboardCorners()
drawChessboardCorners()
cornerSubPix()
calibrateCamera()
initUndistortRectifyMap()
remap()
undistort()
projectPoints()
参考:
首先,相机的成像过程涉及到四个坐标系:世界坐标系、相机坐标系、图像坐标系、像素坐标系。接下来依次介绍:
客观三维世界的绝对坐标系。因为相机安放在三维空间中,我们需要世界坐标系这个基准坐标系来描述相机的位置,并且用它来描述安放在此三维环境中的其它任何物体的位置,表示为:
以相机的光心为坐标原点,X 轴和Y 轴分别平行于图像坐标系的 X 轴和Y 轴,相机的光轴为Z 轴,表示为:
以CCD 图像平面的中心为坐标原点,X轴和Y 轴分别平行于图像平面的两条垂直边。图像坐标系是用物理单位(例如毫米)表示像素在图像中的位置。表示为:
以图像的左上角点为坐标原点,水平方向向右为x轴(u轴),数值方向向下为y轴(v轴)。表示为:
如何成像:
光圈越小,进的光线减少,画面越暗,背景虚化越小
光圈越大,进的光线增加,画面越亮,背景虚化越大
这也说明了,尽管从物理原理来说,小孔成像的像应该是倒像,但是由于相机的自身设计,像一般变为正的了。
首先,我们可以通过两个正的相似三角形可以得到图像坐标系和相机坐标系的关系:
用齐次坐标系和矩阵表示上述关系:
接着,对于像素坐标系和图像坐标系的关系,它们之间相差了一个缩放和一个原点的平移。我们设像素坐标在 u 轴上缩放了 1/dx倍,在 v 上缩放了 1/dy倍,那么我们可以表示为:
用矩阵的形式表示:
相机坐标系到世界坐标系存在一个位姿的变换,[R|T],写成矩阵的形式如下:
组合在一起:
其中, 相机的内参K 为:
R和T分别为世界坐标系和相机坐标系之间的旋转矩阵和平移矩阵,两者组成了相机的外参矩阵,
相机的外参矩阵和内参矩阵构成了相机的投影矩阵P= K[R |t]
简单来说,由透镜形状(相机镜头径向曲率的不规则变化)引起的畸变称为径向畸变,是导致相机成像变形的主要因素。径向畸变主要分为桶形畸变和枕型畸变。在针孔模型中,一条直线投影到像素平面上还是一条直线。 但在实际中,相机的透镜往往使得真实环境中的一条直线在图片中变成了曲线。越靠近图像的边缘现象越明显。 由于透镜往往是中心对称的,这使得不规则畸变通常径向对称。(成像中心处的径向畸变最小,距离中心越远,产生的变形越大,畸变也越明显 )
实际摄像机的透镜总是在成像仪的边缘产生显著的畸变,这种现象来源于“筒形”或“鱼眼”的影响。如下图,光线在原理透镜中心的地方比靠近中心的地方更加弯曲。对于常用的普通透镜来说,这种现象更加严重。筒形畸变在便宜的网络摄像机中非常厉害,但在高端摄像机中不明显,因为这些透镜系统做了很多消除径向畸变的工作。
径向畸变模型: r 为像平面坐标系中点(x, y)与图像中心(x0, y0)的像素距离。
切向畸变是由于相机镜头在制造安装过程中并非完全平行于成像平面造成的。不同于径向畸变在图像中心径向方向上发生偏移变形,切向畸变主要表现为图像点相对理想成像点产生切向偏移。
切向畸变模型可以描述为:p 1和p 2 —镜头的切向畸变系数。
最终需要得到的5个畸变参数:
畸变参数的一般顺序是k1,k2,p1,p2,k3。之所以把k3放在最后其实也很容易理解,因为前面说了一般k1,k2用来处理径向畸变足矣,k3相对而言用的比较少。 在获得了畸变参数以后,也就找到了真实观测的带畸变的像素与无畸变的像素间的关系,重采样即可实现影像校正。
将目标图像中的点对应到原图像中后,找到最相邻的整数坐标点的像素值,作为该点的像素值输出。由最邻近插值法得到的图像放大后有很严重的马赛克,会出现明显的块状效应,缩小后的图像有很严重的失真。
相机标定的基本原理:确定场景中一系列点的三维坐标并拍摄这个场景,然后观测这些点在图像上投影的位置。有了足够多的三维点和图像上对应的二维点,就可以根据投影方程推断出准确的相机参数。
但是,对于DLT标定方法来说,存在一定的问题,因为我们都知道相机得到的图像不是理想的,还存在着畸变,然而该方法没有考虑相机的畸变。
张正友标定法主要利用平面标定板进行实现,虽然精度上不及使用三维标定物,但是这种方法更加的实用(打印一张黑白棋盘格、一块足够平的木板)
OpenCV推荐使用国际象棋棋盘的图案生成用于校准的三维场景点的集合。这个图案在每个方块的角点位置创建场景点,并且由于图案是平面的,因此我们可以假设棋盘位于Z=0且X 和Y 的坐标轴与网格对齐的位置。这样,校准时就只需从不同的视角拍摄棋盘图案。下面是一个6 × 9的图案:
- 寻找棋盘格中的角点
bool found = cv::findChessboardCorners(image,boardSize, imageCorners);
- 对寻找到的角点进行亚像素提纯
cornerSubPix(gray,corners,cv::Size(5, 5), cv::Size(-1, -1),TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.1));
- 可以打印并显示检测到的角点
drawChessboardCorners(img,board_size,corners,found);
- 保存2D角点和3D点
image_points.push_back(corners); object_points.push_back(objectCorners);
- 标定
calibrateCamera(object_points,image_points,img.size(),instrisincMatrix,distortionCoeff,rvecs,tvecs);
矫正
得到标定参数以后,我们需要使用畸变参数对图像进行矫正,OpenCV中对畸变图像进行畸变校正主要用的函数有:
- undistort()函数
- initUndistortRectifyMap()结合remap()函数
其实undistort()就是initUndistortRectifyMap()和remap()的简单组合,效果是一样的。
- 计算重投影误差
projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix,distCoeffs, imagePoints2);
注意:根据经验,10~20个棋盘图像就足够了,但是必须在不同的深度,从不同的视角拍摄。这个函数的两个重要输出对象是相机矩阵和畸变参数。
完整代码链接:里面包含了实现的代码和采集的29张棋盘格照片
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
void computeReprojectionErrors(const vector< vector< Point3f > >& objectPoints,
const vector< vector< Point2f > >& imagePoints,
const vector< Mat >& rvecs, const vector< Mat >& tvecs,
const Mat& cameraMatrix , const Mat& distCoeffs) {
vector< Point2f > imagePoints2;
for (int i = 0; i < (int)objectPoints.size(); ++i) {
double err;
projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix,
distCoeffs, imagePoints2);
err =(double) norm(Mat(imagePoints[i]), Mat(imagePoints2), CV_L2)/objectPoints[i].size();
cout<<"第"< > object_points;
vector< vector< Point2f > > image_points;
int main() {
// 水平和垂直方向内部角点的数量
int board_width=6;
int board_height=9;
//图片的数量
int num_imgs=29;
string base_path="../calib_imgs/1";
Size board_size=Size(board_width,board_height);
vector corners;
std::vector objectCorners;
// 处理所有视角
Mat img,gray;
for (int k = 1; k <= num_imgs; k++)
{
char filename[100];
sprintf(filename,"%s/left%d.jpg",base_path.c_str(),k);
img=imread(filename,CV_LOAD_IMAGE_COLOR);
cvtColor(img,gray,CV_BGR2GRAY);
bool found=false;
found=findChessboardCorners(img,board_size,corners,CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS);
if(found)
{
cornerSubPix(gray,corners,cv::Size(5, 5), cv::Size(-1, -1),TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.1));
drawChessboardCorners(img,board_size,corners,found);
}
// 场景中的三维点:
// 在棋盘坐标系中,初始化棋盘中的角点
// 角点的三维坐标(X,Y,Z)= (j,i,0)
// 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
for (int i=0; i rvecs, tvecs;
int flag = 0;
flag |= CV_CALIB_FIX_K4;
flag |= CV_CALIB_FIX_K5;
calibrateCamera(object_points,image_points,img.size(),instrisincMatrix,distortionCoeff,rvecs,tvecs);
cout<<"instrisincMatrix: "<
bool findChessboardCorners( InputArray image,
Size patternSize,
OutputArray corners,
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
- 功能:用来寻找棋盘图的内角点位置。
- 参数:
- Image:输入的棋盘图,必须是8位的灰度或者彩色图像。
- patternSize:棋盘图中每行和每列角点的个数(内点)。
- imageCorners:存储检测到的内部角点的像素坐标
- Flags:各种操作标志,可以是0或者下面值的组合:
CV_CALIB_CB_ADAPTIVE_THRESH -使用自适应阈值(通过平均图像亮度计算得到)将图像转换为黑白图,而不是一个固定的阈值。
CV_CALIB_CB_NORMALIZE_IMAGE -在利用固定阈值或者自适应的阈值进行二值化之前,先使用cvNormalizeHist来均衡化图像亮度。
CV_CALIB_CB_FILTER_QUADS -使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块。
cv::drawChessboardCorners(image,boardSize, imageCorners,found); // 找到的角点
- 功能:画出棋盘图像上的角点,用线条依次连接起来
- 参数:
- Image:输入的棋盘图,必须是8位的灰度或者彩色图像。
- boardSize:棋盘图中每行和每列角点的个数(内点)。
- imageCorners:存储检测到的内部角点的像素坐标
- found:是否成功找到(bool)
void cornerSubPix(
InputArray image,
InputOutputArray corners,
Size winSize,
Size zeroZone,
TermCriteria criteria);
- 功能:亚像素角点检测
- 参数:
- cv::InputArray image:输入图像
- cv::InputOutputArray corners: 角点(既作为输入,也作为输出)
- cv::Size winSize:区域大小为 NXN; N=(winSize*2+1)
- cv::Size zeroZone:类似于winSize,但是总具有较小的范围,Size(-1,-1)表示忽略
- cv::TermCriteria criteria : 停止优化的标准
TermCriteria:
cv::TermCriteria::TermCriteria ( int type, int maxCount, double epsilon)
- type:迭代终止的条件类型(cv::TermCriteria::MAX_ITER (迭代次数达到了最大次数)、cv::TermCriteria::EPS(角点位置变化的最小值),可以选其一,或两者均选)
- maxCount:最大迭代的次数
- epsilon:收敛的阈值
double calibrateCamera( InputArrayOfArrays objectPoints,
InputArrayOfArrays imagePoints,
Size imageSize,
InputOutputArray cameraMatrix,
InputOutputArray distCoeffs,
OutputArrayOfArrays rvecs,
OutputArrayOfArrays tvecs,
int flags = 0, TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, DBL_EPSILON) );
- 功能:相机标定
- 参数:
- objectPoints: 三维点
- imagePoints,: 图像点
- imageSize:图像尺寸
- cameraMatrix:输出相机矩阵
- distCoeffs: 输出畸变矩阵
- rvecs, tvecs: Rs、Ts
- flag:设置选项
void initUndistortRectifyMap( InputArray cameraMatrix,
InputArray distCoeffs,
InputArray R,
InputArray newCameraMatrix,
Size size,
int m1type,
OutputArray map1,
OutputArray map2 )
- 功能:
这个函数用于计算无畸变和修正转换关系,并以映射的形式表示结果以进行重新映射。无畸变的图像看起来就像原始的图像,就像这个图像是用内参为newCameraMatrix的且无畸变的相机采集得到的。
在单目相机例子中,newCameraMatrix一般和cameraMatrix相等,或者可以用cv::getOptimalNewCameraMatrix来计算,获得一个更好的有尺度的控制结果。
在双目相机例子中,newCameraMatrix一般是用cv::stereoRectify计算而来的,设置为P1或P2。 此外,根据R,新的相机在坐标空间中的取向是不同的。例如,它帮助配准双目相机的两个相机方向,从而使得两个图像的极线是水平的,且y坐标相同(在双目相机的两个相机谁水平放置的情况下)。 该函数实际上为反向映射算法构建映射,供反向映射使用。也就是,对于在已经修正畸变的图像中的每个像素(u,v),该函数计算原来图像(从相机中获得的原始图像)中对应的坐标系
- 参数:
void remap(InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode=BORDER_CONSTANT,
const Scalar& borderValue=Scalar())
- 功能:重映射,把一副图像中某位置的像素放到另一幅图像的指定位置的过程。
例如:g(x,y)为目标图像函数,f(x,y)为源图像,h(x,y)为映射函数
如果要是实现按x的翻转,则h(x,y)=(l.cols-x,y);
- 参数:
src:输入图像,即原图像,需要单通道8位或者浮点类型的图像
dst :输出图像,即目标图像,需和原图形一样的尺寸和类型
map1:它有两种可能表示的对象:(1)表示点(x,y)的第一个映射;(2)表示CV_16SC2,CV_32FC1等
map2:它有两种可能表示的对象:(1)若map1表示点(x,y)时,这个参数不代表任何值;(2)表示 CV_16UC1,CV_32FC1类型的Y值
interpolation:插值方式,有四中插值方式:(1)INTER_NEAREST——最近邻插值
(2)INTER_LINEAR——双线性插值(默认)(3)INTER_CUBIC——双三样条插值(默认)
(4)INTER_LANCZOS4——lanczos插值(默认)
borderMode:边界模式,默认BORDER_CONSTANT
borderValue:边界颜色,默认Scalar()黑色
void undistort(InputArray src,
OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix=noArray() )
- 功能:矫正畸变
- 参数:
void projectPoints( InputArray objectPoints,
InputArray rvec, InputArray tvec,
InputArray cameraMatrix, InputArray distCoeffs,
OutputArray imagePoints,
OutputArray jacobian = noArray(),
double aspectRatio = 0 );
- 功能:通过给定的内参数和外参数计算三维点投影到二维图像平面上的坐标
- 参数:
- objectPoints:输入三维点
- rvec,tvec:相机的位姿R,T
- cameraMatrix:内参矩阵
- distCoeffs:畸变矩阵
- imagePoints:输出像素坐标系坐标
- jacobian:
https://www.cnblogs.com/zyly/p/9373991.html
https://blog.csdn.net/tiemaxiaosu/article/details/51734667
相机畸变与标定:http://zhaoxuhui.top/blog/2018/04/17/CameraCalibration.html
综述|相机标定方法:https://cloud.tencent.com/developer/article/1500292
最详细、最完整的相机标定讲解:https://blog.csdn.net/a083614/article/details/78579163
OpenCV相机标定和姿态更新:https://www.cnblogs.com/mikewolf2002/p/5746667.html
SLAM 中常用的相机模型&畸变模型总结:https://blog.csdn.net/OKasy/article/details/90665534
Opencv畸变矫正原理与损失有效像素原理分析:https://www.cnblogs.com/riddick/p/6711263.html
双目视觉自标定技术的研究:https://wenku.baidu.com/view/f8639568011ca300a6c39057.html?sxts=1585838430474
Zhengyou Zhang: A Flexible New Technique for Camera Calibration. IEEE T-PAMI), 2000.
[图像]畸变校正详解:https://blog.csdn.net/humanking7/article/details/45037239
超实用!图像去畸变矫正及双线性内插法:https://blog.csdn.net/Yong_Qi2015/article/details/107031879