using HalconDotNet; using Newtonsoft.Json; using System.Drawing; using System.Drawing.Imaging; namespace LB_VisionProcesses.Alogrithms.Halcon { 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("ModelWidth", -1.0); Params.Inputs.Add("ModelHeight", -1.0); Params.Inputs.Add("ModelArea", -1.0); Params.Inputs.Add("Tolerance", 100.0); Params.Inputs.Add("Pixel", 1.0); 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("CenterArea", 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 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; } #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); int Count = ho_SelectedRegionsCount.TupleInt(); List CenterX = new List(); List CenterY = new List(); List Phi = new List(); List Width = new List(); List Height = new List(); List CenterArea = new List(); List Area = new List(); double Pixel = Convert.ToDouble(Params.Inputs["Pixel"]); bool isUnionResult = Convert.ToBoolean(Params.Inputs["UnionResult"]); if (!isUnionResult) Pixel = 1.0; if (isUnionResult) { HOperatorSet.Union1(ho_SelectedRegions, out HObject ho_UnionRegion); // 填充为矩形 HOperatorSet.SmallestRectangle2(ho_UnionRegion, out HTuple hv_Row, out HTuple hv_Column, out HTuple hv_Phi, out HTuple hv_Length1, out HTuple hv_Length2); HOperatorSet.AreaCenter(ho_UnionRegion, 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) * Pixel); Height.Add(Math.Round((double)hv_Length2.TupleReal() * 2, 3) * Pixel); CenterArea.Add(Math.Round((double)hv_area.TupleReal(), 3)); Area.Add(Math.Round((double)hv_Length1.TupleReal() * 2 * Pixel * (double)hv_Length2.TupleReal() * 2 * Pixel)); } Record.AddRecord(ho_UnionRegion); } else { 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) * Pixel); Height.Add(Math.Round((double)hv_Length2.TupleReal() * 2, 3) * Pixel); CenterArea.Add(Math.Round((double)hv_area.TupleReal(), 3)); Area.Add(Math.Round((double)hv_Length1.TupleReal() * 2 * Pixel * (double)hv_Length2.TupleReal() * 2 * Pixel)); } } Record.AddRecord(ho_SelectedRegions); } Params.Outputs["CenterX"] = CenterX; Params.Outputs["CenterY"] = CenterY; Params.Outputs["Phi"] = Phi; Params.Outputs["Width"] = Width; Params.Outputs["Height"] = Height; Params.Outputs["CenterArea"] = CenterArea; Params.Outputs["Area"] = Area; Params.Outputs["Count"] = 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 (Count < MinCount || Count > MaxCount) { Msg = string.Format("结果个数超出范围({0},{1})", MinCount, MaxCount); Record.ChangeAll2False(); Result = false; return; } double ModelWidth = Convert.ToDouble(Params.Inputs["ModelWidth"]) , ModelHeight = Convert.ToDouble(Params.Inputs["ModelHeight"]) , ModelArea = Convert.ToDouble(Params.Inputs["ModelArea"]) , Tolerance = Math.Abs(Convert.ToDouble(Params.Inputs["Tolerance"])); if (ModelArea > 0) { if ((ModelWidth - Tolerance * 2) <= 0 || (ModelHeight - Tolerance * 2) <= 0) { Msg = $"公差{Tolerance}过大,值不能超过{Math.Min(ModelWidth / 2, ModelHeight / 2)}"; Record.ChangeAll2False(); Result = false; return; } double MinArea = (ModelWidth - Tolerance * 2) * (ModelHeight - Tolerance * 2); double MaxArea = (ModelWidth + Tolerance * 2) * (ModelHeight + Tolerance * 2); if (ModelWidth == -1 || ModelHeight == -1) { MinArea = ModelArea - Tolerance; MaxArea = ModelArea + Tolerance; } if (Area.Count == 0) { Msg = $"面积结果为空"; Record.ChangeAll2False(); Result = false; return; } if (Area[0] < MinArea || Area[0] > MaxArea) { Msg = $"面积{Area[0]}超出范围({MinArea},{MaxArea})"; Record.ChangeAll2False(); Result = false; return; } } Msg = "运行成功"; Result = true; return; } catch (Exception ex) { Msg = "运行失败,原因是:" + ex.ToString().TrimEnd(); 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("CenterArea", new List()); Params.Outputs.Add("Area", new List()); Params.Outputs.Add("Count", 0); 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; } } }