using HalconDotNet; using LB_VisionProcesses.Alogrithms.Halcon; using Newtonsoft.Json; using Newtonsoft.Json.Linq; 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; using static LB_VisionProcesses.Alogrithms.Halcon.HFindModelTool; using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip; namespace LB_VisionProcesses.Alogrithms.Halcon { [Process("Halcon2D多模板匹配工具", Category = "Halcon2D工具", Description = "创建多模板匹配工具")] public class HFindMultiModelTool : TAlgorithm { public List HFindModelTools = new List(); public HFindMultiModelTool() { strProcessClass = "LB_VisionProcesses.Alogrithms.Halcon.HFindMultiModelTool"; strProcessName = "Halcon2D多模板匹配工具"; //ModelsFullPath:模板路径 Params.Inputs.Add("ModelsFullPath", new List()); //ModelType:模板类型 Params.Inputs.Add("ModelType", "各向异形模板"); //AngleStart:搜索时的起始角度[需要转换为弧度] Params.Inputs.Add("AngleStart", -5.0); //AngleExtent:搜索时的角度范围,0表示无角度搜索[需要转换为弧度] Params.Inputs.Add("AngleExtent", 10.0); //AngleStep:角度步长--弧度[角度步长 >= 0和角度步长 <= pi / 16] Params.Inputs.Add("AngleStep", "auto"); Params.Inputs.Add("ScaleRMin", 0.9); Params.Inputs.Add("ScaleRMax", 1.1); Params.Inputs.Add("ScaleCMin", 0.9); Params.Inputs.Add("ScaleCMax", 1.1); //MinScore:被找到的模板最小分数 Params.Inputs.Add("MinScore", 0.5); //NumMatches:要找到的模板最多的实例数,0则找到所有可能的匹配 Params.Inputs.Add("NumMatches", 0); //MaxOverlap:允许找到的模型实例的最大重叠比例, 建议值:0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0 Params.Inputs.Add("MaxOverlap", 0.2); //SubPixel:计算精度的设置 //'none' 不适用亚像素,最大误差为半个像素 //'interpolation' 差值得到亚像素精度 //'least_squares', 'least_squares_high', 'least_squares_very_high' //'max_deformation 1', 'max_deformation 2', 'max_deformation 3', 'max_deformation 4' Params.Inputs.Add("SubPixel", "none"); //NumLevels:搜索时金字塔的层级,0表示不使用金字塔 Params.Inputs.Add("NumLevels", 0); //Greediness:贪婪度,搜索启发式,一般都设为0.8,越高速度越快,容易出现找不到的情况 Params.Inputs.Add("Greediness", 0.8); Params.Inputs.Add("ResultType", ""); Params.Inputs.Add("MinCount", 0); Params.Inputs.Add("MaxCount", 9999); Params.Outputs.Add("CenterX", new List()); Params.Outputs.Add("CenterY", new List()); Params.Outputs.Add("Phi", new List()); Params.Outputs.Add("Score", new List()); Params.Outputs.Add("Count", 0); } private static readonly object lockObj = new object(); /// /// 运行算子 /// public override void TAlgorithmMain() { lock (lockObj) { #region 初始化变量 #endregion try { if (InputImage == null) { Msg = "输入图片为空"; Result = false; return; } if (InputImage is Bitmap) { try { using (HImage hImage = new HImage()) { Bitmap bitmap = (Bitmap)InputImage; Rectangle rect = new Rectangle(0, 0, ((Bitmap)bitmap).Width, ((Bitmap)bitmap).Height); BitmapData srcBmpData = ((Bitmap)bitmap).LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb); hImage.GenImageInterleaved(srcBmpData.Scan0, "rgbx", ((Bitmap)bitmap).Width, ((Bitmap)bitmap).Height, 0, "byte", ((Bitmap)bitmap).Width, ((Bitmap)bitmap).Height, 0, 0, -1, 0); ((Bitmap)bitmap).UnlockBits(srcBmpData); bitmap.Dispose(); bitmap = null; InputImage = null; InputImage = hImage.Clone(); } } catch (Exception ex) { } } if (!(InputImage is HObject)) { Msg = "输入图片格式不为HObject"; Result = false; return; } object DomainImage = null; #region 裁剪区域 if (!ReduceDomainImage(InputImage, ref DomainImage)) { Msg = "裁剪区域失败"; Result = false; return; } Record = new MsgRecord(); #endregion #region 算子逻辑 //判断是否有模板 if (HFindModelTools.Count <= 0) { Msg = "未创建模板"; Result = false; return; } 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; } } catch { Msg = "输入图片不为灰度图且转换失败"; Result = false; return; } } List CenterX = new List(); List CenterY = new List(); List Phi = new List(); List Score = new List(); double AngleStart = Convert.ToDouble(Params.Inputs["AngleStart"]); double AngleExtent = Convert.ToDouble(Params.Inputs["AngleExtent"]); double MinScore = Convert.ToDouble(Params.Inputs["MinScore"]); int NumMatches = ProcessParams.ConvertToInt32(Params.Inputs["NumMatches"]); double MaxOverlap = Convert.ToDouble(Params.Inputs["MaxOverlap"]); string SubPixel = ProcessParams.ConvertToString(Params.Inputs["SubPixel"]); string NumLevels = ProcessParams.ConvertToString(Params.Inputs["NumLevels"]); double Greediness = Convert.ToDouble(Params.Inputs["Greediness"]); double ScaleRMin = Convert.ToDouble(Params.Inputs["ScaleRMin"]); double ScaleRMax = Convert.ToDouble(Params.Inputs["ScaleRMax"]); double ScaleCMin = Convert.ToDouble(Params.Inputs["ScaleCMin"]); double ScaleCMax = Convert.ToDouble(Params.Inputs["ScaleCMax"]); HTuple AnisoShapeModels = null; HTuple LocalDeformableModel = null; List ModelsName = new List(); List ModelsWidth = new List(); List ModelsHeight = new List(); foreach (var HFindModelTool in HFindModelTools) { if (HFindModelTool.ModelID.Type == ModelType.各向异形模板) { //模板为空时跳过 if (HFindModelTool.ModelID.hvModel == null) { Msg = $"{HFindModelTool.strProcessName}的模板为空"; Result = false; return; } if (AnisoShapeModels == null) AnisoShapeModels = new HTuple(); AnisoShapeModels = AnisoShapeModels.TupleConcat(HFindModelTool.ModelID.hvModel); ModelsName.Add(HFindModelTool.ModelID.ModelName); ModelsWidth.Add(HFindModelTool.ModelID.Width); ModelsHeight.Add(HFindModelTool.ModelID.Height); AngleStart = Convert.ToDouble(HFindModelTool.Params.Inputs["AngleStart"]); AngleExtent = Convert.ToDouble(HFindModelTool.Params.Inputs["AngleExtent"]); MinScore = Convert.ToDouble(HFindModelTool.Params.Inputs["MinScore"]); NumMatches = ProcessParams.ConvertToInt32(HFindModelTool.Params.Inputs["NumMatches"]); MaxOverlap = Convert.ToDouble(HFindModelTool.Params.Inputs["MaxOverlap"]); SubPixel = ProcessParams.ConvertToString(HFindModelTool.Params.Inputs["SubPixel"]); NumLevels = ProcessParams.ConvertToString(HFindModelTool.Params.Inputs["NumLevels"]); Greediness = Convert.ToDouble(HFindModelTool.Params.Inputs["Greediness"]); ScaleRMin = Convert.ToDouble(HFindModelTool.Params.Inputs["ScaleRMin"]); ScaleRMax = Convert.ToDouble(HFindModelTool.Params.Inputs["ScaleRMax"]); ScaleCMin = Convert.ToDouble(HFindModelTool.Params.Inputs["ScaleCMin"]); ScaleCMax = Convert.ToDouble(HFindModelTool.Params.Inputs["ScaleCMax"]); } else if (HFindModelTool.ModelID.Type == ModelType.局部变形模板) { // 局部变形模板没有多模板匹配 暂时顺序遍历 if (LocalDeformableModel == null) LocalDeformableModel = new HTuple(); LocalDeformableModel = LocalDeformableModel.TupleConcat(HFindModelTool.ModelID.hvModel); HFindModelTool.InputImage = hoDomainImage; if (HFindModelTool.Run()) { HOperatorSet.GetImageSize(HFindModelTool.ModelID.hoImage, out HTuple ho_ModelWidth, out HTuple ho_ModelHeight); List resultX = HFindModelTool.Params.Outputs["CenterX"] as List; List resultY = HFindModelTool.Params.Outputs["CenterY"] as List; List resultPhi = HFindModelTool.Params.Outputs["Phi"] as List; List resultScore = HFindModelTool.Params.Outputs["Score"] as List; for (int i = 0; i < resultX.Count; i++) { CenterX.Add(resultX[i]); CenterY.Add(resultY[i]); Phi.Add(resultPhi[i]); Score.Add(resultScore[i]); HOperatorSet.GenRectangle2(out HObject hRectangle, resultY[i], resultX[i], resultPhi[i], ho_ModelWidth / 2, ho_ModelHeight / 2); ((MsgRecord)Record).AddRecord($"{strProcessName}.{HFindModelTool.ModelID.ModelName}-{i}" , resultY[i] - ho_ModelHeight / 2, resultX[i] - ho_ModelWidth / 2, hRectangle); } } } } // 各向异形模板有多模板匹配直接运行 if (AnisoShapeModels != null) { //0.5为最低分数阈值,后续通过MinScore过滤 HOperatorSet.FindAnisoShapeModels(hoDomainImage, AnisoShapeModels , AngleStart / 180.0 * Math.PI, AngleExtent / 180.0 * Math.PI , ScaleRMin, ScaleRMax , ScaleCMin, ScaleCMax , 0.5, NumMatches, MaxOverlap, SubPixel , NumLevels == "auto" ? NumLevels : Convert.ToInt32(NumLevels) , Greediness , out HTuple hv_CenterRow, out HTuple hv_CenterColumn , out HTuple hv_CenterPhi , out _, out _, out HTuple hv_Score, out HTuple model); for (int i = 0; i < model.Length; i++) { if (hv_Score[i].D < MinScore) continue; int index = model[i].I; CenterX.Add(Math.Round(hv_CenterColumn[i].D, 3)); CenterY.Add(Math.Round(hv_CenterRow[i].D, 3)); if (i >= hv_CenterPhi.Length) Phi.Add(0); else Phi.Add(Math.Round(hv_CenterPhi[i].D, 3)); Score.Add(Math.Round(hv_Score[i].D, 3)); HOperatorSet.GenRectangle2(out HObject hRectangle, hv_CenterRow[i].D, hv_CenterColumn[i].D , hv_CenterPhi[i].D, ModelsWidth[index] / 2, ModelsHeight[index] / 2); ((MsgRecord)Record).AddRecord($"{strProcessName}.{ModelsName[index]}-{i}" , hv_CenterRow[i] - ModelsHeight[index] / 2 , hv_CenterColumn[i] - ModelsWidth[index] / 2, hRectangle); } } #endregion #region 结果处理 Params.Outputs["CenterX"] = CenterX; Params.Outputs["CenterY"] = CenterY; Params.Outputs["Phi"] = Phi; Params.Outputs["Score"] = Score; Params.Outputs["Count"] = Score.Count; #endregion #region 生成OutputImage给后续处理 try { OutputImage = hoDomainImage; } catch (Exception ex) { Msg = "生成OutputImage失败,原因是:" + ex.ToString(); Result = false; return; } #endregion if (Msg == "运行超时") { Result = false; Record.ChangeAll2False(); return; } int MinCount = ProcessParams.ConvertToInt32(Params.Inputs["MinCount"]); int MaxCount = ProcessParams.ConvertToInt32(Params.Inputs["MaxCount"]); if (CenterX.Count < MinCount || CenterX.Count > MaxCount) { Msg = string.Format("结果个数超出范围({0},{1})", MinCount, MaxCount); Result = false; Record.ChangeAll2False(); return; } Msg = "运行成功"; Result = true; return; } catch (Exception ex) { Msg = "运行失败,原因是:" + ex.ToString().TrimEnd(); OutputImage = null; Result = false; return; } finally { if (!Result) { Params.Outputs["CenterX"] = 0; Params.Outputs["CenterY"] = 0; Params.Outputs["Angle"] = 0; Params.Outputs["Score"] = 0; Params.Outputs["Count"] = 0; } bCompleted = true; #region 内存释放 #endregion } } } /// /// 加载算法 /// /// 完整路径带.json /// public override bool Load(string fullPath) { try { if (string.IsNullOrEmpty(fullPath)) return false; if (!fullPath.Contains(".json")) { Debug.WriteLine("文件路径不完整"); return false; } if (fullPath.StartsWith(".\\")) { // 判断原字符串长度是否大于等于2,避免越界 if (fullPath.Length >= 2) { // 替换开头两个字符 fullPath = Application.StartupPath + fullPath.Substring(2); Debug.WriteLine($"修改后的字符串: {fullPath}"); } } // 获取不带文件名的目录路径 string directoryPath = Path.GetDirectoryName(fullPath); strProcessName = Path.GetFileNameWithoutExtension(fullPath); // 修正真实路径,模板匹配工具其路径为一个文件夹,而不是一个文件 fullPath = directoryPath + "\\" + strProcessName + "\\" + 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(); HFindModelTools.Clear(); HFindModelTools = new List(); List ModelsFullPath = (JArray.FromObject(Params.Inputs["ModelsFullPath"])).ToObject>(); foreach (var ModelFullPath in ModelsFullPath) { HFindModelTool hFindModelTool = new HFindModelTool(); hFindModelTool.Load(ModelFullPath); hFindModelTool.strProcessName = hFindModelTool.ModelID.ModelName; HFindModelTools.Add(hFindModelTool); } return true; } catch { return false; } } /// /// 保存算法 /// /// 不带.json /// public override bool Save(string filePath) { try { // 获取文件夹名称 string ModelFilePath = filePath + "\\" + strProcessName; if (Directory.Exists(ModelFilePath)) Directory.Delete(ModelFilePath, true); List ModelsFullPath = new List(); foreach (var HFindModelTool in HFindModelTools) { HFindModelTool.Save(ModelFilePath); ModelsFullPath.Add(ModelFilePath + "\\" + HFindModelTool.ModelID.ModelName + ".json"); if (HFindModelTool.InputImage != null && HFindModelTool.InputImage is HObject) { ((HObject)HFindModelTool.InputImage).Dispose(); HFindModelTool.InputImage = null; } } Params.Inputs["ModelsFullPath"] = ModelsFullPath; // 修正真实路径,模板匹配工具其路径为一个文件夹,而不是一个文件 filePath += ("\\" + strProcessName); base.Save(filePath); return true; } catch { return false; } } } }