mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-03-27 09:49:49 +08:00
* 小地图预处理和视角识别算法优化 * 模板匹配的相关类,包括快速带遮罩的SqDiff模板匹配,模板匹配归一化类,简易亚像素模板匹配实现,小地图匹配相关配置,和小地图匹配上下文。 * 实现小地图的分层地图模板匹配,修改 SceneBaseMap 的 GetMiniMapPosition 为 virtual 以便继承覆盖。 * 优化视角识别算法, 消除图标对视角识别影响, 修正上次提交里HImg的范围错误(BGR2HLS_FULL模式下H的范围在0~255), 启用新的视角识别算法。 * 模板匹配分层地图小修改 * 启用模板匹配的分层地图
130 lines
5.5 KiB
C#
130 lines
5.5 KiB
C#
using System.Linq;
|
|
using OpenCvSharp;
|
|
using System;
|
|
|
|
namespace BetterGenshinImpact.GameTask.Common.Map.MiniMap;
|
|
|
|
using static MiniMapPreprocessorUtils;
|
|
|
|
public class CameraOrientationCalculator : IDisposable
|
|
{
|
|
private const int TplOutRad = 78;
|
|
private const int TplInnRad = 19;
|
|
private const int RLength = 60;
|
|
private const int ThetaLength = 360;
|
|
private const int FLength = 256;
|
|
private const int Scale = 2;
|
|
private const int PeakWidth = ThetaLength / 4 * Scale + 1;
|
|
private readonly Mat _rotationRemapDataX = new Mat();
|
|
private readonly Mat _rotationRemapDataY = new Mat();
|
|
private readonly Mat _alphaMask1Remap;
|
|
private readonly Mat _alphaMask2Remap;
|
|
|
|
public CameraOrientationCalculator()
|
|
{
|
|
float[] rArray = LinearSpaced(TplInnRad, TplOutRad, RLength);
|
|
float[] thetaArray = LinearSpaced(0, 360, ThetaLength, false);
|
|
float[] alphaParams1 = [18.632f, 20.157f, 24.093f, 34.617f, 38.566f, 41.94f, 47.654f, 51.087f, 58.561f, 63.925f, 67.759f, 71.77f, 75.214f];
|
|
|
|
using (var r = Mat.FromPixelData(1, RLength, MatType.CV_32F, rArray))
|
|
using (var theta = Mat.FromPixelData(ThetaLength, 1, MatType.CV_32F, thetaArray))
|
|
using (var rMat = r.Repeat(ThetaLength, 1))
|
|
using (var thetaMat = theta.Repeat(1, RLength))
|
|
{
|
|
Cv2.PolarToCart(rMat, thetaMat, _rotationRemapDataX, _rotationRemapDataY, true);
|
|
}
|
|
Cv2.Add(_rotationRemapDataX, Size / 2, _rotationRemapDataX);
|
|
Cv2.Add(_rotationRemapDataY, Size / 2, _rotationRemapDataY);
|
|
|
|
using (var alphaMask2Row = Mat.FromPixelData(1, RLength, MatType.CV_32F, rArray.Select(v => (float)(137 + 1.43 * v)).ToArray()))
|
|
{
|
|
_alphaMask2Remap = alphaMask2Row.Repeat(ThetaLength, 1);
|
|
}
|
|
|
|
using (var alphaMask1Row = Mat.FromPixelData(1, RLength, MatType.CV_32F, rArray.Select(v =>
|
|
{
|
|
var index = Array.BinarySearch(alphaParams1, v);
|
|
return (float)(229 + (index < 0 ? ~index : index));
|
|
}).ToArray()))
|
|
{
|
|
_alphaMask1Remap = alphaMask1Row.Repeat(ThetaLength, 1);
|
|
}
|
|
}
|
|
|
|
public (float, float) PredictRotation(Mat src, Mat mask)
|
|
{
|
|
using var remapSrc = new Mat();
|
|
using var remapMask = new Mat();
|
|
using var hImg = new Mat();
|
|
using var faImg = new Mat();
|
|
using var fbImg = new Mat();
|
|
using var histA = new Mat();
|
|
using var histB = new Mat();
|
|
using var result = Mat.Zeros(1, ThetaLength, MatType.CV_32FC1).ToMat();
|
|
using var resultShift = new Mat();
|
|
Cv2.Remap(src, remapSrc, _rotationRemapDataX, _rotationRemapDataY, InterpolationFlags.Linear);
|
|
Cv2.Remap(mask, remapMask, _rotationRemapDataX, _rotationRemapDataY, InterpolationFlags.Nearest);
|
|
BgrToHue(remapSrc, hImg, faImg);
|
|
using var temp = faImg.Clone();
|
|
ApplyMask(faImg, _alphaMask1Remap, new Scalar(0.0f));
|
|
temp.CopyTo(faImg, remapMask);
|
|
ApplyMask(temp, _alphaMask2Remap, new Scalar(255.0f));
|
|
temp.CopyTo(fbImg);
|
|
ApplyMask(fbImg, _alphaMask1Remap, new Scalar(0.0f));
|
|
temp.CopyTo(fbImg, remapMask);
|
|
int[] channels = [0, 1];
|
|
int[] histSize = [FLength, FLength];
|
|
Rangef[] ranges = [new(0, 256), new(0, 256)];
|
|
Cv2.CalcHist([hImg, faImg], channels, null, histA, 2, histSize, ranges);
|
|
Cv2.CalcHist([hImg, fbImg], channels, null, histB, 2, histSize, ranges);
|
|
unsafe
|
|
{
|
|
var hImgPtr = (float*)hImg.Data;
|
|
var faImgPtr = (float*)faImg.Data;
|
|
var fbImgPtr = (float*)fbImg.Data;
|
|
var histAPtr = (float*)histA.Data;
|
|
var histBPtr = (float*)histB.Data;
|
|
var resultPtr = (float*)result.Data;
|
|
var totalElements = ThetaLength * RLength;
|
|
for (var i = 0; i < totalElements; i++)
|
|
{
|
|
float h = hImgPtr[i];
|
|
float fa = faImgPtr[i];
|
|
float fb = fbImgPtr[i];
|
|
if (h is < 0 or >= 256 || fa < 0 || fb >= 256) continue;
|
|
if (fa >= 256)
|
|
{
|
|
resultPtr[ i / RLength] += 255.0f;
|
|
}
|
|
else if (fb < 0)
|
|
{
|
|
resultPtr[i / RLength] += -255.0f;
|
|
}
|
|
else
|
|
{
|
|
float ha = histAPtr[(int)(h / 256 * FLength) * FLength + (int)(fa / 256 * FLength)];
|
|
float hb = histBPtr[(int)(h / 256 * FLength) * FLength + (int)(fb / 256 * FLength)];
|
|
resultPtr[i / RLength] += (ha > hb) ? 0.0f :
|
|
(Math.Abs(ha - hb) < 0.0001) ? 100.0f :
|
|
255.0f;
|
|
}
|
|
}
|
|
}
|
|
Cv2.Resize(result, result, new Size(result.Width * Scale, 1), 0, 0, InterpolationFlags.Cubic);
|
|
RightShiftCv(result, resultShift, PeakWidth);
|
|
var peakRegionSum = Cv2.Sum(resultShift[0, 1, 0, PeakWidth]).Val0;
|
|
Cv2.Subtract(result, resultShift, resultShift);
|
|
Cv2.Integral(resultShift, result);
|
|
Cv2.MinMaxLoc(result, out _, out var maxVal, out _, out var maxLoc);
|
|
var degree = (float)(maxLoc.X - 1) / ThetaLength * 360 / Scale - 45;
|
|
var rotationConfidence = (float)(maxVal + peakRegionSum) / (PeakWidth * RLength * 255);
|
|
return (degree, rotationConfidence);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_rotationRemapDataX.Dispose();
|
|
_rotationRemapDataY.Dispose();
|
|
_alphaMask2Remap.Dispose();
|
|
}
|
|
} |