c#棋盘格相机标定

记录:
1.opencv的calibrateCamera()函数实现相机标定
2.已知相机内外参矩阵,通过ProjectPoints()函数实现理论角点计算
3.通过理论角点及实际角点计算误差
4.采用getOptimalNewCameraMatrix()函数及已知的相机内外参矩阵计算最优的相机矩阵
5.通过相机内外参矩阵及最优相机矩阵,采用UnDistort()获取无畸变的图像

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenCvSharp;
using System.Threading.Tasks;
using Size = OpenCvSharp.Size;
using Point = OpenCvSharp.Point;
using System.IO;
using System.Reflection;

namespace TargetMeasureObject.Processor
{
    
    
     class CameraCalibrator:NormalProcessor
    {
    
    
        private int len_dist_coeffs;//畸变系数长度,4(k1,k2,p1,p2),5[,k3],6[,k4]
        private string images_directory;//棋盘格图像路径
        private int n;//棋盘格图像数量
        private Mat[] org_images;//棋盘格原始图像
        private Mat[] corned_images;//绘制角点的图像
        private Mat[] undistort_images;//消除畸变的图像
        private double physical_interval;//棋盘格角点物理间距,单位m
        private Size cp_shape_wh;//棋盘格角点排列(宽度上角点数量,高度上角点数量)
        private List<Point3f> cp_world;//世界坐标角点模板
        private List<Point2f[]> cp_pixel;//像素坐标角点集合
        private string[] extension = {
    
     "jpg", "tif", "jpeg" };
        private double[,] cam_intrinsic;//相机内参
        private double[] cam_dist_coeffs;//相机畸变参数
        private Vec3d[] rot_mat_extrinsic;//相机外参-旋转矩阵
        private Vec3d[] trans_vec_extrinsic;//相机外参-平移矩阵
        private bool calibrated_status = false;//标定结束状态
        private bool visualize;//可视化
        public double[] error_reprojection;//二次投影误差
        public CameraCalibrator()
        {
    
    
            
        }
        public CameraCalibrator(string imgs_dir,Size cSeq,double interval,int dist_len,bool visual)
        {
    
    
            this.images_directory = imgs_dir;
            this.physical_interval = interval;
            this.cp_shape_wh= cSeq;
            this.visualize = visual;
            this.len_dist_coeffs = dist_len;
            this.Initial();
        }

        /// <summary>
        /// 初始化棋盘的世界坐标系的点
        /// </summary>
        private void Initial()
        {
    
    
            int w = this.cp_shape_wh.Width;
            int h = this.cp_shape_wh.Height;
            //相机内参矩阵初始化3*3
            this.cam_intrinsic = new double[3, 3];
            //相机畸变参数矩阵初始化(k1,k2,p1,p2[,k3[,k4,k5]])
            this.cam_dist_coeffs = new double[this.len_dist_coeffs];
            //世界坐标系角点初始化模板:对应索引*标定板网格间隔(unit=>m)
            this.cp_world = new List<Point3f>();
            for (int i = 0; i < h; i++)
            {
    
    
                for(int j = 0; j < w; j++)
                {
    
    
                    Point3f pt = new Point3f(j,i,0);
                    pt = pt.Multiply(this.physical_interval);
                    this.cp_world.Add(pt);
                }
            }
        }
        /// <summary>
        /// 执行标定
        /// </summary>
        public void Calibrate()
        {
    
    
            this.cp_pixel = new List<Point2f[]>();
            string[] directs = Directory.GetFiles(this.images_directory);
            this.n = directs.Length;
            this.org_images = new Mat[n];
            this.corned_images = new Mat[n];
            List<List<Point3f>> cp_world = new List<List<Point3f>>();
            for (int i = 0; i < n; i++)
            {
    
    
                //获取标定图像
                this.org_images[i] = Cv2.ImRead(directs[i]);
                //复制角点图像
                this.corned_images[i] = this.org_images[i].Clone();
                Mat gray_img = new Mat();
                Cv2.CvtColor(this.org_images[i], gray_img, ColorConversionCodes.BGR2GRAY);
                //查询像素坐标系棋盘角点
                bool ret = Cv2.FindChessboardCorners(gray_img, this.cp_shape_wh, out Point2f[] corners);
                if (ret)
                {
    
    
                    //像素坐标系角点添加
                    this.cp_pixel.Add(corners);
                    //世界坐标系角点添加
                    cp_world.Add(this.cp_world);
                    //如果要求显示每张标定图像进行角点绘制
                    if (this.visualize)
                    {
    
    
                        bool found = false;
                        //绘制棋盘角点
                        Cv2.DrawChessboardCorners(this.corned_images[i], this.cp_shape_wh, corners, found);
                        string win_name = $"corner image show:{
      
      i}";
                        Cv2.ImShow(win_name, this.corned_images[i]);
                        Cv2.WaitKey(1000);
                        Cv2.DestroyWindow(win_name);
                    }
                }
            }
            //相机标定,获取相机内参,畸变参数,旋转矩阵,平移向量
            Cv2.CalibrateCamera(cp_world, this.cp_pixel, this.org_images[0].Size(), 
                this.cam_intrinsic, this.cam_dist_coeffs, 
                out this.rot_mat_extrinsic, out this.trans_vec_extrinsic); 
            this.calibrated_status= true;
        }

        /// <summary>
        /// 二次投影误差计算
        /// 对世界坐标系点,通过旋转矩阵、位移向量、相机内参矩阵、相机畸变系数计算出新的投影的像素坐标点,
        /// 计算新投影的像素坐标角点与实际图像中的像素角点误差
        /// </summary>
        /// <returns>所有样本误差均值</returns>
        public double ReprojectErrorCalc()
        {
    
    
            this.error_reprojection = new double[this.n];
            for (int i = 0; i < n; i++)
            {
    
    
                Point2f[] cp_reprojects=new Point2f[this.cp_shape_wh.Width*this.cp_shape_wh.Height];

                Cv2.ProjectPoints(this.cp_world,
                    new double[] {
    
     this.rot_mat_extrinsic[i].Item0, this.rot_mat_extrinsic[i].Item1, this.rot_mat_extrinsic[i].Item2 },
                    new double[] {
    
     this.trans_vec_extrinsic[i][0], this.trans_vec_extrinsic[i][1], this.trans_vec_extrinsic[i][2] },
                    this.cam_intrinsic, this.cam_dist_coeffs, out cp_reprojects, out double[,] jacobian);
                double[,] repro_data = new double[cp_reprojects.Length, 2];
                double[,] cp_pixel_data = new double[cp_reprojects.Length, 2];
                for (int j = 0; j < cp_reprojects.Length; j++)
                {
    
    
                    repro_data[j, 0] = cp_reprojects[j].X;
                    repro_data[j, 1] = cp_reprojects[j].Y;
                    cp_pixel_data[j, 0] = this.cp_pixel[i][j].X;
                    cp_pixel_data[j, 1] = this.cp_pixel[i][j].Y;
                }
                Mat repro_mat = new Mat(cp_reprojects.Length, 2, MatType.CV_64FC1, repro_data);
                Mat cp_pixel_mat = new Mat(cp_reprojects.Length, 2, MatType.CV_64FC1, cp_pixel_data);
                double err = Cv2.Norm(cp_pixel_mat,repro_mat,NormTypes.L2)/(double)repro_data.GetLength(0);
                this.error_reprojection[i] = err;
            }
            return this.error_reprojection.ToArray().Average();
        }


        /// <summary>
        /// 消除图像畸变
        /// </summary>
        /// <param name="visualize">是否可见</param>
        public void RemoveDistorts(bool visualize=true)
        {
    
    
            int w = this.org_images[0].Width;
            int h = this.org_images[0].Height;
            if (!this.calibrated_status) return; 
            this.undistort_images = new Mat[this.n];
            //相机内参、畸变参数需矩阵化,防止报错方实现cv.Undistort()
            Mat intrinsic_mat = new Mat(3, 3, MatType.CV_64FC1, this.cam_intrinsic);
            Mat coeffs_mat = new Mat(5, 1, MatType.CV_64FC1, this.cam_dist_coeffs);
            for (int i = 0; i < this.n; i++)
            {
    
    
                var camera_matrix_array= Cv2.GetOptimalNewCameraMatrix(this.cam_intrinsic, this.cam_dist_coeffs, 
                    this.cp_shape_wh, 0, this.cp_shape_wh,out Rect rect);
                Mat camera_matrix_mat = new Mat(camera_matrix_array.GetLength(0),
                    camera_matrix_array.GetLength(1),MatType.CV_64FC1, camera_matrix_array);
                //无畸变图像需初始化,不得为空
                this.undistort_images[i] = new Mat();
                //消除图像畸变并输出
                Cv2.Undistort(this.org_images[i], this.undistort_images[i], intrinsic_mat, 
                    coeffs_mat, camera_matrix_mat);
                if (visualize)
                {
    
    
                    string wn = $"compared:{
      
      i}";
                    Mat compare_imgs=new Mat(this.org_images[i].Height, this.org_images[i].Width*2,MatType.CV_8UC3);
                    compare_imgs[new Rect(0, 0, w, h)] = org_images[i];
                    compare_imgs[new Rect(w - 1, 0, w, h)] = this.undistort_images[i];
                    Cv2.ImShow("compare", compare_imgs);
                    Cv2.WaitKey();
                    Cv2.DestroyAllWindows();
                }
            }

        }
    }
}

猜你喜欢

转载自blog.csdn.net/JAYLEE900/article/details/132507753