最近需要做单目相机校正的项目,自己归纳总结记录一下,以便后期查看和回顾,同时希望帮到需要的人,有需要的可以点赞、收藏、关注、转发一下。
相机校正就摆脱不了标定,现在标定方法主要还是用张正友的相机标定方法,那就先了解几个问题:相机为什么需要标定,标定需要的输入和输出分别是哪些?
相机标定的目的:获取摄像机的内参和外参矩阵(同时也会得到每一幅标定图像的选择和平移矩阵),内参和外参系数可以对之后相机拍摄的图像就进行矫正,得到畸变相对很小的图像。
相机标定的输入:标定图像上所有内角点的图像坐标,标定板图像上所有内角点的空间三维坐标(一般情况下假定图像位于Z=0平面上)。
相机标定的输出:摄像机的内参、外参系数。
这三个基础的问题就决定了使用Opencv实现张正友法标定相机的标定流程、标定结果评价以及使用标定结果矫正原始图像的完整流程:
1. 准备标定图片
2. 对每一张标定图片,提取角点信息
3. 对每一张标定图片,进一步提取亚像素角点信息
4. 在棋盘标定图上绘制找到的内角点(非必须,仅为了显示)
5. 相机标定
6. 对标定结果进行评价
7. 查看标定效果——利用标定结果对棋盘图进行矫正
(不过现在2,3,4,5的标定工作都可以用MATLAB软件自动标定,只需要把标定结果转化为可以程序引用和读取的文本文件就可以,6,7就需要把标定结果带入程序中进行验证即可)
上面的标定方法和内容 网上资料很多就不在文章中赘述。这里主要说下标定结果的验证,也就是把MATLAB生成的文本文件如何带入程序中进行验证和试验:
利用求得的相机的内参和外参数据,可以对图像进行畸变的矫正,这里有两种方法可以达到矫正的目的,分别说明一下。
方法一:使用initUndistortRectifyMap和remap两个函数配合实现。
initUndistortRectifyMap用来计算畸变映射,remap把求得的映射应用到图像上。
initUndistortRectifyMap的函数原型:
//! initializes maps for cv::remap() to correct lens distortion and optionally rectify the image
CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs,
InputArray R, InputArray newCameraMatrix,
Size size, int m1type, OutputArray map1, OutputArray map2 );
第一个参数cameraMatrix为之前求得的相机的内参矩阵;对应标定参数里面的IntrinsicMatrix
第二个参数distCoeffs为之前求得的相机畸变矩阵;对应标定参数里面的RadialDistortion
第三个参数R,可选的输入,是第一和第二相机坐标之间的旋转矩阵;
第四个参数newCameraMatrix,输入的校正后的3X3摄像机矩阵;可以与一个参数相同
第五个参数size,摄像机采集的无失真的图像尺寸;
第六个参数m1type,定义map1的数据类型,可以是CV_32FC1或者CV_16SC2;
第七个参数map1和第八个参数map2,输出的X/Y坐标重映射参数;
remap函数原型:
//! warps the image using the precomputed maps. The maps are stored in either floating-point or integer fixed-point format
CV_EXPORTS_W void remap( InputArray src, OutputArray dst,
InputArray map1, InputArray map2,
int interpolation, int borderMode=BORDER_CONSTANT,
const Scalar& borderValue=Scalar());
第一个参数src,输入参数,代表畸变的原始图像;
第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;
第三个参数map1和第四个参数map2,X坐标和Y坐标的映射;
第五个参数interpolation,定义图像的插值方式;自己使用的线性插值
第六个参数borderMode,定义边界填充方式;
方法二:使用undistort函数实现
undistort函数原型:
//! corrects lens distortion for the given camera matrix and distortion coefficients
CV_EXPORTS_W void undistort( InputArray src, OutputArray dst,
InputArray cameraMatrix,
InputArray distCoeffs,
InputArray newCameraMatrix=noArray() );
第一个参数src,输入参数,代表畸变的原始图像;
第二个参数dst,矫正后的输出图像,跟输入图像具有相同的类型和大小;
第三个参数cameraMatrix为之前求得的相机的内参矩阵;
第四个参数distCoeffs为之前求得的相机畸变矩阵;
第五个参数newCameraMatrix,默认跟cameraMatrix保持一致;
方法一相比方法二执行效率更高一些,推荐使用。到此校正部分说完了
下面就说说使用过程中遇到个几个图片类型转换:Mat、Bitmap、Image<Bgr,byte>转换
1.Bitmap转Image<Bgr,byte>
public Mat GetMatFromSDImage(System.Drawing.Image image)
{
int stride = 0;
Bitmap bmp = new Bitmap(image);
System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
Image<Bgr, byte> cvImage = new Image<Bgr, byte>(bmp.Width, bmp.Height,bmpData. stride, (IntPtr)bmpData.Scan0);
bmp.UnlockBits(bmpData);
return cvImage.Mat;
}
但是这是Emgu4.2上的方法,但是Emgu4.4上面已经更新了新的更简单的方法:将构造函数Image<TColor, TDepth> Constructor (Bitmap)删除,增加了Bitmap扩展方法BitmapExtension.ToImage<TColor, TDepth> Method,用于从Bitmap获取图像,使用起来更加简洁易懂,示例如下:
Bitmap source;
Image<Bgr, byte> image = source.ToImage<Bgr, Byte>();
Mat matImage = image.Mat;
1.Image<Bgr,byte>转Mat
Image<Bgr, byte> cvImage = new Image<Bgr, byte>(bmp.Width, bmp.Height,bmpData. stride,
cvImage.Mat;
是不是方便的惊艳到你了,欢迎补充和交流