mirror of
https://github.com/babalae/better-genshin-impact
synced 2025-01-07 03:17:16 +08:00
new camera orientation algorithm by https://github.com/Limint
This commit is contained in:
parent
b6220db756
commit
e2a25e4492
@ -95,15 +95,15 @@ public partial class MainWindow : Window
|
||||
var path = @"E:\HuiTask\更好的原神\地图匹配\比较\小地图\Clip_20240323_185854.png";
|
||||
|
||||
var pic1 = new Mat(path);
|
||||
CameraOrientationV3 cameraOrientation = new();
|
||||
CameraOrientationFromLimint cameraOrientation = new();
|
||||
var f = cameraOrientation.PredictRotation(pic1);
|
||||
Debug.WriteLine("C#版本 方向1:" + f);
|
||||
|
||||
|
||||
var pic2 = new Mat(path);
|
||||
CameraOrientationV2 cameraOrientation3 = new();
|
||||
var f3 = cameraOrientation3.PredictRotation(pic2);
|
||||
Debug.WriteLine("py直接翻译C#版本 方向1:" + f3);
|
||||
// var pic2 = new Mat(path);
|
||||
// CameraOrientationV2 cameraOrientation3 = new();
|
||||
// var f3 = cameraOrientation3.PredictRotation(pic2);
|
||||
// Debug.WriteLine("py直接翻译C#版本 方向1:" + f3);
|
||||
|
||||
|
||||
var grey = new Mat(path, ImreadModes.Grayscale);
|
||||
|
@ -10,7 +10,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ApplicationIcon>Assets\Images\logo.ico</ApplicationIcon>
|
||||
<AssemblyName>BetterGI</AssemblyName>
|
||||
<AssemblyVersion>0.37.2</AssemblyVersion>
|
||||
<AssemblyVersion>0.37.3</AssemblyVersion>
|
||||
<Platforms>x64</Platforms>
|
||||
<DebugType>embedded</DebugType>
|
||||
</PropertyGroup>
|
||||
@ -48,7 +48,6 @@
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.18.1" />
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime.Managed" Version="1.18.1" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2592.51" />
|
||||
<PackageReference Include="NumpyDotNet" Version="0.9.86.2" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="OpenCvSharp4.WpfExtensions" Version="4.8.0.20230708" />
|
||||
<PackageReference Include="OpenCvSharp4.Extensions" Version="4.8.0.20230708" />
|
||||
|
@ -148,8 +148,8 @@ public class KeyMouseMacroPlayer
|
||||
case MacroEventType.MouseMoveBy:
|
||||
if (e.CameraOrientation != null)
|
||||
{
|
||||
var cao = CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcGreyMat);
|
||||
var diff = (cao - (int)e.CameraOrientation + 180) % 360 - 180;
|
||||
var cao = CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcMat);
|
||||
var diff = ((int)Math.Round(cao) - (int)e.CameraOrientation + 180) % 360 - 180;
|
||||
diff += diff < -180 ? 360 : 0;
|
||||
//过滤一下特别大的角度偏差
|
||||
if (diff != 0 && diff < 8 && diff > -8)
|
||||
|
@ -168,7 +168,7 @@ public class KeyMouseRecorder
|
||||
if ((now - LastOrientationDetection).TotalMilliseconds > 100.0)
|
||||
{
|
||||
LastOrientationDetection = now;
|
||||
cao = CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcGreyMat);
|
||||
cao = (int)Math.Round(CameraOrientation.Compute(TaskControl.CaptureToRectArea().SrcMat));
|
||||
}
|
||||
}
|
||||
MacroEvents.Add(new MacroEvent
|
||||
|
@ -133,11 +133,11 @@ public class AutoDomainTask : ISoloTask
|
||||
|
||||
NotificationHelper.SendTaskNotificationWithScreenshotUsing(b => b.Domain().Progress().Build());
|
||||
}
|
||||
|
||||
|
||||
await Delay(2000, ct);
|
||||
await Bv.WaitForMainUi(_ct, 30);
|
||||
await Delay(2000, ct);
|
||||
|
||||
|
||||
await ArtifactSalvage();
|
||||
}
|
||||
|
||||
@ -699,7 +699,7 @@ public class AutoDomainTask : ISoloTask
|
||||
while (!cts.Token.IsCancellationRequested)
|
||||
{
|
||||
using var captureRegion = CaptureToRectArea();
|
||||
var angle = CameraOrientation.Compute(captureRegion.SrcGreyMat);
|
||||
var angle = CameraOrientation.Compute(captureRegion.SrcMat);
|
||||
CameraOrientation.DrawDirection(captureRegion, angle);
|
||||
if (angle is >= 356 or <= 4)
|
||||
{
|
||||
@ -710,7 +710,7 @@ public class AutoDomainTask : ISoloTask
|
||||
if (angle <= 180)
|
||||
{
|
||||
// 左移视角
|
||||
var moveAngle = angle;
|
||||
var moveAngle = (int)Math.Round(angle);
|
||||
if (moveAngle > 2)
|
||||
{
|
||||
moveAngle *= 2;
|
||||
@ -722,7 +722,7 @@ public class AutoDomainTask : ISoloTask
|
||||
else if (angle is > 180 and < 360)
|
||||
{
|
||||
// 右移视角
|
||||
var moveAngle = 360 - angle;
|
||||
var moveAngle = 360 - (int)Math.Round(angle);
|
||||
if (moveAngle > 2)
|
||||
{
|
||||
moveAngle *= 2;
|
||||
@ -898,7 +898,7 @@ public class AutoDomainTask : ISoloTask
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!int.TryParse(_taskParam.MaxArtifactStar, out var star))
|
||||
{
|
||||
star = 4;
|
||||
|
@ -19,9 +19,9 @@ public class CameraRotateTask(CancellationToken ct)
|
||||
/// <param name="targetOrientation"></param>
|
||||
/// <param name="imageRegion"></param>
|
||||
/// <returns></returns>
|
||||
public int RotateToApproach(int targetOrientation, ImageRegion imageRegion)
|
||||
public float RotateToApproach(float targetOrientation, ImageRegion imageRegion)
|
||||
{
|
||||
var cao = CameraOrientation.Compute(imageRegion.SrcGreyMat);
|
||||
var cao = CameraOrientation.Compute(imageRegion.SrcMat);
|
||||
var diff = (cao - targetOrientation + 180) % 360 - 180;
|
||||
diff += diff < -180 ? 360 : 0;
|
||||
if (diff == 0)
|
||||
|
@ -0,0 +1,149 @@
|
||||
using BetterGenshinImpact.GameTask.Model.Area;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map.Camera;
|
||||
|
||||
public class CameraOrientationFromGia
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前小地图摄像机朝向的角度
|
||||
/// </summary>
|
||||
/// <param name="mat">小地图灰度图</param>
|
||||
/// <returns>角度</returns>
|
||||
public static float ComputeMiniMap(Mat mat)
|
||||
{
|
||||
// 如果不是灰度图,转换成灰度图
|
||||
if (mat.Channels() == 3)
|
||||
{
|
||||
Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
|
||||
}
|
||||
|
||||
Cv2.GaussianBlur(mat, mat, new Size(3, 3), 0);
|
||||
// 极坐标展开
|
||||
var centerPoint = new Point2f(mat.Width / 2f, mat.Height / 2f);
|
||||
var polarMat = new Mat();
|
||||
Cv2.WarpPolar(mat, polarMat, new Size(360, 360), centerPoint, 360d, InterpolationFlags.Linear, WarpPolarMode.Linear);
|
||||
// Cv2.ImShow("polarMat", polarMat);
|
||||
var polarRoiMat = new Mat(polarMat, new Rect(10, 0, 70, polarMat.Height));
|
||||
Cv2.Rotate(polarRoiMat, polarRoiMat, RotateFlags.Rotate90Counterclockwise);
|
||||
|
||||
var scharrResult = new Mat();
|
||||
Cv2.Scharr(polarRoiMat, scharrResult, MatType.CV_32F, 1, 0);
|
||||
|
||||
// 求波峰
|
||||
var left = new int[360];
|
||||
var right = new int[360];
|
||||
|
||||
scharrResult.GetArray<float>(out var array);
|
||||
var leftPeaks = FindPeaks(array);
|
||||
leftPeaks.ForEach(i => left[i % 360]++);
|
||||
|
||||
var reversedArray = array.Select(x => -x).ToArray();
|
||||
var rightPeaks = FindPeaks(reversedArray);
|
||||
rightPeaks.ForEach(i => right[i % 360]++);
|
||||
|
||||
// 优化
|
||||
var left2 = left.Zip(right, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
var right2 = right.Zip(left, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
|
||||
// 左移后相乘 在附近2°内寻找最大值
|
||||
var sum = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
var all = left2.Zip(Shift(right2, -90 + i), (x, y) => x * y * (3 - Math.Abs(i)) / 3).ToArray();
|
||||
sum = sum.Zip(all, (x, y) => x + y).ToArray();
|
||||
}
|
||||
|
||||
// 卷积
|
||||
var result = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
var all = Shift(sum, i);
|
||||
for (var j = 0; j < all.Length; j++)
|
||||
{
|
||||
all[j] = all[j] * (3 - Math.Abs(i)) / 3;
|
||||
}
|
||||
|
||||
result = result.Zip(all, (x, y) => x + y).ToArray();
|
||||
}
|
||||
|
||||
// 计算结果角度
|
||||
var maxIndex = result.ToList().IndexOf(result.Max());
|
||||
var angle = maxIndex + 45;
|
||||
if (angle > 360)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
public static void DrawDirection(ImageRegion region, double angle, string name = "camera", Pen? pen = null)
|
||||
{
|
||||
// 绘图
|
||||
var scale = TaskContext.Instance().SystemInfo.AssetScale;
|
||||
const int r = 100;
|
||||
var center = new Point(168 * scale, 125 * scale); // 地图中心点 后续建议调整
|
||||
var x1 = center.X + r * Math.Cos(angle * Math.PI / 180);
|
||||
var y1 = center.Y + r * Math.Sin(angle * Math.PI / 180);
|
||||
|
||||
// var line = new LineDrawable(center, new Point(x1, y1))
|
||||
// {
|
||||
// Pen = new Pen(Color.Yellow, 1)
|
||||
// };
|
||||
// VisionContext.Instance().DrawContent.PutLine("camera", line);
|
||||
|
||||
pen ??= new Pen(Color.Yellow, 1);
|
||||
|
||||
region.DrawLine(center.X, center.Y, (int)x1, (int)y1, name, pen);
|
||||
}
|
||||
|
||||
static List<int> FindPeaks(float[] data)
|
||||
{
|
||||
List<int> peakIndices = [];
|
||||
|
||||
for (int i = 1; i < data.Length - 1; i++)
|
||||
{
|
||||
if (data[i] > data[i - 1] && data[i] > data[i + 1])
|
||||
{
|
||||
peakIndices.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return peakIndices;
|
||||
}
|
||||
|
||||
public static int[] RightShift(int[] array, int k)
|
||||
{
|
||||
return array.Skip(array.Length - k)
|
||||
.Concat(array.Take(array.Length - k))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static int[] LeftShift(int[] array, int k)
|
||||
{
|
||||
return array.Skip(k)
|
||||
.Concat(array.Take(k))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static int[] Shift(int[] array, int k)
|
||||
{
|
||||
if (k > 0)
|
||||
{
|
||||
return RightShift(array, k);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LeftShift(array, -k);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class CameraOrientationV3
|
||||
/// <summary>
|
||||
/// author: https://github.com/Limint
|
||||
/// </summary>
|
||||
public class CameraOrientationFromLimint
|
||||
{
|
||||
private bool debugEnable;
|
||||
private static int tplSize = 216;
|
||||
private int tplOutRad = 78;
|
||||
private int tplInnRad = 19;
|
||||
@ -22,20 +25,12 @@ public class CameraOrientationV3
|
||||
private Mat alphaMask1;
|
||||
private Mat alphaMask2;
|
||||
|
||||
public CameraOrientationV3(bool debugEnable = false)
|
||||
public CameraOrientationFromLimint()
|
||||
{
|
||||
this.debugEnable = debugEnable;
|
||||
GeneratePoints();
|
||||
CreateAlphaMask();
|
||||
}
|
||||
|
||||
/*
|
||||
public float Compute(Mat bgrMat)
|
||||
{
|
||||
var mat = new Mat(bgrMat, MapAssets.Instance.MimiMapRect);
|
||||
return PredictRotation(mat);
|
||||
}
|
||||
*/
|
||||
public static float[] RightShift(float[] array, int k)
|
||||
{
|
||||
return array.Skip(array.Length - k)
|
||||
@ -149,7 +144,12 @@ public class CameraOrientationV3
|
||||
}
|
||||
}
|
||||
|
||||
public float PredictRotation(Mat image, float confidence = 0.3f)
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <returns>视角,置信度</returns>
|
||||
public (float, float) PredictRotationWithConfidence(Mat image)
|
||||
{
|
||||
using (Mat remap = new Mat())
|
||||
using (Mat faImg = new Mat())
|
||||
@ -215,14 +215,19 @@ public class CameraOrientationV3
|
||||
}
|
||||
|
||||
float rotationConfidence = (conv[maxIndex] + mean.Skip(thetaLength - peakWidth).Sum()) / (peakWidth * 255);
|
||||
Console.WriteLine(rotationConfidence);
|
||||
if (rotationConfidence < confidence)
|
||||
{
|
||||
Console.WriteLine($"置信度{rotationConfidence}<{confidence}, 不可靠视角 {degree}");
|
||||
return degree;
|
||||
}
|
||||
Debug.WriteLine($"置信度:{rotationConfidence}");
|
||||
// if (rotationConfidence < confidence)
|
||||
// {
|
||||
// Debug.WriteLine($"置信度{rotationConfidence}<{confidence}, 不可靠视角 {degree}");
|
||||
// return degree;
|
||||
// }
|
||||
|
||||
return degree;
|
||||
return (degree, rotationConfidence);
|
||||
}
|
||||
}
|
||||
|
||||
public float PredictRotation(Mat image)
|
||||
{
|
||||
return PredictRotationWithConfidence(image).Item1;
|
||||
}
|
||||
}
|
@ -1,93 +1,43 @@
|
||||
using BetterGenshinImpact.GameTask.Model.Area;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using BetterGenshinImpact.GameTask.Common.Element.Assets;
|
||||
using BetterGenshinImpact.GameTask.Common.Map.Camera;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class CameraOrientation
|
||||
{
|
||||
private static CameraOrientationFromLimint _coV2 = new();
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前小地图摄像机朝向的角度
|
||||
/// </summary>
|
||||
/// <param name="greyMat">完整游戏截图</param>
|
||||
/// <param name="mat">完整游戏截图彩色</param>
|
||||
/// <returns>角度</returns>
|
||||
public static int Compute(Mat greyMat)
|
||||
public static float Compute(Mat mat)
|
||||
{
|
||||
var mat = new Mat(greyMat, MapAssets.Instance.MimiMapRect);
|
||||
return ComputeMiniMap(mat);
|
||||
var mimiMap = new Mat(mat, MapAssets.Instance.MimiMapRect);
|
||||
return ComputeMiniMap(mimiMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算当前小地图摄像机朝向的角度
|
||||
/// </summary>
|
||||
/// <param name="mat">小地图灰度图</param>
|
||||
/// <param name="mat">小地图彩色图</param>
|
||||
/// <returns>角度</returns>
|
||||
public static int ComputeMiniMap(Mat mat)
|
||||
public static float ComputeMiniMap(Mat mat)
|
||||
{
|
||||
Cv2.GaussianBlur(mat, mat, new Size(3, 3), 0);
|
||||
// 极坐标展开
|
||||
var centerPoint = new Point2f(mat.Width / 2f, mat.Height / 2f);
|
||||
var polarMat = new Mat();
|
||||
Cv2.WarpPolar(mat, polarMat, new Size(360, 360), centerPoint, 360d, InterpolationFlags.Linear, WarpPolarMode.Linear);
|
||||
// Cv2.ImShow("polarMat", polarMat);
|
||||
var polarRoiMat = new Mat(polarMat, new Rect(10, 0, 70, polarMat.Height));
|
||||
Cv2.Rotate(polarRoiMat, polarRoiMat, RotateFlags.Rotate90Counterclockwise);
|
||||
|
||||
var scharrResult = new Mat();
|
||||
Cv2.Scharr(polarRoiMat, scharrResult, MatType.CV_32F, 1, 0);
|
||||
|
||||
// 求波峰
|
||||
var left = new int[360];
|
||||
var right = new int[360];
|
||||
|
||||
scharrResult.GetArray<float>(out var array);
|
||||
var leftPeaks = FindPeaks(array);
|
||||
leftPeaks.ForEach(i => left[i % 360]++);
|
||||
|
||||
var reversedArray = array.Select(x => -x).ToArray();
|
||||
var rightPeaks = FindPeaks(reversedArray);
|
||||
rightPeaks.ForEach(i => right[i % 360]++);
|
||||
|
||||
// 优化
|
||||
var left2 = left.Zip(right, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
var right2 = right.Zip(left, (x, y) => Math.Max(x - y, 0)).ToArray();
|
||||
|
||||
// 左移后相乘 在附近2°内寻找最大值
|
||||
var sum = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
var all = left2.Zip(Shift(right2, -90 + i), (x, y) => x * y * (3 - Math.Abs(i)) / 3).ToArray();
|
||||
sum = sum.Zip(all, (x, y) => x + y).ToArray();
|
||||
}
|
||||
|
||||
// 卷积
|
||||
var result = new int[360];
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
var all = Shift(sum, i);
|
||||
for (var j = 0; j < all.Length; j++)
|
||||
{
|
||||
all[j] = all[j] * (3 - Math.Abs(i)) / 3;
|
||||
}
|
||||
|
||||
result = result.Zip(all, (x, y) => x + y).ToArray();
|
||||
}
|
||||
|
||||
// 计算结果角度
|
||||
var maxIndex = result.ToList().IndexOf(result.Max());
|
||||
var angle = maxIndex + 45;
|
||||
if (angle > 360)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
|
||||
return angle;
|
||||
var (angle, confidence) = _coV2.PredictRotationWithConfidence(mat);
|
||||
if (confidence < 0.3)
|
||||
{
|
||||
Debug.WriteLine($"置信度过低, {confidence}<0.3, 不可靠视角 {angle}");
|
||||
return CameraOrientationFromGia.ComputeMiniMap(mat);
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
public static void DrawDirection(ImageRegion region, double angle, string name = "camera", Pen? pen = null)
|
||||
@ -109,45 +59,4 @@ public class CameraOrientation
|
||||
|
||||
region.DrawLine(center.X, center.Y, (int)x1, (int)y1, name, pen);
|
||||
}
|
||||
|
||||
static List<int> FindPeaks(float[] data)
|
||||
{
|
||||
List<int> peakIndices = [];
|
||||
|
||||
for (int i = 1; i < data.Length - 1; i++)
|
||||
{
|
||||
if (data[i] > data[i - 1] && data[i] > data[i + 1])
|
||||
{
|
||||
peakIndices.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return peakIndices;
|
||||
}
|
||||
|
||||
public static int[] RightShift(int[] array, int k)
|
||||
{
|
||||
return array.Skip(array.Length - k)
|
||||
.Concat(array.Take(array.Length - k))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static int[] LeftShift(int[] array, int k)
|
||||
{
|
||||
return array.Skip(k)
|
||||
.Concat(array.Take(k))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static int[] Shift(int[] array, int k)
|
||||
{
|
||||
if (k > 0)
|
||||
{
|
||||
return RightShift(array, k);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LeftShift(array, -k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,164 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NumpyDotNet;
|
||||
using OpenCvSharp;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class CameraOrientationV2
|
||||
{
|
||||
private double tplSize = 216;
|
||||
private double tplOutRad = 78;
|
||||
private int tplInnRad = 19;
|
||||
private ndarray alphaParams1 = np.array(new[] { 18.632, 20.157, 24.093, 34.617, 38.566, 41.94, 47.654, 51.087, 58.561, 63.925, 67.759, 71.77, 75.214 });
|
||||
|
||||
private double rLength = 60;
|
||||
private int thetaLength = 360;
|
||||
private int peakWidth;
|
||||
private ndarray rotationRemapDataX;
|
||||
private ndarray rotationRemapDataY;
|
||||
|
||||
private ndarray alphaMask1;
|
||||
private ndarray alphaMask2;
|
||||
private double rotation;
|
||||
private double rotationConfidence;
|
||||
|
||||
public CameraOrientationV2()
|
||||
{
|
||||
this.peakWidth = thetaLength / 4;
|
||||
|
||||
var points = GeneratePoints(tplSize / 2.0, tplSize / 2.0, tplInnRad, tplOutRad, rLength, thetaLength);
|
||||
this.rotationRemapDataX = points.Item1;
|
||||
this.rotationRemapDataY = points.Item2;
|
||||
|
||||
var masks = CreatAlphaMask();
|
||||
this.alphaMask1 = masks.Item1;
|
||||
this.alphaMask2 = masks.Item2;
|
||||
}
|
||||
|
||||
private ndarray ApplyMask(ndarray img, ndarray mask, double bkg)
|
||||
{
|
||||
return (img.astype(np.Float32) - bkg) / mask * 255 + bkg;
|
||||
}
|
||||
|
||||
private ndarray Bgr2h(ndarray bgr)
|
||||
{
|
||||
var cmax = np.max(bgr, axis: -1);
|
||||
var cmin = np.min(bgr, axis: -1);
|
||||
var mask = cmax > cmin;
|
||||
|
||||
var hue = np.zeros_like(cmax).astype(np.Float32);
|
||||
// var maskIndices = np.where(mask);
|
||||
|
||||
hue[mask] = ((cmax[mask] - 2 * (ndarray)cmin[mask] - ((ndarray)bgr["...", 2])[mask] + ((ndarray)bgr["...", 1])[mask] + ((ndarray)bgr["...", 0])[mask]) / ((ndarray)cmax[mask] - (ndarray)cmin[mask])) * 60;
|
||||
|
||||
hue[np.where(~mask)] = -1;
|
||||
hue = (ndarray)np.where((ndarray)bgr["...", 1] >= (ndarray)bgr["...", 0], hue, 360 - hue);
|
||||
|
||||
return hue;
|
||||
}
|
||||
|
||||
private (ndarray, ndarray) GeneratePoints(double x, double y, double d1, double d2, double n, double m)
|
||||
{
|
||||
var r = np.linspace(d1, d2, ref n);
|
||||
var theta = np.linspace(0, 360, ref m, endpoint: false);
|
||||
var thetaRad = np.radians(theta);
|
||||
|
||||
var meshgrid = np.meshgrid([r, thetaRad]);
|
||||
var rGrid = meshgrid[0];
|
||||
var thetaGrid = meshgrid[1];
|
||||
|
||||
var xCartesian = rGrid * np.cos(thetaGrid) + x;
|
||||
var yCartesian = rGrid * np.sin(thetaGrid) + y;
|
||||
|
||||
return (xCartesian.astype(np.Float32), yCartesian.astype(np.Float32));
|
||||
}
|
||||
|
||||
private (ndarray, ndarray) CreatAlphaMask()
|
||||
{
|
||||
var values = np.linspace(tplInnRad, tplOutRad, ref rLength);
|
||||
var alphaMask1 = (229 + np.searchsorted(alphaParams1, values)).astype(np.Float32);
|
||||
var alphaMask2 = (111.7 + 1.836 * values).astype(np.Float32);
|
||||
|
||||
return (alphaMask1, alphaMask2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="image">3通道图像</param>
|
||||
/// <param name="confidence"></param>
|
||||
/// <returns></returns>
|
||||
public double? PredictRotation(Mat image, double confidence = 0.3)
|
||||
{
|
||||
Mat remap = new();
|
||||
Cv2.Remap(image, remap, InputArray.Create(rotationRemapDataX.AsFloatArray()), InputArray.Create(rotationRemapDataY.AsFloatArray()),
|
||||
InterpolationFlags.Lanczos4);
|
||||
Cv2.Split(remap, out var bgrChannels);
|
||||
List<float[]> remapChannels = [];
|
||||
foreach (var channel in bgrChannels)
|
||||
{
|
||||
Mat floatChannel = new Mat();
|
||||
channel.ConvertTo(floatChannel, MatType.CV_32F);
|
||||
floatChannel.GetArray<float>(out var channelArray);
|
||||
remapChannels.Add(channelArray);
|
||||
}
|
||||
// TODO float[,,] -> ndarray
|
||||
|
||||
var bgrImg = ApplyMask(np.array(remapChannels), alphaMask1.reshape(-1, 1), 0);
|
||||
var hImg = Bgr2h(bgrImg);
|
||||
var faImg = np.mean(bgrImg, axis: 2);
|
||||
var fbImg = ApplyMask(faImg, alphaMask2, 255);
|
||||
|
||||
var histA = new Mat();
|
||||
var histB = new Mat();
|
||||
|
||||
Rangef[] ranges = [new(0, 360), new(0, 256)];
|
||||
int[] histSize = [360, 256];
|
||||
Cv2.CalcHist([InputArray.Create(hImg.AsFloatArray()).GetMat(), InputArray.Create(faImg.AsFloatArray()).GetMat()], [0, 1], null, histA, 2, histSize, ranges);
|
||||
Cv2.CalcHist([InputArray.Create(hImg.AsFloatArray()).GetMat(), InputArray.Create(fbImg.AsFloatArray()).GetMat()], [0, 1], null, histB, 2, histSize, ranges);
|
||||
|
||||
var result = np.zeros((thetaLength, rLength), np.UInt8);
|
||||
var h = np.floor(hImg).astype(np.Int32);
|
||||
var fa = np.floor(faImg).astype(np.Int32);
|
||||
var fb = np.floor(fbImg).astype(np.Int32);
|
||||
|
||||
var flag = (h >= 0) & (h < 360) & (fa >= 0) & (fa < 256) & (fb >= 0) & (fb < 256);
|
||||
|
||||
var histANp = np.array(histA);
|
||||
var histBNp = np.array(histB);
|
||||
result[flag] = np.select(
|
||||
[
|
||||
(ndarray)histANp[h[flag], fa[flag]] > (ndarray)histBNp[h[flag], fb[flag]],
|
||||
((ndarray)histANp[h[flag], fa[flag]]).Equals((ndarray)histBNp[h[flag], fb[flag]]),
|
||||
(ndarray)histANp[h[flag], fa[flag]] < (ndarray)histBNp[h[flag], fb[flag]]
|
||||
],
|
||||
[
|
||||
np.array(0),
|
||||
np.array(128),
|
||||
np.array(255)
|
||||
]
|
||||
).astype(np.UInt8);
|
||||
|
||||
// 处理结果计算
|
||||
var resultMean = np.mean(result, axis: 1);
|
||||
var resultConv = np.cumsum(resultMean - np.roll(resultMean, peakWidth)) + (double)np.sum((ndarray)resultMean[-peakWidth + ":"]);
|
||||
|
||||
var maxInd = np.argmax(resultConv);
|
||||
var degree = ((int)maxInd + 0.5) / thetaLength * 360 - 45;
|
||||
|
||||
if (degree < 0)
|
||||
degree = 360 + degree;
|
||||
|
||||
this.rotation = degree;
|
||||
this.rotationConfidence = (float)resultConv[(int)maxInd] / (peakWidth * 255);
|
||||
|
||||
if (this.rotationConfidence < confidence)
|
||||
{
|
||||
Console.WriteLine($"置信度{this.rotationConfidence}<{confidence}, 不可靠视角 {this.rotation}");
|
||||
return null;
|
||||
}
|
||||
|
||||
return degree;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user