- 使用的是VS调用OpenCvSharp资源库进行一个Winform操作界面编写,网上找了很多开源的程序,发现根本用不了的,用的时候还需要你配置各种电脑系统变量,显得好麻烦。现在弄了个简单的标定助手,可以完美运行,带有棋盘格图像生成工具,操作简单,源码也不复杂。
这里是
using OpenCvSharp;
using OpenCvSharp.Extensions;
using Size = OpenCvSharp.Size;
public Mat ChessBoardMat;
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);
}
结果如下:
点击“导入图像”后,选择采集的棋盘格图像所在文件夹,结果如下:
可以在图像列表看到导入结果,在参数设置那里设置正确的棋盘格角点数量后,在图像列表双击图像路径,可以实现棋盘格图像角点的提取显示:
可以提取角点之后就可以标定啦!
标定完成后,可以进行畸变矫正,这个是进行畸变矫正的结果,在这里并没有写保存矫正结果的代码。
所以只是看看就好
然后所有结果都出来了,并且自动保存结果在图像文件里内!
这里是核心源码,至于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));
List<Mat> TranslationMats = new List<Mat>();
List<Mat> RotationMats = new List<Mat>();
public double[,] CameraParameterArray = new double[3, 3];
public double[] DistCoeffsArray = new double[12];
public Vec3d[] Rotation;
public Vec3d[] Translation;
public double MeanError = 0.0;
public List<double> TotalError = new List<double>();
public Size BoardImageSize = new Size();
#endregion
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, AspectRatio;
public Point2d Principal;
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;
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
{
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));
Cv2.Rodrigues(TranslationMats[i], rotation_matrix);
AllRotation_Matrix.Add(rotation_matrix);
}
catch (Exception Err)
{
ErrorMessage = "旋转矩阵计算失败!" + "\n\r" + Err.Message;
return false;
}
}
#endregion
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);
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));
}
}
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#下,有些变量类型略有不同,具体怎么样看看它的类就知道了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)