76 lines
2.9 KiB
C#
76 lines
2.9 KiB
C#
using OpenCvSharp;
|
|
using System.Drawing.Imaging;
|
|
|
|
namespace anjianjingling.Vision
|
|
{
|
|
/// <summary>
|
|
/// 找图:OpenCV 模板匹配(TM_CCOEFF_NORMED),支持相似度阈值。
|
|
/// </summary>
|
|
public sealed class ImageFinder : IDisposable
|
|
{
|
|
private readonly Dictionary<string, Mat> _templateCache = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private static Mat BitmapToMat(Bitmap bmp)
|
|
{
|
|
using var ms = new MemoryStream();
|
|
bmp.Save(ms, ImageFormat.Png);
|
|
return Cv2.ImDecode(ms.ToArray(), ImreadModes.Color);
|
|
}
|
|
|
|
private Mat LoadTemplate(string path)
|
|
{
|
|
if (_templateCache.TryGetValue(path, out var cached)) return cached;
|
|
if (!File.Exists(path))
|
|
throw new FileNotFoundException($"模板图片不存在:{path}");
|
|
var mat = Cv2.ImRead(path, ImreadModes.Color);
|
|
if (mat.Empty())
|
|
throw new InvalidDataException($"模板图片无法读取:{path}");
|
|
_templateCache[path] = mat;
|
|
return mat;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在 haystack 位图中查找模板。
|
|
/// </summary>
|
|
/// <param name="haystack">被查找的画面(通常是区域截图)</param>
|
|
/// <param name="templatePath">模板图路径</param>
|
|
/// <param name="threshold">相似度阈值 0~1</param>
|
|
/// <param name="offsetX">结果坐标要加上的屏幕偏移 X</param>
|
|
/// <param name="offsetY">结果坐标要加上的屏幕偏移 Y</param>
|
|
/// <returns>匹配中心屏幕坐标与相似度;未达阈值返回 (-1,-1,score)</returns>
|
|
public (int X, int Y, double Score) Find(Bitmap haystack, string templatePath,
|
|
double threshold, int offsetX = 0, int offsetY = 0)
|
|
{
|
|
using var hay = BitmapToMat(haystack);
|
|
using var tpl = LoadTemplate(templatePath);
|
|
|
|
if (tpl.Width > hay.Width || tpl.Height > hay.Height)
|
|
throw new ArgumentException($"模板图({tpl.Width}x{tpl.Height})大于查找区域({hay.Width}x{hay.Height})");
|
|
|
|
using var result = new Mat();
|
|
Cv2.MatchTemplate(hay, tpl, result, TemplateMatchModes.CCoeffNormed);
|
|
Cv2.MinMaxLoc(result, out _, out double maxVal, out _, out OpenCvSharp.Point maxLoc);
|
|
|
|
if (maxVal >= threshold)
|
|
{
|
|
int cx = offsetX + maxLoc.X + tpl.Width / 2;
|
|
int cy = offsetY + maxLoc.Y + tpl.Height / 2;
|
|
return (cx, cy, maxVal);
|
|
}
|
|
return (-1, -1, maxVal);
|
|
}
|
|
|
|
/// <summary>清除模板缓存(模板文件更新后调用)。</summary>
|
|
public void ClearCache()
|
|
{
|
|
foreach (var m in _templateCache.Values) m.Dispose();
|
|
_templateCache.Clear();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ClearCache();
|
|
}
|
|
}
|
|
}
|