图像简单处理

需求:对上面多张这样的图像,将成员识别出来,读取本周活跃值。生成一张全部由成员组成的大图

思路:使用OpenCVSharp进行目标区域边框检测,根据大小过滤目标区域。使用Tesseract对指定位置进行文字识别(貌似识别精度不高,有乱码。)

主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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);
        }
    }
}
复制代码

 

posted @   荀幽  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
点击右上角即可分享
微信分享提示