using System;
using System.Linq;
namespace BetterGenshinImpact.GameTask.AutoPick;
using OpenCvSharp;
public static class TextRectExtractor
{
///
/// 从图片中提取文字范围(假定文字从最左边贴边开始,向右连续)
/// 结果矩形固定 x=0,y=0,h=原图高度,只计算连续文字宽度。
///
/// 文字图片
/// 二值化阈值
/// 二值化阈值
///
public static Rect GetTextBoundingRect(Mat textMat, double min = 160, double max = 255)
{
// 转换为灰度图
Mat gray;
if (textMat.Channels() == 3)
{
gray = new Mat();
Cv2.CvtColor(textMat, gray, ColorConversionCodes.BGR2GRAY);
}
else
{
gray = textMat.Clone();
}
// 使用阈值160进行二值化处理
using var bin = new Mat();
Cv2.Threshold(gray, bin, min, max, ThresholdTypes.Binary);
// 形态学操作:先腐蚀后膨胀,去除噪点并保持文字完整
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
Cv2.Erode(bin, bin, kernel, iterations: 1);
Cv2.Dilate(bin, bin, kernel, iterations: 2);
kernel.Dispose();
gray.Dispose();
return ProjectionRect(textMat, bin);
}
public static Rect GetNumberBoundingRect(Mat textMat, double min = 180, double max = 255)
{
// 转换为灰度图
Mat gray;
if (textMat.Channels() == 3)
{
gray = new Mat();
Cv2.CvtColor(textMat, gray, ColorConversionCodes.BGR2GRAY);
}
else
{
gray = textMat.Clone();
}
// 使用阈值160进行二值化处理
using var bin = new Mat();
Cv2.Threshold(gray, bin, min, max, ThresholdTypes.Binary);
// 形态学操作:直接膨胀
Cv2.Dilate(bin, bin, new Mat(), iterations: 2);
gray.Dispose();
var rect = ProjectionRect(textMat, bin);
// 数字后面加点宽度
if (rect != new Rect())
{
var newWidth = rect.Width + 10;
rect.Width = newWidth > textMat.Width ? textMat.Width : newWidth;
}
return rect;
}
///
/// 投影, 获取连续文字的边界矩形
///
///
///
/// 允许的最大连续空列数
///
public static Rect ProjectionRect(Mat textMat, Mat bin, int maxGap = 30)
{
// 投影:对行做 ReduceSum,得到 1 x width 的列和
using var projection = new Mat();
Cv2.Reduce(bin, projection, 0, ReduceTypes.Sum, MatType.CV_32S);
int width = projection.Cols;
projection.GetArray(out int[] colSums);
int gapCount = 0;
int lastNonEmpty = -1;
for (int x = 0; x < width; x++)
{
bool hasInk = colSums[x] > 0;
if (hasInk)
{
lastNonEmpty = x;
gapCount = 0;
}
else
{
gapCount++;
if (gapCount > maxGap)
{
break;
}
}
}
if (lastNonEmpty == -1)
{
// 没有检测到文字
return new Rect();
}
Rect boundingRect = new Rect(0, 0, lastNonEmpty, textMat.Height);
return boundingRect;
}
}