using Microsoft.Extensions.Logging; using OpenCvSharp; using System; using System.Collections.Generic; namespace BetterGenshinImpact.Core.Recognition.OpenCv; /// /// 多目标模板匹配demo /// https://github.com/shimat/opencvsharp/issues/182 /// public class MatchTemplateHelper { private static readonly ILogger _logger = App.GetLogger(); /// /// 模板匹配 /// /// 原图像 /// 模板 /// 匹配方式 /// 遮罩 /// 阈值 /// 左上角的标点,由于(0,0)点作为未匹配的结果,所以不能做完全相同的模板匹配 public static Point MatchTemplate(Mat srcMat, Mat dstMat, TemplateMatchModes matchMode, Mat? maskMat = null, double threshold = 0.8) { try { using var result = new Mat(); Cv2.MatchTemplate(srcMat, dstMat, result, matchMode, maskMat!); if (matchMode is TemplateMatchModes.SqDiff or TemplateMatchModes.CCoeff or TemplateMatchModes.CCorr) { Cv2.Normalize(result, result, 0, 1, NormTypes.MinMax); } Cv2.MinMaxLoc(result, out var minValue, out var maxValue, out var minLoc, out var maxLoc); if (matchMode is TemplateMatchModes.SqDiff or TemplateMatchModes.SqDiffNormed) { if (minValue <= 1 - threshold) { return minLoc; } } else { if (maxValue >= threshold) { return maxLoc; } } return default; } catch (Exception ex) { _logger.LogError(ex.Message); _logger.LogDebug(ex, ex.Message); return default; } } /// /// 模板匹配多个结果 /// 不好用 /// /// /// /// /// /// /// [Obsolete] public static List MatchTemplateMulti(Mat srcMat, Mat dstMat, Mat? maskMat = null, double threshold = 0.8, int maxCount = 8) { var points = new List(); try { using var result = new Mat(); Cv2.MatchTemplate(srcMat, dstMat, result, TemplateMatchModes.CCoeffNormed, maskMat!); var mask = new Mat(result.Height, result.Width, MatType.CV_8UC1, Scalar.White); var maskSub = new Mat(result.Height, result.Width, MatType.CV_8UC1, Scalar.Black); while (true) { Cv2.MinMaxLoc(result, out _, out var maxValue, out _, out var maxLoc, mask); var maskRect = new Rect(maxLoc.X, maxLoc.Y, dstMat.Width, dstMat.Height); maskSub.Rectangle(maskRect, Scalar.White, -1); mask -= maskSub; if (maxValue >= threshold) points.Add(maxLoc); else break; } return points; } catch (Exception ex) { _logger.LogError(ex.Message); _logger.LogDebug(ex, ex.Message); return points; } } public static List MatchTemplateMulti(Mat srcMat, Mat dstMat, double threshold) { return MatchTemplateMulti(srcMat, dstMat, null, threshold); } /// /// 在一张图中查找多个模板 /// 查到一个遮盖一个的笨方法,效率很低,但是很准确 /// /// /// /// /// public static Dictionary> MatchMultiPicForOnePic(Mat srcMat, Dictionary imgSubDictionary, double threshold = 0.8) { var dictionary = new Dictionary>(); foreach (var kvp in imgSubDictionary) { var list = new List(); while (true) { var point = MatchTemplate(srcMat, kvp.Value, TemplateMatchModes.CCoeffNormed, null, threshold); if (point != new Point()) { // 把结果给遮掩掉,避免重复识别 Cv2.Rectangle(srcMat, point, new Point(point.X + kvp.Value.Width, point.Y + kvp.Value.Height), Scalar.Black, -1); list.Add(point); } else { break; } } dictionary.Add(kvp.Key, list); } return dictionary; } /// /// 在一张图中查找多个模板 /// 查到一个遮盖一个的笨方法,效率很低,但是很准确 /// /// /// /// /// public static List MatchMultiPicForOnePic(Mat srcMat, List imgSubList, double threshold = 0.8) { List list = []; foreach (var sub in imgSubList) while (true) { var point = MatchTemplate(srcMat, sub, TemplateMatchModes.CCoeffNormed, null, threshold); if (point != new Point()) { // 把结果给遮掩掉,避免重复识别 Cv2.Rectangle(srcMat, point, new Point(point.X + sub.Width, point.Y + sub.Height), Scalar.Black, -1); list.Add(new Rect(point.X, point.Y, sub.Width, sub.Height)); } else { break; } } return list; } /// /// 在一张图中查找一个个模板 /// /// /// /// /// /// public static List MatchOnePicForOnePic(Mat srcMat, Mat dstMat, Mat? maskMat = null, double threshold = 0.8) { List list = []; while (true) { var point = MatchTemplate(srcMat, dstMat, TemplateMatchModes.CCoeffNormed, maskMat, threshold); if (point != new Point()) { // 把结果给遮掩掉,避免重复识别 Cv2.Rectangle(srcMat, point, new Point(point.X + dstMat.Width, point.Y + dstMat.Height), Scalar.Black, -1); list.Add(new Rect(point.X, point.Y, dstMat.Width, dstMat.Height)); } else { break; } } return list; } /// /// 在一张图中查找一个个模板 /// /// /// /// /// /// /// /// public static List MatchOnePicForOnePic(Mat srcMat, Mat dstMat, TemplateMatchModes matchMode, Mat? maskMat, double threshold, int maxCount = -1) { List list = []; if (maxCount < 0) { maxCount = srcMat.Width * srcMat.Height / dstMat.Width / dstMat.Height; } for (int i = 0; i < maxCount; i++) { var point = MatchTemplate(srcMat, dstMat, matchMode, maskMat, threshold); if (point != new Point()) { // 把结果给遮掩掉,避免重复识别 Cv2.Rectangle(srcMat, point, new Point(point.X + dstMat.Width, point.Y + dstMat.Height), Scalar.Black, -1); list.Add(new Rect(point.X, point.Y, dstMat.Width, dstMat.Height)); } else { break; } } return list; } }