图像简单处理

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

思路:使用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);
        }
    }
}

 

posted @ 2024-04-17 10:31  荀幽  阅读(9)  评论(0编辑  收藏  举报