删除简单的opencv模板匹配算子,改为使用更高级一点的可旋转匹配算子(C++)

This commit is contained in:
李伟
2026-05-13 14:02:34 +08:00
parent b9106acdf0
commit aedbef5ecc
26 changed files with 1135 additions and 2429 deletions
@@ -0,0 +1,249 @@
// ============================================================================
// TemplateMatchNative.cs
// C++ DLL P/Invoke 封装层
// 提供对 TemplateMatchLib.dll 的托管调用接口
// ============================================================================
using System;
using System.Runtime.InteropServices;
namespace XP.ImageProcessing.Processors;
/// <summary>
/// 匹配参数(与C++ TM_Params对应)
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct TM_Params
{
/// <summary>匹配阈值 (0~1)</summary>
public double Score;
/// <summary>角度容差 (度)0表示不旋转</summary>
public double ToleranceAngle;
/// <summary>最大重叠比例 (0~1)</summary>
public double MaxOverlap;
/// <summary>最大匹配数</summary>
public int MaxCount;
/// <summary>金字塔最小面积,默认256</summary>
public int MinReduceArea;
/// <summary>是否使用SIMD加速 (1=是, 0=否)</summary>
public int UseSIMD;
/// <summary>是否亚像素估计 (1=是, 0=否)</summary>
public int UseSubPixel;
/// <summary>
/// 创建默认参数
/// </summary>
public static TM_Params Default => new TM_Params
{
Score = 0.75,
ToleranceAngle = 0,
MaxOverlap = 0.3,
MaxCount = 1,
MinReduceArea = 256,
UseSIMD = 1,
UseSubPixel = 0
};
}
/// <summary>
/// 单个匹配结果(与C++ TM_Result对应)
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct TM_Result
{
/// <summary>匹配中心X</summary>
public double CenterX;
/// <summary>匹配中心Y</summary>
public double CenterY;
/// <summary>匹配角度 (度)</summary>
public double Angle;
/// <summary>匹配分数</summary>
public double Score;
/// <summary>左上角X</summary>
public double LtX;
/// <summary>左上角Y</summary>
public double LtY;
/// <summary>右上角X</summary>
public double RtX;
/// <summary>右上角Y</summary>
public double RtY;
/// <summary>右下角X</summary>
public double RbX;
/// <summary>右下角Y</summary>
public double RbY;
/// <summary>左下角X</summary>
public double LbX;
/// <summary>左下角Y</summary>
public double LbY;
}
/// <summary>
/// TemplateMatchLib.dll P/Invoke 接口
/// </summary>
public static class TemplateMatchNative
{
private const string DllName = "TemplateMatchLib.dll";
/// <summary>创建匹配器实例</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TM_Create();
/// <summary>销毁匹配器实例</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TM_Destroy(IntPtr handle);
/// <summary>从内存数据学习模板</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TM_LearnPattern(IntPtr handle,
IntPtr templateData, int width, int height, int step);
/// <summary>从文件学习模板</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_LearnPatternFromFile(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
/// <summary>执行模板匹配</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TM_Match(IntPtr handle,
IntPtr srcData, int srcWidth, int srcHeight, int srcStep,
ref TM_Params param,
[Out] TM_Result[] results, int maxResults);
/// <summary>获取上次匹配耗时(毫秒)</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern double TM_GetLastMatchTime(IntPtr handle);
/// <summary>获取模板信息</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TM_GetTemplateInfo(IntPtr handle,
out int width, out int height, out int pyramidLayers);
/// <summary>保存训练好的模型到文件</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_SaveModel(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
/// <summary>从文件加载已训练的模型</summary>
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_LoadModel(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
}
/// <summary>
/// 模板匹配器托管封装(自动管理非托管资源)
/// </summary>
public sealed class TemplateMatcherHandle : IDisposable
{
private IntPtr _handle;
private bool _disposed;
public TemplateMatcherHandle()
{
_handle = TemplateMatchNative.TM_Create();
if (_handle == IntPtr.Zero)
throw new InvalidOperationException("Failed to create TemplateMatcher instance");
}
/// <summary>
/// 从文件学习模板
/// </summary>
public bool LearnPatternFromFile(string filePath)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_LearnPatternFromFile(_handle, filePath) == 0;
}
/// <summary>
/// 从EmguCV Image学习模板
/// </summary>
public bool LearnPattern(IntPtr data, int width, int height, int step)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_LearnPattern(_handle, data, width, height, step) == 0;
}
/// <summary>
/// 执行匹配
/// </summary>
public TM_Result[] Match(IntPtr srcData, int srcWidth, int srcHeight, int srcStep, TM_Params param)
{
ThrowIfDisposed();
var results = new TM_Result[param.MaxCount];
int count = TemplateMatchNative.TM_Match(_handle, srcData, srcWidth, srcHeight, srcStep,
ref param, results, param.MaxCount);
if (count <= 0)
return Array.Empty<TM_Result>();
if (count < results.Length)
Array.Resize(ref results, count);
return results;
}
/// <summary>
/// 获取上次匹配耗时(毫秒)
/// </summary>
public double LastMatchTime
{
get
{
ThrowIfDisposed();
return TemplateMatchNative.TM_GetLastMatchTime(_handle);
}
}
/// <summary>
/// 获取模板信息
/// </summary>
public bool GetTemplateInfo(out int width, out int height, out int pyramidLayers)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_GetTemplateInfo(_handle, out width, out height, out pyramidLayers) == 0;
}
/// <summary>
/// 保存训练好的模型到文件
/// </summary>
/// <param name="filePath">模型文件路径(建议扩展名 .tmmodel</param>
/// <returns>是否成功</returns>
public bool SaveModel(string filePath)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_SaveModel(_handle, filePath) == 0;
}
/// <summary>
/// 从文件加载已训练的模型(跳过LearnPattern
/// </summary>
/// <param name="filePath">模型文件路径</param>
/// <returns>是否成功</returns>
public bool LoadModel(string filePath)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_LoadModel(_handle, filePath) == 0;
}
private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(nameof(TemplateMatcherHandle));
}
public void Dispose()
{
if (!_disposed)
{
if (_handle != IntPtr.Zero)
{
TemplateMatchNative.TM_Destroy(_handle);
_handle = IntPtr.Zero;
}
_disposed = true;
}
}
~TemplateMatcherHandle()
{
Dispose();
}
}