当前位置: 首页 > news >正文

机器视觉开发教程——封装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 boxusing (HDevDisposeHelper dh = new HDevDisposeHelper()){{HTupleExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat("box");hv_GenParamName.Dispose();hv_GenParamName = ExpTmpLocalVar_GenParamName;}}using (HDevDisposeHelper dh = new HDevDisposeHelper()){{HTupleExpTmpLocalVar_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()){{HTupleExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat("box_color");hv_GenParamName.Dispose();hv_GenParamName = ExpTmpLocalVar_GenParamName;}}using (HDevDisposeHelper dh = new HDevDisposeHelper()){{HTupleExpTmpLocalVar_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()){{HTupleExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat("shadow");hv_GenParamName.Dispose();hv_GenParamName = ExpTmpLocalVar_GenParamName;}}using (HDevDisposeHelper dh = new HDevDisposeHelper()){{HTupleExpTmpLocalVar_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()){{HTupleExpTmpLocalVar_GenParamName = hv_GenParamName.TupleConcat("shadow_color");hv_GenParamName.Dispose();hv_GenParamName = ExpTmpLocalVar_GenParamName;}}using (HDevDisposeHelper dh = new HDevDisposeHelper()){{HTupleExpTmpLocalVar_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];elsereturn 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.0Params.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();#endregiontry{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个:输出匹配位置的行和列坐标、角度、得分 【中心坐标】#endregionHOperatorSet.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);}//生成RecordImagetry{HOperatorSet.DumpWindowImage(out hoRecordImage, hWindowControl.HalconWindow);}catch (Exception ex){strMsg = "生成RecordImage失败,原因是:" + ex.ToString();bResult = false;return;}#endregionif (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 // 设置最小对比度#endregionHOperatorSet.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模板匹配工具

相关文章:

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

目录 引言前期准备Step1 设计可序列化的输入输出集合【不支持多线程】Step2 设计程序框架1、抽象层【IProcess】2、父类【HAlgorithm】3、子类【HFindModelTool】 Step3 设计UI结果展示 引言 通过仿照VisionPro软件二次开发Halcon的模板匹配工具&#xff0c;便于在客户端软件中…...

UDP透传程序

UDP透传程序 本脚本用于在 设备 A 和 设备 B 之间建立 UDP 数据转发桥梁&#xff0c;适用于 A 和 B 设备无法直接通信的情况。 流程&#xff1a; A --> 电脑 (中继) --> B B --> 电脑 (中继) --> A 需要修改参数&#xff1a; B_IP “192.168.1.123” # 设备 B 的…...

【USRP】NVIDIA Sionna:用于 6G 物理层研究的开源库

目录 Sionna&#xff1a;用于 6G 物理层研究的开源库主要特点实现6G研究的民主化支持 5G、6G 等模块化、可扩展、可伸缩快速启动您的研究 好处原生人工智能支持综合研究平台开放生态系统 安装笔记使用 pip 安装基于Docker的安装从源代码安装“你好世界&#xff01;”探索锡奥纳…...

Spring WebFlux 中 WebSocket 使用 DataBuffer 的注意事项

以下是修改后的完整文档&#xff0c;包含在多个多线程环境中使用 retain() 和 release() 方法的示例&#xff0c;且确保在 finally 块中调用 release()&#xff1a; 在 Spring WebFlux 中&#xff0c;WebSocketMessage 主要用于表示 WebSocket 的消息载体&#xff0c;其中 getP…...

SQL经典常用查询语句

1. 基础查询语句 1.1 查询表中所有数据 在SQL中&#xff0c;查询表中所有数据是最基本的操作之一。通过使用SELECT * FROM table_name;语句&#xff0c;可以获取指定表中的所有记录和列。例如&#xff0c;假设有一个名为employees的表&#xff0c;包含员工的基本信息&#xf…...

0005__PyTorch 教程

PyTorch 教程 | 菜鸟教程 离线包&#xff1a;torch-1.13.1cpu-cp39-cp39-win_amd64.whl https://download.pytorch.org/whl/torch_stable.html...

高并发场景下的数据库优化

在高并发系统中&#xff0c;数据库通常是性能瓶颈。面对高并发请求&#xff0c;我们需要采用合适的优化策略&#xff0c;以保证数据库的稳定性和高效性。本文将介绍数据库高并发问题的成因&#xff0c;并结合 Mybatis-Plus&#xff0c;探讨 乐观锁、悲观锁、高并发优化及数据库…...

Linux:同步

目录 一、同步概念 条件变量 二、生产者消费者模型 三、环形队列 一、同步概念 互斥用来解决 访问临界资源 的非原子性&#xff0c;通俗来说&#xff0c;由于互斥锁的实现&#xff0c;保证了在用户角度看&#xff0c;同一个时间内访问临界资源的代码只有一个线程在执行。 而…...

GB28181开发--ZLMediaKit‌+WVP+Jessibuca‌

一、核心组件功能 1‌、ZLMediaKit‌ 定位‌:基于 C++11 的高性能流媒体服务框架,支持 RTSP/RTMP/HLS/HTTP-FLV 等协议互转,具备低延迟(最低 100ms)、高并发(单机 10W 级连接)特性,适用于商用级流媒体服务器部署‌。 ‌特性‌:跨平台(Linux/Windows/ARM 等)、支持 …...

23种设计模式之《备忘录模式(Memento)》在c#中的应用及理解

程序设计中的主要设计模式通常分为三大类&#xff0c;共23种&#xff1a; 1. 创建型模式&#xff08;Creational Patterns&#xff09; 单例模式&#xff08;Singleton&#xff09;&#xff1a;确保一个类只有一个实例&#xff0c;并提供全局访问点。 工厂方法模式&#xff0…...

Oracle删除重复数据保留其中一条

Oracle删除重复数据保留其中一条 在Oracle数据库中&#xff0c;要删除重复数据并保留其中一条记录&#xff0c;可以使用多种方法。这里介绍两种常见的方法&#xff1a;使用ROWID或使用ROW_NUMBER()窗口函数。 方法1&#xff1a;使用ROWID ROWID是Oracle中用来唯一标识表中每…...

deepseek助力运维和监控自动化

将DeepSeek与Agent、工作流及Agent编排技术结合&#xff0c;可实现IT运维与监控的智能化闭环管理。以下是具体应用框架和场景示例&#xff1a; 一、智能Agent体系设计 多模态感知Agent 日志解析Agent&#xff1a;基于DeepSeek的NLP能力&#xff0c;实时解析系统日志中的语义&a…...

16.1STM32_ADC

STM32_ADC 数字信号分为高/低电平两种状态 模拟信号就是任意的电压值 STM32芯片内就是一整套的数字逻辑电路&#xff0c;来实现我们的程序执行&#xff0c;以及各种各样的外设功能&#xff0c; ADC&#xff08;模拟-数字转换技术&#xff09;的功能就是将模拟信号转化为数字…...

神经网络 - 激活函数(Swish函数、GELU函数)

一、Swish 函数 Swish 函数是一种较新的激活函数&#xff0c;由 Ramachandran 等人在 2017 年提出&#xff0c;其数学表达式通常为 其中 σ(x) 是 Sigmoid 函数&#xff08;Logistic 函数&#xff09;。 如何理解 Swish 函数 自门控特性 Swish 函数可以看作是对输入 x 进行“…...

VS2015 c++和cmake配置编程

Visual Studio 2015&#xff1a;确保安装了C开发工具&#xff0c;并安装“使用C的桌面开发”工作负载。CMake&#xff1a;可以从 CMake官网 下载并安装&#xff0c;并将其添加到系统环境变量中。vs加载项目启动Visual Studio。选择“继续但无代码”。点击“文件”。选择 “打开…...

如何为 Web 前端开发面试做好准备

大家好&#xff01;我是 [数擎AI]&#xff0c;一位热爱探索新技术的前端开发者&#xff0c;在这里分享前端和Web3D、AI技术的干货与实战经验。如果你对技术有热情&#xff0c;欢迎关注我的文章&#xff0c;我们一起成长、进步&#xff01; 开发领域&#xff1a;前端开发 | AI 应…...

深入探索像ChatGPT这样的大语言模型

参考 【必看珍藏】2月6日&#xff0c;安德烈卡帕西最新AI普及课&#xff1a;深入探索像ChatGPT这样的大语言模型&#xff5c;Andrej Karpathy fineweb知乎翻译介绍 fineweb-v1原始连接 fineweb中文翻译版本 Chinese Fineweb Edu数据集 查看网络的内部结果&#xff0c;可以参…...

代码贴——堆(二叉树)数据结构

头文件Heap.h #pragma once #include<bits/stdc.h> typedef int HPDataType;typedef struct Heap {HPDataType* a;int size;int capacity; }HP;void HPInit(HP* php); void HPDestory(HP* php); //出入后保持数据是堆 void HPPush(HP* php,HPDataType x); HPDataType HP…...

office或者word排版中,复制/黏贴进来文字不会自动换行,如何处理?

李升伟 整理 一、思考与分析 在Office或Word中复制粘贴文字时&#xff0c;文字不会自动换行&#xff0c;需要处理这个问题。首先&#xff0c;我得回想一下常见的原因和解决方法。可能的情况有很多&#xff0c;比如文本带有硬回车、段落格式设置问题&#xff0c;或者文本框的自…...

最新!!!DeepSeek开源周发布内容汇总

本周&#xff0c;人工智能领域的新锐力量DeepSeek宣布将于本周举办“开源周”&#xff08;Open Source Week&#xff09;&#xff0c;连续五天每日开源一个核心代码库&#xff0c;以透明的方式与全球开发者分享其在通用人工智能&#xff08;AGI&#xff09;探索中的最新成果。以…...

【MySQL】(2) 库的操作

SQL 关键字&#xff0c;大小写不敏感。 一、查询数据库 show databases; 注意加分号&#xff0c;才算一句结束。 二、创建数据库 {} 表示必选项&#xff0c;[] 表示可选项&#xff0c;| 表示任选其一。 示例&#xff1a;建议加上 if not exists 选项。 三、字符集编码和排序…...

记一次渗透测试实战:SQL注入漏洞的挖掘与利用

0x01 漏洞发现 在对某网站进行安全测试时&#xff0c;发现以下URL存在异常&#xff1a; https://******.com/search.php?keyword1&zt1954&dw1885&zz& 当参数keyword和zt被赋值为-1时页面返回特殊内容&#xff0c;初步判断存在SQL注入漏洞。 0x02 注入验证…...

Gin框架从入门到实战:核心用法与最佳实践

为什么选择Gin框架&#xff1f; Gin 是一个基于 Go 语言的高性能 Web 框架&#xff0c;具备以下优势&#xff1a; 轻量高效&#xff1a;底层依赖 net/http&#xff0c;性能接近原生。简洁优雅&#xff1a;API 设计友好&#xff0c;支持路由分组、中间件链、参数绑定等特性。生…...

PyTorch 的 nn.NLLLoss:负对数似然损失全解析

PyTorch 的 nn.NLLLoss&#xff1a;负对数似然损失全解析 在 PyTorch 的损失函数家族中&#xff0c;nn.NLLLoss&#xff08;Negative Log Likelihood Loss&#xff0c;负对数似然损失&#xff09;是一个不太起眼但非常重要的成员。它经常跟 LogSoftmax 搭配出现&#xff0c;尤…...

ROS2软件调用架构和机制解析:Publisher创建

术语 DDS (Data Distribution Service): 用于实时系统的数据分发服务标准&#xff0c;是ROS 2底层通信的基础RMW (ROS Middleware): ROS中间件接口&#xff0c;提供与具体DDS实现无关的抽象APIQoS (Quality of Service): 服务质量策略&#xff0c;控制通信的可靠性、历史记录、…...

vue2 以及vue3中 v-if和v-for是否可以同时使用

vue2以及vue3官方文档中都明确的指出 避免 v-if 和 v-for 用在一起 vue2 官方文档 解释 在 Vue 2 中&#xff0c;v-for 的优先级高于 v-if&#xff0c;也就是说&#xff0c;Vue 2 在渲染时&#xff0c;会先处理 v-for 生成列表项&#xff0c;再对子项判断 v-if 是否渲染。 …...

Hbase伪分布安装教程,详细版

注意Hbase版本与Hadoop版本的兼容&#xff0c;还有与JDK版本的兼容 本次用到的Hbase为2.4.6版本&#xff0c;Hadoop为3.1.3版本&#xff0c;JDK为JDK8 打开下面的网址查看兼容问题 Apache HBase Reference Guidehttps://hbase.apache.org/book.html#configuration 点击基础先…...

SSL: CERTIFICATE_VERIFY_FAILED Error in Python 是什么问题?

在最新版本的Stable Diffusion webui 版本上使用最新下载的模型时&#xff0c;出现了类似的错误。 SSL: CERTIFICATE_VERIFY_FAILED 错误在Python中通常表示你的程序试图通过HTTPS连接到某个服务器&#xff0c;但Python无法验证该服务器提供的SSL证书。这可能是因为以下几种原…...

15Metasploit框架介绍

metasploit目录结构 MSF ——the metasploit framework 的简称。MSF高度模块化&#xff0c;即框架结构由多个module组成&#xff0c;是全球最受欢迎的工具 是一筐开源安全漏洞利用和测试工具&#xff0c;集成了各种平台上常见的溢出漏洞和流行sheellcode&#xff0c;并且保持…...

【Qt】ffmpeg解码—照片提取、视频播放▲

目录 一、图像的成像原理&#xff1a; RGB成像原理&#xff1a; YUV成像原理&#xff1a; 二、多线程 三、ffmpeg解码&#xff08;照片提取&#xff09; 1.准备工作 &#xff08;1&#xff09;在工程文件夹里面新建三个文件夹 &#xff08;2&#xff09;在main函数中加…...

Springboot整合WebSocket+Redis以及微信小程序如何调用

一、 Springboot整合WebSocket 1. 引入socket依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>引入依赖后需要刷新maven,Websocket的版本默认跟随S…...

HOW - 在Windows浏览器中模拟MacOS的滚动条

目录 一、原生 CSS 代码实现模拟 macOS 滚动条额外优化应用到某个特定容器 二、Antd table中的滚动条场景三、使用第三方工具/扩展 如果你想让 Windows 里的滚动条 模拟 macOS 的效果&#xff08;细窄、圆角、隐藏默认轨道&#xff09;。 可以使用以下几种方案&#xff1a; 一…...

openEuler环境下GlusterFS分布式存储集群部署指南

1.环境准备&#xff1a; os&#xff1a;openEuler 22.03 主机名 IP地址 主机用途 Rocky8192.168.121.160客户端 open-Euler1192.168.121.150节点1&#xff0c;提供两块6G硬盘open-Euler4192.168.121.153节点2&#xff0c;提供两块6G硬盘open-Euler5192.168.121.154 …...

C++学习(七)(标准库+STL(iotstream公司,日期/时间,器皿,算法,迭代器,多线程))

C 标准模板库 &#xff08;STL&#xff09; C 标准模板库 &#xff08;STL&#xff09; 是头文件的集合&#xff0c;提供了多种数据结构、算法和函数&#xff0c;以简化您的 C 编码体验。STL 的主要目的是通过提供一套现成的有用工具来节省时间并提高效率。STL 最常用的功能可…...

c高级第五天

1> 在终端提示输入一个成绩&#xff0c;通过shell判断该成绩的等级 [90,100] : A [80, 90) : B [70, 80) : C [60, 70) : D [0, 60) : 不及格 #!/bin/bash# 提示用户输入成绩 read -p "请输入成绩&#xff08;0-100&#xff09;&#xff1a;" score# 判断成…...

Windows上使用go-ios实现iOS17自动化

前言 在Windows上运行iOS的自动化&#xff0c;tidevice对于iOS17以上并不支持&#xff0c;原因是iOS 17 引入新通信协议 ‌RemoteXPCQUIC‌&#xff0c;改变了 XCUITest 的启动方式。 一、go-ios的安装 1、安装命令&#xff1a;npm i go-ios 2、安装完成后输入命令which io…...

迷你世界脚本小地图接口:Mapmark

小地图接口&#xff1a;Mapmark 彼得兔 更新时间: 2023-10-25 10:33:48 具体函数名及描述如下: 序号 函数名 函数描述 1 newShape(...) 新增一个形状(线&#xff0c;矩形&#xff0c;圆形) 2 deleteShape(...) 删除一个形状 3 setShapeColor(...) 设置…...

TMS320F28P550SJ9学习笔记1:CCS导入工程以及测试连接单片机仿真器

学习记录如何用 CCS导入工程以及测试连接单片机仿真器 以下为我的CCS 以及驱动库C2000ware 的版本 CCS版本&#xff1a; Code Composer Studio 12.8.1 C2000ware &#xff1a;C2000Ware_5_04_00_00 目录 CCS导入工程&#xff1a; 创建工程&#xff1a; 添加工程&#xff1a; C…...

为什么要提倡尽早返回(Early Return)

为什么要提倡尽早返回&#xff08;Early Return&#xff09; 在编程中&#xff0c;“尽早返回”&#xff08;Early Return&#xff09;是一种常被提倡的编程方式&#xff0c;特别是在需要提升代码可读性、减少嵌套层级、以及快速处理异常情况时。本文将讨论尽早返回的优点、应…...

Gartner发布安全运营指标构建指南

如何为安全运营指标构建坚实的基础 安全运营经理需要报告威胁检测、调查和响应计划的有效性&#xff0c;但难以驾驭大量潜在的 SOC 指标。本研究提供了设计针对 SOC 的指标系统的示例和实践。 主要发现 需要清晰、一致的衡量标准来向董事会成员或服务提供商等更广泛的团队传达…...

vue3:初学 vue-router 路由配置

承上一篇&#xff1a;nodejs&#xff1a;express js-mdict 作为后端&#xff0c;vue 3 vite 作为前端&#xff0c;在线查询英汉词典 安装 cnpm install vue-router -S 现在讲一讲 vue3&#xff1a;vue-router 路由配置 cd \js\mydict-web\src mkdir router cd router 我还…...

数据结构入门篇——什么是数据结构。

一、引入 工具是一种什么东西呢&#xff1f;是一种转化媒介&#xff0c;我们需要熟食&#xff0c;我们要通过用火来将生肉烤熟。在这个过程中。我们要输入一个东西——生肉&#xff0c;通过工具——火的加工&#xff0c;从而得到我们的目的产物——熟肉。 将上面的例子和红字部…...

uniapp+vue3搭建项目

工具使用&#xff1a; Pinia Vue 3 官方推荐的状态管理库&#xff0c;比 Vuex 更轻量&#xff0c;支持模块化&#xff0c;结合 persistedstate 插件可以持久化存储数据。uView-plus 专为 UniApp 设计&#xff0c;支持 App、小程序、H5。UnoCSS 更轻量&#xff0c;比 TailwindCS…...

unity大坐标抖动处理测试

第二幅图就是相机坐标是0 6360094 0的地方看见的模型&#xff0c;可以看见这个球体已经烂了 那么这里可以知道的是坐标太大了导致的&#xff0c;那么把所有物体共同偏移一下&#xff0c;即可得到第一幅图的效果&#xff0c;圆润的sphere又回来了 浮点数的计算是需要位数的&…...

CASAIM与承光电子达成深度合作,三维扫描逆向建模技术助力车灯设计与制造向数字化与智能化转型

近日&#xff0c;CASAIM与广州承光电子科技有限公司正式达成深度合作&#xff0c;CASAIM将为承光电子提供全方位的技术支持&#xff0c;包括高精度三维扫描设备、逆向建模软件以及定制化的技术解决方案。双方将共同组建技术团队&#xff0c;针对车灯设计中的难点进行攻关&#…...

C++类与对象:银行管理系统项目实战开发LeetCode每日一题

[Bank-Management-System]银行管理系统项目 以下是一个可运行的C银行账户类&#xff08;支持简单的存款/取款&#xff09;。后面会继续完善该项目&#xff1a; #include <iostream> #include <string> using namespace std;class Account{public://构造函数Accou…...

领域驱动设计:事件溯源架构简介

概述 事件溯源架构通常由3种应用设计模式组成,分别是:事件驱动(Event Driven),事件溯源(Event Source)、CQRS(读写分离)。这三种应用设计模式常见于领域驱动设计(DDD)中,但它们本身是一种应用设计的思想,不仅仅局限于DDD,每一种模式都可以单独拿出来使用。 E…...

景联文科技:以专业标注赋能AI未来,驱动智能时代的精准跃迁

在人工智能技术重塑全球产业格局的今天&#xff0c;高质量训练数据已成为驱动算法进化的核心燃料。作为数据智能服务领域的领军者&#xff0c;景联文科技深耕数据标注行业多年&#xff0c;以全栈式数据解决方案为核心&#xff0c;构建起覆盖数据采集、清洗、标注、质检及算法调…...

LeetCode 热题 100----1.两数之和

LeetCode 热题 100----1.两数之和 题目描述 我的解法 语言&#xff1a;js 思路就是&#xff1a;用双重循环去找哪两个数字相加等于target&#xff0c;目前的时间复杂度为O(n2)&#xff0c;之后右优化思路再更新。...

GIT 常用命令

/ 一、环境&#xff1a; ssh-keygen -t rsa -C "wangxiaoerqq.com.cn" 生成本地秘钥&#xff08;邮箱换成自己的邮箱&#xff09; 使用cat ~/.ssh/id_rsa.pub查看秘钥 git config --global user.name "wangxiaoer" git config --global wangxiaoerqq.…...