机器视觉开发教程——封装Halcon通用模板匹配工具【含免费教程源码】

目录

  • 引言
  • 前期准备
  • 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模板匹配工具

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/982042.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux跬步积累】—— 线程池详解(有源代码)

文章目录 一、如何实现一个线程1、基本结构2、实现成员函数3、演示4、代码总汇Thread.hppMain.cc 二、如何封装线程池1、设计成员变量2、构造函数与析构函数3、初始化4、启动与回收5、主线程放入任务6、子线程读取任务7、终止线程池 三、测试四、线程池总代码1、ThreadPool.hpp…

【Linux】自定协议和序列化与反序列化

目录 一、序列化与反序列化概念 二、自定协议实现一个加法网络计算器 &#xff08;一&#xff09;TCP如何保证接收方的接收到数据是完整性呢&#xff1f; &#xff08;二&#xff09;自定义协议 &#xff08;三&#xff09;自定义协议的实现 1、基础类 2、序列化与反序列…

hive之LEAD 函数详解

1. 函数概述 LEAD 是 Hive 中的窗口函数&#xff0c;用于获取当前行之后指定偏移量处的行的值。常用于分析时间序列数据、计算相邻记录的差异或预测趋势。 2. 语法 LEAD(column, offset, default) OVER ([PARTITION BY partition_column] [ORDER BY order_column [ASC|DESC]…

ZYNQ-PL学习实践(二)按键和定时器控制LED闪烁灯

ZYNQ-PL学习实践&#xff08;二&#xff09;按键和定时器控制LED闪烁灯&#xff09; 1 创建工程2 verilog 代码3 约束4 综合5 生成bit总结 1 创建工程 2 verilog 代码 添加key_led.v 文件&#xff0c; module key_led(input sys_clk , //系统时钟50MHzinput …

【Python爬虫】利用代理IP爬取跨境电商AI选品分析

引言 随着DeepSeek的流行&#xff0c;越来越多的用户开始尝试将AI工具融入到日常工作当中&#xff0c;借助AI的强大功能提高工作效率。最近又掀起了一波企业出海的小高潮&#xff0c;那么如果是做跨境电商业务&#xff0c;怎么将AI融入工作流中呢&#xff1f;在做跨境电商的时候…

设计一个SVF下载器之一:整体思路

CPLD或者FPGA开发工具会生成SVF文件用以通过JTAG口配置CPLD或者FPGA。这里有些基本控制JTAG状态机的指令&#xff0c;其实就是主要两条SIR和SDR分别实现对IR寄存器和DR寄存器的写。 这样我们的这个下载器的基本工作变成了解析SVF文件之后对JTAG的TAP状态机进行操作实现对IR和D…

计算机视觉算法实战——图像配准(主页有源码)

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​ 1. 领域简介 图像配准&#xff08;Image Registration&#xff09;是计算机视觉中的一个重要研究方向&#xff0c;旨在将两幅或多幅…

ArcGIS操作:07 绘制矢量shp面

1、点击目录 2、右侧显示目录 3、选择要存储的文件夹&#xff0c;新建shp 4、定义名称、要素类型、坐标系 5、点击开始编辑 6、点击创建要素 7、右侧选择图层、创建面 8、开始绘制&#xff0c;双击任意位置结束绘制

靶场(二)---靶场心得小白分享

开始&#xff1a; 看一下本地IP 21有未授权访问的话&#xff0c;就从21先看起 PORT STATE SERVICE VERSION 20/tcp closed ftp-data 21/tcp open ftp vsftpd 2.0.8 or later | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_Cant get dire…

一周学会Flask3 Python Web开发-WTForms表单验证

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们可以通过WTForms表单类属性的validators属性来实现表单验证。 常用的WTForms验证器 验证器说明DataRequired(messageNo…

C 语 言 --- 猜 数 字 游 戏

C 语 言 --- 猜 数 字 游 戏 代 码 全 貌 与 功 能 介 绍游 戏 效 果 展 示游 戏 代 码 详 解头 文 件 引 入菜单函数游 戏 逻 辑 函 数 gamerand 函 数 详 解逻 辑 函 数 game 主 函 数 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验…

深入探索C++17文件系统库:std::filesystem全面解析

前言 在C编程中&#xff0c;文件系统操作是许多应用程序的基础功能之一。无论是读写文件、创建目录&#xff0c;还是遍历文件系统&#xff0c;文件系统操作几乎无处不在。然而&#xff0c;在C17之前&#xff0c;标准库并没有提供一个统一、高效且易用的文件系统操作接口。开发…

C++学习之C++初识、C++对C语言增强、对C语言扩展

一.C初识 1.C简介 2.第一个C程序 //#include <iostream> //iostream 相当于 C语言下的 stdio.h i - input 输入 o -output 输出 //using namespace std; //using 使用 namespace 命名空间 std 标准 &#xff0c;理解为打开一个房间&#xff0c;房间里有我们所需…

transformer架构解析{掩码,(自)注意力机制,多头(自)注意力机制}(含代码)-3

目录 前言 掩码张量 什么是掩码张量 掩码张量的作用 生成掩码张量实现 注意力机制 学习目标 注意力计算规则 注意力和自注意力 注意力机制 注意力机制计算规则的代码实现 多头注意力机制 学习目标 什么是多头注意力机制 多头注意力计算机制的作用 多头注意力机…

【大模型基础_毛玉仁】1.3 基于Transformer 的语言模型

【大模型基础_毛玉仁】1.3 基于Transformer 的语言模型 1.3 基于Transformer 的语言模型1.3.1 Transformer1&#xff09;注意力层&#xff08;AttentionLayer&#xff09;2&#xff09;全连接前馈层&#xff08;Fully-connected Feedforwad Layer&#xff09;3&#xff09;层正…

Beeline的使用和Hive JDBC

目录 1. 引言1.1 Hadoop1.2 HBase1.3 Hive 2. Beeline2.1 使用Beeline访问Hive2.1.1 通过beeline直接连接Hive2.1.2 先进入beeline客户端再连接Hive2.1.3 先进入beeline客户端再连接MySQL 2.2 Beeline命令 3. Hive JDBC3.1 pom.xml中依赖配置3.2 Util工具类3.3 代码3.4 结果 参…

分布式多卡训练(DDP)踩坑

多卡训练最近在跑yolov10版本的RT-DETR&#xff0c;用来进行目标检测。 单卡训练语句&#xff08;正常运行&#xff09;&#xff1a; python main.py多卡训练语句&#xff1a; 需要通过torch.distributed.launch来启动&#xff0c;一般是单节点&#xff0c;其中CUDA_VISIBLE…

30秒从零搭建机器人管理系统(Trae)

1. 安装 [Trae官网】(https://www.trae.com.cn/) 2. 提示词 创建一个BS架构的机器人远程操控系统&#xff0c;具备机器人状态及位置实时更新&#xff0c;可以实现机器人远程遥控&#xff0c;可以对机器人工作日志进行统计分析&#xff0c;以及其它管理系统的常用功能3. 模型…

软考-数据库开发工程师-3.1-数据结构-线性结构

第3章内容比较多&#xff0c;内容考试分数占比较大&#xff0c;6分左右 线性表 1、线性表的定义 一个线性表是n个元素的有限序列(n≥0)&#xff0c;通常表示为(a1&#xff0c;a2, a3,…an). 2、线性表的顺序存储(顺序表) 是指用一组地址连续的存储单元依次存储线性表中的数据元…

解锁数据潜能,永洪科技以数据之力简化中粮可口可乐决策之路

企业数字化转型是指企业利用数字技术和信息通信技术来改变自身的商业模式、流程和增值服务&#xff0c;以提高企业的竞争力和创新能力。数字化转型已经成为企业发展的重要战略&#xff0c;尤其在当前信息技术高速发展的时代。数字化转型还涉及到企业与消费者之间的互动和沟通。…