The current hand games are basically repetitive operations. One action has to wait for a long time. After the end, another action is continued. It is very troublesome, so I moved my mind to write a game assistant.
The auxiliary itself is not very difficult, it is through continuous screenshots, and then from this screenshot to find a pre cut can represent the corresponding action button or trigger conditions of the small map.
Find the upper-left coordinates of the sub-area, and then call the mouse or keyboard through the windows API to do the operation.
The most difficult one is to find maps, because it is necessary to find maps accurately, and it is better to adapt to different resolutions, so on the basis of template matching, there are SIFT and SURF feature point mapping methods.
In the process of writing, most of the information is found in C++ or python. There are few native C# implementations, so I translated them directly (with a slight change).
SIFT algorithm
public static Bitmap MatchPicBySift(Bitmap imgSrc, Bitmap imgSub) { using (Mat matSrc = imgSrc.ToMat()) using (Mat matTo = imgSub.ToMat()) using (Mat matSrcRet = new Mat()) using (Mat matToRet = new Mat()) { KeyPoint[] keyPointsSrc, keyPointsTo; using (var sift = OpenCvSharp.XFeatures2D.SIFT.Create()) { sift.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet); sift.DetectAndCompute(matTo, null, out keyPointsTo, matToRet); } using (var bfMatcher = new OpenCvSharp.BFMatcher()) { var matches = bfMatcher.KnnMatch(matSrcRet, matToRet, k: 2); var pointsSrc = new List<Point2f>(); var pointsDst = new List<Point2f>(); var goodMatches = new List<DMatch>(); foreach (DMatch[] items in matches.Where(x => x.Length > 1)) { if (items[0].Distance < 0.5 * items[1].Distance) { pointsSrc.Add(keyPointsSrc[items[0].QueryIdx].Pt); pointsDst.Add(keyPointsTo[items[0].TrainIdx].Pt); goodMatches.Add(items[0]); Console.WriteLine($"{keyPointsSrc[items[0].QueryIdx].Pt.X}, {keyPointsSrc[items[0].QueryIdx].Pt.Y}"); } } var outMat = new Mat(); // algorithm RANSAC Filter the matched results var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d); var pDst = pointsDst.ConvertAll(Point2fToPoint2d); var outMask = new Mat(); // If the original matching result is null, Skip the filtering step if (pSrc.Count > 0 && pDst.Count > 0) Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask); // If passed RANSAC After processing, the matching points are more than 10.,Only filters are used. Otherwise, use the original matching point result(When the matching point is too small, it passes through RANSAC After treatment,It is possible to get the result of 0 matching points.). if (outMask.Rows > 10) { byte[] maskBytes = new byte[outMask.Rows * outMask.Cols]; outMask.GetArray(0, 0, maskBytes); Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints); } else Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints); return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat); } } }
SURF algorithm
public static Bitmap MatchPicBySurf(Bitmap imgSrc, Bitmap imgSub, double threshold = 400) { using (Mat matSrc = imgSrc.ToMat()) using (Mat matTo = imgSub.ToMat()) using (Mat matSrcRet = new Mat()) using (Mat matToRet = new Mat()) { KeyPoint[] keyPointsSrc, keyPointsTo; using (var surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold,4,3,true,true)) { surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet); surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet); } using (var flnMatcher = new OpenCvSharp.FlannBasedMatcher()) { var matches = flnMatcher.Match(matSrcRet, matToRet); //Finding the Minimum and Maximum Distance double minDistance = 1000;//Backward approximation double maxDistance = 0; for (int i = 0; i < matSrcRet.Rows; i++) { double distance = matches[i].Distance; if (distance > maxDistance) { maxDistance = distance; } if (distance < minDistance) { minDistance = distance; } } Console.WriteLine($"max distance : {maxDistance}"); Console.WriteLine($"min distance : {minDistance}"); var pointsSrc = new List<Point2f>(); var pointsDst = new List<Point2f>(); //Screening better matching points var goodMatches = new List<DMatch>(); for (int i = 0; i < matSrcRet.Rows; i++) { double distance = matches[i].Distance; if (distance < Math.Max(minDistance * 2, 0.02)) { pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt); pointsDst.Add(keyPointsTo[matches[i].TrainIdx].Pt); //Compression of new ones with distances less than ranges DMatch goodMatches.Add(matches[i]); } } var outMat = new Mat(); // algorithm RANSAC Filter the matched results var pSrc = pointsSrc.ConvertAll(Point2fToPoint2d); var pDst = pointsDst.ConvertAll(Point2fToPoint2d); var outMask = new Mat(); // If the original matching result is null, Skip the filtering step if (pSrc.Count > 0 && pDst.Count > 0) Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask); // If passed RANSAC After processing, the matching points are more than 10.,Only filters are used. Otherwise, use the original matching point result(When the matching point is too small, it passes through RANSAC After treatment,It's possible to get the result of 0 matching points.). if (outMask.Rows > 10) { byte[] maskBytes = new byte[outMask.Rows * outMask.Cols]; outMask.GetArray(0, 0, maskBytes); Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, matchesMask: maskBytes, flags: DrawMatchesFlags.NotDrawSinglePoints); } else Cv2.DrawMatches(matSrc, keyPointsSrc, matTo, keyPointsTo, goodMatches, outMat, flags: DrawMatchesFlags.NotDrawSinglePoints); return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat); } } }
template matching
public static System.Drawing.Point FindPicFromImage(Bitmap imgSrc, Bitmap imgSub, double threshold = 0.9) { OpenCvSharp.Mat srcMat = null; OpenCvSharp.Mat dstMat = null; OpenCvSharp.OutputArray outArray = null; try { srcMat = imgSrc.ToMat(); dstMat = imgSub.ToMat(); outArray = OpenCvSharp.OutputArray.Create(srcMat); OpenCvSharp.Cv2.MatchTemplate(srcMat, dstMat, outArray, Common.templateMatchModes); double minValue, maxValue; OpenCvSharp.Point location, point; OpenCvSharp.Cv2.MinMaxLoc(OpenCvSharp.InputArray.Create(outArray.GetMat()), out minValue, out maxValue, out location, out point); Console.WriteLine(maxValue); if (maxValue >= threshold) return new System.Drawing.Point(point.X, point.Y); return System.Drawing.Point.Empty; } catch(Exception ex) { return System.Drawing.Point.Empty; } finally { if (srcMat != null) srcMat.Dispose(); if (dstMat != null) dstMat.Dispose(); if (outArray != null) outArray.Dispose(); } }