// ============================================================================
// TemplateMatchNative.cs
// C++ DLL P/Invoke 封装层
// 提供对 TemplateMatchLib.dll 的托管调用接口
// ============================================================================
using System;
using System.Runtime.InteropServices;
namespace XP.ImageProcessing.Processors;
///
/// 匹配参数(与C++ TM_Params对应)
///
[StructLayout(LayoutKind.Sequential)]
public struct TM_Params
{
/// 匹配阈值 (0~1)
public double Score;
/// 角度容差 (度),0表示不旋转
public double ToleranceAngle;
/// 最大重叠比例 (0~1)
public double MaxOverlap;
/// 最大匹配数
public int MaxCount;
/// 金字塔最小面积,默认256
public int MinReduceArea;
/// 是否使用SIMD加速 (1=是, 0=否)
public int UseSIMD;
/// 是否亚像素估计 (1=是, 0=否)
public int UseSubPixel;
///
/// 创建默认参数
///
public static TM_Params Default => new TM_Params
{
Score = 0.75,
ToleranceAngle = 0,
MaxOverlap = 0.3,
MaxCount = 1,
MinReduceArea = 256,
UseSIMD = 1,
UseSubPixel = 0
};
}
///
/// 单个匹配结果(与C++ TM_Result对应)
///
[StructLayout(LayoutKind.Sequential)]
public struct TM_Result
{
/// 匹配中心X
public double CenterX;
/// 匹配中心Y
public double CenterY;
/// 匹配角度 (度)
public double Angle;
/// 匹配分数
public double Score;
/// 左上角X
public double LtX;
/// 左上角Y
public double LtY;
/// 右上角X
public double RtX;
/// 右上角Y
public double RtY;
/// 右下角X
public double RbX;
/// 右下角Y
public double RbY;
/// 左下角X
public double LbX;
/// 左下角Y
public double LbY;
}
///
/// TemplateMatchLib.dll P/Invoke 接口
///
public static class TemplateMatchNative
{
private const string DllName = "TemplateMatchLib.dll";
/// 创建匹配器实例
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr TM_Create();
/// 销毁匹配器实例
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern void TM_Destroy(IntPtr handle);
/// 从内存数据学习模板
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TM_LearnPattern(IntPtr handle,
IntPtr templateData, int width, int height, int step);
/// 从文件学习模板
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_LearnPatternFromFile(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
/// 执行模板匹配
[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);
/// 获取上次匹配耗时(毫秒)
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern double TM_GetLastMatchTime(IntPtr handle);
/// 获取模板信息
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern int TM_GetTemplateInfo(IntPtr handle,
out int width, out int height, out int pyramidLayers);
/// 保存训练好的模型到文件
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_SaveModel(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
/// 从文件加载已训练的模型
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int TM_LoadModel(IntPtr handle,
[MarshalAs(UnmanagedType.LPStr)] string filePath);
}
///
/// 模板匹配器托管封装(自动管理非托管资源)
///
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");
}
///
/// 从文件学习模板
///
public bool LearnPatternFromFile(string filePath)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_LearnPatternFromFile(_handle, filePath) == 0;
}
///
/// 从EmguCV Image学习模板
///
public bool LearnPattern(IntPtr data, int width, int height, int step)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_LearnPattern(_handle, data, width, height, step) == 0;
}
///
/// 执行匹配
///
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();
if (count < results.Length)
Array.Resize(ref results, count);
return results;
}
///
/// 获取上次匹配耗时(毫秒)
///
public double LastMatchTime
{
get
{
ThrowIfDisposed();
return TemplateMatchNative.TM_GetLastMatchTime(_handle);
}
}
///
/// 获取模板信息
///
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;
}
///
/// 保存训练好的模型到文件
///
/// 模型文件路径(建议扩展名 .tmmodel)
/// 是否成功
public bool SaveModel(string filePath)
{
ThrowIfDisposed();
return TemplateMatchNative.TM_SaveModel(_handle, filePath) == 0;
}
///
/// 从文件加载已训练的模型(跳过LearnPattern)
///
/// 模型文件路径
/// 是否成功
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();
}
}