namespace OcrDaemon; using System.Drawing; using System.Drawing.Imaging; using OpenCvSharp; using OpenCvSharp.Extensions; class TemplateMatchHandler { public object HandleTemplateMatch(Request req) { if (string.IsNullOrEmpty(req.Path)) return new ErrorResponse("match-template command requires 'path' (template image file)"); if (!System.IO.File.Exists(req.Path)) return new ErrorResponse($"Template file not found: {req.Path}"); using var screenshot = ScreenCapture.CaptureOrLoad(req.File, req.Region); using var screenMat = BitmapConverter.ToMat(screenshot); using var template = Cv2.ImRead(req.Path, ImreadModes.Color); if (template.Empty()) return new ErrorResponse($"Failed to load template image: {req.Path}"); // Convert screenshot from BGRA to BGR if needed using var screenBgr = new Mat(); if (screenMat.Channels() == 4) Cv2.CvtColor(screenMat, screenBgr, ColorConversionCodes.BGRA2BGR); else screenMat.CopyTo(screenBgr); // Template must fit within screenshot if (template.Rows > screenBgr.Rows || template.Cols > screenBgr.Cols) return new TemplateMatchResponse { Found = false }; using var result = new Mat(); Cv2.MatchTemplate(screenBgr, template, result, TemplateMatchModes.CCoeffNormed); Cv2.MinMaxLoc(result, out _, out double maxVal, out _, out OpenCvSharp.Point maxLoc); double threshold = req.Threshold > 0 ? req.Threshold / 100.0 : 0.7; if (maxVal < threshold) return new TemplateMatchResponse { Found = false, Confidence = maxVal }; // Calculate center coordinates — offset by region origin if provided int offsetX = req.Region?.X ?? 0; int offsetY = req.Region?.Y ?? 0; return new TemplateMatchResponse { Found = true, X = offsetX + maxLoc.X + template.Cols / 2, Y = offsetY + maxLoc.Y + template.Rows / 2, Width = template.Cols, Height = template.Rows, Confidence = maxVal, }; } }