using HalconDotNet;
|
using Newtonsoft.Json;
|
using System.Drawing.Imaging;
|
|
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<BlobAfterProcess>()));
|
Params.Inputs.Add("UnionResult", false);
|
Params.Inputs.Add("MinCount", 0);
|
Params.Inputs.Add("MaxCount", 9999);
|
|
Params.Outputs.Add("CenterX", new List<double>());
|
Params.Outputs.Add("CenterY", new List<double>());
|
Params.Outputs.Add("Phi", new List<double>());
|
Params.Outputs.Add("Width", new List<double>());
|
Params.Outputs.Add("Height", new List<double>());
|
Params.Outputs.Add("Area", new List<double>());
|
Params.Outputs.Add("Count", 0);
|
}
|
|
/// <summary>
|
/// 算子逻辑
|
/// </summary>
|
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 = hImage.Clone();
|
}
|
}
|
catch (Exception ex)
|
{
|
Msg = "转图出错:" + ex.Message;
|
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<BlobAfterProcess> BlobAfterProcesses
|
= JsonConvert.DeserializeObject<List<BlobAfterProcess>>(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<double> CenterX = new List<double>();
|
List<double> CenterY = new List<double>();
|
List<double> Phi = new List<double>();
|
List<double> Width = new List<double>();
|
List<double> Height = new List<double>();
|
List<double> Area = new List<double>();
|
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;
|
}
|
}
|
}
|