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; } }