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<HFindModelTool> HFindModelTools = new List<HFindModelTool>();
|
|
public HFindMultiModelTool()
|
{
|
strProcessClass = "LB_VisionProcesses.Alogrithms.Halcon.HFindMultiModelTool";
|
strProcessName = "Halcon2D多模板匹配工具";
|
|
//ModelsFullPath:模板路径
|
Params.Inputs.Add("ModelsFullPath", new List<string>());
|
|
//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<double>());
|
Params.Outputs.Add("CenterY", new List<double>());
|
Params.Outputs.Add("Phi", new List<double>());
|
Params.Outputs.Add("Score", new List<double>());
|
Params.Outputs.Add("Count", 0);
|
}
|
|
private static readonly object lockObj = new object();
|
|
/// <summary>
|
/// 运行算子
|
/// </summary>
|
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<double> CenterX = new List<double>();
|
List<double> CenterY = new List<double>();
|
List<double> Phi = new List<double>();
|
List<double> Score = new List<double>();
|
|
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<string> ModelsName = new List<string>();
|
List<double> ModelsWidth = new List<double>();
|
List<double> ModelsHeight = new List<double>();
|
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<double> resultX = HFindModelTool.Params.Outputs["CenterX"] as List<double>;
|
List<double> resultY = HFindModelTool.Params.Outputs["CenterY"] as List<double>;
|
List<double> resultPhi = HFindModelTool.Params.Outputs["Phi"] as List<double>;
|
List<double> resultScore = HFindModelTool.Params.Outputs["Score"] as List<double>;
|
|
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
|
}
|
}
|
}
|
|
/// <summary>
|
/// 加载算法
|
/// </summary>
|
/// <param name="fullPath">完整路径带.json</param>
|
/// <returns></returns>
|
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<ProcessParams>(strJson);
|
if (Params == null)
|
return false;
|
// 反序列化后修复数据
|
Params.FixDeserializedData();
|
|
HFindModelTools.Clear(); HFindModelTools = new List<HFindModelTool>();
|
List<string> ModelsFullPath = (JArray.FromObject(Params.Inputs["ModelsFullPath"])).ToObject<List<string>>();
|
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; }
|
}
|
|
/// <summary>
|
/// 保存算法
|
/// </summary>
|
/// <param name="filePath">不带.json</param>
|
/// <returns></returns>
|
public override bool Save(string filePath)
|
{
|
try
|
{
|
// 获取文件夹名称
|
string ModelFilePath = filePath + "\\" + strProcessName;
|
|
if (Directory.Exists(ModelFilePath))
|
Directory.Delete(ModelFilePath, true);
|
|
List<string> ModelsFullPath = new List<string>();
|
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; }
|
}
|
}
|
}
|