using HalconDotNet; using Newtonsoft.Json; namespace LB_VisionProcesses.Alogrithms.Halcon { [Process("Halcon2D斑点工具", Category = "Halcon2D工具", Description = "创建斑点工具")] public class HBlobTool : TAlgorithm { public HBlobTool() { strProcessClass = "LB_VisionProcesses.Alogrithms.Halcon.HBlobTool"; strProcessName = "Halcon2D斑点工具"; Params.Inputs.Add("MinThreshold", 0); Params.Inputs.Add("MaxThreshold", 255); Params.Inputs.Add("MinArea", 0); Params.Inputs.Add("MaxArea", 0); //区域中心的行列坐标(row、col) Params.Inputs.Add("MinRow", 0.0); Params.Inputs.Add("MaxRow", 0.0); Params.Inputs.Add("MinColumn", 0.0); Params.Inputs.Add("MaxColumn", 0.0); //圆度(circularity) Params.Inputs.Add("MinCircularity", 0.0); Params.Inputs.Add("MaxCircularity", 1.0); //矩形度(rectangularity) Params.Inputs.Add("MinRectangularity", 0.0); Params.Inputs.Add("MaxRectangularity", 1.0); //周长(contlength) Params.Inputs.Add("MinContlength", 0.0); Params.Inputs.Add("MaxContlength", 0.0); //椭圆长半轴与短半轴的比值(anisometry) Params.Inputs.Add("MinAnisometry", 0.0); Params.Inputs.Add("MaxAnisometry", 0.0); //区域边界到中心的平均距离(dist_mean) Params.Inputs.Add("MinDistMean", 0.0); Params.Inputs.Add("MaxDistMean", 0.0); //区域边界到中心的平均方差(dist_deviation) Params.Inputs.Add("MinDistDeviation", 0.0); Params.Inputs.Add("MaxDistDeviation", 0.0); Params.Inputs.Add("BlobAfterProcesses", JsonConvert.SerializeObject(new List())); Params.Inputs.Add("UnionResult", false); 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("Width", new List()); Params.Outputs.Add("Height", new List()); Params.Outputs.Add("Area", new List()); Params.Outputs.Add("Count", 0); } /// /// 算子逻辑 /// public override void TAlgorithmMain() { #region 初始化变量 HObject ho_Regions, ho_ConnectedRegions, ho_SelectedRegions; HOperatorSet.GenEmptyObj(out ho_Regions); HOperatorSet.GenEmptyObj(out ho_ConnectedRegions); #endregion try { if (InputImage == null) { Msg = "输入图片为空"; Result = false; return; } if (!(InputImage is HObject)) { Msg = "输入图片格式不为HObject"; Result = false; return; } #region 裁剪区域 object DomainImage = null; if (!ReduceDomainImage(InputImage, ref DomainImage)) { Msg = "裁剪区域失败"; Result = false; return; } #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; } } catch { Msg = "输入图片不为灰度图且转换失败"; Result = false; return; } } //阈值分割 ho_Regions.Dispose(); HOperatorSet.Threshold(hoDomainImage, out ho_Regions , (HTuple)ProcessParams.ConvertToInt32(Params.Inputs["MinThreshold"]), (HTuple)ProcessParams.ConvertToInt32(Params.Inputs["MaxThreshold"])); //形态学处理 try { List BlobAfterProcesses = JsonConvert.DeserializeObject>(Params.Inputs["BlobAfterProcesses"].ToString()); for (int i = 0; i < BlobAfterProcesses.Count; i++) { if (!BlobAfterProcesses[i].Enable) continue; int width = BlobAfterProcesses[i].Width; int height = BlobAfterProcesses[i].Height; HOperatorSet.GenRectangle1(out HObject rec, 0, 0, height, width); HOperatorSet.GenCircle(out HObject cir, 0, 0, width); switch (BlobAfterProcesses[i].Type) { //闭运算(先膨胀再腐蚀) case BlobAfterProcess.ProcessType.Close: HOperatorSet.Closing(ho_Regions, rec, out ho_Regions); break; //开运算(先腐蚀再膨胀) case BlobAfterProcess.ProcessType.Open: HOperatorSet.Opening(ho_Regions, rec, out ho_Regions); break; //膨胀 case BlobAfterProcess.ProcessType.Dilation: if (BlobAfterProcesses[i].Shape == BlobAfterProcess.ShapeType.Rectangle) HOperatorSet.DilationRectangle1(ho_Regions, out ho_Regions, height, width); else HOperatorSet.DilationCircle(ho_Regions, out ho_Regions, width); break; //腐蚀 case BlobAfterProcess.ProcessType.Erosion: if (BlobAfterProcesses[i].Shape == BlobAfterProcess.ShapeType.Rectangle) HOperatorSet.ErosionRectangle1(ho_Regions, out ho_Regions, height, width); else HOperatorSet.ErosionCircle(ho_Regions, out ho_Regions, width); break; //顶帽 case BlobAfterProcess.ProcessType.TopHat: break; //底帽 case BlobAfterProcess.ProcessType.BottomHat: break; } } } catch { } //断开连通域 ho_ConnectedRegions.Dispose(); HOperatorSet.Connection(ho_Regions, out ho_ConnectedRegions); //结果筛选 ho_SelectedRegions = ho_ConnectedRegions.Clone(); if (ProcessParams.ConvertToInt32(Params.Inputs["MinArea"]) != ProcessParams.ConvertToInt32(Params.Inputs["MaxArea"])) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "area", "and" , (HTuple)ProcessParams.ConvertToInt32(Params.Inputs["MinArea"]) , (HTuple)ProcessParams.ConvertToInt32(Params.Inputs["MaxArea"])); if ((double)Params.Inputs["MinRow"] != (double)Params.Inputs["MaxRow"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "row", "and" , (HTuple)(double)Params.Inputs["MinRow"] , (HTuple)(double)Params.Inputs["MaxRow"]); if ((double)Params.Inputs["MinColumn"] != (double)Params.Inputs["MaxColumn"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "column", "and" , (HTuple)(double)Params.Inputs["MinColumn"] , (HTuple)(double)Params.Inputs["MaxColumn"]); //if ((double)Params.Inputs["MinCircularity"] != (double)Params.Inputs["MaxCircularity"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "circularity", "and" , (HTuple)(double)Params.Inputs["MinCircularity"] , (HTuple)(double)Params.Inputs["MaxCircularity"]); //if ((double)Params.Inputs["MinRectangularity"] != (double)Params.Inputs["MaxRectangularity"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "rectangularity", "and" , (HTuple)(double)Params.Inputs["MinRectangularity"] , (HTuple)(double)Params.Inputs["MaxRectangularity"]); //周长(contlength) if ((double)Params.Inputs["MinContlength"] != (double)Params.Inputs["MaxContlength"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "contlength", "and" , (HTuple)(double)Params.Inputs["MinContlength"] , (HTuple)(double)Params.Inputs["MaxContlength"]); //椭圆长半轴与短半轴的比值(anisometry) if ((double)Params.Inputs["MinAnisometry"] != (double)Params.Inputs["MaxAnisometry"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "anisometry", "and" , (HTuple)(double)Params.Inputs["MinAnisometry"] , (HTuple)(double)Params.Inputs["MaxAnisometry"]); //区域边界到中心的平均距离(dist_mean) if ((double)Params.Inputs["MinDistMean"] != (double)Params.Inputs["MaxDistMean"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "dist_mean", "and" , (HTuple)(double)Params.Inputs["MinDistMean"] , (HTuple)(double)Params.Inputs["MaxDistMean"]); //区域边界到中心的平均方差(dist_deviation) if ((double)Params.Inputs["MinDistDeviation"] != (double)Params.Inputs["MaxDistDeviation"]) HOperatorSet.SelectShape(ho_SelectedRegions, out ho_SelectedRegions, "dist_deviation", "and" , (HTuple)(double)Params.Inputs["MinDistDeviation"] , (HTuple)(double)Params.Inputs["MaxDistDeviation"]); #endregion #region 结果处理 //计数用于后续判断 HOperatorSet.CountObj(ho_SelectedRegions, out HTuple ho_SelectedRegionsCount); List CenterX = new List(); List CenterY = new List(); List Phi = new List(); List Width = new List(); List Height = new List(); List Area = new List(); for (int i = 1; i <= ho_SelectedRegionsCount.TupleInt(); i++) { HOperatorSet.SelectObj(ho_SelectedRegions, out HObject ho_SelectedRegion, (HTuple)i); // 填充为矩形 HOperatorSet.SmallestRectangle2(ho_SelectedRegion, out HTuple hv_Row, out HTuple hv_Column, out HTuple hv_Phi, out HTuple hv_Length1, out HTuple hv_Length2); HOperatorSet.AreaCenter(ho_SelectedRegion, out HTuple hv_area, out _, out _); if ((double)hv_area.TupleReal() != 0) { CenterX.Add(Math.Round((double)hv_Column.TupleReal(), 3)); CenterY.Add(Math.Round((double)hv_Row.TupleReal(), 3)); Phi.Add(Math.Round((double)hv_Phi.TupleReal(), 3)); Width.Add(Math.Round((double)hv_Length1.TupleReal() * 2, 3)); Height.Add(Math.Round((double)hv_Length2.TupleReal() * 2, 3)); Area.Add(Math.Round((double)hv_area.TupleReal(), 3)); } } Params.Outputs["CenterX"] = CenterX; Params.Outputs["CenterY"] = CenterY; Params.Outputs["Phi"] = Phi; Params.Outputs["Width"] = Width; Params.Outputs["Height"] = Height; Params.Outputs["Area"] = Area; Params.Outputs["Count"] = CenterX.Count; #endregion #region 生成OutputImage给后续处理 try { OutputImage = DomainImage; } catch (Exception ex) { Msg = "生成OutputImage失败,原因是:" + ex.ToString(); Result = false; return; } #endregion if (Msg == "运行超时") { Result = false; 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); ((ObjectRecord)Record).AddRecord(ho_SelectedRegions, false); Result = false; return; } Msg = "运行成功"; ((ObjectRecord)Record).AddRecord(ho_SelectedRegions); Result = true; return; } catch (Exception ex) { Msg = "运行失败,原因是:" + ex.ToString().TrimEnd(); OutputImage = null; Result = false; return; } finally { bCompleted = true; #region 内存释放 ho_Regions.Dispose(); ho_ConnectedRegions.Dispose(); #endregion } } } [Serializable] public class BlobAfterProcess { public enum ProcessType { Close, Open, Dilation, Erosion, TopHat, BottomHat } public enum ShapeType { Rectangle, Circle } [JsonProperty] public int Width { get; set; } = 3; [JsonProperty] public int Height { get; set; } = 3; [JsonProperty] public bool Enable { get; set; } = true; [JsonProperty] public ProcessType Type { get; set; } = ProcessType.Close; [JsonProperty] public ShapeType Shape { get; set; } = ShapeType.Rectangle; [JsonConstructor] public BlobAfterProcess() { } public BlobAfterProcess(ProcessType Type, ShapeType Shape = ShapeType.Rectangle, int Width = 3, int Height = 3, bool Enable = true) { this.Type = Type; this.Shape = Shape; this.Width = Width; this.Height = Height; this.Enable = Enable; } } }