C# OpenCvSharp Demo - 棋盘格相机标定

C# OpenCvSharp Demo - 棋盘格相机标定

目录

效果

项目

代码

下载


效果

项目

代码

using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;

namespace OpenCvSharp_Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string startupPath;
        string image_path;

        Stopwatch stopwatch = new Stopwatch();

        Mat image;
        Mat result_image;

        //棋盘格的宽度和高度
        int BoardSize_Width = 9;
        int BoardSize_Height = 6;
        OpenCvSharp.Size BoardSize;

        //每个方格的宽度
        private  int SquareSize = 50;
        private  int winSize = 11;

        StringBuilder sb=new StringBuilder();

        private void Form1_Load(object sender, EventArgs e)
        {
            startupPath = System.Windows.Forms.Application.StartupPath;

            BoardSize = new OpenCvSharp.Size(BoardSize_Width, BoardSize_Height);

            image_path = "1.jpg";
            pictureBox1.Image = new Bitmap(image_path);
            image = new Mat(image_path);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;

            pictureBox1.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";

            image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(image_path);
            image = new Mat(image_path);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            stopwatch.Restart();
            sb.Clear();

            result_image = image.Clone();

            // 存储每个图像的棋盘角点
            List<Point2f[]> imagesPoints = new List<Point2f[]>();

            // 相机内参矩阵和畸变系数
            Mat cameraMatrix = new Mat(), distCoeffs = new Mat();

            // 图像的尺寸
            OpenCvSharp.Size imageSize = new OpenCvSharp.Size();
            bool found = false;

            // 读取图像
            Mat view = new Mat(image_path);
            Mat p = null;

            if (!view.Empty())
            {
                imageSize = view.Size();
                Point2f[] pointBuf;

                // 查找棋盘角点
                found = Cv2.FindChessboardCorners(view, BoardSize, out pointBuf, ChessboardFlags.AdaptiveThresh | ChessboardFlags.NormalizeImage);

                if (found)
                {
                    // 灰度化
                    Mat viewGray = new Mat();
                    Cv2.CvtColor(view, viewGray, ColorConversionCodes.BGR2GRAY);

                    // 亚像素精确化
                    Cv2.CornerSubPix(viewGray, pointBuf, new OpenCvSharp.Size(winSize, winSize), new OpenCvSharp.Size(-1, -1), new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.Count, 30, 0.0001));

                    // 存储角点坐标
                    imagesPoints.Add(pointBuf);
                    p = Mat.FromArray<Point2f>(pointBuf);

                    // 在图像上绘制角点
                    Cv2.DrawChessboardCorners(view, BoardSize, pointBuf, found);
                    Mat temp = view.Clone();
                    Cv2.ImShow("Image View", view);
                }
            }

            Mat[] rvecs = new Mat[0];
            Mat[] tvecs = new Mat[0];

            // 运行相机标定
            RunCalibration(1, imageSize, out cameraMatrix, out distCoeffs, new Mat[] { p }, out rvecs, out tvecs, out double totalAvgErr);

            // 相机矩阵、畸变系数和平均误差
            sb.AppendLine(string.Format("相机矩阵:\n{0}", Cv2.Format(cameraMatrix) + "\n"));
            sb.AppendLine(string.Format("畸变系数:\n{0}", Cv2.Format(distCoeffs) + "\n"));
            sb.AppendLine(string.Format("平均误差:\n{0}", totalAvgErr + "\n"));

            // 畸变校正
            Mat map1 = new Mat();
            Mat map2 = new Mat();
            Mat newCameraMatrix = Cv2.GetOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, out Rect roi);
            Cv2.InitUndistortRectifyMap(cameraMatrix, distCoeffs, new Mat(), newCameraMatrix, imageSize, MatType.CV_16SC2, map1, map2);

            // 显示校正后的图像
            Mat temp2 = Cv2.ImRead(image_path, ImreadModes.Color);
            Mat rview = new Mat();

            // 校正
            Cv2.Remap(temp2, rview, map1, map2, InterpolationFlags.Linear);

            double costTime = stopwatch.Elapsed.TotalMilliseconds;

            sb.AppendLine( $"\r\n耗时:{costTime:F2}ms");
            textBox1.Text = sb.ToString();
            pictureBox2.Image = new Bitmap(rview.ToMemoryStream());

        }

        // 运行相机标定
        private void RunCalibration(int imagesCount, OpenCvSharp.Size imageSize, out Mat cameraMatrix, out Mat distCoeffs, Mat[] imagePoints, out Mat[] rvecs, out Mat[] tvecs, out double totalAvgErr)
        {
            // 初始化相机矩阵和畸变系数
            cameraMatrix = Mat.Eye(new OpenCvSharp.Size(3, 3), MatType.CV_64F);
            distCoeffs = Mat.Zeros(new OpenCvSharp.Size(8, 1), MatType.CV_64F);

            // 计算棋盘角点的世界坐标
            Mat[] objectPoints = CalcBoardCornerPositions(BoardSize, SquareSize, imagesCount);

            // 进行相机标定
            double rms = Cv2.CalibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, out rvecs, out tvecs, CalibrationFlags.None);

            // 检查相机矩阵和畸变系数的范围
            bool ok = Cv2.CheckRange(InputArray.Create(cameraMatrix)) && Cv2.CheckRange(InputArray.Create(distCoeffs));

            // 计算重投影误差
            totalAvgErr = ComputeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs);
        }

        // 计算棋盘角点的世界坐标
        private Mat[] CalcBoardCornerPositions(OpenCvSharp.Size BoardSize, float SquareSize, int imagesCount)
        {
            Mat[] corners = new Mat[imagesCount];
            // 遍历每张图片
            for (int k = 0; k < imagesCount; k++)
            {
                Point3f[] p = new Point3f[BoardSize.Height * BoardSize.Width];

                for (int i = 0; i < BoardSize.Height; i++)
                {
                    for (int j = 0; j < BoardSize.Width; j++)
                    {
                        // 计算每个格子的三维坐标并储存在一维数组 p 中
                        p[i * BoardSize.Width + j] = new Point3f(j * SquareSize, i * SquareSize, 0);
                    }
                }
                // 将三维坐标转换成 Mat 类型并存储再 corners 数组中
                corners[k] = Mat.FromArray<Point3f>(p);
            }
            return corners;
        }

        // 计算重投影误差
        private double ComputeReprojectionErrors(Mat[] objectPoints, Mat[] imagePoints, Mat[] rvecs, Mat[] tvecs, Mat cameraMatrix, Mat distCoeffs)
        {
            Mat imagePoints2 = new Mat();
            int totalPoints = 0;
            double totalErr = 0, err;

            for (int i = 0; i < objectPoints.Length; ++i)
            {
                Cv2.ProjectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);

                err = Cv2.Norm(imagePoints[i], imagePoints2, NormTypes.L2);

                int n = objectPoints[i].Width * objectPoints[i].Height;
                totalErr += err * err;
                totalPoints += n;
            }

            return Math.Sqrt(totalErr / totalPoints);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            if (pictureBox2.Image == null)
            {
                return;
            }
            Bitmap output = new Bitmap(pictureBox2.Image);
            var sdf = new SaveFileDialog();
            sdf.Title = "保存";
            sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp|Images (*.emf)|*.emf|Images (*.exif)|*.exif|Images (*.gif)|*.gif|Images (*.ico)|*.ico|Images (*.tiff)|*.tiff|Images (*.wmf)|*.wmf";
            if (sdf.ShowDialog() == DialogResult.OK)
            {
                switch (sdf.FilterIndex)
                {
                    case 1:
                        {
                            output.Save(sdf.FileName, ImageFormat.Jpeg);
                            break;
                        }
                    case 2:
                        {
                            output.Save(sdf.FileName, ImageFormat.Png);
                            break;
                        }
                    case 3:
                        {
                            output.Save(sdf.FileName, ImageFormat.Bmp);
                            break;
                        }
                    case 4:
                        {
                            output.Save(sdf.FileName, ImageFormat.Emf);
                            break;
                        }
                    case 5:
                        {
                            output.Save(sdf.FileName, ImageFormat.Exif);
                            break;
                        }
                    case 6:
                        {
                            output.Save(sdf.FileName, ImageFormat.Gif);
                            break;
                        }
                    case 7:
                        {
                            output.Save(sdf.FileName, ImageFormat.Icon);
                            break;
                        }
                    case 8:
                        {
                            output.Save(sdf.FileName, ImageFormat.Tiff);
                            break;
                        }
                    case 9:
                        {
                            output.Save(sdf.FileName, ImageFormat.Wmf);
                            break;
                        }
                }
                MessageBox.Show("保存成功,位置:" + sdf.FileName);
            }
        }

    }
}

using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;namespace OpenCvSharp_Demo
{public partial class Form1 : Form{public Form1(){InitializeComponent();}string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";string startupPath;string image_path;Stopwatch stopwatch = new Stopwatch();Mat image;Mat result_image;//棋盘格的宽度和高度int BoardSize_Width = 9;int BoardSize_Height = 6;OpenCvSharp.Size BoardSize;//每个方格的宽度private  int SquareSize = 50;private  int winSize = 11;StringBuilder sb=new StringBuilder();private void Form1_Load(object sender, EventArgs e){startupPath = System.Windows.Forms.Application.StartupPath;BoardSize = new OpenCvSharp.Size(BoardSize_Width, BoardSize_Height);image_path = "1.jpg";pictureBox1.Image = new Bitmap(image_path);image = new Mat(image_path);}private void button1_Click(object sender, EventArgs e){OpenFileDialog ofd = new OpenFileDialog();ofd.Filter = fileFilter;if (ofd.ShowDialog() != DialogResult.OK) return;pictureBox1.Image = null;pictureBox2.Image = null;textBox1.Text = "";image_path = ofd.FileName;pictureBox1.Image = new Bitmap(image_path);image = new Mat(image_path);}private void button2_Click(object sender, EventArgs e){stopwatch.Restart();sb.Clear();result_image = image.Clone();// 存储每个图像的棋盘角点List<Point2f[]> imagesPoints = new List<Point2f[]>();// 相机内参矩阵和畸变系数Mat cameraMatrix = new Mat(), distCoeffs = new Mat();// 图像的尺寸OpenCvSharp.Size imageSize = new OpenCvSharp.Size();bool found = false;// 读取图像Mat view = new Mat(image_path);Mat p = null;if (!view.Empty()){imageSize = view.Size();Point2f[] pointBuf;// 查找棋盘角点found = Cv2.FindChessboardCorners(view, BoardSize, out pointBuf, ChessboardFlags.AdaptiveThresh | ChessboardFlags.NormalizeImage);if (found){// 灰度化Mat viewGray = new Mat();Cv2.CvtColor(view, viewGray, ColorConversionCodes.BGR2GRAY);// 亚像素精确化Cv2.CornerSubPix(viewGray, pointBuf, new OpenCvSharp.Size(winSize, winSize), new OpenCvSharp.Size(-1, -1), new TermCriteria(CriteriaTypes.Eps | CriteriaTypes.Count, 30, 0.0001));// 存储角点坐标imagesPoints.Add(pointBuf);p = Mat.FromArray<Point2f>(pointBuf);// 在图像上绘制角点Cv2.DrawChessboardCorners(view, BoardSize, pointBuf, found);Mat temp = view.Clone();Cv2.ImShow("Image View", view);}}Mat[] rvecs = new Mat[0];Mat[] tvecs = new Mat[0];// 运行相机标定RunCalibration(1, imageSize, out cameraMatrix, out distCoeffs, new Mat[] { p }, out rvecs, out tvecs, out double totalAvgErr);// 相机矩阵、畸变系数和平均误差sb.AppendLine(string.Format("相机矩阵:\n{0}", Cv2.Format(cameraMatrix) + "\n"));sb.AppendLine(string.Format("畸变系数:\n{0}", Cv2.Format(distCoeffs) + "\n"));sb.AppendLine(string.Format("平均误差:\n{0}", totalAvgErr + "\n"));// 畸变校正Mat map1 = new Mat();Mat map2 = new Mat();Mat newCameraMatrix = Cv2.GetOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, out Rect roi);Cv2.InitUndistortRectifyMap(cameraMatrix, distCoeffs, new Mat(), newCameraMatrix, imageSize, MatType.CV_16SC2, map1, map2);// 显示校正后的图像Mat temp2 = Cv2.ImRead(image_path, ImreadModes.Color);Mat rview = new Mat();// 校正Cv2.Remap(temp2, rview, map1, map2, InterpolationFlags.Linear);double costTime = stopwatch.Elapsed.TotalMilliseconds;sb.AppendLine( $"\r\n耗时:{costTime:F2}ms");textBox1.Text = sb.ToString();pictureBox2.Image = new Bitmap(rview.ToMemoryStream());}// 运行相机标定private void RunCalibration(int imagesCount, OpenCvSharp.Size imageSize, out Mat cameraMatrix, out Mat distCoeffs, Mat[] imagePoints, out Mat[] rvecs, out Mat[] tvecs, out double totalAvgErr){// 初始化相机矩阵和畸变系数cameraMatrix = Mat.Eye(new OpenCvSharp.Size(3, 3), MatType.CV_64F);distCoeffs = Mat.Zeros(new OpenCvSharp.Size(8, 1), MatType.CV_64F);// 计算棋盘角点的世界坐标Mat[] objectPoints = CalcBoardCornerPositions(BoardSize, SquareSize, imagesCount);// 进行相机标定double rms = Cv2.CalibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, out rvecs, out tvecs, CalibrationFlags.None);// 检查相机矩阵和畸变系数的范围bool ok = Cv2.CheckRange(InputArray.Create(cameraMatrix)) && Cv2.CheckRange(InputArray.Create(distCoeffs));// 计算重投影误差totalAvgErr = ComputeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, distCoeffs);}// 计算棋盘角点的世界坐标private Mat[] CalcBoardCornerPositions(OpenCvSharp.Size BoardSize, float SquareSize, int imagesCount){Mat[] corners = new Mat[imagesCount];// 遍历每张图片for (int k = 0; k < imagesCount; k++){Point3f[] p = new Point3f[BoardSize.Height * BoardSize.Width];for (int i = 0; i < BoardSize.Height; i++){for (int j = 0; j < BoardSize.Width; j++){// 计算每个格子的三维坐标并储存在一维数组 p 中p[i * BoardSize.Width + j] = new Point3f(j * SquareSize, i * SquareSize, 0);}}// 将三维坐标转换成 Mat 类型并存储再 corners 数组中corners[k] = Mat.FromArray<Point3f>(p);}return corners;}// 计算重投影误差private double ComputeReprojectionErrors(Mat[] objectPoints, Mat[] imagePoints, Mat[] rvecs, Mat[] tvecs, Mat cameraMatrix, Mat distCoeffs){Mat imagePoints2 = new Mat();int totalPoints = 0;double totalErr = 0, err;for (int i = 0; i < objectPoints.Length; ++i){Cv2.ProjectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2);err = Cv2.Norm(imagePoints[i], imagePoints2, NormTypes.L2);int n = objectPoints[i].Width * objectPoints[i].Height;totalErr += err * err;totalPoints += n;}return Math.Sqrt(totalErr / totalPoints);}private void button3_Click(object sender, EventArgs e){if (pictureBox2.Image == null){return;}Bitmap output = new Bitmap(pictureBox2.Image);var sdf = new SaveFileDialog();sdf.Title = "保存";sdf.Filter = "Images (*.jpg)|*.jpg|Images (*.png)|*.png|Images (*.bmp)|*.bmp|Images (*.emf)|*.emf|Images (*.exif)|*.exif|Images (*.gif)|*.gif|Images (*.ico)|*.ico|Images (*.tiff)|*.tiff|Images (*.wmf)|*.wmf";if (sdf.ShowDialog() == DialogResult.OK){switch (sdf.FilterIndex){case 1:{output.Save(sdf.FileName, ImageFormat.Jpeg);break;}case 2:{output.Save(sdf.FileName, ImageFormat.Png);break;}case 3:{output.Save(sdf.FileName, ImageFormat.Bmp);break;}case 4:{output.Save(sdf.FileName, ImageFormat.Emf);break;}case 5:{output.Save(sdf.FileName, ImageFormat.Exif);break;}case 6:{output.Save(sdf.FileName, ImageFormat.Gif);break;}case 7:{output.Save(sdf.FileName, ImageFormat.Icon);break;}case 8:{output.Save(sdf.FileName, ImageFormat.Tiff);break;}case 9:{output.Save(sdf.FileName, ImageFormat.Wmf);break;}}MessageBox.Show("保存成功,位置:" + sdf.FileName);}}}
}

下载

源码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/690440.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

判断点在多边形内部

0. 介绍 网上资料很多&#xff0c;只简单介绍下&#xff0c;方便自己今后的理解。 1. 射线法 从该点引一条射线出来&#xff0c;如果和多边形有偶数个交点&#xff0c;则点在多边形外部。 因为有入必有出&#xff0c;所以从外部引进来的射线一定是交多边形偶数个点。 如图…

Pyecharts的编程环境准备

一&#xff0c;准备Python编程环境&#xff1a; Python版本&#xff1a;3.10以上&#xff0c;最高版本3.12 https://www.python.org/ 进入官网&#xff0c;点击downloads—>windows进入下载页面&#xff0c;搜索”3.10.6”找到指定版本&#xff0c;下载并安装64位Installer…

完整性验证器:迈向 Starknet 超高可扩展性的一大步

原文&#xff1a;https://www.starknet.io/en/content/the-integrity-verifier-a-leap-toward-starknet-hyperscaling&#xff1b;https://www.starknet.io/en/ecosystem/grant 编译&#xff1a;TinTinLand 核心观点 由 Herodotus 开发的完整性验证器&#xff0c;使开发者能够…

一文了解Simhash原理和用法-计算文章相似度

Simhash原理 1&#xff1a;背景 SimHash算法是Google在2007年发表的论文《Detecting Near-Duplicates for Web Crawling》中提到的一种指纹生成算法&#xff0c;被应用在Google搜索引擎网页去重的工作之中。SimHash值不但提供了原始值是否相等这一信息&#xff0c;还能通过该…

转载:ubuntu18.04 安装wine以及添加mono和gecko打开简单.net应用的方法

https://www.cnblogs.com/jinanxiaolaohu/p/12191576.html 1. 今天突然想试试能不能用ubuntu跑一下公司的.net的智能客户端(SmartClient). 想到的办法就是 安装wine 但是过程略坑..这里简单说一下总结之后的过程. 2. 第一步安装wine相关内容 查了下有winehq和wine两种. …

【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 容器类控件 | Tab Widget的使用及说明 文章编号&#xf…

LeetCode---396周赛

题目列表 3136. 有效单词 3137. K 周期字符串需要的最少操作次数 3138. 同位字符串连接的最小长度 3139. 使数组中所有元素相等的最小开销 一、有效单词 按照题目要求&#xff0c;统计个数&#xff0c;看是否符合条件即可&#xff0c;代码如下 class Solution { public:b…

Android 简单的下拉选择框实现

要实现这种效果,目前知道的方法有以下两种,Spinner 和 ListPopupWindow,当然肯定还有很多别的方法,这里我们先尝试使用ListPopupWindow来实现这个效果; 以下是一个简单的demo: public class MainActivity extends AppCompatActivity {private List<String> dataList;pr…

AIGC|将GPTBots与10000+主流软件连接,实现应用场景全覆盖

一、自动化工作流的无限可能&#xff0c;由AI带来 当前市场上存在许多自动化工作流工具&#xff0c;这些工具在很大程度上提升了人们的工作效率&#xff0c;为企业节省了大量时间和人力成本。然而&#xff0c;这些工具并非万能&#xff0c;它们在实际应用中仍存在一定的局限性…

1056: 邻接表到邻接矩阵

解法&#xff1a; #include<iostream> #include<vector> #include<string> using namespace std; int arr[100][100]; int main() {int n;cin >> n;getchar();vector<string> s(n);for (int i 0; i < n; i) {getline(cin, s[i]);}for (int …

波动性悖论:为何低风险股票长期跑赢高风险对手?

从去年开始&#xff0c;“红利低波”类的产品净值稳步向上&#xff0c;不断新高&#xff0c;让很多人关注到了A股“分红高”、“波动率低”这两类股票。分红高的公司更受投资者青睐&#xff0c;这从基本面的角度很容易理解&#xff0c;那么波动率低的股票明明波动更小&#xff…

[数据集][目标检测]交通灯检测数据集VOC+YOLO格式2600张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2600 标注数量(xml文件个数)&#xff1a;2600 标注数量(txt文件个数)&#xff1a;2600 标注…