diff --git a/BetterGenshinImpact/Core/Recognition/OpenCv/ImageDifferenceDetector.cs b/BetterGenshinImpact/Core/Recognition/OpenCv/ImageDifferenceDetector.cs new file mode 100644 index 00000000..e88a8a20 --- /dev/null +++ b/BetterGenshinImpact/Core/Recognition/OpenCv/ImageDifferenceDetector.cs @@ -0,0 +1,95 @@ +using System; +using System.Diagnostics; +using OpenCvSharp; + +namespace BetterGenshinImpact.Core.Recognition.OpenCv; + +public class ImageDifferenceDetector +{ + /// + /// 找出四张灰度图中与其他三张差异最大的图片 + /// 投票机制:每张图片投票给与它差异最大的图片,如果某张图片获得3票则返回其索引 + /// + /// 包含4张Mat对象的数组 + /// 差异最大的图片索引(0-3),如果没有图片获得3票则返回-1 + public static int FindMostDifferentImage(Mat[] images) + { + if (images is not { Length: 4 }) + { + throw new ArgumentException("必须提供4张图片"); + } + + // 验证所有图片尺寸相同 + Size firstSize = images[0].Size(); + for (int i = 1; i < 4; i++) + { + if (images[i].Size() != firstSize) + { + throw new ArgumentException("所有图片必须具有相同的尺寸"); + } + } + + // 存储每张图片获得的投票数 + int[] votes = new int[4]; + + // 每张图片投票给与它差异最大的图片 + for (int i = 0; i < 4; i++) + { + int maxDiffImageIndex = -1; + double maxDifference = 0; + + // 找出与图片i差异最大的图片 + for (int j = 0; j < 4; j++) + { + if (i != j) + { + double difference = CalculateDifference(images[i], images[j]); + Debug.WriteLine($"{i} vs {j} 差异像素数量: {difference}"); + + if (difference > maxDifference) + { + maxDifference = difference; + maxDiffImageIndex = j; + } + } + } + + // 图片i投票给差异最大的图片 + if (maxDiffImageIndex != -1) + { + votes[maxDiffImageIndex]++; + Debug.WriteLine($"图片 {i} 投票给图片 {maxDiffImageIndex} (差异值: {maxDifference:F2})"); + } + } + + // 输出投票结果 + Debug.WriteLine("\n投票结果:"); + for (int i = 0; i < 4; i++) + { + Debug.WriteLine($"图片 {i}: {votes[i]} 票"); + } + + // 检查是否有图片获得3票 + for (int i = 0; i < 4; i++) + { + if (votes[i] >= 3) + { + return i; + } + } + + return -1; + } + + /// + /// 计算差值 + /// + private static double CalculateDifference(Mat img1, Mat img2) + { + using var diff = new Mat(); + // 计算差值的绝对值 + Cv2.Absdiff(img1, img2, diff); + // 统计非零像素点(即颜色不同的像素点) + return Cv2.CountNonZero(diff); + } +} \ No newline at end of file diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs index 7af84055..f608ec70 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/Model/CombatScenes.cs @@ -460,7 +460,7 @@ public class CombatScenes : IDisposable { // 多次识别失败则尝试刷新角色编号位置 // 应对草露问题 - if (context.TotalCheckFailedCount > 3) + if (context.TotalCheckFailedCount % 3 == 0 && context.TotalCheckFailedCount > 0 && context.TotalCheckFailedCount < 10) { // 失败多次,识别是否存在满足预期的编号框 if (PartyAvatarSideIndexHelper.CountIndexRect(imageRegion) == Avatars.Length) diff --git a/BetterGenshinImpact/GameTask/AutoFight/Model/PartyAvatarSideIndexHelper.cs b/BetterGenshinImpact/GameTask/AutoFight/Model/PartyAvatarSideIndexHelper.cs index 888b7216..411cff71 100644 --- a/BetterGenshinImpact/GameTask/AutoFight/Model/PartyAvatarSideIndexHelper.cs +++ b/BetterGenshinImpact/GameTask/AutoFight/Model/PartyAvatarSideIndexHelper.cs @@ -373,9 +373,12 @@ public class PartyAvatarSideIndexHelper var whiteCount = 0; var notWhiteRectNum = 0; var mat = imageRegion.CacheGreyMat; + Mat[] mats = new Mat[rectArray.Length]; for (int i = 0; i < rectArray.Length; i++) { - if (IsWhiteRect(mat, rectArray[i])) + using var indexMat = new Mat(mat, rectArray[i]); + mats[i] = indexMat; + if (IsWhiteRect(indexMat)) { whiteCount++; } @@ -391,16 +394,17 @@ public class PartyAvatarSideIndexHelper } else { - return -1; + // 使用更加靠谱的差值识别(-1是未识别) + return ImageDifferenceDetector.FindMostDifferentImage(mats); } } - public static bool IsWhiteRect(Mat greyMat, Rect rect) + public static bool IsWhiteRect(Mat indexMat) { - using var indexMat = new Mat(greyMat, rect); + var count1 = OpenCvCommonHelper.CountGrayMatColor(indexMat, 251, 255); // 白 var count2 = OpenCvCommonHelper.CountGrayMatColor(indexMat, 50, 54); // 黑色文字 - if ((count1 + count2) * 1.0 / (indexMat.Width * indexMat.Height) > 0.5) + if ((count1 + count2) * 1.0 / (indexMat.Width * indexMat.Height) > 0.4) { // Debug.WriteLine($"白色矩形占比{(count1 + count2) * 1.0 / (indexMat.Width * indexMat.Height)}"); return true; @@ -424,6 +428,11 @@ public class PartyAvatarSideIndexHelper } var curr = imageRegion.Find(ElementAssets.Instance.CurrentAvatarThreshold); // 当前出战角色标识 + if (curr.IsEmpty()) + { + return -1; + } + for (int i = 0; i < rectArray.Length; i++) { if (IsIntersecting(curr.Y, curr.Height, rectArray[i].Y, rectArray[i].Height)) diff --git a/Test/BetterGenshinImpact.Test/Cv/ImageDifferenceDetectorTest.cs b/Test/BetterGenshinImpact.Test/Cv/ImageDifferenceDetectorTest.cs new file mode 100644 index 00000000..f6ffab02 --- /dev/null +++ b/Test/BetterGenshinImpact.Test/Cv/ImageDifferenceDetectorTest.cs @@ -0,0 +1,19 @@ +using BetterGenshinImpact.Core.Recognition.OpenCv; +using OpenCvSharp; + +namespace BetterGenshinImpact.Test.Cv; + +public class ImageDifferenceDetectorTest +{ + public static void Test() + { + int i = ImageDifferenceDetector.FindMostDifferentImage([ + Cv2.ImRead(@"E:\1.png", ImreadModes.Grayscale), + Cv2.ImRead(@"E:\2.png", ImreadModes.Grayscale), + Cv2.ImRead(@"E:\3.png", ImreadModes.Grayscale), + Cv2.ImRead(@"E:\4.png", ImreadModes.Grayscale) + ]); + + Console.WriteLine($"差异最大的图片索引是: {i}"); + } +} \ No newline at end of file