mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-25 22:29:47 +08:00
* 小地图预处理和视角识别算法优化 * 模板匹配的相关类,包括快速带遮罩的SqDiff模板匹配,模板匹配归一化类,简易亚像素模板匹配实现,小地图匹配相关配置,和小地图匹配上下文。 * 实现小地图的分层地图模板匹配,修改 SceneBaseMap 的 GetMiniMapPosition 为 virtual 以便继承覆盖。
107 lines
4.6 KiB
C#
107 lines
4.6 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 = 180;
|
|
private const int FLength = 256;
|
|
private readonly Mat _rotationRemapDataX = new Mat();
|
|
private readonly Mat _rotationRemapDataY = new Mat();
|
|
private readonly Mat _alphaMask2Remap;
|
|
|
|
public CameraOrientationCalculator()
|
|
{
|
|
float[] rArray = LinearSpaced(TplInnRad, TplOutRad, RLength);
|
|
float[] thetaArray = LinearSpaced(0, 360, ThetaLength, false);
|
|
|
|
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)(111.7 + 1.836 * v)).ToArray()))
|
|
{
|
|
_alphaMask2Remap = alphaMask2Row.Repeat(ThetaLength, 1);
|
|
}
|
|
}
|
|
|
|
public (float, float) PredictRotation(Mat outMat)
|
|
{
|
|
using var remap = 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();
|
|
using var mask = new Mat();
|
|
using var maskSum = new Mat();
|
|
Cv2.Remap(outMat, remap, _rotationRemapDataX, _rotationRemapDataY, InterpolationFlags.Nearest);
|
|
Cv2.InRange(remap, new Scalar(0, 0, 0), new Scalar(1, 1, 1), mask);
|
|
Cv2.BitwiseNot(mask,mask);
|
|
Cv2.Reduce(mask, maskSum, ReduceDimension.Column, ReduceTypes.Sum, MatType.CV_32F);
|
|
Cv2.Transpose(maskSum, maskSum);
|
|
BgrToHue(remap, hImg, faImg);
|
|
faImg.CopyTo(fbImg);
|
|
ApplyMask(fbImg, _alphaMask2Remap, new Scalar(255.0f));
|
|
int[] channels = [0, 1];
|
|
int[] histSize = [ThetaLength, FLength];
|
|
Rangef[] ranges = [new(0, 180), 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 >= 180 || fa is < 0 or >= 256 || fb is < 0 or >= 256) continue;
|
|
float ha = histAPtr[(int)(h / 180 * ThetaLength) * FLength + (int)(fa / 256 * FLength)];
|
|
float hb = histBPtr[(int)(h / 180 * ThetaLength) * FLength + (int)(fb / 256 * FLength)];
|
|
resultPtr[i / RLength] += (ha > hb) ? 0.0f :
|
|
(Math.Abs(ha - hb) < 0.0001) ? 60.0f :
|
|
255.0f;
|
|
}
|
|
}
|
|
Cv2.Divide(result, maskSum, result);
|
|
Cv2.Resize(result, result, new Size(result.Width * 2, 1), 0, 0, InterpolationFlags.Cubic);
|
|
var peakWidth = ThetaLength / 2 + 1;
|
|
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 * 180 - 45;
|
|
var rotationConfidence = (float)(maxVal + peakRegionSum) / (peakWidth * RLength * 255);
|
|
return (degree, rotationConfidence);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_rotationRemapDataX.Dispose();
|
|
_rotationRemapDataY.Dispose();
|
|
_alphaMask2Remap.Dispose();
|
|
}
|
|
} |