Files
better-genshin-impact/BetterGenshinImpact/Core/Recognition/OpenCv/OpenCvCommonHelper.cs
FishmanTheMurloc fce70c0e96 分解5星圣遗物 (#1383)
* 分解圣遗物基础设施建设

* 分解圣遗物独立任务基本功能完成:单独的启动按钮,正则表达式逐一筛选;代码文件整理到单独的文件夹

* 自动分解5星圣遗物功能初步完成

* 修复上次修改快速分解产生的问题,主要点击分解按钮时的bug,还有与五星分解步骤衔接的问题

* 针对切换队伍时,多语言识别效果不佳的情况,将用户设定的队伍名作为正则表达式进行模糊匹配,并在LogInfo输出相关提示;传送任务对任务取消进行单独的异常处理

* 一个便于测试分解圣遗物OCR识别和正则匹配结果的弹窗
2025-04-05 19:53:52 +08:00

127 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using OpenCvSharp;
using System.Diagnostics;
using System.Linq;
namespace BetterGenshinImpact.Core.Recognition.OpenCv;
public class OpenCvCommonHelper
{
/// <summary>
/// 计算灰度图中某个颜色的像素个数
/// </summary>
/// <param name="mat"></param>
/// <param name="color"></param>
/// <returns></returns>
public static int CountGrayMatColor(Mat mat, byte color)
{
Debug.Assert(mat.Depth() == MatType.CV_8U);
return SplitChannal(mat, m => CountGrayMatColorC1(m, color)).Sum();
}
private static IEnumerable<T> SplitChannal<T>(Mat mat, Func<Mat, T> func)
{
if (mat.Empty()) return [];
if (mat.Channels() == 1)
{
return [func.Invoke(mat)];
}
Mat[]? channels = null;
try
{
channels = mat.Split();
return channels.AsParallel().Select(func);
}
finally
{
// 释放所有分离出的通道内存
if (channels is not null)
foreach (var ch in channels)
ch.Dispose();
}
}
/// <summary>
/// 仅限单通道,统计等于color的颜色
/// </summary>
/// <param name="mat">矩阵</param>
/// <param name="color">值</param>
/// <returns></returns>
public static int CountGrayMatColorC1(Mat mat, byte color)
{
Debug.Assert(mat.Depth() == MatType.CV_8U);
Debug.Assert(mat.Channels() == 1);
using var dst = new Mat();
Cv2.Compare(mat, color, dst, CmpType.EQ);
return Cv2.CountNonZero(dst);
}
/// <summary>
/// 仅限单通道,统计颜色在lowColor和highColor中范围
/// </summary>
/// <param name="mat">矩阵</param>
/// <param name="lowColor">低</param>
/// <param name="highColor">高</param>
/// <returns></returns>
public static int CountGrayMatColorC1(Mat mat, byte lowColor, byte highColor)
{
using var mask = new Mat();
// 使用InRange直接生成二值掩膜
Cv2.InRange(mat, new Scalar(lowColor), new Scalar(highColor), mask);
return Cv2.CountNonZero(mask);
}
public static int CountGrayMatColor(Mat mat, byte lowColor, byte highColor)
{
return SplitChannal(mat, m => CountGrayMatColorC1(m, lowColor, highColor)).Sum();
}
public static Mat Threshold(Mat src, Scalar low, Scalar high)
{
using var mask = new Mat();
using var rgbMat = new Mat();
Cv2.CvtColor(src, rgbMat, ColorConversionCodes.BGR2RGB);
Cv2.InRange(rgbMat, low, high, mask);
// Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化 //不需要
return mask.Clone();
}
public static Mat InRangeHsv(Mat src, Scalar low, Scalar high)
{
using var rgbMat = src.CvtColor(ColorConversionCodes.BGR2HSV);
return rgbMat.InRange(low, high);
}
public static Mat InRangeHsvFull(Mat src, Scalar low, Scalar high)
{
using var rgbMat = src.CvtColor(ColorConversionCodes.BGR2HSV_FULL);
return rgbMat.InRange(low, high);
}
public static Scalar CommonHSV2OpenCVHSVFull(Scalar commonHSV)
{
return new Scalar(commonHSV.Val0 / 255 * 180, commonHSV.Val1 * 255, commonHSV.Val2 * 255);
}
public static Mat Threshold(Mat src, Scalar s)
{
return Threshold(src, s, s);
}
/// <summary>
/// 和二值化的颜色刚好相反
/// </summary>
/// <param name="src"></param>
/// <param name="s"></param>
/// <returns></returns>
public static Mat CreateMask(Mat src, Scalar s)
{
var mask = new Mat();
Cv2.InRange(src, s, s, mask);
return ~ mask;
}
}