删除简单的opencv模板匹配算子,改为使用更高级一点的可旋转匹配算子(C++)
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user