目录
- 引言
- 前期准备
- Step1 设计可序列化的输入输出集合【不支持多线程】
- Step2 设计程序框架
- 1、抽象层【IProcess】
- 2、父类【HAlgorithm】
- 3、子类【HFindModelTool】
- Step3 设计UI
- 结果展示
引言
通过仿照VisionPro软件二次开发Halcon的模板匹配工具,便于在客户端软件中调试,可以保存到本地导入复用,后续开发软件也可快速调用。
前期准备
安装Halcon【教程的版本为19.05】
Step1 设计可序列化的输入输出集合【不支持多线程】
设计可序列化的输入输出集合
1、可通过名称索引,也可以通过序号索引【示例:Inputs[“ABC”]或Inputs[0]】
2、可任意添加和移除【示例:Inputs.Add(“123”,0),Inputs.Remove(0)】
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Linq;
namespace MyProcesses.Alogrithms
{
[Serializable]
public class AlogrithmCollections<T> : ICollection<AlogrithmCollectionItem<T>>
{
private List<AlogrithmCollectionItem<T>> _items = new List<AlogrithmCollectionItem<T>>();
// 通过名称查找
public AlogrithmCollectionItem<T> Get(string name)
{
var item = _items.FirstOrDefault(item => item.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (item == null)
return default;
return item;
}
// 通过索引查找
public AlogrithmCollectionItem<T> Get(int index)
{
var item = _items.FirstOrDefault(item => item.Index == index);
if (item == null)
return default;
return item;
}
// ICollection<NamedItem<T>> 实现
public int Count => _items.Count;
public bool IsReadOnly => false;
void ICollection<AlogrithmCollectionItem<T>>.Add(AlogrithmCollectionItem<T> item)
{
var existingItem = Get(item.Name);
if (existingItem != null)
{
// 如果已存在,更新现有项的值
existingItem = item;
}
else
{
// 如果不存在,创建新项并分配索引
_items.Add(item);
}
// 更新后自动排序
SortItems();
}
// 添加项时为新项分配一个索引
public void Add(string name, T value)
{
var existingItem = Get(name);
if (existingItem != null)
{
// 如果已存在,更新现有项的值
existingItem.Value = value;
}
else
{
// 如果不存在,创建新项并分配索引
var newItem = new AlogrithmCollectionItem<T>(name, _items.Count > 0 ? _items.Max(i => i.Index) + 1 : 0, value);
_items.Add(newItem);
}
// 更新后自动排序
SortItems();
}
// 按 Index 排序集合
private void SortItems()
{
_items = _items.OrderBy(item => item.Index).ToList();
}
// 通过索引器实现基于序号的访问
public T this[int index]
{
get
{
if (index < 0 || index >= _items.Count)
{
return default;
}
return _items[index].Value;
}
set
{
if (index < 0 || index >= _items.Count)
{
return;
}
_items[index].Value = value;
}
}
public T this[string name]
{
get
{
var existingItem = Get(name);
if (existingItem != null)
{
// 如果已存在,更新现有项的值
return existingItem.Value;
}
return default;
}
set
{
var item = _items.FirstOrDefault(i => i.Name == name);
if (item != null)
{
item.Value = value;
}
}
}
public void Clear()
{
_items.Clear();
_items.TrimExcess(); // 将容量缩减到实际元素数量
}
public bool Contains(AlogrithmCollectionItem<T> item)
{
return _items.Contains(item);
}
public bool Contains(string name)
{
var item = _items.FirstOrDefault(i => i.Name == name);
if (item != null)
return true;
return false;
}
public void CopyTo(AlogrithmCollectionItem<T>[] array, int arrayIndex)
{
_items.CopyTo(array, arrayIndex);
}
public bool Remove(AlogrithmCollectionItem<T> item)
{
var removed = _items.Remove(item);
if (removed)
{
// 移除元素后,调整后续元素的索引
UpdateIndexes();
}
return removed;
}
public IEnumerator<AlogrithmCollectionItem<T>> GetEnumerator()
{
return _items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
// 通过名称删除
public bool Remove(string name)
{
var namedItem = Get(name);
if (namedItem != null)
{
return Remove(namedItem);
}
return false;
}
// 通过索引删除
public bool Remove(int index)
{
var namedItem = Get(index);
if (namedItem != null)
{
return Remove(namedItem);
}
return false;
}
// 删除项后,调整其他项的索引
private void UpdateIndexes()
{
for (int i = 0; i < _items.Count; i++)
{
_items[i].Index = i;
}
}
}
public class AlogrithmCollectionItem<T>
{
public string Name { get; set; }
public int Index { get; set; }
public T Value { get; set; } // 存储的对象
public AlogrithmCollectionItem(string name, int index, T value)
{
Name = name;
Index = index;
Value = value;
}
public override string ToString()
{
return $"{Name} (Index: {Index}, Value: {Value})";
}
}
}
Step2 设计程序框架
1、抽象层【IProcess】
dicProcesses为可用于实例化的类全称,后续可以通过反射直接实例化该类,这些类都是继承于HAlgorithm
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MyProcesses
{
public abstract class IProcess : IDisposable, ICloneable
{
/// <summary>
/// 流程集合
/// </summary>
public static Dictionary<string, string> dicProcesses = new Dictionary<string, string>
{
{ "相机取图", "MyCameras.BaseCamera" },
{ "图片转换工具", "MyProcesses.Alogrithms.Halcon.BitmapConvertToHoImageTool" },
{ "斑点工具", "MyProcesses.Alogrithms.Halcon.HBlobTool" },
{ "模板匹配工具", "MyProcesses.Alogrithms.Halcon.HFindModelTool" },
{ "延时", "MyProcesses.Processes.DelayTime" }
};
/// <summary>
/// 运行日志
/// </summary>
public string strMsg = "运行成功";
/// <summary>
/// 运行结果
/// </summary>
public bool bResult = true;
/// <summary>
/// 运行时间
/// </summary>
public double RunTime = 0;
/// <summary>
/// 允许运行时间
/// </summary>
public double MaxTimeOut = 1000;
/// <summary>
/// 运行完成标记
/// </summary>
protected bool bCompleted = false;
/// <summary>
/// 工具名称
/// </summary>
public string strProcessName = string.Empty;
/// <summary>
/// 工具名称
/// </summary>
public string strProcessClass = "MyProcesses.IProcess";
/// <summary>
/// 运行参数
/// </summary>
public ProcessCollections<object> ProcessCollections = new ProcessCollections<object>();
public void InitRunParams()
{
bResult = false;
bCompleted = false;
strMsg = "";
}
public abstract bool Run();
public abstract bool Load(string fullPath);
public abstract bool Save(string fullPath);
public static Assembly GetExecutingAssembly()
{
return Assembly.GetExecutingAssembly();
}
public abstract void Dispose();
public abstract object Clone();
}
}
2、父类【HAlgorithm】
父类重载了抽象类的成员函数,后续拓展Halcon算法类只需要继承父类和实例化父类即可
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Collections.Specialized.BitVector32;
using System.Data.Common;
using OpenCvSharp;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
using System.Drawing;
using Newtonsoft.Json;
using MyControls;
using System.Net;
namespace MyProcesses.Alogrithms.Halcon
{
public class HAlgorithm : IProcess
{
/// <summary>
/// 输入图片
/// </summary>
public object InputImage;
/// <summary>
/// 输出图片
/// </summary>
public object OutputImage;
/// <summary>
/// 运行图片
/// </summary>
public HObject hoDomainImage;
/// <summary>
/// 结果图片
/// </summary>
public HObject hoRecordImage;
/// <summary>
/// 算法参数
/// </summary>
public AlogrithmParams Params = new AlogrithmParams();
public bool ReduceDomainImage()
{
if (InputImage == null)
{
strMsg = "输入图片为空";
bResult = false;
return false;
}
if (!(InputImage is HObject))
{
strMsg = "输入图片不是HObject,未经过转换";
bResult = false;
return false;
}
lock (InputImage)
{
try
{
switch (Params.ROI?.GetType().Name)
{
case "HRectangle2":
HOperatorSet.GenRectangle2(out HObject hRectangle2, (HTuple)((HRectangle2)Params.ROI).Row, (HTuple)((HRectangle2)Params.ROI).Column
, (HTuple)((HRectangle2)Params.ROI).Phi, (HTuple)((HRectangle2)Params.ROI).SemiLength1, (HTuple)((HRectangle2)Params.ROI).SemiLength2);
HOperatorSet.ReduceDomain((HObject)InputImage, hRectangle2, out hoDomainImage);
break;
case "HCircle":
HOperatorSet.GenCircle(out HObject hCircle, (HTuple)((HCircle)Params.ROI).Row, (HTuple)((HCircle)Params.ROI).Column, (HTuple)((HCircle)Params.ROI).Radius);
HOperatorSet.ReduceDomain((HObject)InputImage, hCircle, out hoDomainImage);
break;
case "ROI":
default:
hoDomainImage = ((HObject)InputImage)?.CopyObj(1, -1);
break;
}
return true;
}
catch
{
strMsg = "裁剪区域失败";
bResult = false;
return false;
}
}
}
public override void Dispose()
{
if (InputImage != null)
{
if (InputImage is HObject)
((HObject)InputImage).Dispose();
}
if (hoDomainImage != null)
hoDomainImage.Dispose();
if (hoRecordImage != null)
hoRecordImage.Dispose();
//this.Dispose();
}
public override object Clone()
{
return MemberwiseClone();
}
public override bool Run()
{
DateTime StartTime = DateTime.Now;
InitRunParams();
// 创建并启动任务
Task.Factory.StartNew(() => { HAlgorithmMain(); });
while ((DateTime.Now - StartTime).TotalMilliseconds <= MaxTimeOut)
{
if (bCompleted)
return bResult;
}
strMsg = "运行超时";
return false;
}
/// <summary>
/// 算子逻辑
/// </summary>
public virtual void HAlgorithmMain()
{
bCompleted = true;
Console.WriteLine("任务完成");
}
/// <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"))
{
Console.WriteLine("文件路径不完整");
return false;
}
if (!File.Exists(fullPath))
{
Console.WriteLine("文件不存在创建空文件");
// 获取不带文件名的目录路径
string directoryPath = Path.GetDirectoryName(fullPath);
strProcessName = Path.GetFileNameWithoutExtension(fullPath);
Save(directoryPath);
return true;
}
string strJson = string.Empty;
using (StreamReader streamReader = new StreamReader(fullPath, Encoding.UTF8))
{
strJson = streamReader.ReadToEnd();
streamReader.Close();
}
Params = JsonConvert.DeserializeObject<AlogrithmParams>(strJson);
if (Params == null)
return false;
return true;
}
catch { return false; }
}
/// <summary>
/// 保存算法
/// </summary>
/// <param name="filePath">不带.json</param>
/// <returns></returns>
public override bool Save(string filePath = "")
{
try
{
if (string.IsNullOrEmpty(filePath))
return false;
string strJson = string.Empty;
strJson = JsonConvert.SerializeObject(Params);
JsonFormatting(ref strJson);
Params = JsonConvert.DeserializeObject<AlogrithmParams>(strJson);
//判断文件夹是否存在,防呆输入为文件名称
if (!Directory.Exists(filePath))
{
try
{
Directory.CreateDirectory(filePath);
}
catch (Exception)
{ }
}
File.WriteAllText(filePath + "//" + strProcessName + ".json", strJson, Encoding.UTF8);
return true;
}
catch { return false; }
}
#region 自定义的算法
public static int JsonFormatting(ref string strJson)
{
try
{
JsonSerializer jsonSerializer = new JsonSerializer();
JsonReader reader = new JsonTextReader(new StringReader(strJson));
object obj = jsonSerializer.Deserialize(reader);
if (obj == null)
return 0;
StringWriter stringWriter = new StringWriter();
JsonTextWriter jsonTextWriter1 = new JsonTextWriter(stringWriter);
jsonTextWriter1.Formatting = Formatting.Indented;
jsonTextWriter1.Indentation = 4;
jsonTextWriter1.IndentChar = ' ';
JsonTextWriter jsonTextWriter2 = jsonTextWriter1;
jsonSerializer.Serialize(jsonTextWriter2, obj);
strJson = stringWriter.ToString();
return 1;
}
catch (Exception ex)
{
return -1;
}
}
public static void Bitmap2HObject(Bitmap bmp, out HObject image)
{
try
{
if (bmp == null)
{
image = null;
return;
}
BitmapData srcBmpData;
switch (bmp.PixelFormat)
{
case PixelFormat.Format24bppRgb:
srcBmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
HOperatorSet.GenImageInterleaved(out image, srcBmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
bmp.UnlockBits(srcBmpData);
break;
default:
srcBmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
HOperatorSet.GenImage1(out image, "byte", bmp.Width, bmp.Height, srcBmpData.Scan0);
bmp.UnlockBits(srcBmpData);
break;
}
}
catch (Exception ex)
{
image = null;
}
}
/// <summary>
/// 显示符号
/// </summary>
/// <param name="hv_WindowHandle"></param>
/// <param name="hv_String"></param>
/// <param name="hv_CoordSystem"></param>
/// <param name="hv_Row"></param>
/// <param name="hv_Column"></param>
/// <param name="hv_Color"></param>
/// <param name="hv_Box"></param>
public static void disp_message(HTuple hv_WindowHandle, HTuple hv_String, HTuple hv_CoordSystem, HTuple hv_Row, HTuple hv_Column, HTuple hv_Color, HTuple hv_Box)
{
// Local iconic variables
// Local control variables
HTuple hv_GenParamName = new HTuple(), hv_GenParamValue = new HTuple();
HTuple hv_Color_COPY_INP_TMP = new HTuple(hv_Color);
HTuple hv_Column_COPY_INP_TMP = new HTuple(hv_Column);
HTuple hv_CoordSystem_COPY_INP_TMP = new HTuple(hv_CoordSystem);
HTuple hv_Row_COPY_INP_TMP = new HTuple(hv_Row);
try
{
if ((int)new HTuple(hv_Row_COPY_INP_TMP.TupleEqual(new HTuple())).TupleOr(
new HTuple(hv_Column_COPY_INP_TMP.TupleEqual(new HTuple()))) != 0)
{
hv_Color_COPY_INP_TMP.Dispose();
hv_Column_COPY_INP_TMP.Dispose();
hv_CoordSystem_COPY_INP_TMP.Dispose();
hv_Row_COPY_INP_TMP.Dispose();
hv_GenParamName.Dispose();
hv_GenParamValue.Dispose();
return;
}
if ((int)new HTuple(hv_Row_COPY_INP_TMP.TupleEqual(-1)) != 0)
{
hv_Row_COPY_INP_TMP.Dispose();
hv_Row_COPY_INP_TMP = 12;
}
if ((int)new HTuple(hv_Column_COPY_INP_TMP.TupleEqual(-1)) != 0)
{
hv_Column_COPY_INP_TMP.Dispose();
hv_Column_COPY_INP_TMP = 12;
}
//
//Convert the parameter Box to generic parameters.
hv_GenParamName.Dispose();
hv_GenParamName = new HTuple();
hv_GenParamValue.Dispose();
hv_GenParamValue = new HTuple();
if ((int)new HTuple(new HTuple(hv_Box.TupleLength()).TupleGreater(0)) != 0)
{
if ((int)new HTuple(hv_Box.TupleSelect(0).TupleEqual("false")) != 0)
{
//Display no box
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat(
"box");
hv_GenParamName.Dispose();
hv_GenParamName = ExpTmpLocalVar_GenParamName;
}
}
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamValue = hv_GenParamValue.TupleConcat(
"false");
hv_GenParamValue.Dispose();
hv_GenParamValue = ExpTmpLocalVar_GenParamValue;
}
}
}
else if ((int)new HTuple(hv_Box.TupleSelect(0).TupleNotEqual(
"true")) != 0)
{
//Set a color other than the default.
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat(
"box_color");
hv_GenParamName.Dispose();
hv_GenParamName = ExpTmpLocalVar_GenParamName;
}
}
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamValue = hv_GenParamValue.TupleConcat(
hv_Box.TupleSelect(0));
hv_GenParamValue.Dispose();
hv_GenParamValue = ExpTmpLocalVar_GenParamValue;
}
}
}
}
if ((int)new HTuple(new HTuple(hv_Box.TupleLength()).TupleGreater(1)) != 0)
{
if ((int)new HTuple(hv_Box.TupleSelect(1).TupleEqual("false")) != 0)
{
//Display no shadow.
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat(
"shadow");
hv_GenParamName.Dispose();
hv_GenParamName = ExpTmpLocalVar_GenParamName;
}
}
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamValue = hv_GenParamValue.TupleConcat(
"false");
hv_GenParamValue.Dispose();
hv_GenParamValue = ExpTmpLocalVar_GenParamValue;
}
}
}
else if ((int)new HTuple(hv_Box.TupleSelect(1).TupleNotEqual(
"true")) != 0)
{
//Set a shadow color other than the default.
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat(
"shadow_color");
hv_GenParamName.Dispose();
hv_GenParamName = ExpTmpLocalVar_GenParamName;
}
}
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
{
HTuple
ExpTmpLocalVar_GenParamValue = hv_GenParamValue.TupleConcat(
hv_Box.TupleSelect(1));
hv_GenParamValue.Dispose();
hv_GenParamValue = ExpTmpLocalVar_GenParamValue;
}
}
}
}
//Restore default CoordSystem behavior.
if ((int)new HTuple(hv_CoordSystem_COPY_INP_TMP.TupleNotEqual("window")) != 0)
{
hv_CoordSystem_COPY_INP_TMP.Dispose();
hv_CoordSystem_COPY_INP_TMP = "image";
}
//
if ((int)new HTuple(hv_Color_COPY_INP_TMP.TupleEqual("")) != 0)
{
//disp_text does not accept an empty string for Color.
hv_Color_COPY_INP_TMP.Dispose();
hv_Color_COPY_INP_TMP = new HTuple();
}
//
//HOperatorSet.DispText(hv_WindowHandle, hv_String, hv_CoordSystem_COPY_INP_TMP,
// hv_Row_COPY_INP_TMP, hv_Column_COPY_INP_TMP, hv_Color_COPY_INP_TMP, hv_GenParamName,hv_GenParamValue);
HOperatorSet.SetTposition(hv_WindowHandle, hv_Row_COPY_INP_TMP, hv_Column_COPY_INP_TMP);
HOperatorSet.WriteString(hv_WindowHandle, hv_String);
hv_Color_COPY_INP_TMP.Dispose();
hv_Column_COPY_INP_TMP.Dispose();
hv_CoordSystem_COPY_INP_TMP.Dispose();
hv_Row_COPY_INP_TMP.Dispose();
hv_GenParamName.Dispose();
hv_GenParamValue.Dispose();
return;
}
catch (HalconException HDevExpDefaultException)
{
hv_Color_COPY_INP_TMP.Dispose();
hv_Column_COPY_INP_TMP.Dispose();
hv_CoordSystem_COPY_INP_TMP.Dispose();
hv_Row_COPY_INP_TMP.Dispose();
hv_GenParamName.Dispose();
hv_GenParamValue.Dispose();
throw HDevExpDefaultException;
}
}
public static void SetColor(HTuple hv_WindowHandle)
{
HOperatorSet.SetColor(hv_WindowHandle, GetRandomColor());
}
/// <summary>
/// 设置颜色("dark olive green")
/// </summary>
/// <param name="hv_WindowHandle"></param>
/// <param name="color"></param>
public static void SetColor(HTuple hv_WindowHandle, string color)
{
HOperatorSet.SetColor(hv_WindowHandle, color);
}
/// <summary>
/// 生成Halcon随机颜色
/// </summary>
/// <returns></returns>
public static string GetRandomColor()
{
// 获取当前时间的毫秒数作为种子
int seed = DateTime.Now.Millisecond;
// 使用种子创建 Random 实例
Random random = new Random(seed);
// 生成随机数
int randomNumber = random.Next(0, 18);
// 延时随机时间变更随机种子
Thread.Sleep(randomNumber);
string[] strsColors = new string[]
{
"red", "green","blue", "cyan", "magenta",
"yellow", "dim gray", "gray","light gray", "medium slate blue", "coral", "slate blue",
"spring green", "orange red", "orange", "dark olive green","pink", "forest green", "cadet blue"
};
if (randomNumber <= strsColors.Length)
return strsColors[randomNumber];
else
return strsColors[0];
}
/// <summary>
/// 计算两点的距离
/// </summary>
/// <param name="startPoint"></param>
/// <param name="endPoint"></param>
/// <returns></returns>
public static double GetDistanceP2P(HPoint startPoint, HPoint endPoint)
{
try
{
return Math.Sqrt(Math.Pow(startPoint.X - endPoint.X, 2) + Math.Pow(startPoint.Y - endPoint.Y, 2));
}
catch { return 9994; }
}
public static double GetDistanceP2P(System.Drawing.Point startPoint, System.Drawing.Point endPoint)
{
return GetDistanceP2P(new HPoint(startPoint), new HPoint(endPoint));
}
public static double DistanceP2P(double startX, double startY, double endX, double endY)
{
return GetDistanceP2P(new HPoint(startX, startY), new HPoint(endX, endY));
}
/// <summary>
/// 获取两点的中点
/// </summary>
/// <param name="startPoint"></param>
/// <param name="endPoint"></param>
/// <returns></returns>
public static HPoint GetMidPoint(HPoint startPoint, HPoint endPoint)
{
return new HPoint((startPoint.X + endPoint.X) / 2, (startPoint.Y + endPoint.Y) / 2);
}
public static OpenCvSharp.Point GetMidPoint(OpenCvSharp.Point startPoint, OpenCvSharp.Point endPoint)
{
return new OpenCvSharp.Point((startPoint.X + endPoint.X) / 2, (startPoint.Y + endPoint.Y) / 2);
}
/// <summary>
/// 判断点是否在线段上
/// </summary>
/// <param name="point"></param>
/// <param name="segment"></param>
/// <returns></returns>
public static bool IsPointOnSegment(HPoint pt, HSegment segment, double tolerance = 1e-3)
{
// 计算直线方程的系数
double A = segment.EndY - segment.StartX;
double B = segment.StartX - segment.EndX;
double C = segment.EndX * segment.StartY - segment.StartX * segment.EndY;
// 计算点到直线的距离
double distance = Math.Abs(A * pt.X + B * pt.Y + C) / Math.Sqrt(A * A + B * B);
// 允许一个很小的误差
if (distance < tolerance)
{
// 判断点是否在直线段范围内
if (Math.Min(segment.StartX, segment.EndX) <= pt.X && pt.X <= Math.Max(segment.StartX, segment.EndX)
&& Math.Min(segment.StartY, segment.EndY) <= pt.Y && pt.Y <= Math.Max(segment.StartY, segment.EndY))
{
return true;
}
}
return false;
}
public static bool IsPointOnSegment(double px, double py, double x1, double y1, double x2, double y2)
{
return IsPointOnSegment(new HPoint(px, py), new HSegment(x1, y1, x2, y2));
}
/// <summary>
/// 判断点是否在矩形边附近
/// </summary>
/// <param name="pt"></param>
/// <param name="rect"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
static bool IsPointNearRectangleSilde(OpenCvSharp.Point pt, Rectangle rect, double tolerance = 100)
{
try
{
// 如果点的 X 坐标等于矩形的左边 (rect.Left) 或右边 (rect.Right),并且 Y 坐标在矩形的上下边界之间,那么点在矩形的垂直边界上。
// 如果点的 Y 坐标等于矩形的上边(rect.Top) 或下边(rect.Bottom),并且 X 坐标在矩形的左右边界之间,那么点在矩形的水平边界上。
return (Math.Abs(pt.X - rect.Left) <= tolerance || Math.Abs(pt.X - rect.Right) <= tolerance) && pt.Y >= rect.Top + tolerance && pt.Y <= rect.Bottom - tolerance
|| (Math.Abs(pt.Y - rect.Top) <= tolerance || Math.Abs(pt.Y - rect.Bottom) <= tolerance) && pt.X >= rect.Left - tolerance && pt.X <= rect.Right + tolerance;
}
catch { return false; }
}
public static bool IsPointNearRectangleSilde(HPoint pt, HRectangle2 rect, double tolerance = 100)
{
return IsPointNearRectangleSilde(new OpenCvSharp.Point((int)pt.X, (int)pt.Y), new Rectangle((int)rect.CenterX, (int)rect.CenterY, (int)rect.Width, (int)rect.Height), tolerance);
}
/// <summary>
/// 判断点是否在点附近
/// </summary>
/// <param name="pt1"></param>
/// <param name="pt2"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
public static bool IsPointNearPoint(HPoint pt1, HPoint pt2, double tolerance = 100)
{
if (GetDistanceP2P(pt1, pt2) <= tolerance)
return true;
return false;
}
public static bool IsPointNearPoint(System.Drawing.Point pt1, System.Drawing.Point pt2, double tolerance = 100)
{
if (GetDistanceP2P(pt1, pt2) <= tolerance)
return true;
return false;
}
public static bool IsPointNearPoint(double x1, double y1, double x2, double y2, int tolerance = 100)
{
return IsPointNearPoint(new HPoint(x1, y1), new HPoint(x2, y2), tolerance);
}
/// <summary>
/// 判断点是否在矩形角上
/// </summary>
/// <param name="pt"></param>
/// <param name="rect"></param>
/// <param name="corner"></param>
/// <param name="tolerance"></param>
/// <returns></returns>
public static bool IsPointNearRectangleCorner(System.Drawing.Point pt, Rectangle rect, out string corner, double tolerance = 10)
{
try
{
//按顺时针去匹配角位
System.Drawing.Point LeftTopPoint = new System.Drawing.Point(rect.Left, rect.Top);
if (IsPointNearPoint(LeftTopPoint, pt, tolerance))
{
corner = "LeftTop";
return true;
}
System.Drawing.Point RightTopPoint = new System.Drawing.Point(rect.Right, rect.Top);
if (IsPointNearPoint(RightTopPoint, pt, tolerance))
{
corner = "RightTop";
return true;
}
System.Drawing.Point RightBtmPoint = new System.Drawing.Point(rect.Right, rect.Bottom);
if (IsPointNearPoint(RightBtmPoint, pt, tolerance))
{
corner = "RightBtm";
return true;
}
System.Drawing.Point LeftBtmPoint = new System.Drawing.Point(rect.Left, rect.Bottom);
if (IsPointNearPoint(LeftBtmPoint, pt, tolerance))
{
corner = "LeftBtm";
return true;
}
corner = "";
return false;
}
catch { corner = ""; return false; }
}
public static bool IsPointNearRectangleCorner(HPoint pt, HRectangle2 rect, out string corner, double tolerance = 10)
{
try
{
//按顺时针去匹配角位
var rectCorners = rect.Corners;
HPoint LeftTopPoint = rectCorners[0];
if (IsPointNearPoint(LeftTopPoint, pt, tolerance))
{
corner = "LeftTop";
return true;
}
HPoint RightTopPoint = rectCorners[1];
if (IsPointNearPoint(RightTopPoint, pt, tolerance))
{
corner = "RightTop";
return true;
}
HPoint RightBtmPoint = rectCorners[2];
if (IsPointNearPoint(RightBtmPoint, pt, tolerance))
{
corner = "RightBtm";
return true;
}
HPoint LeftBtmPoint = rectCorners[3];
if (IsPointNearPoint(LeftBtmPoint, pt, tolerance))
{
corner = "LeftBtm";
return true;
}
corner = "";
return false;
}
catch { corner = ""; return false; }
}
#endregion
}
}
3、子类【HFindModelTool】
后续只需要通过CreateModel,Save,Load,Run等封装好的函数既可以运行模板匹配工具
using HalconDotNet;
using MyProcesses.Alogrithms.Halcon;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip;
namespace MyProcesses.Alogrithms.Halcon
{
public class HFindModelTool : HAlgorithm
{
public HModel ModelID = new HModel();
public HFindModelTool()
{
strProcessClass = "MyProcesses.Alogrithms.Halcon.HFindModelTool";
strProcessName = "模板匹配工具";
//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", 1);
//Params.Inputs.Add("GenParamName", new HTuple());
//Params.Inputs.Add("GenParamValue", new HTuple());
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("Angle", new List<double>());
Params.Outputs.Add("Score", new List<double>());
Params.Outputs.Add("Count", 0);
}
/// <summary>
/// 运行算子
/// </summary>
public override void HAlgorithmMain()
{
#region 初始化变量
HTuple hv_CenterRow = new HTuple();
HTuple hv_CenterColumn = new HTuple();
HTuple hv_CenterPhi = new HTuple();
HTuple hv_Score = new HTuple();
//创建虚拟HWindow用于显示图片【需要释放】
HWindowControl hWindowControl = new HWindowControl();
#endregion
try
{
if (InputImage == null)
{
strMsg = "输入图片为空";
bResult = false;
return;
}
#region 裁剪区域
if (!ReduceDomainImage())
{
strMsg = "裁剪区域失败";
bResult = false;
return;
}
#endregion
//判断是否有模板
if (ModelID.hvModel == null || ModelID.hvModel.Length == 0)
{
strMsg = "未创建模板";
bResult = false;
return;
}
#region 算子逻辑
HOperatorSet.Rgb1ToGray(hoDomainImage, out hoDomainImage);
//判断是否为灰度图
using (HDevDisposeHelper dh = new HDevDisposeHelper())
{
HOperatorSet.CountChannels(hoDomainImage, out HTuple hv_Channels);
if (hv_Channels.TupleInt() != 1)
{
strMsg = "输入图片不为灰度图";
bResult = false;
return;
}
}
string type = ModelID.Type.ToString();
double AngleStart = (double)Params.Inputs["AngleStart"];
double AngleExtent = (double)Params.Inputs["AngleExtent"];
double MinScore = (double)Params.Inputs["MinScore"];
int NumMatches = AlogrithmParams.ConvertToInt32(Params.Inputs["NumMatches"]);
double MaxOverlap = (double)Params.Inputs["MaxOverlap"];
string SubPixel = AlogrithmParams.ConvertToString(Params.Inputs["SubPixel"]);
int NumLevels = AlogrithmParams.ConvertToInt32(Params.Inputs["NumLevels"]);
double Greediness = (double)Params.Inputs["Greediness"];
switch (type)
{
case "ShapeModel":
//形状模板匹配
#region 参数介绍
//* 参数1:输入图像
//* 参数2:模板句柄
//* 参数3:搜索时的起始角度
//* 参数4:搜索时的终止角度,必须与创建模板时的有交集
//* 参数5:被找到的模板最小分数--大于等于这个值才能被匹配
//* 默认值:0.5 建议值:0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0
//* 典型值范围:0≤MinScore ≤ 1
//* 最小增量:0.01 建议增量:0.05
//* 参数6:要找到的模板最大实例数
//* 0 不限制
//* 参数7:要找到的模型实例的最大重叠比例
//* 默认值:0.5 建议值:0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0
//* 典型值范围:0≤ MaxOverlap≤ 1 最小增量:0.01 建议增量:0.05
//* 0 表示不允许重叠
//* 参数8:计算精度的设置
//* 'none' 不适用亚像素,最大误差为半个像素
//* 'interpolation' 差值得到亚像素精度
//* 'least_squares', 'least_squares_high', 'least_squares_very_high'
//* 'max_deformation 1', 'max_deformation 2', 'max_deformation 3', 'max_deformation 4'
//* 'max_deformation 5', 'max_deformation 6'
//* 参数9:搜索时金字塔的层级
//* 参数10:贪婪度,搜索启发式,一般都设为0.8,越高速度快,容易出现找不到的情况
//* 0≤ Greediness ≤ 1
//* 最后4个:输出匹配位置的行和列坐标、角度、得分 【中心坐标】
#endregion
HOperatorSet.FindShapeModel(hoDomainImage, ModelID.hvModel
, (HTuple)(AngleStart / 180.0 * Math.PI)
, (HTuple)(AngleExtent / 180.0 * Math.PI)
, (HTuple)MinScore
, (HTuple)NumMatches
, (HTuple)MaxOverlap
, (HTuple)SubPixel
, (HTuple)NumLevels
, (HTuple)Greediness
, out hv_CenterRow, out hv_CenterColumn
, out hv_CenterPhi, out hv_Score);
break;
case "LocalDeformableModel":
break;
default:
strMsg = "未创建模板";
bResult = false;
return;
}
#endregion
#region 结果处理
OutputImage = hoDomainImage;
List<double> CenterX = new List<double>();
List<double> CenterY = new List<double>();
List<double> Angle = new List<double>();
List<double> Score = new List<double>();
for (int i = 0; i < hv_CenterRow.Length; i++)
{
CenterX.Add(Math.Round(hv_CenterColumn[i].D, 3));
CenterY.Add(Math.Round(hv_CenterRow[i].D, 3));
Angle.Add(Math.Round(hv_CenterPhi[i].D / Math.PI * 180, 3));
Score.Add(Math.Round(hv_Score[i].D, 3));
}
Params.Outputs["CenterX"] = CenterX;
Params.Outputs["CenterY"] = CenterY;
Params.Outputs["Angle"] = Angle;
Params.Outputs["Score"] = Score;
Params.Outputs["Count"] = CenterX.Count;
#endregion
#region 生成RecordImage便于分析和显示
HOperatorSet.GetImageSize(hoDomainImage, out HTuple ho_ImageWidth, out HTuple ho_ImageHeight);
HOperatorSet.SetPart(hWindowControl.HalconWindow, 0, 0, ho_ImageHeight - 1, ho_ImageWidth - 1);
HOperatorSet.ClearWindow(hWindowControl.HalconWindow);
HOperatorSet.DispObj(hoDomainImage, hWindowControl.HalconWindow);
for (int i = 0; i < hv_CenterRow.Length; i++)
{
SetColor(hWindowControl.HalconWindow, "dark olive green");
HOperatorSet.GenCrossContourXld(out HObject hCross, hv_CenterRow[i].D, hv_CenterColumn[i].D, 300, 0);
HOperatorSet.DispObj(hCross, hWindowControl.HalconWindow);
//HOperatorSet.GenCircle(out HObject hPoint, hv_CenterRow[i].D, hv_CenterColumn[i].D, 30);
//HOperatorSet.DispObj(hPoint, hWindowControl.HalconWindow);
}
//生成RecordImage
try
{
HOperatorSet.DumpWindowImage(out hoRecordImage, hWindowControl.HalconWindow);
}
catch (Exception ex)
{
strMsg = "生成RecordImage失败,原因是:" + ex.ToString();
bResult = false;
return;
}
#endregion
if (strMsg == "运行超时")
{
bResult = false;
return;
}
int MinCount = AlogrithmParams.ConvertToInt32(Params.Inputs["MinCount"]);
int MaxCount = AlogrithmParams.ConvertToInt32(Params.Inputs["MaxCount"]);
if (CenterX.Count < MinCount || CenterX.Count > MaxCount)
{
strMsg = string.Format("结果个数超出范围({0},{1})", MinCount, MaxCount);
bResult = false;
return;
}
strMsg = "运行成功";
bResult = true;
return;
}
catch (Exception ex)
{
strMsg = "运行失败,原因是:" + ex.ToString().TrimEnd();
HOperatorSet.GenEmptyObj(out hoRecordImage);
bResult = false;
return;
}
finally
{
bCompleted = true;
#region 内存释放
hWindowControl.Dispose();
hoDomainImage.Dispose();
#endregion
}
}
public bool CreateModel(HObject Template, HTuple NumLevels, HTuple AngleStart, HTuple AngleExtent, HTuple AngleStep
, HTuple Optimization, HTuple Metric, HTuple Contrast, HTuple MinContrast, ModelType modelType)
{
try
{
switch (modelType)
{
case ModelType.ShapeModel:
//形状模板匹配
#region 参数介绍
//Template: : //reduce_domain后的模板图像
//NumLevels ,//金字塔的层数,可设为“auto”或0—10的整数
//AngleStart ,//模板旋转的起始角度
//AngleExtent ,//模板旋转角度范围, >=0
//AngleStep ,//旋转角度的步长, >=0 and <=pi/16
//Optimization ,//设置模板优化和模板创建方法
//Metric , //匹配方法设置
//Contrast ,//设置对比度
//MinContrast // 设置最小对比度
#endregion
HOperatorSet.CreateShapeModel(Template, NumLevels, AngleStart, AngleExtent, AngleStep, Optimization, Metric, Contrast, MinContrast, out ModelID.hvModel);
ModelID.hoImage = Template.CopyObj(1, -1);
return true;
case ModelType.LocalDeformableModel:
default:
return false;
}
}
catch
{
return false;
}
}
/// <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"))
{
Console.WriteLine("文件路径不完整");
return false;
}
if (fullPath.StartsWith(".\\"))
{
// 判断原字符串长度是否大于等于2,避免越界
if (fullPath.Length >= 2)
{
// 替换开头两个字符
fullPath = Application.StartupPath + fullPath.Substring(2);
Console.WriteLine($"修改后的字符串: {fullPath}");
}
}
if (!File.Exists(fullPath))
{
Console.WriteLine("文件不存在创建空文件");
// 获取不带文件名的目录路径
string directoryPath = Path.GetDirectoryName(fullPath);
strProcessName = Path.GetFileNameWithoutExtension(fullPath);
ModelID = new HModel(fullPath, ModelType.ShapeModel);
Save(directoryPath);
ModelID.Save(fullPath, ModelID.Type);
return true;
}
string strJson = string.Empty;
using (StreamReader streamReader = new StreamReader(fullPath, Encoding.UTF8))
{
strJson = streamReader.ReadToEnd();
streamReader.Close();
}
Params = JsonConvert.DeserializeObject<AlogrithmParams>(strJson);
if (Params == null)
return false;
if (!ModelID.Load(fullPath, Params.Inputs["ModelType"].ToString()))
return false;
return true;
}
catch { return false; }
}
/// <summary>
/// 保存算法
/// </summary>
/// <param name="filePath">不带.json</param>
/// <returns></returns>
public override bool Save(string filePath)
{
try
{
base.Save(filePath);
//if (string.IsNullOrEmpty(filePath))
// return false;
//string strJson = string.Empty;
//strJson = JsonConvert.SerializeObject(Params);
//JsonFormatting(ref strJson);
判断文件夹是否存在,防呆输入为文件名称
//if (!Directory.Exists(filePath))
//{
// try
// {
// Directory.CreateDirectory(filePath);
// }
// catch (Exception)
// { }
//}
//File.WriteAllText(filePath + "//" + strProcessName + ".json", strJson, Encoding.UTF8);
ModelID.Save(filePath + "//" + strProcessName + ".json", ModelID.Type);
return true;
}
catch { return false; }
}
}
public enum ModelType { None, ShapeModel, LocalDeformableModel };
public class HModel
{
public HModel(string modelName = "") { ModelName = modelName; }
public HModel(string modelFullPath, string modelType)
{
ModelFullPath = modelFullPath;
switch (modelType)
{
case "形状":
Type = ModelType.ShapeModel; break;
case "可形变":
Type = ModelType.LocalDeformableModel; break;
default:
Type = ModelType.None; break;
}
Load(ModelFullPath, modelType);
}
public HModel(string modelFullPath, ModelType modelType)
{
ModelFullPath = modelFullPath;
switch (modelType)
{
case ModelType.ShapeModel:
Type = ModelType.ShapeModel;
Load(ModelFullPath, "形状");
break;
case ModelType.LocalDeformableModel:
Type = ModelType.LocalDeformableModel;
Load(ModelFullPath, "可形变");
break;
default:
Type = ModelType.None; break;
}
}
/// <summary>
/// 模板路径
/// </summary>
public string ModelFullPath = "C:\\MyVisionModel\\ModelName.none";
/// <summary>
/// 模板名称
/// </summary>
public string ModelName = string.Empty;
/// <summary>
/// Halcon模板句柄
/// </summary>
public HTuple hvModel;
/// <summary>
/// 模板图片
/// </summary>
public HObject hoImage;
/// <summary>
/// Halcon模板类型
/// </summary>
public ModelType Type = ModelType.ShapeModel;
/// <summary>
/// 加载模板(带.spm)
/// </summary>
/// <param name="fullPath">完整路径带.spm</param>
/// <returns></returns>
public bool Load(string fullPath)
{
try
{
if (string.IsNullOrEmpty(fullPath))
return false;
string filePath = Path.GetFullPath(fullPath);
ModelFullPath = fullPath;
ModelName = Path.GetFileNameWithoutExtension(fullPath);
// 使用 Path.GetExtension 提取扩展名
string extension = Path.GetExtension(filePath);
switch (extension)
{
case ".spm":
if (File.Exists(ModelFullPath))
HOperatorSet.ReadShapeModel(ModelFullPath, out hvModel);
Type = ModelType.ShapeModel;
break;
case ".dfm":
if (File.Exists(ModelFullPath))
HOperatorSet.ReadDeformableModel(ModelFullPath, out hvModel);
Type = ModelType.LocalDeformableModel;
break;
default:
hvModel = new HTuple();
Type = ModelType.None;
return false;
}
string ImageFileName = Path.GetFileNameWithoutExtension(fullPath);
string ImageFullPath = filePath + "\\" + ImageFileName + ".bmp";
if (File.Exists(ImageFullPath))
HOperatorSet.ReadImage(out hoImage, ImageFullPath);
return true;
}
catch { hvModel = new HTuple(); Type = ModelType.None; return false; }
}
/// <summary>
/// 加载模板(带.json)
/// </summary>
/// <param name="fullPath">完整路径带.json</param>
/// <returns></returns>
public bool Load(string fullPath, string modelType)
{
try
{
if (string.IsNullOrEmpty(fullPath))
return false;
string filePath = Path.GetDirectoryName(fullPath);
ModelName = Path.GetFileNameWithoutExtension(fullPath);
ModelFullPath = filePath + "\\" + ModelName;
switch (modelType.ToString())
{
case "形状":
ModelFullPath += ".spm";
if (File.Exists(ModelFullPath))
HOperatorSet.ReadShapeModel(ModelFullPath, out hvModel);
Type = ModelType.ShapeModel;
break;
case "可形变":
ModelFullPath += ".dfm";
if (File.Exists(ModelFullPath))
HOperatorSet.ReadDeformableModel(ModelFullPath, out hvModel);
Type = ModelType.LocalDeformableModel;
break;
default:
Type = ModelType.None;
return false;
}
string ImageFileName = Path.GetFileNameWithoutExtension(fullPath);
string ImageFullPath = filePath + "\\" + ImageFileName + ".bmp";
if (File.Exists(ImageFullPath))
HOperatorSet.ReadImage(out hoImage, ImageFullPath);
return true;
}
catch { Type = ModelType.None; return false; }
}
/// <summary>
/// 保存模板(路径带.spm)
/// </summary>
/// <param name="fullPath">带.spm</param>
/// <returns></returns>
public bool Save(string fullPath)
{
try
{
if (string.IsNullOrEmpty(fullPath))
return false;
string filePath = Path.GetDirectoryName(fullPath);
//判断文件夹是否存在
if (!Directory.Exists(filePath))
{
try
{
Directory.CreateDirectory(filePath);
}
catch (Exception)
{ }
}
ModelName = Path.GetFileNameWithoutExtension(fullPath);
// 使用 Path.GetExtension 提取扩展名
string extension = Path.GetExtension(filePath);
switch (extension)
{
case ".spm":
HOperatorSet.WriteShapeModel(hvModel, fullPath);
Type = ModelType.ShapeModel;
break;
case ".dfm":
HOperatorSet.WriteDeformableModel(hvModel, fullPath);
Type = ModelType.LocalDeformableModel;
break;
default:
hvModel = new HTuple();
HOperatorSet.WriteDeformableModel(hvModel, fullPath);
Type = ModelType.None;
break;
}
string ImageFileName = Path.GetFileNameWithoutExtension(fullPath);
string ImageFullPath = filePath + "\\" + ImageFileName + ".bmp";
HOperatorSet.WriteImage(hoImage, "bmp", 0, ImageFullPath);
return true;
}
catch { return false; }
}
/// <summary>
/// 保存模板(路径带.json)
/// </summary>
/// <param name="fullPath">带.json</param>
/// <returns></returns>
public bool Save(string fullPath, ModelType modelType)
{
try
{
if (string.IsNullOrEmpty(fullPath))
return false;
string filePath = Path.GetDirectoryName(fullPath);
//判断文件夹是否存在
if (!Directory.Exists(filePath))
{
try
{
Directory.CreateDirectory(filePath);
}
catch (Exception)
{ }
}
ModelName = Path.GetFileNameWithoutExtension(fullPath);
ModelFullPath = filePath + "\\" + ModelName;
switch (modelType.ToString())
{
case "ShapeModel":
ModelFullPath += ".spm";
HOperatorSet.WriteShapeModel(hvModel, ModelFullPath);
Type = ModelType.ShapeModel;
break;
case "LocalDeformableModel":
ModelFullPath += ".spm";
HOperatorSet.WriteDeformableModel(hvModel, ModelFullPath);
Type = ModelType.LocalDeformableModel;
break;
default:
hvModel = new HTuple();
Type = ModelType.None;
break;
}
string ImageFileName = Path.GetFileNameWithoutExtension(fullPath);
string ImageFullPath = filePath + "\\" + ImageFileName + ".bmp";
HOperatorSet.WriteImage(hoImage, "bmp", 0, ImageFullPath);
return true;
}
catch { return false; }
}
}
}
Step3 设计UI
UI设计读者可自行完成 这里展示的是用WinForm实现的
结果展示
Halcon模板匹配工具