using HalconDotNet;
using MvCameraControl;
using LB_VisionControls;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using OpenCvSharp;
using SharpCompress.Common;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace LB_VisionProcesses.Alogrithms.Halcon
{
public class HCaltabTool : TAlgorithm
{
public HCaltabTool()
{
strProcessClass = "LB_VisionProcesses.Alogrithms.Halcon.HCaltabTool";
strProcessName = "Halcon2D_畸变矫正工具";
Params.Inputs.Add("XNum", 7);
Params.Inputs.Add("YNum", 7);
Params.Inputs.Add("MarkDist", 12.5);
Params.Inputs.Add("DiameterRatio", 0.5);
Params.Inputs.Add("Focus", 12);
Params.Inputs.Add("Kappa", 0);
Params.Inputs.Add("Sx", 2.4);
Params.Inputs.Add("Sy", 2.4);
Params.Inputs.Add("Width", 5472);
Params.Inputs.Add("Height", 3648);
}
public HCaltabTool(HCaltabTool HCaltab)
{
Record.AddXld(HCaltab.Record.RecordObject_OK.Clone());
Record.AddXld(HCaltab.Record.RecordObject_NG.Clone());
strProcessClass = "LB_VisionProcesses.Alogrithms.Halcon.HCaltabTool";
strProcessName = HCaltab.strProcessName + "(Copy)";
Params.Inputs.Add("XNum", HCaltab.HCaltabParam.XNum);
Params.Inputs.Add("YNum", HCaltab.HCaltabParam.YNum);
Params.Inputs.Add("MarkDist", HCaltab.HCaltabParam.MarkDist);
Params.Inputs.Add("DiameterRatio", HCaltab.HCaltabParam.DiameterRatio);
Params.Inputs.Add("Focus", HCaltab.HCaltabParam.Focus);
Params.Inputs.Add("Kappa", HCaltab.HCaltabParam.Kappa);
Params.Inputs.Add("Sx", HCaltab.HCaltabParam.Sx);
Params.Inputs.Add("Sy", HCaltab.HCaltabParam.Sy);
Params.Inputs.Add("Width", HCaltab.HCaltabParam.Width);
Params.Inputs.Add("Height", HCaltab.HCaltabParam.Height);
}
public override void Dispose()
{
try
{
ClearCaltbImages();
if (HCaltabMap != null)
HCaltabMap.Dispose();
if (Record != null)
Record.Dispose();
}
catch { }
}
///
/// 结果
///
public bool IsCaltab
{
get
{
return HCaltabMap.IsCaltab;
}
}
string _filePath = $"C:\\Name\\";
///
/// 文件路径
///
public string FilePath
{
get { return _filePath; }
set
{
_filePath = value;
HCaltabParam.FilePath = value;
}
}
///
/// 标定参数文件
///
public HCaltabParam HCaltabParam = new HCaltabParam();
HCaltabMap HCaltabMap = new HCaltabMap();
public HObject Image
{
get
{
if (HCaltabMap.GetCaltbImages().Count > 0)
return HCaltabMap.GetCaltbImages()[0];
else
return null;
}
}
#region 矫正结果
public bool GetCaltbImage(HObject OriImage, out HObject CaltbImage)
{
CaltbImage = OriImage;
try
{
return HCaltabMap.GetCaltbImage(OriImage, out CaltbImage);
}
catch { return false; }
}
public bool GetCaltbPoint(HPoint OriPoint, out HPoint CaltbPoint)
{
CaltbPoint = new HPoint(OriPoint);
try
{
return HCaltabMap.GetCaltbPoint(OriPoint, out CaltbPoint);
}
catch { return false; }
}
#endregion
public override bool Run()
{
#region 初始化变量
DateTime StartTime = DateTime.Now;
InitRunParams();
HOperatorSet.GenEmptyObj(out HObject EmptyObj);
OutputImage = EmptyObj;
#endregion
try
{
if (InputImage == null)
{
Msg = "输入图片为空";
Result = false;
return Result;
}
//if (InputImage is Bitmap)
//{
// try
// {
// using (HImage hImage = new HImage())
// {
// Rectangle rect = new Rectangle(0, 0, ((Bitmap)InputImage).Width, ((Bitmap)InputImage).Height);
// BitmapData srcBmpData = ((Bitmap)InputImage).LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
// hImage.GenImageInterleaved(srcBmpData.Scan0, "rgbx", ((Bitmap)InputImage).Width, ((Bitmap)InputImage).Height, 0, "byte", ((Bitmap)InputImage).Width, ((Bitmap)InputImage).Height, 0, 0, -1, 0);
// ((Bitmap)InputImage).UnlockBits(srcBmpData);
// //((Bitmap)InputImage).Dispose();
// InputImage = null;
// InputImage = hImage.Clone();
// }
// }
// catch (Exception ex)
// {
// }
//}
if (!(InputImage is HObject))
{
Msg = "输入图片格式不为HObject";
Result = false;
return Result;
}
#region 裁剪区域
object DomainImage = null;
if (!ReduceDomainImage(InputImage, ref DomainImage))
{
Msg = "裁剪区域失败";
Result = false;
return Result;
}
#endregion
#region 算子逻辑
Record = new ObjectRecord();
HObject hoDomainImage = DomainImage as HObject;
HTuple hv_Channels = new HTuple();
//判断是否为灰度图
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
try
{
HOperatorSet.CountChannels(hoDomainImage, out hv_Channels);
if (hv_Channels.TupleInt() != 1)
HOperatorSet.Rgb1ToGray(hoDomainImage, out hoDomainImage);
//转换后再次检查是否为灰度图
HOperatorSet.CountChannels(hoDomainImage, out hv_Channels);
if (hv_Channels.TupleInt() != 1)
{
HOperatorSet.Rgb1ToGray(hoDomainImage, out hoDomainImage);
Msg = "输入图片不为灰度图";
Result = false;
return Result;
}
}
catch
{
Msg = "输入图片不为灰度图且转换失败";
Result = false;
return Result;
}
}
if (HCaltabMap == null || !HCaltabMap.IsCaltab)
{
Msg = "未进行标定,无法矫正畸变";
Result = false;
return Result;
}
if (!GetCaltbImage(hoDomainImage, out hoDomainImage))
{
Msg = "矫正畸变失败";
Result = false;
return Result;
}
#endregion
#region 生成OutputImage给后续处理
try
{
OutputImage = hoDomainImage;
}
catch (Exception ex)
{
Msg = "生成OutputImage失败,原因是:" + ex.ToString();
Result = false;
return Result;
}
#endregion
Msg = "运行成功";
Result = true;
return Result;
}
catch (Exception ex)
{
Msg = "运行失败,原因是:" + ex.ToString().TrimEnd();
OutputImage = null;
Result = false;
return Result;
}
finally
{
RunTime = (DateTime.Now - StartTime).TotalMilliseconds;
}
}
public bool AddCaltbImage(HObject image)
{
try
{
if (image == null || !image.IsInitialized())
return false;
HCaltabMap.AddCaltbImage(image.CopyObj(1, -1));
return true;
}
catch { return false; }
}
public bool LoadCaltbImages()
{
try
{
ClearCaltbImages();
return HCaltabMap.LoadCaltbImages(FilePath);
}
catch { return false; }
}
public bool SaveCaltbImages()
{
//判断文件夹是否存在,防呆输入为文件名称
if (!Directory.Exists(FilePath))
{
try
{
Directory.CreateDirectory(FilePath);
}
catch { }
}
return true;
}
public void ClearCaltbImages()
{
HCaltabMap.ClearCaltbImages();
}
public bool CheckCaltbImage(List lst_Images = null, int Width = 0, int Height = 0)
{
if (lst_Images == null)
lst_Images = HCaltabMap.GetCaltbImages();
if (Width == 0 || Height == 0)
{
Width = HCaltabParam.Width;
Height = HCaltabParam.Height;
}
foreach (var imge in lst_Images)
{
if (imge == null || !imge.IsInitialized())
return false;
HOperatorSet.GetImageSize(imge, out HTuple hv_Width, out HTuple hv_Height);
if (hv_Width.Length == 0 || hv_Height.Length == 0)
{
Msg = "标定图片长度为空";
return false;
}
if (hv_Width.I != Width || hv_Height.I != Height)
{
Msg = $"标定图片长度{hv_Width.I}*{hv_Height.I}与相机参数{Width}*{Height}不符";
return false;
}
}
return true;
}
public bool CreateCaltbFile(int XNum, int YNum, double MarkDist, double DiameterRatio, out string FilePath)
{
FilePath = this.FilePath;
try
{
BaseCalib.CreateCaltab(out _, FilePath, XNum, YNum, MarkDist, DiameterRatio);
return true;
}
catch { MessageBox.Show("文件名称含非法字符,创建标定文件失败"); return false; }
}
public bool Caltb(int XNum, int YNum, double MarkDist, double DiameterRatio, int Focus, int Kappa
, double Sx, double Sy, int Width, int Height)
{
try
{
List CaltbImages_Copy = new List();
foreach (var image in HCaltabMap.GetCaltbImages())
{
if (image != null && image.IsInitialized())
CaltbImages_Copy.Add(image.CopyObj(1, -1));
}
return Caltb(CaltbImages_Copy, HCaltabParam.XNum, HCaltabParam.YNum, HCaltabParam.MarkDist, HCaltabParam.DiameterRatio
, HCaltabParam.Focus, HCaltabParam.Kappa, HCaltabParam.Sx, HCaltabParam.Sy, HCaltabParam.Width, HCaltabParam.Height);
}
catch { return false; }
}
public bool Caltb(List lst_Images)
{
try
{
return Caltb(lst_Images, HCaltabParam.XNum, HCaltabParam.YNum, HCaltabParam.MarkDist, HCaltabParam.DiameterRatio
, HCaltabParam.Focus, HCaltabParam.Kappa, HCaltabParam.Sx, HCaltabParam.Sy, HCaltabParam.Width, HCaltabParam.Height);
}
catch { return false; }
}
public bool Caltb(List lst_Images, int XNum, int YNum, double MarkDist, double DiameterRatio
, double Focus, double Kappa, double Sx, double Sy, int Width, int Height)
{
try
{
if (!CheckCaltbImage(lst_Images, Width, Height))
return false;
BaseCalib.CreateCaltab(out string CalPlateDescr, FilePath, XNum, YNum, MarkDist, DiameterRatio);
BaseCalib.gen_cam_par_area_scan_division(Focus / 1000.0, Kappa
, Sx / 1000000.0, Sy / 1000000.0
, Width / 2.0, Height / 2.0, Width, Height, out HTuple CameraParam);
BaseCalib.CaltabCalib(CameraParam, lst_Images
, out HTuple hv_CameraInner, out HTuple hv_CameraPose, out HTuple hv_CameraInner_Adaptive
, out HObject ho_Map, out HObject Xld, out Msg, CalPlateDescr);
Record.Dispose();
if (hv_CameraInner == null || hv_CameraPose == null || hv_CameraInner_Adaptive == null
|| ho_Map == null || !ho_Map.IsInitialized())
return false;
Record = new ObjectRecord();
Record.AddXld(Xld);
HCaltabMap.Dispose();
HCaltabMap = new HCaltabMap(hv_CameraInner, hv_CameraInner_Adaptive, hv_CameraPose);
HCaltabMap.AddCaltbImages(lst_Images);
HCaltabParam = new HCaltabParam(FilePath, XNum, YNum, MarkDist, DiameterRatio, Focus, Kappa, Sx, Sy, Width, Height);
return HCaltabMap.Save(FilePath);
}
catch { return false; }
}
public override bool Load(string fullPath)
{
try
{
// 获取不带文件名的目录路径
string directoryPath = Path.GetDirectoryName(fullPath);
strProcessName = Path.GetFileNameWithoutExtension(fullPath);
// 修正真实路径,工具其路径为一个文件夹,而不是一个文件
FilePath = directoryPath + "\\" + strProcessName;
fullPath = FilePath + "\\" + strProcessName + ".json";
if (!File.Exists(fullPath))
{
Debug.WriteLine("文件不存在创建空文件");
Save(directoryPath);
return true;
}
string strJson = string.Empty;
using (StreamReader streamReader = new StreamReader(fullPath, Encoding.UTF8))
{
strJson = streamReader.ReadToEnd();
streamReader.Close();
}
Params = JsonConvert.DeserializeObject(strJson);
if (Params == null)
return false;
Params.FixDeserializedData();
int XNum = ProcessParams.ConvertToInt32(Params.Inputs["XNum"]);
int YNum = ProcessParams.ConvertToInt32(Params.Inputs["YNum"]);
double MarkDist = ProcessParams.ConvertToDouble(Params.Inputs["MarkDist"]);
double DiameterRatio = ProcessParams.ConvertToDouble(Params.Inputs["DiameterRatio"]);
double Focus = ProcessParams.ConvertToDouble(Params.Inputs["Focus"]);
double Kappa = ProcessParams.ConvertToDouble(Params.Inputs["Kappa"]);
double Sx = ProcessParams.ConvertToDouble(Params.Inputs["Sx"]);
double Sy = ProcessParams.ConvertToDouble(Params.Inputs["Sy"]);
int Width = ProcessParams.ConvertToInt32(Params.Inputs["Width"]);
int Height = ProcessParams.ConvertToInt32(Params.Inputs["Height"]);
HCaltabParam = new HCaltabParam(FilePath, XNum, YNum, MarkDist, DiameterRatio, Focus, Kappa, Sx, Sy, Width, Height);
bool result = HCaltabMap.Load(FilePath);
if (result)
{
if (!dicHCaltabMaps.ContainsKey(strProcessName))
dicHCaltabMaps.TryAdd(strProcessName, HCaltabMap);
else if (dicHCaltabMaps.ContainsKey(strProcessName))
dicHCaltabMaps[strProcessName] = HCaltabMap;
}
return result;
}
catch { return false; }
}
public override bool Save(string filePath)
{
try
{
if (string.IsNullOrEmpty(filePath) || filePath.Trim() == "")
{
Debug.WriteLine("文件路径不完整");
return false;
}
// 修正真实路径,工具其路径为一个文件夹,而不是一个文件
filePath += ("\\" + strProcessName);
string strJson = string.Empty;
var settings = new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
// 自定义缩进(4空格)
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
strJson = JsonConvert.SerializeObject(Params, settings);
//判断文件夹是否存在,防呆输入为文件名称
if (!Directory.Exists(filePath))
{
try
{
Directory.CreateDirectory(filePath);
}
catch (Exception)
{ }
}
// filePath 已经含了Name
File.WriteAllText(filePath + $"\\{strProcessName}.json", strJson, Encoding.UTF8);
// filePath 已经含了Name
FilePath = filePath;
bool result = HCaltabMap.Save(FilePath);
if (result)
{
if (!dicHCaltabMaps.ContainsKey(strProcessName))
dicHCaltabMaps.TryAdd(strProcessName, HCaltabMap);
else if (dicHCaltabMaps.ContainsKey(strProcessName))
dicHCaltabMaps[strProcessName] = HCaltabMap;
}
return result;
}
catch { return false; }
}
}
public class HCaltabMap : IDisposable
{
#region 标定图片
List CaltbImages = new List();
public List GetCaltbImages()
{
List CaltbImages_Copy = new List();
foreach (var image in CaltbImages)
{
if (image != null && image.IsInitialized())
CaltbImages_Copy.Add(image.CopyObj(1, -1));
}
return CaltbImages_Copy;
}
public bool AddCaltbImages(List images)
{
try
{
foreach (var image in images)
{
if (image != null && image.IsInitialized())
{
HTuple hv_Channels = new HTuple();
//判断是否为灰度图
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
try
{
HOperatorSet.CountChannels(image, out hv_Channels);
HObject GrayImage = image.CopyObj(1, -1);
if (hv_Channels.TupleInt() != 1)
HOperatorSet.Rgb1ToGray(GrayImage, out GrayImage);
//转换后再次检查是否为灰度图
HOperatorSet.CountChannels(GrayImage, out hv_Channels);
if (hv_Channels.TupleInt() == 1)
CaltbImages.Add(GrayImage);
}
catch { }
}
}
}
return true;
}
catch { return false; }
}
public bool AddCaltbImage(HObject image)
{
try
{
if (image != null && image.IsInitialized())
{
HTuple hv_Channels = new HTuple();
//判断是否为灰度图
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
try
{
HOperatorSet.CountChannels(image, out hv_Channels);
HObject GrayImage = image.CopyObj(1, -1);
if (hv_Channels.TupleInt() != 1)
HOperatorSet.Rgb1ToGray(GrayImage, out GrayImage);
//转换后再次检查是否为灰度图
HOperatorSet.CountChannels(GrayImage, out hv_Channels);
if (hv_Channels.TupleInt() == 1)
{
CaltbImages.Add(GrayImage);
return true;
}
}
catch { }
}
}
return false;
}
catch { return false; }
}
public bool LoadCaltbImages(string FilePath)
{
try
{
ClearCaltbImages();
for (int i = 0; i < 99; i++)
{
string ImagePath = FilePath + $"\\CaltbImages_{i}.bmp";
if (!File.Exists(ImagePath))
break;
HOperatorSet.ReadImage(out HObject ho_Image, ImagePath);
}
return true;
}
catch { return false; }
}
public bool SaveCaltbImages(string FilePath)
{
//判断文件夹是否存在,防呆输入为文件名称
if (!Directory.Exists(FilePath))
{
try
{
Directory.CreateDirectory(FilePath);
}
catch { }
}
try
{
for (int i = 0; i < CaltbImages.Count; i++)
{
string ImagePath = FilePath + $"\\CaltbImages_{i}.bmp";
var Image = CaltbImages[i];
if (Image != null && Image.IsInitialized())
{
HOperatorSet.CountChannels(Image, out HTuple hv_Channels);
if (hv_Channels.TupleInt() != 1)
HOperatorSet.Rgb1ToGray(Image, out Image);
//Halcon存图需要用斜杠
ImagePath.Replace("\\", "/");
HOperatorSet.WriteImage(Image, "bmp", 0, ImagePath);
}
}
}
catch { return false; }
return true;
}
public void ClearCaltbImages()
{
try
{
foreach (var image in CaltbImages)
{
if (image != null && image.IsInitialized())
image.Dispose();
}
CaltbImages.Clear();
CaltbImages = new List();
}
catch { Debug.WriteLine($"ClearCaltbImages失败"); }
}
#endregion
public void Dispose()
{
try
{
ClearCaltbImages();
if (Map != null && Map.IsInitialized())
Map.Dispose();
}
catch { }
}
///
/// 结果
///
public bool IsCaltab
{
get
{
if (CameraInner_Adaptive == null || CameraInner_Adaptive.Length == 0
|| CameraInner == null || CameraInner.Length == 0
|| CameraPose == null || CameraPose.Length == 0)
return false;
if (Map == null || !Map.IsInitialized())
return false;
return true;
}
}
public HCaltabMap() { }
public HCaltabMap(HTuple CameraInner, HTuple CameraInner_Adaptive, HTuple CameraPose)
{
this.CameraInner = CameraInner.Clone();
this.CameraInner_Adaptive = CameraInner_Adaptive.Clone();
this.CameraPose = CameraPose.Clone();
HOperatorSet.GenRadialDistortionMap(out _mAp, CameraInner, CameraInner_Adaptive, "bilinear");
}
///
/// 相机内参
///
HTuple CameraInner = new HTuple();
///
/// 相机外参
///
HTuple CameraPose = new HTuple();
///
/// 无畸变的相机内参
///
HTuple CameraInner_Adaptive = new HTuple();
///
/// 畸变变换图
///
HObject Map
{
get
{
try
{
if (_mAp == null || !_mAp.IsInitialized())
HOperatorSet.GenRadialDistortionMap(out _mAp, CameraInner, CameraInner_Adaptive, "bilinear");
return _mAp;
}
catch { return null; }
}
set
{
if (_mAp != null && _mAp.IsInitialized())
_mAp.Dispose();
_mAp = value;
}
}
HObject _mAp = new HObject();
public bool GetCaltbImage(HObject OriImage, out HObject CaltbImage)
{
CaltbImage = OriImage;
try
{
if (OriImage == null || !OriImage.IsInitialized()
|| Map == null || !Map.IsInitialized())
return false;
HOperatorSet.MapImage(OriImage, Map, out CaltbImage);
return true;
}
catch { return false; }
}
public bool GetCaltbPoint(HPoint OriPoint, out HPoint CaltbPoint)
{
CaltbPoint = new HPoint(OriPoint);
try
{
if (CameraInner_Adaptive == null || CameraInner_Adaptive.Length == 0
|| CameraInner == null || CameraInner.Length == 0
|| CameraPose == null || CameraPose.Length == 0)
return false;
HOperatorSet.ImagePointsToWorldPlane(CameraInner_Adaptive, CameraPose
, OriPoint.Row, OriPoint.Column, "mm", out HTuple row, out HTuple col);
if (row.Length == 0 || col.Length == 0)
return false;
CaltbPoint.Row = row.D;
CaltbPoint.Column = col.D;
return true;
}
catch { return false; }
}
public bool Save(string FilePath)
{
try
{
string CameraInnerAdaptivePath = FilePath + "\\CameraInner_Adaptive.tup";
string CameraInnerPath = FilePath + "\\CameraInner.tup";
string CameraPosePath = FilePath + "\\CameraPose.tup";
//相机内参
HOperatorSet.WriteTuple(CameraInner_Adaptive, CameraInnerAdaptivePath);
HOperatorSet.WriteTuple(CameraInner, CameraInnerPath);
//相机外参
HOperatorSet.WriteTuple(CameraPose, CameraPosePath);
return SaveCaltbImages(FilePath);
}
catch { return false; }
}
public bool Load(string FilePath)
{
try
{
Dispose();
string CameraInnerAdaptivePath = FilePath + "\\CameraInner_Adaptive.tup";
string CameraInnerPath = FilePath + "\\CameraInner.tup";
string CameraPosePath = FilePath + "\\CameraPose.tup";
//相机内参
HOperatorSet.ReadTuple(CameraInnerAdaptivePath, out CameraInner_Adaptive);
//相机内参
HOperatorSet.ReadTuple(CameraInnerPath, out CameraInner);
//相机外参
HOperatorSet.ReadTuple(CameraPosePath, out CameraPose);
HOperatorSet.GenRadialDistortionMap(out _mAp, CameraInner, CameraInner_Adaptive, "bilinear");
return true;
}
catch { return false; }
}
}
///
/// 畸变标定参数类
///
[Serializable]
public class HCaltabParam
{
public HCaltabParam() { }
public HCaltabParam(string FilePath, int XNum = 7, int YNum = 7, double MarkDist = 12.5, double DiameterRatio = 0.5
, double Focus = 8.0, double Kappa = 0, double Sx = 2.6, double Sy = 2.6, int Width = 5472, int Height = 3648)
{
this.FilePath = FilePath;
this.XNum = XNum;
this.YNum = YNum;
this.MarkDist = MarkDist;
this.DiameterRatio = DiameterRatio;
this.Focus = Focus;
this.Kappa = Kappa;
this.Sx = Sx;
this.Sy = Sy;
this.Width = Width;
this.Height = Height;
}
///
/// 文件路径
///
public string FilePath = @"C:\\";
///
/// 圆点行数
///
public int XNum { get; set; } = 7;
///
/// 圆点行数
///
public int YNum { get; set; } = 7;
///
/// 两个圆之间的距离, 单位mm
///
public double MarkDist { get; set; } = 12.5;
///
/// 比例值, Mark直径比上Mark中心距离
///
public double DiameterRatio { get; set; } = 0.5;
///
/// 用于标定的描述文件(命名规范不要修改)
///
public string CalPlateDescr { get { return FilePath + $"\\Caltab_{XNum}_{YNum}_{MarkDist}.descr"; } }
///
/// 制作标定板的PS文件(命名规范不要修改)
///
public string CalPlatePSFile { get { return FilePath + $"\\Caltab_{XNum}_{YNum}_{MarkDist}.ps"; } }
///
/// 厚度
///
public int Thickness { get; set; } = 1;
///
/// 相机类型(面扫描(除法))
///
public string CamType { get; set; } = "area_scan_division";
///
/// 镜头的焦距
///
public double Focus { get; set; } = 12;
public double Kappa { get; set; } = 0;
///
/// 单个相机像元的宽
///
public double Sx { get; set; } = 2.4;
///
/// 单个相机像元的高
///
public double Sy { get; set; } = 2.4;
///
/// 图像宽
///
public int Width { get; set; } = 5472;
///
/// 图像高
///
public int Height { get; set; } = 3648;
///
/// 标定效果, 越接近0越越好
///
public double RMS { get; set; } = 999.9;
}
}