Files
辉鸭蛋 9e41808326 独立与分层地图支持 (#1503)
* 抽象基础类

* 修改定义

* 抽象出Feature2D相关能力

* 新增地图基类实现

* 临时提交

* 迁移坐标计算

* 加载分层特征数据

* 新增独立地图 层岩巨渊,渊下宫,旧日之海

* 支持不切分特征点匹配

* 添加远古圣山,修改地图参数

* 提瓦特大陆的大地图匹配

* 提瓦特大陆地图大地图位置获取使用256级别的地图

* 替换大地图匹配类 BigMap.cs

* 替换小地图匹配类 EntireMap

* 修改tp的入参方式,删除无用类

* 兼容新提交的内容

* 修复类方法覆盖不生效的问题

* 修复定位问题,迁移部分 MapCoordinate 的代码。MapCoordinate 标记为废弃

* 更多坐标方法的迁移

* 修复不正确的坐标转换

* 是用正确的特征匹配

* 体积较小的地图动态生成特征

* 路径追踪窗体支持多地图

* 传送时切换独立地图地区

* 更新传送点信息

* 修改独立地图相关命名,使用 Scene(场景) 命名,和原神内部命名保持一致

* 录制支持多独立地图

* 修复地区切换失败的问题
2025-05-03 21:59:37 +08:00

245 lines
8.8 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using OpenCvSharp;
namespace BetterGenshinImpact.Test.Simple.AllMap;
public class MapPuzzleOther
{
public static List<string> PicWhiteHashList = new List<string>
{
};
public static MD5 Md5Service = MD5.Create();
public static void PutAll()
{
// 保存1024
var img1024 = Put(1024);
Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\有用的素材\5.2\DeepSea_1024_1x2.png", img1024);
}
public static Mat Put(int block = 1024)
{
string folderPath = @"E:\HuiTask\更好的原神\地图匹配\UI_Map_最新"; // 图片文件夹路径
// 层岩巨渊 UI_MapBack_TheChasm_
// 渊下宫 UI_MapBack_AbyssalPalace_
// UI_MapBack_DeepSea_
// UI_Map_Volcano_
string pattern = @"UI_MapBack_DeepSea_([-+]?\d+)_([-+]?\d+)(.*)";
var images = Directory.GetFiles(folderPath, "*.png", SearchOption.TopDirectoryOnly); // 获取所有图片文件路径
// 解析图片位置信息并存储到字典中
var imageLocations = new Dictionary<(int row, int col), ImgInfo>();
foreach (var imagePath in images)
{
// 远古圣山 特殊逻辑
// if (imagePath.Contains("UI_Map_Volcano_800_01.png") || imagePath.Contains("UI_Map_Volcano_01.png"))
// {
// continue;
// }
// if (!imagePath.Contains("UI_Map_Volcano_0_0.png")
// &&!imagePath.Contains("UI_Map_Volcano_0_-1.png")
// &&!imagePath.Contains("UI_Map_Volcano_-1_0.png")
// &&!imagePath.Contains("UI_Map_Volcano_-1_-1.png"))
// {
// continue;
// }
// 层岩巨渊 特殊逻辑
// if (!imagePath.Contains("UI_MapBack_TheChasm_0_0.png")
// &&!imagePath.Contains("UI_MapBack_TheChasm_0_1.png")
// &&!imagePath.Contains("UI_MapBack_TheChasm_1_0.png")
// &&!imagePath.Contains("UI_MapBack_TheChasm_1_1.png"))
// {
// continue;
// }
// 旧日之海
if (!imagePath.Contains("UI_MapBack_DeepSea_1_4.png")
&&!imagePath.Contains("UI_MapBack_DeepSea_1_3.png"))
{
continue;
}
// 获取文件大小
var fileInfo = new FileInfo(imagePath);
// 解析图片名称中的行列信息
var name = Path.GetFileNameWithoutExtension(imagePath);
var match = Regex.Match(name, pattern);
int row, col;
if (match.Success)
{
// Debug.WriteLine($"已匹配 ({match.Groups[1].Value}, {match.Groups[2].Value}) {name}");
row = int.Parse(match.Groups[1].Value);
col = int.Parse(match.Groups[2].Value);
}
else
{
// Debug.WriteLine($"未匹配 {name}");
continue;
}
// 排除指定行列的图片
// if ((row, col) == (4, 6) || (row, col) == (5, 6) || (row, col) == (5, 5) || (row, col) == (5, 2) || (row, col) == (5, 1) || (row, col) == (4, 1))
// {
// continue;
// }
// 读取图片并计算hash值
Mat img = Cv2.ImRead(imagePath);
var hashBytes = Md5Service.ComputeHash(File.ReadAllBytes(imagePath));
var hash = BitConverter.ToString(hashBytes).Replace("-", "").ToUpperInvariant();
if (img.Width < 8)
{
Debug.WriteLine($"太小的不要 ({row}, {col}) {img.Width} {img.Height} {name}");
continue;
}
// 如果当前位置已经有图片了,保留尺寸较大的图片
if (imageLocations.ContainsKey((row, col)))
{
// 如果当前位置的图片已经被hash锁定跳过
if (imageLocations[(row, col)].Locked)
{
Debug.WriteLine($"已锁定 ({row}, {col}) {name}");
continue;
}
if (img.Width > imageLocations[(row, col)].Img.Width || fileInfo.Length > imageLocations[(row, col)].FileLength || PicWhiteHashList.Contains(hash))
{
imageLocations[(row, col)] = new ImgInfo(img, name, fileInfo.Length, PicWhiteHashList.Contains(hash));
}
else
{
Debug.WriteLine($"重复 ({row}, {col}) {img.Width} {img.Height} {name}");
}
}
else
{
imageLocations[(row, col)] = new ImgInfo(img, name, fileInfo.Length, PicWhiteHashList.Contains(hash));
}
}
int minRow = imageLocations.Keys.Min(key => key.row);
int minCol = imageLocations.Keys.Min(key => key.col);
// 确定大图的行数和列数
int maxRow = imageLocations.Keys.Max(key => key.row);
int maxCol = imageLocations.Keys.Max(key => key.col);
// 计算大图的总宽度和高度
var lenCol = maxCol - minCol;
var lenRow = maxRow - minRow;
Debug.WriteLine($"列数X: {lenCol + 1}, 行数Y: {lenRow + 1}");
int totalWidth = (lenCol + 1) * block;
int totalHeight = (lenRow + 1) * block;
// 创建空白大图
Mat largeImage = new Mat(totalHeight, totalWidth, MatType.CV_8UC3, new Scalar(0, 0, 0));
// 拼接图片
int[,] arr = new int[lenRow + 1, lenCol + 1];
foreach (var location in imageLocations)
{
int row = location.Key.row - minRow;
int col = location.Key.col - minCol;
Mat img = location.Value.Img;
arr[row, col] = 1;
// 计算当前图片在大图中的左上角位置
int x = (lenCol - col) * block; // 顺序倒过来了,屮
int y = (lenRow - row) * block; // 顺序倒过来了,屮
// 将图片粘贴到大图上
if (img.Width != block || img.Height != block)
{
img = img.Resize(new Size(block, block), 0, 0, InterpolationFlags.Nearest);
}
// 添加位置标识
// img.PutText($"{location.Key.row} , {location.Key.col}", new Point(50, 50), HersheyFonts.HersheyComplex, 2, Scalar.Red, 2, LineTypes.Link8);
img.CopyTo(new Mat(largeImage, new Rect(x, y, img.Width, img.Height)));
}
// 康康二维数组的拼接结果
for (int i = 0; i < arr.GetLength(0); i++)
{
for (int j = 0; j < arr.GetLength(1); j++)
{
Debug.Write(arr[i, j] + " ");
}
Debug.WriteLine("");
}
// 地图图片块
// SaveImagesAs1024X1024(arr, imageLocations, @"E:\HuiTask\更好的原神\地图匹配\有用的素材\5.0\地图块", minRow, minCol);
// Cv2.ImWrite(@"E:\HuiTask\更好的原神\地图匹配\combined_image_small.png", largeImage.Resize(new Size(1400, 1300), 0, 0, InterpolationFlags.Cubic));
// 释放资源
return largeImage;
}
public static void SaveImagesAs1024X1024(int[,] arr, Dictionary<(int row, int col), ImgInfo> imageLocations, string outputFolder, int minRow, int minCol)
{
if (!Directory.Exists(outputFolder))
{
Directory.CreateDirectory(outputFolder);
}
for (int row = 0; row < arr.GetLength(0); row++)
{
for (int col = 0; col < arr.GetLength(1); col++)
{
if (arr[row, col] == 1)
{
var key = (row + minRow, col + minCol);
if (imageLocations.TryGetValue(key, out var imgInfo))
{
var img = imgInfo.Img;
if (img.Width != 1204 || img.Height != 1204)
{
img = imgInfo.Img.Resize(new Size(1024, 1024), 0, 0, InterpolationFlags.Nearest);
}
var outputPath = Path.Combine(outputFolder, $"{row}_{col}.png");
Cv2.ImWrite(outputPath, img);
img.Dispose();
}
}
}
}
}
public class ImgInfo
{
public Mat Img { get; set; }
public string Name { get; set; }
/// <summary>
/// 文件大小
/// </summary>
public long FileLength { get; set; }
public bool Locked { get; set; }
public ImgInfo(Mat mat, string name, long fileLength, bool locked = false)
{
Img = mat;
Name = name;
FileLength = fileLength;
Locked = locked;
}
}
}