鏡頭的內參,外參的詳細細節請自行百度。我這里直接上代碼:
封裝類名:class MyCameraCalibration
類內數據:
//圖片格式后綴:jpg 或者bmp
public string Img_Extent_Name
{
set;
get;
}
//枚舉
public enum Imgformat
{
Jpg = 0,
Bmp = 1
}
//棋盤格內角點數量,寬度方向,單位:個
public double Pattern_Size_W
{
set;
get;
}
//棋盤格內角點數量,高度方向,單位:個
public double Pattern_Size_H
{
set;
get;
}
//棋盤格,單個格子寬度,單位mm
public double Squart_Size_W
{
set;
get;
}
//棋盤格,單個格子高度,單位mm
public double Squart_Size_H
{
set;
get;
}
//實現圖像校正后的視場縮放:1 不變,其他縮放視場
public double Alpha
{
set;
get;
}
//相機內參
double[,] camera_matrix;
//相機畸變參數
double[] dist_coeffs;
//n個棋盤物理點坐標
List<List<Point3f>> objectPlist;
//n個圖像中找到的角點坐標
List<List<Point2f>> imagePlist;
//一個棋盤圖片中對應的實際棋盤坐標
List<Point3f> one_img_Plist;
public Size Imgsize;
public int Imgsize_W
{
set;
get;
}
public int Imgsize_H
{
set;
get;
}
//圖像校正的兩個矩陣
public Mat MapX;
public Mat MapY;
//角點繪制圖像保存文件夾
private string Corner_Draw_Path;
//相機內參和畸變數據保存文件夾
private string Camera_Data_Path;
//封裝方法1:通過讀取文件夾內的棋盤圖片來完成相機內參和畸變參數的獲取和保存
public int Calibrate_Chess_Images_Array(string imgfolder) { int result = 0; try { var allimages = MyFile.ListFilenames(imgfolder, Img_Extent_Name); //Get Related data first! for (int i = 0; i < allimages.Count; i++) { Mat chessimg_temp = Cv2.ImRead(allimages[i], ImreadModes.Color); Mat gray_temp = chessimg_temp.CvtColor(ColorConversionCodes.BGR2GRAY); Point2f[] corners = null; Point2f[] corners_subpix = null; if (Imgsize.Width == 0 || Imgsize.Height == 0) { Imgsize = chessimg_temp.Size(); Imgsize_W = Imgsize.Width; Imgsize_H = Imgsize.Height; WriteConfig(this); } //尋找角點 bool inner_result1 = Cv2.FindChessboardCornersSB(chessimg_temp, new Size(Pattern_Size_W, Pattern_Size_H), out corners, ChessboardFlags.None); if (inner_result1) { //加入成功找到 //亞像素 corners_subpix = Cv2.CornerSubPix(gray_temp, corners, new Size(11, 11), new Size(-1, -1), new TermCriteria(CriteriaType.Eps, 30, 0.1)); if (corners_subpix != null) { imagePlist.Add(corners_subpix.ToList()); objectPlist.Add(one_img_Plist.ToList()); //draw corners Cv2.DrawChessboardCorners(chessimg_temp, new Size(Pattern_Size_W, Pattern_Size_H), corners_subpix, true); //saveimg chessimg_temp.SaveImage(Corner_Draw_Path + i.ToString()+ ".jpg"); } } chessimg_temp.Dispose(); gray_temp.Dispose(); } Vec3d[] rvecs = null; Vec3d[] tvecs = null; var err= Cv2.CalibrateCamera(objectPlist,imagePlist, Imgsize, camera_matrix, dist_coeffs, out rvecs,out tvecs); //save FileStorage fs = new FileStorage(Camera_Data_Path+ "Intrinsics.xml", FileStorage.Mode.FormatYaml | FileStorage.Mode.Write); Cv2.Write(fs, "Intrinsics", Build_Camera_Matrix_from_data(camera_matrix)); fs.Dispose(); fs = new FileStorage(Camera_Data_Path + "Distortion.xml", FileStorage.Mode.FormatYaml | FileStorage.Mode.Write); Cv2.Write(fs, "Distortion", Build_Camera_Distort_from_data(dist_coeffs)); fs.Dispose(); } catch (Exception ex) { result = -1; } return result; }
封裝方法2:主要完成畸變校正矩陣的計算
/// <summary> /// 計算默認的畸變映射矩陣 /// </summary> /// <returns></returns> public int Cal_Image_Map_Array() { int result = 0; try { // Mat R = Mat.Eye(3, 3, MatType.CV_32FC1); MapX = new Mat(Imgsize, MatType.CV_32FC1, Scalar.Black); MapY = new Mat(Imgsize, MatType.CV_32FC1, Scalar.Black); //read FileStorage fs = new FileStorage(Camera_Data_Path + "Intrinsics.xml", FileStorage.Mode.FormatYaml | FileStorage.Mode.Read); Mat camerra_matrix= Cv2.ReadMat(fs["Intrinsics"]); fs.Dispose(); fs = new FileStorage(Camera_Data_Path + "Distortion.xml", FileStorage.Mode.FormatYaml | FileStorage.Mode.Read); Mat camera_distortion = Cv2.ReadMat(fs["Distortion"]); fs.Dispose(); Rect validPixROI; Cv2.InitUndistortRectifyMap(camerra_matrix, camera_distortion, R, Cv2.GetOptimalNewCameraMatrix(camerra_matrix, camera_distortion, Imgsize, Alpha, Imgsize,out validPixROI) , Imgsize, MatType.CV_32FC1, MapX,MapY); R?.Dispose(); camerra_matrix?.Dispose(); camera_distortion?.Dispose(); } catch (Exception ex) { result = -1; } return result; }
其他相關封裝方法:
/// <summary> /// Squart_Size_W,Squart_Size_H 棋盤格每個格子的實際尺寸 ,單位mm /// </summary> /// <returns></returns> private int Get_Chess_Images_ObjPoints() { int result = 0; try { for(int j=0;j<Pattern_Size_H;j++) { for (int i = 0; i < Pattern_Size_W; i++) { //假定Z=0 Point3f point3F = new Point3f(); point3F.X = i* (float)Squart_Size_W; point3F.Y = j* (float)Squart_Size_H; point3F.Z = 0; one_img_Plist.Add(point3F); } } } catch(Exception ex) { result = -1; } return result; } /// <summary> /// 將數組轉換成矩陣來保存 /// </summary> /// <param name="inputdata"></param> /// <returns></returns> private Mat Build_Camera_Matrix_from_data(double[,] inputdata) { Mat Camera_Matrix =null; try { int row= inputdata.GetUpperBound(0); int col = inputdata.GetUpperBound(1); Camera_Matrix = new Mat(row+1, col+1, MatType.CV_32FC1, Scalar.Black); for(int j=0;j<=row;j++) { for(int i=0;i<=col;i++) { Camera_Matrix.Set<float>(j, i, (float)inputdata[j,i]); var test = Camera_Matrix.Get<float>(j, i); } } } catch(Exception ex) { Camera_Matrix?.Dispose(); Camera_Matrix = null; } return Camera_Matrix; } /// <summary> /// 將數組轉換成矩陣來保存 /// </summary> /// <param name="inputdata"></param> /// <returns></returns> private Mat Build_Camera_Distort_from_data(double[] inputdata) { Mat Camera_Distort = new Mat(); try { int row = inputdata.GetUpperBound(0); // int col = inputdata.GetUpperBound(1); Camera_Distort = new Mat(row+1, 1, MatType.CV_32FC1, Scalar.Black); for (int j = 0; j <= row; j++) { Camera_Distort.Set<float>(j, 0, (float)inputdata[j]); } } catch (Exception ex) { Camera_Distort?.Dispose(); Camera_Distort = null; } return Camera_Distort; }
文件枚舉方法:
public static List<string> ListFilenames(string path, string extName) { List<string> allfilename = new List<string>(); try { if (path == null || path == "") { return allfilename; } string[] dir = Directory.GetDirectories(path); //文件夾列表 DirectoryInfo fdir = new DirectoryInfo(path); FileInfo[] file = fdir.GetFiles(); if (file.Length != 0 || dir.Length != 0) //當前目錄文件或文件夾不為空 { foreach (FileInfo f in file) //顯示當前目錄所有文件 { if (f.Extension.ToLower().IndexOf(extName.ToLower()) >= 0) { allfilename.Add(f.FullName); } } foreach (string d in dir) { getdir(d, extName);//遞歸 } } return allfilename; } catch (Exception ex) { return allfilename; throw ex; } }
/// <summary> /// 私有方法,遞歸獲取指定類型文件,包含子文件夾 /// </summary> /// <param name="path"></param> /// <param name="extName"></param> private static void getdir(string path, string extName) { try { if(path==null|| path=="") { return; } string[] dir = Directory.GetDirectories(path); //文件夾列表 DirectoryInfo fdir = new DirectoryInfo(path); FileInfo[] file = fdir.GetFiles(); if (file.Length != 0 || dir.Length != 0) //當前目錄文件或文件夾不為空 { foreach (FileInfo f in file) //顯示當前目錄所有文件 { if (f.Extension.ToLower().IndexOf(extName.ToLower()) >= 0) { List.Add(f); } } foreach (string d in dir) { getdir(d, extName);//遞歸 } } } catch (Exception ex) { throw ex; } }
部分參數需要初始化:
Imgsize = new Size(Imgsize_W, Imgsize_H); // objectPoints = new List<Mat>(); imagePoints = new List<Mat>(); //內參和畸變 cameraMatrix = new Mat(3, 3, MatType.CV_32FC1, Scalar.Black); distCoeffs = new Mat(5, 1, MatType.CV_32FC1, Scalar.Black); camera_matrix = new double[3,3]; dist_coeffs = new double[5];
使用前注意賦值給相關參數;
使用效果:
棋盤圖片:如果需要可以聯系我,太大了。
計算獲取的參數:
內參:
%YAML:1.0
---
Distortion: !!opencv-matrix
rows: 5
cols: 1
dt: f
data: [ -1.07264303e-01, 7.62544945e-02, -1.15676608e-03,
1.04630087e-03, 4.89084125e-02 ]
畸變:
%YAML:1.0
---
Intrinsics: !!opencv-matrix
rows: 3
cols: 3
dt: f
data: [ 1.40335022e+03, 0., 1.21855811e+03, 0., 1.40350696e+03,
1.06063965e+03, 0., 0., 1. ]
棋盤角點圖片:
畸變校正效果:畸變前
校正后:
仔細對比,還是有一定效果。
自動化比較忙。有問題可以留言。