图像简单处理

需求:对上面多张这样的图像,将成员识别出来,读取本周活跃值。生成一张全部由成员组成的大图
思路:使用OpenCVSharp进行目标区域边框检测,根据大小过滤目标区域。使用Tesseract对指定位置进行文字识别(貌似识别精度不高,有乱码。)
主要代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Security.Cryptography;
using System.Web;
using System.Windows;
using System.Windows.Documents;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenCvSharp.Internal.Vectors;
using Tesseract;
namespace ShanHaiJingToolkit.Common
{
public class CvHelper
{
public void ReadPictureActivityValue(string imagePath, out int itemWidth, out int itemHeight)
{
System.IO.DirectoryInfo directoryInfo = new System.IO.DirectoryInfo(imagePath);
List<Mat> items = new List<Mat>();
itemWidth = 0;
itemHeight = 0;
foreach (var file in directoryInfo.GetFiles())
{
// 读取图像
using (var src = new Mat(file.FullName, ImreadModes.Color))
{
// 转换为灰度图像,因为边缘检测通常在灰度图像上进行
using (var gray = new Mat())
{
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
// 应用Canny边缘检测
using (var edges = new Mat())
{
double lowThreshold = 50.0;
double highThreshold = 150.0;
Cv2.Canny(gray, edges, lowThreshold, highThreshold);
Cv2.FindContours(edges, out var contours1, out var hierarchy1, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 定义矩形颜色(BGR格式)和线条厚度
Scalar color = new Scalar(0, 255, 0); // 绿色
List<OpenCvSharp.Rect> listRects = new List<OpenCvSharp.Rect>();
for (int i = 0; i < contours1.Length; i++)
{
var points = contours1[i];
var rect = Cv2.BoundingRect(points);
var widthScale = (float)rect.Width / src.Width;
var heightScale = (float)rect.Height / src.Height;
//if ((widthScale > 0.827 && widthScale < 0.871) && (rect.Height > 0.060 && rect.Height < 0.073))
if ((widthScale > 0.827 && widthScale < 0.871) && (heightScale > 0.060 && heightScale < 0.073))
{
bool isAdd = false;
foreach (var item in listRects)
{
if (item.TopLeft.DistanceTo(rect.TopLeft) < 10)
{
isAdd = true;
break;
}
}
if (isAdd) continue;
listRects.Add(rect);
if (itemWidth == 0)
{
itemWidth = rect.Width;
itemHeight = rect.Height;
}
var newRect = new OpenCvSharp.Rect(rect.Left, rect.Top, itemWidth, itemHeight);
// 在图像上绘制矩形
//Cv2.Rectangle(src, newRect.TopLeft, newRect.BottomRight, color, 4);
var tempMat = src[newRect];
items.Add(tempMat);
}
}
}
}
}
}
var tempImagesDirectory = imagePath + "\\temp\\";
if (!System.IO.Directory.Exists(tempImagesDirectory))
{
System.IO.Directory.CreateDirectory(tempImagesDirectory);
}
//保存到本地
for (int i = 0; i < items.Count; i++)
{
//using (Mat gray = new Mat())
//{
// Cv2.CvtColor(items[i], gray, ColorConversionCodes.BGR2GRAY);
// //二值化
// using (Mat target = new Mat())
// {
// Cv2.Threshold(items[i], target, 150, 200, ThresholdTypes.Binary);
// Cv2.ImWrite(tempImagesDirectory + $"{i}.png", target);
// }
//}
//降噪高斯模糊,边缘检测
// 定义高斯模糊的核大小和标准差
//OpenCvSharp.Size ksize = new OpenCvSharp.Size(5, 5); // 核大小,必须是奇数
//double sigmaX = 0; // X方向的标准差,如果为0,则由核函数宽度计算得出
//double sigmaY = 0; // Y方向的标准差,如果为0,则由核函数高度计算得出
//// 应用高斯模糊
//using (Mat blurredImage = new Mat())
//{
// Cv2.GaussianBlur(items[i], blurredImage, ksize, sigmaX, sigmaY);
// // 定义Canny边缘检测的双阈值
// double threshold1 = 50.0; // 低阈值
// double threshold2 = 150.0; // 高阈值,通常是高于低阈值的2到3倍
// // 边缘检测
// using (Mat edges = new Mat())
// {
// Cv2.Canny(blurredImage, edges, threshold1, threshold2);
// Cv2.ImWrite(tempImagesDirectory + $"{i}.png", edges);
// }
//};
using (Mat txtMat = items[i][new OpenCvSharp.Rect((int)(0.181 * itemWidth), itemHeight / 2, (int)(0.353 * itemWidth), itemHeight / 2)])
{
Cv2.ImWrite(tempImagesDirectory + $"{i}.png", txtMat);
}
//Cv2.ImWrite(tempImagesDirectory + $"{i}.png", items[i]);
}
//读取活跃值
List<Tuple<int, Mat>> targetVals = new List<Tuple<int, Mat>>();
var modelPath = AppDomain.CurrentDomain.BaseDirectory;
using (var engine = new TesseractEngine(modelPath, "chi_sim", EngineMode.Default))
{
for (int i = 0; i < items.Count; i++)
{
var imageFile = tempImagesDirectory + $"{i}.png";
// 打开图片并识别文字
var readTxt = string.Empty;
using (var img = Pix.LoadFromFile(imageFile))
{
using (var page = engine.Process(img))
{
readTxt = page.GetText();
}
}
System.Text.RegularExpressions.MatchCollection matchCollection = System.Text.RegularExpressions.Regex.Matches(readTxt, "\\d+");
if (matchCollection.Count > 0)
{
int.TryParse(matchCollection[matchCollection.Count - 1].Value, out var val);
targetVals.Add(new Tuple<int, Mat>(val, items[i]));
if (File.Exists(imageFile))
{
File.Delete(imageFile);
}
}
}
}
//if (items.Count != targetVals.Count)
//{
// return false;
//}
//识别成功的
int count = 0;
foreach (var tuple in targetVals)
{
var imageFile = tempImagesDirectory + $"{count++}-{tuple.Item1}.png";
Cv2.ImWrite(imageFile, tuple.Item2);
}
//using (var dst = new Mat())
//{
// Cv2.VConcat(items, dst);
// Cv2.ImWrite("E:\\ShanHaiJingPic\\result.jpg", dst);
// // 定义新的尺寸
// //OpenCvSharp.Size newSize = new OpenCvSharp.Size(src.Cols / 2, src.Rows / 2); // 缩小到原来的一半
// //Cv2.Resize(src, dst, newSize);
// //using (var window = new OpenCvSharp.Window("Rectangle Detection"))
// //{
// // window.ShowImage(dst);
// // Cv2.WaitKey(0);
// //}
//}
}
}
}
主界面:
<Window x:Class="ShanHaiJingToolkit.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ShanHaiJingToolkit" mc:Ignorable="d" Title="统计部落活跃" Height="1020" Width="1050"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10 0 0 0"> <TextBlock Text="图片路径:"/> <TextBox Text="E:\ShanHaiJingPic\20240415" x:Name="filePath" Width="200"/> <TextBlock Text="需要加活跃数:" Margin="10 0 0 0"/> <TextBox x:Name="txtAdd" Text="0" Width="100"/> <TextBlock Text="生成标题:" Margin="10 0 0 0"/> <TextBox x:Name="txtTitle" Text="31305部落活跃奖励 2024-04-08至04-14" Width="250"/> <Button Content="统计活跃" Margin="10 0 0 0" Click="Button_Click"/> <Button Content="生成大图" Margin="10 0 0 0" Click="Button_Click_1"/> </StackPanel> <GroupBox Header="数据列表" Grid.Row="1"> <ScrollViewer> <WrapPanel x:Name="dataPanel"/> </ScrollViewer> </GroupBox> </Grid> </Window>
using ShanHaiJingToolkit.Common; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace ShanHaiJingToolkit { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { dataPanel.Children.Clear(); var fileDirectory = filePath.Text?.Trim(); int itemWidth = 0; int itemHeight = 0; List<string> files = new List<string>(); await Task.Run(() => { try { CvHelper cvHelper = new CvHelper(); cvHelper.ReadPictureActivityValue(fileDirectory, out itemWidth, out itemHeight); } catch (Exception ex) { Dispatcher.Invoke(() => { MessageBox.Show("读取出错!"); }); } var tempImagesDirectory = fileDirectory + "\\temp\\"; System.IO.DirectoryInfo directoryInfo = new System.IO.DirectoryInfo(tempImagesDirectory); foreach (var file in directoryInfo.GetFiles()) { files.Add(file.FullName); } }); itemWidth /= 3; itemHeight /= 3; Grid titleGrid = new Grid() { Width = dataPanel.ActualWidth, Height = itemHeight }; TextBlock textBlock = new TextBlock() { Text = txtTitle.Text, FontSize = 30, Foreground = Brushes.YellowGreen, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center, }; titleGrid.Children.Add(textBlock); dataPanel.Children.Add(titleGrid); int.TryParse(txtAdd.Text, out var addVal); int sumHuoYue = 0; int sumJiangli = 0; List<Tuple<int, string>> tuples = new List<Tuple<int, string>>(); foreach (var file in files) { var fileName = file.Substring(file.LastIndexOf('\\') + 1).Replace(".png", ""); int countResult = GetVal(fileName); sumHuoYue += countResult; countResult += addVal; var jiangliCount = GetFinalVal(countResult); sumJiangli += jiangliCount; tuples.Add(new Tuple<int, string>(jiangliCount, file)); } tuples = tuples.OrderByDescending(x => x.Item1).ToList(); foreach (var item in tuples) { Grid grid = new Grid() { Width = itemWidth, Height = itemHeight, }; grid.Children.Add(new Image() { Source = new BitmapImage(new Uri(item.Item2, UriKind.Absolute)), Width = itemWidth, Height = itemHeight, }); grid.Children.Add(new TextBlock() { FontSize = 18, Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF087559")), FontWeight = FontWeights.Bold, Text = $"奖励:{item.Item1}芯", HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0, 0, 10, 0), }); dataPanel.Children.Add(grid); } Grid sumGrid = new Grid() { Width = itemWidth, Height = itemHeight, }; var sumTextBlock = new TextBlock() { FontSize = 20, Foreground = Brushes.Red, Text = $"总活跃:{sumHuoYue},总奖励芯数:{sumJiangli}", VerticalAlignment = VerticalAlignment.Center, }; sumGrid.Children.Add(sumTextBlock); dataPanel.Children.Add(sumGrid); //底部 Grid bottomGrid = new Grid() { Width = dataPanel.ActualWidth, Height = itemHeight }; Rectangle rectangle = new Rectangle() { Width = dataPanel.ActualWidth, Height = 2, Fill = Brushes.Black }; bottomGrid.Children.Add(rectangle); dataPanel.Children.Add(bottomGrid); } //VLOOKUP(I2,{0,0;280,3;300,4;320,6;340,8;360,9;380,11},2,1) int GetFinalVal(int val) { if (val < 280) return 0; if (val <= 300 && val >= 280) return 3; if (val <= 320 && val > 300) return 4; if (val <= 340 && val > 320) return 6; if (val <= 360 && val > 340) return 8; if (val <= 380 && val > 360) return 9; if (val > 380) return 11; return 0; } int GetVal(string val) { int result = 0; if (!string.IsNullOrEmpty(val)) { var splits = val.Split('-'); int.TryParse(splits[1], out result); } return result; } public void SaveVisualToPng(Visual visual, string filePath, int dpiX = 96, int dpiY = 96) { // 获取控件的边界大小 Rect bounds = VisualTreeHelper.GetDescendantBounds(visual); // 创建 RenderTargetBitmap,其大小与控件大小一致,并设置DPI RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap( (int)(bounds.Width * dpiX / 96), (int)(bounds.Height * dpiY / 96), dpiX, dpiY, PixelFormats.Pbgra32); // 将控件渲染到 RenderTargetBitmap 上 renderTargetBitmap.Render(visual); // 将 RenderTargetBitmap 编码为 PNG 图片 PngBitmapEncoder pngEncoder = new PngBitmapEncoder(); pngEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap)); // 保存图片到文件 using (Stream stream = File.Create(filePath)) { pngEncoder.Save(stream); } } private void Button_Click_1(object sender, RoutedEventArgs e) { var fileDirectory = filePath.Text?.Trim(); var fileName = fileDirectory + $"\\{txtTitle.Text}.png"; SaveVisualToPng(dataPanel, fileName); MessageBox.Show("大图生成成功!" + fileName); // 使用默认的文件浏览器打开文件夹 Process.Start("explorer.exe", fileDirectory); } } }

浙公网安备 33010602011771号