精华内容
下载资源
问答
  • 使用了OpenCvSharp资源开发包,在VS下做了一个棋盘格图像下的相机标定助手小Demo,显然,C#也可以用OpenCv了。这是一个比较好的案例,可以参考下。鄙人不才,也用它做了一个SFM三维重建的Demo,这里就不放了。 ...
    • 使用的是VS调用OpenCvSharp资源库进行一个Winform操作界面编写,网上找了很多开源的程序,发现根本用不了的,用的时候还需要你配置各种电脑系统变量,显得好麻烦。现在弄了个简单的标定助手,可以完美运行,带有棋盘格图像生成工具,操作简单,源码也不复杂。
      这里是
    using OpenCvSharp;//需要引用OpenCvSharp
    using OpenCvSharp.Extensions;
    using Size = OpenCvSharp.Size;
    
            public Mat ChessBoardMat;
            /// <summary>
            /// 生成棋盘格图像
            /// </summary>
            /// <param name="BoardSize">输入棋盘格角点数量的大小</param>
            /// <param name="ImagePixel">输入棋盘格的像素大小</param>
            /// <returns></returns>
    		public Bitmap GenChessBoard(OpenCvSharp.Size BoardSize, OpenCvSharp.Size ImagePixel)
            {
                int perBoardPixel = ImagePixel.Height / BoardSize.Height;
                int basisHeight = (ImagePixel.Height - perBoardPixel * BoardSize.Height) / 2;
                int basisWidth = (ImagePixel.Width - perBoardPixel * BoardSize.Width) / 2;
                if (basisHeight < 0 || basisWidth < 0)
                {
                    return null;
                }
                ChessBoardMat = new Mat(ImagePixel, MatType.CV_8UC1, Scalar.All(255));
                int flag;
                for (int j = 0; j < BoardSize.Height; j++)
                {
                    for (int i = 0; i < BoardSize.Width; i++)
                    {
                        flag = (i + j) % 2;
                        if (flag == 0)
                        {
                            for (int n = j * perBoardPixel; n < (j + 1) * perBoardPixel; n++)
                                for (int m = i * perBoardPixel; m < (i + 1) * perBoardPixel; m++)
                                    ChessBoardMat.At<byte>(n + basisHeight, m + basisWidth) = 0;
                        }
                    }
                }
                return BitmapConverter.ToBitmap(ChessBoardMat);//返回棋盘格图像结果
                //Cv2.ImWrite("chessBoard1.bmp", image);//这里是保存生成的图像,可以不使用
            }
    

    结果如下:
    棋盘格生成工具点击“导入图像”后,选择采集的棋盘格图像所在文件夹,结果如下:
    棋盘格标定助手 导入图像

    可以在图像列表看到导入结果,在参数设置那里设置正确的棋盘格角点数量后,在图像列表双击图像路径,可以实现棋盘格图像角点的提取显示:
    棋盘格标定助手 图像角点显示
    可以提取角点之后就可以标定啦!
    棋盘格标定助手  标定结果标定完成后,可以进行畸变矫正,这个是进行畸变矫正的结果,在这里并没有写保存矫正结果的代码。
    所以只是看看就好

    然后所有结果都出来了,并且自动保存结果在图像文件里内!
    标定结果数据查看

    这里是核心源码,至于Winform的操作代码,这里就不放了。感兴趣的朋友可以去下载看看

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using OpenCvSharp;
    using OpenCvSharp.Extensions;
    using Size = OpenCvSharp.Size;
    
    namespace CvCalibrate
    {
        public class Calib_Class
        {
            public Mat ChessBoardMat;
            #region 初始化变量
            /*内外参数*/
            Mat cameraMatrix = new Mat(3, 3, MatType.CV_64FC1, Scalar.All(0)); /* 摄像机内参数矩阵 */
            List<int> point_counts = new List<int>();  // 每幅图像中角点的数量
            Mat distCoeffs = new Mat(1, 12, MatType.CV_64FC1, Scalar.All(0)); /* 摄像机的5个畸变系数:k1,k2,p1,p2,k3 */  /*加上薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
            List<Mat> TranslationMats = new List<Mat>();  /* 每幅图像的旋转向量 */
            List<Mat> RotationMats = new List<Mat>(); /* 每幅图像的平移向量 */
            /*Mat数据类型转换double的结果*/
            public double[,] CameraParameterArray = new double[3, 3];//摄像机内参数矩阵
            public double[] DistCoeffsArray = new double[12];//摄像机的5个畸变系数:k1,k2,p1,p2,k3 /*加上薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
            public Vec3d[] Rotation; //存放所有图像的3*1旋转向量,每一副图像的旋转向量为一个mat
            public Vec3d[] Translation;//存放所有图像的3*1平移向量,每一副图像的平移向量为一个mat
            public double MeanError = 0.0; /* 所有图像的平均误差的总和 */
            public List<double> TotalError = new List<double>(); /* 每幅图像的平均误差 */
            public Size BoardImageSize = new Size();
            #endregion
            /// <summary>
            /// 角点提取,返回Bitmap数据
            /// </summary>
            /// <param name="imageInput">图像输入</param>
            /// <param name="BoardSize">棋盘格角点数量大小</param>
            /// <returns></returns>
            public Bitmap FindChessboardCorners(Image imageInput, OpenCvSharp.Size BoardSize)
            {
                try
                {
                    Point2f[] image_points_buf;  /* 缓存每幅图像上检测到的角点 */
                    List<Point2f[]> image_points_seq = new List<Point2f[]>(); /* 保存检测到的所有角点 */
                    Mat image = BitmapConverter.ToMat(new Bitmap(imageInput));
                    /* 提取角点 */
                    Cv2.FindChessboardCorners(image, BoardSize, out image_points_buf);
                    Mat view_gray = new Mat();
                    Cv2.CvtColor(image, view_gray, ColorConversionCodes.RGB2GRAY);
                    /* 亚像素精确化 */
                    Point2f[] SubPix_points = Cv2.CornerSubPix(view_gray, image_points_buf, new Size(3, 3), new Size(-1, -1), TermCriteria.Both(30, 0.1));
                    image_points_seq.Add(SubPix_points);  //保存亚像素角点
                    /* 在图像上显示角点位置 */
                    Cv2.DrawChessboardCorners(view_gray, BoardSize, image_points_buf, true); //用于在图片中标记角点
                    return BitmapConverter.ToBitmap(view_gray);
                }
                catch (Exception)
                {
                    return new Bitmap(imageInput);
                }
    
            }
            public double Fovx/*沿水平传感器轴的视野*/, Fovy/*沿竖直传感器轴的视野*/, FocalLength/*透镜焦距mm*/, AspectRatio/*fy/fx*/;
            public Point2d Principal/*主点坐标mm*/;
            /// <summary>
            /// 运行标定
            /// </summary>
            /// <param name="ChessBoardFiles">棋盘格图像文件路径</param>
            /// <param name="BoardNum">棋盘格角点数量大小</param>
            /// <param name="ErrorMessage">返回判断的消息</param>
            /// <returns></returns>
            public bool Calibrate(List<string> ChessBoardFiles, Size BoardNum, string ErrorMessage)
            {
                int image_count = 0;  /* 图像数量 */
                Size image_size = new Size();  /* 图像的尺寸 */
                if (ChessBoardFiles.Count() > 0)
                {
    
                    #region 角点提取                 
                    Point2f[] image_points_buf;  /* 缓存每幅图像上检测到的角点 */
                    List<Point2f[]> image_points_seq = new List<Point2f[]>(); /* 保存检测到的所有角点 */
                    /*读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化*/
                    for (int i = 0; i < ChessBoardFiles.Count(); i++)
                    {
                        try
                        {
                            Mat imageInput = Cv2.ImRead(ChessBoardFiles[i]);
                            image_count++;
                            if (image_count == 1)  //读入第一张图片时获取图像宽高信息
                            {
                                BoardImageSize.Width = image_size.Width = imageInput.Cols;
                                BoardImageSize.Height = image_size.Height = imageInput.Rows;
                            }
                            image_points_buf = FindChessboardCorners(imageInput, BoardNum, ErrorMessage);
                            if (image_points_buf.Count() > 0)
                            {
                                image_points_seq.Add(image_points_buf);
                            }
                            else
                            {
                                ErrorMessage += "图 " + i + ", ";
                            }
    
                        }
                        catch (Exception Err)
                        {
                            ErrorMessage = " 角点提取失败!" + "\n\r" + Err.Message;
                            return false;
                        }
                    }
                    #endregion
    
                    #region 标定板初始化
                    //棋盘三维信息
                    // 初始化标定板上角点的三维坐标
                    //生成一个标准的标定板角点坐标集合
                    Size square_size = new Size(20, 20);  //初始化标定板上每个棋盘格的大小 
                    List<List<Point3f>> object_points = new List<List<Point3f>>(); // 保存标定板上角点的三维坐标
                    if (BoardNum.Width > 0)
                    {
                        try
                        {
                            for (int t = 0; t < image_count; t++)
                            {
                                List<Point3f> tempPointSet = new List<Point3f>();
                                for (int i = 0; i < BoardNum.Height; i++)
                                {
                                    for (int j = 0; j < BoardNum.Width; j++)
                                    {
                                        Point3f realPoint;
                                        // 假设标定板放在世界坐标系中z=0的平面上
                                        realPoint.X = i * square_size.Width;
                                        realPoint.Y = j * square_size.Height;
                                        realPoint.Z = 0;
                                        tempPointSet.Add(realPoint);
                                    }
                                }
                                object_points.Add(tempPointSet);
                                // 初始化每幅图像中的角点数量,假定每幅图像中都可以看到完整的标定板 
                                point_counts.Add(BoardNum.Width * BoardNum.Height);
                            }
                        }
                        catch (Exception Err)
                        {
                            ErrorMessage = "初始化棋盘格失败!" + "\n\r" + Err.Message;
                        }
                    }
                    else
                    {
                        ErrorMessage = "棋盘格大小设置错误!";
                        return false;
                    }
    
                    #endregion
    
                    #region 开始标定
                    try
                    {
                        /*薄棱镜畸变模型,启用畸变系数S1、S2、S3和S4*/
                        /*迭代标准*/
                        TermCriteria criteria = new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.MaxIter, 30, 0.01);
                        Cv2.CalibrateCamera(object_points, image_points_seq, image_size, CameraParameterArray, DistCoeffsArray, out Rotation, out Translation, CalibrationFlags.ThinPrismModel, criteria);
    
                    }
                    catch (Exception Err)
                    {
                        ErrorMessage = "标定失败!" + "\n\r" + Err.Message;
                        return false;
                    }
                    #endregion
    
                    #region 数据类型转换
                    for (int r = 0; r < CameraParameterArray.GetLength(0); r++)
                    {
                        for (int c = 0; c < CameraParameterArray.GetLength(1); c++)
                        {
                            cameraMatrix.At<double>(r, c) = CameraParameterArray[r, c];
                        }
                    }
    
                    for (int r = 0; r < DistCoeffsArray.Length; r++)
                    {
                        distCoeffs.At<double>(0, r) = DistCoeffsArray[r];
                    }
                    for (int r = 0; r < Rotation.Length; r++)
                    {
                        Mat TempRotation = new Mat(3, 1, MatType.CV_64FC1);
    
                        TempRotation.At<double>(0, 0) = Rotation[r].Item0;
                        TempRotation.At<double>(1, 0) = Rotation[r].Item1;
                        TempRotation.At<double>(2, 0) = Rotation[r].Item2;
                        RotationMats.Add(TempRotation);
    
                        Mat TempTrans = new Mat(3, 1, MatType.CV_64FC1);
    
                        TempTrans.At<double>(0, 0) = Translation[r].Item0;
                        TempTrans.At<double>(1, 0) = Translation[r].Item1;
                        TempTrans.At<double>(2, 0) = Translation[r].Item2;
                        TranslationMats.Add(TempTrans);
                    }
                    #endregion
    
                    #region 重新投影计算,误差计算
                    double total_err = 0.0;            // 所有图像的平均误差的总和 
                    double Error;                      //每幅图像的平均误差 
    
                    for (int i = 0; i < image_count; i++)
                    {
                        try
                        {
                            List<Point3f> tempPointSet = object_points[i];
                            /* 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 */
                            Mat NewProjectPoints = new Mat();/* 保存重新计算得到的投影点 */
                            Mat jacobian = new Mat();//雅可比矩阵
                            Cv2.ProjectPoints(InputArray.Create<Point3f>(tempPointSet), RotationMats[i], TranslationMats[i], cameraMatrix, distCoeffs, NewProjectPoints, jacobian, 0);
                            List<Point2f> NewProjectPointsToPoint2f = new List<Point2f>(); /* 保存重新计算得到的投影点,格式转换 */
                            {
                                string[] PointsArray = Cv2.Format(NewProjectPoints, FormatType.CSV).Split('\n');//分割出来最后一个回车符号占了一个空间
                                for (int r = 0; r < PointsArray.Length - 1; r++)
                                {
                                    Point2f Temp = new Point2f();//临时缓存
                                    string[] PointTemp = PointsArray[r].Split(',');
                                    Temp.X = float.Parse(PointTemp[0]);
                                    Temp.Y = float.Parse(PointTemp[1]);
                                    NewProjectPointsToPoint2f.Add(Temp);
                                }
                            }
    
                            /* 计算新的投影点和旧的投影点之间的误差*/
                            Point2f[] OldProjectPoints = image_points_seq[i];
                            Mat tempImagePointMat = new Mat(1, OldProjectPoints.Length, MatType.CV_32FC2);
                            Mat image_points2Mat = new Mat(1, NewProjectPoints.Rows, MatType.CV_32FC2);
                            for (int j = 0; j < OldProjectPoints.Count(); j++)
                            {
                                image_points2Mat.At<Vec2f>(0, j) = new Vec2f(NewProjectPointsToPoint2f[j].X, NewProjectPointsToPoint2f[j].Y);
                                tempImagePointMat.At<Vec2f>(0, j) = new Vec2f(OldProjectPoints[j].X, OldProjectPoints[j].Y);
                            }
                            Error = Cv2.Norm(image_points2Mat, tempImagePointMat, NormTypes.L2);//当前图像的投影误差
                            total_err += Error * Error;
                            TotalError.Add(Error);
                            MeanError += Error /= point_counts[i];//总的平均误差
                            double 重投影误差3 = (total_err / image_count);
                        }
                        catch (Exception Err)
                        {
                            ErrorMessage = "误差计算失败!" + "\n\r" + Err.Message;
                            return false;
                        }
                    }
                    #endregion
    
                    #region 旋转矩阵计算
                    List<Mat> AllRotation_Matrix = new List<Mat>(); /* 保存每幅图像的旋转矩阵 */
                    for (int i = 0; i < image_count; i++)
                    {
                        try
                        {
                            Mat rotation_matrix = new Mat(3, 3, MatType.CV_64FC1, Scalar.All(0));
                            /* 将旋转向量转换为相对应的旋转矩阵 */
                            /*Rodrigues()可以将旋转向量转化为旋转矩阵,也可以将旋转矩阵转化为旋转向量。旋转向量指定了旋转轴,同时它的模长也指定了旋转角度。*/
                            Cv2.Rodrigues(TranslationMats[i], rotation_matrix);
                            AllRotation_Matrix.Add(rotation_matrix);
                        }
                        catch (Exception Err)
                        {
                            ErrorMessage = "旋转矩阵计算失败!" + "\n\r" + Err.Message;
                            return false;
                        }
    
                    }
                    #endregion
                    //Mat Rvec = new Mat();
                    //Mat Tvec = new Mat();
                    //Cv2.SolvePnP(InputArray.Create<Point3f>(object_points[0]), InputArray.Create<Point2f>(image_points_seq[0]), cameraMatrix, distCoeffs, Rvec, Tvec);
                    //double[] R = MatToDouble(Rvec);
                    //double[] T = MatToDouble(Tvec);
                    Cv2.CalibrationMatrixValues(cameraMatrix, BoardImageSize, 5.3, 7.2, out Fovx, out Fovy, out FocalLength, out Principal, out AspectRatio);
    
                }
                else
                {
                    ErrorMessage = "输入图像路径为空!";
                    return false;
                }
                return true;
            }
    
            private delegate Point2f[] FindCornersDelegate(Mat Src, Size BoardNum, string ErrorMessage);
    
            /// <summary>
            /// 角点提取
            /// </summary>
            /// <param name="Src">图源</param>
            /// <param name="BoardNum">棋盘格角点数量大小</param>
            /// <param name="ErrorMessage">返回判断的消息</param>
            /// <returns></returns>
            private static Point2f[] FindChessboardCorners(Mat Src, Size BoardNum, string ErrorMessage)
            {
                Point2f[] image_points_buf = new Point2f[BoardNum.Height * BoardNum.Width];  /* 缓存每幅图像上检测到的角点 */
                Point2f[] SubPix_points = new Point2f[BoardNum.Height * BoardNum.Width];
                try
                {
                    /* 提取角点 */
                    if (!Cv2.FindChessboardCorners(Src, BoardNum, out image_points_buf))
                    {
                        //粗提取角点,若无法提取则返回失败,反之进行精细化提取
                        ErrorMessage = " 角点提取失败!";
                    }
                    else
                    {
                        Mat view_gray = new Mat();
                        Cv2.CvtColor(Src, view_gray, ColorConversionCodes.RGB2GRAY);
                        SubPix_points = Cv2.CornerSubPix(view_gray, image_points_buf, new Size(3, 3), new Size(-1, -1), TermCriteria.Both(30, 0.1));//对粗提取的角点进行精确化
                        //Cv2.Find4QuadCornerSubpix(view_gray, image_points_buf, new Size(5, 5)); //对粗提取的角点进行精确化
                    }
                }
                catch (Exception Err)
                {
                    ErrorMessage = Err.Message;
                }
                return SubPix_points;
            }
    
            #region 类型转换
            static double[] MatToDouble(Mat InMat)
            {
                if (InMat.Rows >= 1 && InMat.Cols == 1)
                {
                    double[] OutDouble = new double[InMat.Rows];
                    for (int i = 0; i < InMat.Rows; i++)
                    {
                        OutDouble[i] = InMat.At<double>(i);
                    }
                    return OutDouble;
                }
                else if (InMat.Rows == 1 && InMat.Cols >= 1)
                {
                    double[] OutDouble = new double[InMat.Cols];
                    for (int i = 0; i < InMat.Cols; i++)
                    {
                        OutDouble[i] = InMat.At<double>(i);
                    }
                    return OutDouble;
                }
                else
                {
                    return null;
                }
            }
            static double[,] MatToDouble2D(Mat InMat)
            {
                if (InMat.Rows >= 1 && InMat.Cols >= 1)
                {
                    double[,] OutDouble = new double[InMat.Rows, InMat.Cols];
                    for (int i = 0; i < InMat.Rows; i++)
                    {
                        for (int j = 0; j < InMat.Cols; j++)
                        {
                            OutDouble[i, j] = InMat.At<double>(i, j);
                        }
                    }
                    return OutDouble;
                }
                else
                {
                    return null;
                }
            }
            static Mat DoubleToMat(double[] InDouble)
            {
                Mat OutMat = new Mat(1, InDouble.Length, MatType.CV_64FC1);
                for (int r = 0; r < InDouble.Length; r++)
                {
                    OutMat.At<double>(0, r) = InDouble[r];
                }
                return OutMat;
            }
            static Mat Double2DToMat(double[,] InDouble)
            {
                Mat OutMat = new Mat(InDouble.GetLength(0), InDouble.GetLength(1), MatType.CV_64FC1);
                for (int r = 0; r < InDouble.GetLength(0); r++)
                {
                    for (int c = 0; c < InDouble.GetLength(1); c++)
                    {
                        OutMat.At<double>(r, c) = InDouble[r, c];
                    }
    
                }
                return OutMat;
            }
            #endregion
    
    
    
           
        }
    }
    

    付费资源,各取所需吧,花了时间和心思做出来的东西让我免费共享是不可能的。
    项目资源下载
    对了,下载的源码里面有畸变矫正功能的,但是参数那些暂时没设置正确,所以矫正出来的图像似乎没那么准确,有兴趣的朋友可以去研究下,其实所有的代码都是可以参考OpenCv的,只不过是放在了C#下,有些变量类型略有不同,具体怎么样看看它的类就知道了。

    展开全文
  • 今天,我看见我一同学在用photoshop画棋盘格,用于相机标定的。然后我就不淡定了,决定给他写程序完成这工作。写了1个多小时,终于整完了。把代码放在这,希望能帮到别人。malab的代码,下面是主函数。主函数:...

    今天,我看见我一同学在用photoshop画棋盘格,用于相机标定的。然后我就不淡定了,决定给他写个程序完成这个工作。写了1个多小时,终于整完了。把代码放在这,希望能帮到别人。malab的代码,下面是主函数。

    主函数:

    function M = writeChessBoard( width, height, size, xnum, ynum, position, background)

    % This function writes a chessboard on image.

    % 该方程创建一个棋盘格图像。

    %参数解释:

    %   width: 宽度

    %   height:高度

    %   size:单个小方块的大小

    %   xum:x方向上的个数(每4个小方块一组)

    %   xum:x方向上的个数(每4个小方块一组)

    %   position:起始位置的坐标(为了留出边,便于打印)

    %   background:背景亮度, 0<=background<=1, 0表示纯黑,1表示纯白

    M=ones(width,height);

    M(:)=background;

    block=size*2;

    for i=1:xnum,

    for j=1:ynum,

    pos=[position(1)+block*(i-1), position(2)+block*(j-1)];

    M=ChessBoard(M,size,pos);

    end

    end

    end

    生成的棋盘样图:

    23263cdc479d4b389bf67403c7f0a735.png

    完工!

    

    展开全文
  • 利用棋盘格图案完成相机标定

    千次阅读 2019-05-16 16:57:44
    摄像机标定 单孔摄像机(照相机)会给图像...棋盘格标定 OpenCV使用棋盘格板进行标定,如下图所示。为了标定相机,我们需要输入一系列三维点和它们对应的二维图像点。在黑白相间的棋盘格上,二维图像点很容易通过...

    摄像机标定

    单孔摄像机(照相机)会给图像带来很多畸变。畸变主要有两种:径向畸变和切想畸变。如下图所示,用红色直线将棋盘的两个边标注出来,但是你会发现棋盘的边界并不和红线重合。所有我们认为应该是直线的也都凸出来了。

    输入图片说明

    所以需要进行相机标定

    棋盘格标定

    OpenCV使用棋盘格板进行标定,如下图所示。为了标定相机,我们需要输入一系列三维点和它们对应的二维图像点。在黑白相间的棋盘格上,二维图像点很容易通过角点检测找到。而对于真实世界中的三维点呢?由于我们采集中,是将相机放在一个地方,而将棋盘格定标板进行移动变换不同的位置,然后对其进行拍摄。所以我们需要知道(X,Y,Z)的值。但是简单来说,我们定义棋盘格所在平面为XY平面,即Z=0。对于定标板来说,我们可以知道棋盘格的方块尺寸,例如30mm,这样我们就可以把棋盘格上的角点坐标定义为(0,0,0),(30,0,0),(60,0,0),···,这个结果的单位是mm。
      3D点称为object points,2D图像点称为image points。

    输入图片说明

    检测棋盘格角点

    为了找到棋盘格模板,我们使用openCV中的函数cv2.findChessboardCorners()。我们也需要告诉程序我们使用的模板是什么规格的,例如88的棋盘格或者55棋盘格等,建议使用x方向和y方向个数不相等的棋盘格模板。下面实验中,我们使用的是86的棋盘格,每个方格边长是20mm,即含有75的内部角点。这个函数如果检测到模板,会返回对应的角点,并返回true。当然不一定所有的图像都能找到需要的模板,所以我们可以使用多幅图像进行定标。除了使用棋盘格,我们还可以使用圆点阵,对应的函数为cv2.findCirclesGrid()。
      
    找到角点后,我们可以使用cv2.cornerSubPix()可以得到更为准确的角点像素坐标。我们也可以使用cv2.drawChessboardCorners()将角点绘制到图像上显示。如下图所示:

    输入图片说明

    用手机拍摄了14张棋盘格图片,并将分辨率调整到832x624,既将原分辨率缩小20%

    输入图片说明

    标定

    通过上面的步骤,我们得到了用于标定的三维点和与其对应的图像上的二维点对。我们使用cv2.calibrateCamera()进行标定,这个函数会返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量。

    ret: 0.49780289785568144
    内参数矩阵:
     [[662.65760958   0.         411.7554067 ]
     [  0.         643.62036576 312.36138692]
     [  0.           0.           1.        ]]
    畸变系数:
     [[ 1.75605608e-01 -7.32319628e-01  1.74170917e-04  1.07586378e-04
       8.15249028e-01]]
    旋转向量: [array([[ 0.06848192],
           [-0.11021601],
           [ 0.19342032]]), array([[ 0.08989725],
           [-0.2535112 ],
           [-0.02463974]]), array([[0.09910906],
           [0.25820579],
           [0.00510581]]), array([[-0.14623837],
           [ 0.00945762],
           [ 0.001026  ]]), array([[ 0.20322661],
           [-0.02586824],
           [-0.00751255]]), array([[ 0.20643026],
           [-0.087326  ],
           [-0.03735129]]), array([[ 0.05928905],
           [-0.31920575],
           [-0.01057067]]), array([[0.07565014],
           [0.21659039],
           [0.00056359]]), array([[0.20951298],
           [0.23124036],
           [0.05598759]]), array([[0.02498131],
           [0.05687095],
           [0.00956028]]), array([[-0.1802921 ],
           [-0.03419934],
           [-0.00123688]])]
    平移向量: [array([[-2.36878258],
           [-2.90199425],
           [ 7.32577822]]), array([[-2.62155616],
           [-1.99853277],
           [ 6.49172398]]), array([[-3.28628108],
           [-2.26570619],
           [ 7.62271742]]), array([[-3.06893891],
           [-2.59407061],
           [ 7.86211726]]), array([[-3.09682038],
           [-2.22304886],
           [ 7.07652815]]), array([[-2.73779139],
           [-1.77450487],
           [ 6.88388611]]), array([[-2.28058289],
           [-2.10824111],
           [ 6.15841957]]), array([[-3.35984547],
           [-2.08592   ],
           [ 7.32862568]]), array([[-3.70854886],
           [-2.24924643],
           [ 7.31569625]]), array([[-3.26049871],
           [-2.235856  ],
           [ 7.17300778]]), array([[-3.03386901],
           [-2.40116135],
           [ 7.3427078 ]])]
    

    由于手机拍摄的图片分辨率过高,原图:4160X3120的分辨率,相当于把图片分辨率缩小20%。
    根据摄像头自身参数

    焦距f:4mm

    像素大小Δx=Δy​:1.12um

    计算可知:fx=fΔx=3571.42857、fy=fΔy=3571.42857

    u0=4160/2=2080、v0=3120/2=1560

    通过相机标定的值:

    fx=662.65760958 、fy=643.62036576

    u0=411.7554067、v0=312.36138692

    换算以后:

    fx=3313.28805 、 fy=3218.10183

    u0=2058.77703 、 v0=1561.80693

    由于预先图片分辨率缩减了20%,再加上摄像头的误差,加上我自己拍摄的因素,虽然误差达到了200多,但是最后得到的这个结果,我觉得也是合理的。

    去畸变

    第三步我们已经得到了相机内参和畸变系数,在将图像去畸变之前,我们还可以使用cv.getOptimalNewCameraMatrix()优化内参数和畸变系数,通过设定自由自由比例因子alpha。当alpha设为0的时候,将会返回一个剪裁过的将去畸变后不想要的像素去掉的内参数和畸变系数;当alpha设为1的时候,将会返回一个包含额外黑色像素点的内参数和畸变系数,并返回一个ROI用于将其剪裁掉。

    然后我们就可以使用新得到的内参数矩阵和畸变系数对图像进行去畸变了。

    输入图片说明

    反投影误差

    通过反投影误差,我们可以来评估结果的好坏。越接近0,说明结果越理想。通过之前计算的内参数矩阵、畸变系数、旋转矩阵和平移向量,使用cv2.projectPoints()计算三维点到二维图像的投影,然后计算反投影得到的点与图像上检测到的点的误差,最后计算一个对于所有标定图像的平均误差,这个值就是反投影误差。

    total error: 0.07951334176924868
    

    完整代码如下:

    #coding:utf-8
    import cv2
    import numpy as np
    import glob
    
    # 找棋盘格角点
    # 阈值
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    #棋盘格模板规格
    w = 9
    h = 6
    
    # 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
    objp = np.zeros((w*h,3), np.float32)
    objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2)
    
    # 储存棋盘格角点的世界坐标和图像坐标对
    objpoints = [] # 在世界坐标系中的三维点
    imgpoints = [] # 在图像平面的二维点
    
    images = glob.glob('test_pic/*.jpg')
    for fname in images:
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  #图像二值化
    
        # 找到棋盘格角点
        ret, corners = cv2.findChessboardCorners(gray, (w,h),None)
    
        # 如果找到足够点对,将其存储起来
        if ret == True:
            cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
            objpoints.append(objp)
            imgpoints.append(corners)
    
            # 将角点在图像上显示
            cv2.drawChessboardCorners(img, (w,h), corners, ret)
            cv2.imshow('findCorners',img)
            cv2.waitKey(500)
    #cv2.destroyAllWindows()#删除建立的全部窗口
    
    # 标定
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
    #相机标定 返回标定结果、相机的内参数矩阵、畸变系数、旋转矩阵和平移向量
    
    
    # 保存相机参数
    np.savez('C.npz', mtx=mtx, dist=dist, rvecs=rvecs, tvecs=tvecs)
    print("ret:", ret)
    print("内参数矩阵:\n", mtx)
    print("畸变系数:\n", dist)
    print("旋转向量:", rvecs)  # 外参数
    print("平移向量:", tvecs)  # 外参数
    
    
    
    # 去畸变
    img2 = cv2.imread('test_pic/left05.jpg')
    h,  w = img2.shape[:2]
    newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),0,(w,h)) # 自由比例参数
    dst = cv2.undistort(img2, mtx, dist, None, newcameramtx)
    
    # 根据前面ROI区域裁剪图片
    #x,y,w,h = roi
    #dst = dst[y:y+h, x:x+w]
    cv2.imwrite('calibresult.jpg',dst)
    
    # 反投影误差
    total_error = 0
    for i in range(len(objpoints)):
        imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
        error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2)
        total_error += error
    print("total error: {}".format(total_error/len(objpoints)))
    
    
    
    1. 在1基础上,使用同一相机,将棋盘格放在前方1m左右固定,然后使用线性方法进行相对位姿估计,然后评价结果的合理性。

    在上问的摄像机标定中,我们已经得到了摄像机矩阵,畸变系数等。有了这些信息我们就可以估计图像中图案的姿势,比如目标对象是如何摆放,如何旋转等。对一个平面对象来说,我们可以假设 Z=0,这样问题就转化成摄像机在空间中是如何摆放(然后拍摄)的。所以,如果我们知道对象在空间中的姿势,我们就可以在图像中绘制一些 2D 的线条来产生 3D 的效果。

    输入图片说明

    完整代码:

    import cv2
    import numpy as np
    import glob
    
    # 加载相机标定的数据
    with np.load('C.npz') as X:
        mtx, dist, _, _ = [X[i] for i in ('mtx', 'dist', 'rvecs', 'tvecs')]
    
    
    def draw(img, corners, imgpts):
        """
        在图片上画出三维坐标轴
        :param img: 图片原数据
        :param corners: 图像平面点坐标点
        :param imgpts: 三维点投影到二维图像平面上的坐标
        :return:
        """
        corner = tuple(corners[0].ravel())
        cv2.line(img, corner, tuple(imgpts[0].ravel()), (255, 0, 0), 5)
        cv2.line(img, corner, tuple(imgpts[1].ravel()), (0, 255, 0), 5)
        cv2.line(img, corner, tuple(imgpts[2].ravel()), (0, 0, 255), 5)
        return img
    
    
    # 初始化目标坐标系的3D点
    objp = np.zeros((6 * 7, 3), np.float32)
    objp[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)
    # 初始化三维坐标系
    axis = np.float32([[3, 0, 0], [0, 3, 0], [0, 0, -3]]).reshape(-1, 3)  # 坐标轴
    # 加载打包所有图片数据
    images = glob.glob('test_pic/*.jpg')
    for fname in images:
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 找到图像平面点坐标点
        ret, corners = cv2.findChessboardCorners(gray, (7, 6), None)
        if ret:
            # PnP计算得出旋转向量和平移向量
            _, rvecs, tvecs, _ = cv2.solvePnPRansac(objp, corners, mtx, dist)
            print("旋转变量", rvecs)
            print("平移变量", tvecs)
            # 计算三维点投影到二维图像平面上的坐标
            imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
            # 把坐标显示图片上
            img = draw(img, corners, imgpts)
            cv2.imshow('img', img)
            cv2.waitKey(500)
    
    #cv2.destroyAllWindows()
    
    
    展开全文
  • 棋盘格生成器可以生成上面四种格式的标定板,想要大想要几行几列都可以动态设置。 非常好用,对于自己写代码或用cad画都比较浪费时间,这生成器可以立刻生成pdf,只要打印机不设置缩放,即可正常尺寸打印。 ...

    棋盘格生成器可以生成上面四种格式的标定板,想要多大想要几行几列都可以动态设置。

    非常好用,对于自己写代码或用cad画都比较浪费时间,这个生成器可以立刻生成pdf,只要打印机不设置缩放,即可正常尺寸打印。

    非常非常好用,介绍给大家这个好用的地址。

    1、二维码棋盘格

    2、棋盘格

    3、圆点整列

    4、非对称圆

    棋盘格生成器可以生成上面四种格式的标定板,想要多大想要几行几列都可以动态设置。

    非常好用,对于自己写代码或用cad画都比较浪费时间,这个生成器可以立刻生成pdf,只要打印机不设置缩放,即可正常尺寸打印。

    非常非常好用,介绍给大家这个好用的地址。

    https://calib.io/pages/camera-calibration-pattern-generator

    如果对你有帮助,给点个赞吧!

     

    展开全文
  • 今天,我看见我一同学在用photoshop画棋盘格,用于相机标定的。然后我就不淡定了,决定给他写程序完成这工作。写了1个多小时,终于整完了。把代码放在这,希望能帮到别人。malab的代码,下面是主函数,下载全部...
  • OpenCV生成标定图(棋盘格)

    千次阅读 2018-10-29 14:19:24
    网上查了一下工业视觉标定板,少则几百大洋,则几千大洋,就想在A4纸上山寨打印一个标定图,就是黑白方格相间的那种。A4纸的标准大小为210*297mm。搞了把小时,其实想明白了之后很简单。从每像素的角度考虑,...
  • 今天,我看见我一同学在用photoshop画棋盘格,用于相机标定的。然后我就不淡定了,决定给他写程序完成这工作。写了1个多小时,终于整完了。把代码放在这,希望能帮到别人。malab的代码,下面是主函数。主函数:...
  • Visionpro棋盘格校正

    2020-09-23 21:11:18
    棋盘标定板 1.棋盘标定板的要求: 1黑白块必须水平和垂直方向交替排列; 1.2黑白块必须大小相同; 1.3黑白块必须是矩形的,如果它们不是正方形,它们的长宽比应该在0.90到1.10的范围内; 2.采集到的标定板图像的...
  • 最后,最小化多个相关点坐标的实际距离与其观测距离的差值并求解,实现角点的精确定位。实验结果表明,本文提出的棋盘格角点方法与现有方法相比,其标定误差明显减少;在光照不理想时,本文方法可以实现精确检测。该方法为...
  • ROS标定工具是采用棋盘格,具体实现是OPENCV。 将输入图像降采样到大约VGA分辨率,并在全尺寸图像中检测校准目标角点。 结合这些明显正交的职责作为一优化。棋盘式检测在大图像上太昂贵,所以最好在较小的显示图像...
  • matlab单目相机标定

    千次阅读 热门讨论 2019-03-23 21:17:02
    文章目录1、标定图片2、标定3、获取标定结果 使用matlab进行相机参数...采集标定板图片需要在相机各个方位进行拍摄,不要在同一位置获取张图片,标定板的大小根据相机视野而定,一般选择棋盘格图案占图片大小的...
  • 标定的目的 1.为什么要用标定板图片做标定?  具体数学实现题主可以自行找文献,这里只说原理。单目标定说白了其实就是解一个矩阵方程,其未知...理论上,只需2个棋盘格便足够解出全部参数了,但是matlab、ope
  • 2d Laser 和 camera 标定工具原理及使用方法

    千次阅读 多人点赞 2018-12-14 14:28:44
    2d 激光和相机之间之间的标定早在 04 年就出了成熟的论文和方法,去年 17 年 ICCV, IROS,今年 IROS 等依然还有论文产出。具体的论文列表可以参考我的《论文阅读...准备材料:标定板一(平板上贴有棋盘格或者二维...
  • 双目视觉的标定

    2018-09-17 22:41:45
    双目视觉的标定 1.为什么要用标定板图片做标定? 具体数学实现题主可以自行找文献,这里只说原理。单目标定说白了其实就是解一个...理论上,只需2个棋盘格便足够解出全部参数了,但是matlab、opencv等为了增强鲁...
  • 双目相机标定结果内参及外参验证

    千次阅读 2019-07-12 14:32:54
    双目相机标定结果(内参,外参)验证–在OpenGL中重投影目标模型到图像平面,与左右视角图像中的模型...我这里用到的方法也很简单,用的是比较经典的棋盘格标定法。两相机设置一定的拍摄角度,同时拍摄若干组标定板...
  • 并且将棋盘格固定放到一平板上,使用同一相机从不同的位置,不同的角度,拍摄标定板的张照片(10-20张左右),将照片放到文件夹中: 二、像机标定 利用摄像机所拍摄到的图像来还原空间中的物体。在这里,不妨...
  • Kinect2.0相机标定

    2020-06-22 16:18:45
    我搜相机标定的时候有很种方法,小白也不知道哪一能用有用,感觉乱七八糟的,这总结的很好Kinect2的标定和图像配准,我选的最简单的用matlab计算的。 下面是一些需要知道的基础知识,更好的操作和理解一些步骤...
  • 相机标定和校正

    千次阅读 2018-09-30 19:05:58
    首先要制作一个标定板,OpenCV包里有一黑白棋盘标定板,直接A4纸打印就OK了。然后用需要标定的摄像头拍几张照片(注意:要从不同角度拍几张,我用了22张。标定板上的点的个数为54,一行9,一列6),首先...
  • 用matlab软件写代码生成了一个棋盘格图 将上图棋盘格图片打印,然后再将打印出的纸固定放到一个平板上,使用同一相机从不同的位置,不同的角度,拍摄标定板的张照片(10-20张最佳),将照片放到文件夹中: 二、...
  • 并基于不同视角下的标定图像中,同一像素点对应的多个位于不同局部坐标系下的控制点应在某一公共坐标系下共线的基本假设,提出基于棋盘格平面标定板的两步标定方法。利用多幅具有足够重叠度的标定图像进行粗标定,再...
  • [OpenCV实战]38 基于OpenCV的相机标定

    千次阅读 多人点赞 2020-03-06 15:29:19
    2 图像形成几何学2.1 设定2.1.1 世界坐标系2.1.2 相机坐标系2.1.3 图像坐标系2.2 图像形成方法总结3 基于OpenCV的相机标定原理3.1 相机标定相关参数3.2 相机标定的目标3.3 不同类型的相机标定方法4 相机标定示例步骤...
  • 看到有人传的那个太大了,传一精简版,删除了calibration stereoData birdseye 三貌似都是和黑白棋盘格有关的图片。还删除了两avi的 这样压缩一下只有1M了 《Learning OpenCV》这本书的pdf文件可以从下面两...
  • 只需要将一个绘制有棋盘格图案的平面参照物在两个传感器公共的测量范围内任意摆放几个位置, 即可完成双传感器标定, 并建立两个传感器之间的相互关系, 进一步将多个局部世界坐标系下的标定特征点统一到一个全局世界...
  • 简单来说,2D相机通过张正友标定法,是提取黑白棋盘格的角点,来得到相机位姿变换的。 点云相机呢,就需要利用它的点云数据了。 原理用几句话说,就是: 在机械臂上安装好相机(需绝对固定),使用代码或者示教器...
  • 转载自:知乎用户 陈明猷 补充回答题主的问题: 1.为什么要用标定板图片做标定? 具体数学实现题主可以自行找文献,这里只说原理。单目标定说白了其实就是解一...理论上,只需2个棋盘格便足够解出全部参数了,但
  • 所给出的方法在全局优化之后,7幅图像的重投影平均像素误差为0.0043 pixel,耗时为1.7911260 s,可见速度快且精度高,可广泛应用于计算机视觉研究、工业三维测量与重构、道路测绘等多个领域的摄像机标定
  • 相机标定+鸟瞰图生成(VS2017+OpenCV)

    千次阅读 2019-10-13 22:35:27
    一、要求: 1.用自己的手机采集棋盘板定标数据; 2.实现或调用 角点检测、局部特征提取、局部特征匹配算法,标定自己手机的内参; 3.改变外参,生成不同视角的新图像。...标定板上每个棋盘格的大小:30mmx...
  • 最近在做一关于目视觉测量的项目,现在先记录一下相机标定的方法及原理 原理部分 这一部分的内容,主要是相机标定的主要原理,具体的可以参照一下两博主的博客: 张正友标定算法 设定 我使用的测试标定图片是...
  • 网上的代码基本上都是基于棋盘格的,初始不懂,当你抄了,自然就懂了。原理不做详细介绍,简单介绍一下实现过程以及遇到的问题,还有最后一部分的重建不能确保准确,因为重建出来的图像单纯的不好看!文中的代码...

空空如也

空空如也

1 2 3
收藏数 48
精华内容 19
关键字:

多个棋盘格标定