机器视觉开发教程——封装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的模板匹配工具,便于在客户端软件中…...
UDP透传程序
UDP透传程序 本脚本用于在 设备 A 和 设备 B 之间建立 UDP 数据转发桥梁,适用于 A 和 B 设备无法直接通信的情况。 流程: A --> 电脑 (中继) --> B B --> 电脑 (中继) --> A 需要修改参数: B_IP “192.168.1.123” # 设备 B 的…...
【USRP】NVIDIA Sionna:用于 6G 物理层研究的开源库
目录 Sionna:用于 6G 物理层研究的开源库主要特点实现6G研究的民主化支持 5G、6G 等模块化、可扩展、可伸缩快速启动您的研究 好处原生人工智能支持综合研究平台开放生态系统 安装笔记使用 pip 安装基于Docker的安装从源代码安装“你好世界!”探索锡奥纳…...
Spring WebFlux 中 WebSocket 使用 DataBuffer 的注意事项
以下是修改后的完整文档,包含在多个多线程环境中使用 retain() 和 release() 方法的示例,且确保在 finally 块中调用 release(): 在 Spring WebFlux 中,WebSocketMessage 主要用于表示 WebSocket 的消息载体,其中 getP…...
SQL经典常用查询语句
1. 基础查询语句 1.1 查询表中所有数据 在SQL中,查询表中所有数据是最基本的操作之一。通过使用SELECT * FROM table_name;语句,可以获取指定表中的所有记录和列。例如,假设有一个名为employees的表,包含员工的基本信息…...
0005__PyTorch 教程
PyTorch 教程 | 菜鸟教程 离线包:torch-1.13.1cpu-cp39-cp39-win_amd64.whl https://download.pytorch.org/whl/torch_stable.html...
高并发场景下的数据库优化
在高并发系统中,数据库通常是性能瓶颈。面对高并发请求,我们需要采用合适的优化策略,以保证数据库的稳定性和高效性。本文将介绍数据库高并发问题的成因,并结合 Mybatis-Plus,探讨 乐观锁、悲观锁、高并发优化及数据库…...
Linux:同步
目录 一、同步概念 条件变量 二、生产者消费者模型 三、环形队列 一、同步概念 互斥用来解决 访问临界资源 的非原子性,通俗来说,由于互斥锁的实现,保证了在用户角度看,同一个时间内访问临界资源的代码只有一个线程在执行。 而…...
GB28181开发--ZLMediaKit+WVP+Jessibuca
一、核心组件功能 1、ZLMediaKit 定位:基于 C++11 的高性能流媒体服务框架,支持 RTSP/RTMP/HLS/HTTP-FLV 等协议互转,具备低延迟(最低 100ms)、高并发(单机 10W 级连接)特性,适用于商用级流媒体服务器部署。 特性:跨平台(Linux/Windows/ARM 等)、支持 …...
23种设计模式之《备忘录模式(Memento)》在c#中的应用及理解
程序设计中的主要设计模式通常分为三大类,共23种: 1. 创建型模式(Creational Patterns) 单例模式(Singleton):确保一个类只有一个实例,并提供全局访问点。 工厂方法模式࿰…...
Oracle删除重复数据保留其中一条
Oracle删除重复数据保留其中一条 在Oracle数据库中,要删除重复数据并保留其中一条记录,可以使用多种方法。这里介绍两种常见的方法:使用ROWID或使用ROW_NUMBER()窗口函数。 方法1:使用ROWID ROWID是Oracle中用来唯一标识表中每…...
deepseek助力运维和监控自动化
将DeepSeek与Agent、工作流及Agent编排技术结合,可实现IT运维与监控的智能化闭环管理。以下是具体应用框架和场景示例: 一、智能Agent体系设计 多模态感知Agent 日志解析Agent:基于DeepSeek的NLP能力,实时解析系统日志中的语义&a…...
16.1STM32_ADC
STM32_ADC 数字信号分为高/低电平两种状态 模拟信号就是任意的电压值 STM32芯片内就是一整套的数字逻辑电路,来实现我们的程序执行,以及各种各样的外设功能, ADC(模拟-数字转换技术)的功能就是将模拟信号转化为数字…...
神经网络 - 激活函数(Swish函数、GELU函数)
一、Swish 函数 Swish 函数是一种较新的激活函数,由 Ramachandran 等人在 2017 年提出,其数学表达式通常为 其中 σ(x) 是 Sigmoid 函数(Logistic 函数)。 如何理解 Swish 函数 自门控特性 Swish 函数可以看作是对输入 x 进行“…...
VS2015 c++和cmake配置编程
Visual Studio 2015:确保安装了C开发工具,并安装“使用C的桌面开发”工作负载。CMake:可以从 CMake官网 下载并安装,并将其添加到系统环境变量中。vs加载项目启动Visual Studio。选择“继续但无代码”。点击“文件”。选择 “打开…...
如何为 Web 前端开发面试做好准备
大家好!我是 [数擎AI],一位热爱探索新技术的前端开发者,在这里分享前端和Web3D、AI技术的干货与实战经验。如果你对技术有热情,欢迎关注我的文章,我们一起成长、进步! 开发领域:前端开发 | AI 应…...
深入探索像ChatGPT这样的大语言模型
参考 【必看珍藏】2月6日,安德烈卡帕西最新AI普及课:深入探索像ChatGPT这样的大语言模型|Andrej Karpathy fineweb知乎翻译介绍 fineweb-v1原始连接 fineweb中文翻译版本 Chinese Fineweb Edu数据集 查看网络的内部结果,可以参…...
代码贴——堆(二叉树)数据结构
头文件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中复制粘贴文字时,文字不会自动换行,需要处理这个问题。首先,我得回想一下常见的原因和解决方法。可能的情况有很多,比如文本带有硬回车、段落格式设置问题,或者文本框的自…...
最新!!!DeepSeek开源周发布内容汇总
本周,人工智能领域的新锐力量DeepSeek宣布将于本周举办“开源周”(Open Source Week),连续五天每日开源一个核心代码库,以透明的方式与全球开发者分享其在通用人工智能(AGI)探索中的最新成果。以…...
【MySQL】(2) 库的操作
SQL 关键字,大小写不敏感。 一、查询数据库 show databases; 注意加分号,才算一句结束。 二、创建数据库 {} 表示必选项,[] 表示可选项,| 表示任选其一。 示例:建议加上 if not exists 选项。 三、字符集编码和排序…...
记一次渗透测试实战:SQL注入漏洞的挖掘与利用
0x01 漏洞发现 在对某网站进行安全测试时,发现以下URL存在异常: https://******.com/search.php?keyword1&zt1954&dw1885&zz& 当参数keyword和zt被赋值为-1时页面返回特殊内容,初步判断存在SQL注入漏洞。 0x02 注入验证…...
Gin框架从入门到实战:核心用法与最佳实践
为什么选择Gin框架? Gin 是一个基于 Go 语言的高性能 Web 框架,具备以下优势: 轻量高效:底层依赖 net/http,性能接近原生。简洁优雅:API 设计友好,支持路由分组、中间件链、参数绑定等特性。生…...
PyTorch 的 nn.NLLLoss:负对数似然损失全解析
PyTorch 的 nn.NLLLoss:负对数似然损失全解析 在 PyTorch 的损失函数家族中,nn.NLLLoss(Negative Log Likelihood Loss,负对数似然损失)是一个不太起眼但非常重要的成员。它经常跟 LogSoftmax 搭配出现,尤…...
ROS2软件调用架构和机制解析:Publisher创建
术语 DDS (Data Distribution Service): 用于实时系统的数据分发服务标准,是ROS 2底层通信的基础RMW (ROS Middleware): ROS中间件接口,提供与具体DDS实现无关的抽象APIQoS (Quality of Service): 服务质量策略,控制通信的可靠性、历史记录、…...
vue2 以及vue3中 v-if和v-for是否可以同时使用
vue2以及vue3官方文档中都明确的指出 避免 v-if 和 v-for 用在一起 vue2 官方文档 解释 在 Vue 2 中,v-for 的优先级高于 v-if,也就是说,Vue 2 在渲染时,会先处理 v-for 生成列表项,再对子项判断 v-if 是否渲染。 …...
Hbase伪分布安装教程,详细版
注意Hbase版本与Hadoop版本的兼容,还有与JDK版本的兼容 本次用到的Hbase为2.4.6版本,Hadoop为3.1.3版本,JDK为JDK8 打开下面的网址查看兼容问题 Apache HBase Reference Guidehttps://hbase.apache.org/book.html#configuration 点击基础先…...
SSL: CERTIFICATE_VERIFY_FAILED Error in Python 是什么问题?
在最新版本的Stable Diffusion webui 版本上使用最新下载的模型时,出现了类似的错误。 SSL: CERTIFICATE_VERIFY_FAILED 错误在Python中通常表示你的程序试图通过HTTPS连接到某个服务器,但Python无法验证该服务器提供的SSL证书。这可能是因为以下几种原…...
15Metasploit框架介绍
metasploit目录结构 MSF ——the metasploit framework 的简称。MSF高度模块化,即框架结构由多个module组成,是全球最受欢迎的工具 是一筐开源安全漏洞利用和测试工具,集成了各种平台上常见的溢出漏洞和流行sheellcode,并且保持…...
【Qt】ffmpeg解码—照片提取、视频播放▲
目录 一、图像的成像原理: RGB成像原理: YUV成像原理: 二、多线程 三、ffmpeg解码(照片提取) 1.准备工作 (1)在工程文件夹里面新建三个文件夹 (2)在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 的效果(细窄、圆角、隐藏默认轨道)。 可以使用以下几种方案: 一…...
openEuler环境下GlusterFS分布式存储集群部署指南
1.环境准备: os:openEuler 22.03 主机名 IP地址 主机用途 Rocky8192.168.121.160客户端 open-Euler1192.168.121.150节点1,提供两块6G硬盘open-Euler4192.168.121.153节点2,提供两块6G硬盘open-Euler5192.168.121.154 …...
C++学习(七)(标准库+STL(iotstream公司,日期/时间,器皿,算法,迭代器,多线程))
C 标准模板库 (STL) C 标准模板库 (STL) 是头文件的集合,提供了多种数据结构、算法和函数,以简化您的 C 编码体验。STL 的主要目的是通过提供一套现成的有用工具来节省时间并提高效率。STL 最常用的功能可…...
c高级第五天
1> 在终端提示输入一个成绩,通过shell判断该成绩的等级 [90,100] : A [80, 90) : B [70, 80) : C [60, 70) : D [0, 60) : 不及格 #!/bin/bash# 提示用户输入成绩 read -p "请输入成绩(0-100):" score# 判断成…...
Windows上使用go-ios实现iOS17自动化
前言 在Windows上运行iOS的自动化,tidevice对于iOS17以上并不支持,原因是iOS 17 引入新通信协议 RemoteXPCQUIC,改变了 XCUITest 的启动方式。 一、go-ios的安装 1、安装命令:npm i go-ios 2、安装完成后输入命令which io…...
迷你世界脚本小地图接口:Mapmark
小地图接口:Mapmark 彼得兔 更新时间: 2023-10-25 10:33:48 具体函数名及描述如下: 序号 函数名 函数描述 1 newShape(...) 新增一个形状(线,矩形,圆形) 2 deleteShape(...) 删除一个形状 3 setShapeColor(...) 设置…...
TMS320F28P550SJ9学习笔记1:CCS导入工程以及测试连接单片机仿真器
学习记录如何用 CCS导入工程以及测试连接单片机仿真器 以下为我的CCS 以及驱动库C2000ware 的版本 CCS版本: Code Composer Studio 12.8.1 C2000ware :C2000Ware_5_04_00_00 目录 CCS导入工程: 创建工程: 添加工程: C…...
为什么要提倡尽早返回(Early Return)
为什么要提倡尽早返回(Early Return) 在编程中,“尽早返回”(Early Return)是一种常被提倡的编程方式,特别是在需要提升代码可读性、减少嵌套层级、以及快速处理异常情况时。本文将讨论尽早返回的优点、应…...
Gartner发布安全运营指标构建指南
如何为安全运营指标构建坚实的基础 安全运营经理需要报告威胁检测、调查和响应计划的有效性,但难以驾驭大量潜在的 SOC 指标。本研究提供了设计针对 SOC 的指标系统的示例和实践。 主要发现 需要清晰、一致的衡量标准来向董事会成员或服务提供商等更广泛的团队传达…...
vue3:初学 vue-router 路由配置
承上一篇:nodejs:express js-mdict 作为后端,vue 3 vite 作为前端,在线查询英汉词典 安装 cnpm install vue-router -S 现在讲一讲 vue3:vue-router 路由配置 cd \js\mydict-web\src mkdir router cd router 我还…...
数据结构入门篇——什么是数据结构。
一、引入 工具是一种什么东西呢?是一种转化媒介,我们需要熟食,我们要通过用火来将生肉烤熟。在这个过程中。我们要输入一个东西——生肉,通过工具——火的加工,从而得到我们的目的产物——熟肉。 将上面的例子和红字部…...
uniapp+vue3搭建项目
工具使用: Pinia Vue 3 官方推荐的状态管理库,比 Vuex 更轻量,支持模块化,结合 persistedstate 插件可以持久化存储数据。uView-plus 专为 UniApp 设计,支持 App、小程序、H5。UnoCSS 更轻量,比 TailwindCS…...
unity大坐标抖动处理测试
第二幅图就是相机坐标是0 6360094 0的地方看见的模型,可以看见这个球体已经烂了 那么这里可以知道的是坐标太大了导致的,那么把所有物体共同偏移一下,即可得到第一幅图的效果,圆润的sphere又回来了 浮点数的计算是需要位数的&…...
CASAIM与承光电子达成深度合作,三维扫描逆向建模技术助力车灯设计与制造向数字化与智能化转型
近日,CASAIM与广州承光电子科技有限公司正式达成深度合作,CASAIM将为承光电子提供全方位的技术支持,包括高精度三维扫描设备、逆向建模软件以及定制化的技术解决方案。双方将共同组建技术团队,针对车灯设计中的难点进行攻关&#…...
C++类与对象:银行管理系统项目实战开发LeetCode每日一题
[Bank-Management-System]银行管理系统项目 以下是一个可运行的C银行账户类(支持简单的存款/取款)。后面会继续完善该项目: #include <iostream> #include <string> using namespace std;class Account{public://构造函数Accou…...
领域驱动设计:事件溯源架构简介
概述 事件溯源架构通常由3种应用设计模式组成,分别是:事件驱动(Event Driven),事件溯源(Event Source)、CQRS(读写分离)。这三种应用设计模式常见于领域驱动设计(DDD)中,但它们本身是一种应用设计的思想,不仅仅局限于DDD,每一种模式都可以单独拿出来使用。 E…...
景联文科技:以专业标注赋能AI未来,驱动智能时代的精准跃迁
在人工智能技术重塑全球产业格局的今天,高质量训练数据已成为驱动算法进化的核心燃料。作为数据智能服务领域的领军者,景联文科技深耕数据标注行业多年,以全栈式数据解决方案为核心,构建起覆盖数据采集、清洗、标注、质检及算法调…...
LeetCode 热题 100----1.两数之和
LeetCode 热题 100----1.两数之和 题目描述 我的解法 语言:js 思路就是:用双重循环去找哪两个数字相加等于target,目前的时间复杂度为O(n2),之后右优化思路再更新。...
GIT 常用命令
/ 一、环境: ssh-keygen -t rsa -C "wangxiaoerqq.com.cn" 生成本地秘钥(邮箱换成自己的邮箱) 使用cat ~/.ssh/id_rsa.pub查看秘钥 git config --global user.name "wangxiaoer" git config --global wangxiaoerqq.…...