找回密码
 立即注册
首页 业界区 业界 Github Copilot 实战: 从零开始用AI写一个OCR工具 (1) ...

Github Copilot 实战: 从零开始用AI写一个OCR工具 (1)

宁觅波 2025-6-3 00:28:39
最近AI很火,咱也尝试一下由浅入深探索一下 Github Copilot 的能力和底限.
环境是 Windows11 + Microsoft Visual Studio Enterprise 2022 (64 位) - Current 版本 17.13.7 + VS内置的 Github Copilot Pro
项目目标

本项目旨在利用 Github Copilot 辅助开发,从零实现一个基于 AI 的 OCR(光学字符识别)工具。项目采用 .NET 9 和 WPF 技术栈,集成了 PaddleOCR 作为核心识别引擎,实现了图片文字识别、区域选择、结果高亮与复制等实用功能。
主要技术与依赖

•        开发语言与平台:C#,.NET 9,WPF
•        OCR引擎:Sdcb.OpenVINO.PaddleOCR
•        图像处理:OpenCvSharp
•        界面交互:WPF,支持拖拽、粘贴、截图等多种图片输入方式
•        AI辅助开发:Github Copilot 提供代码建议与自动补全
核心功能


  • 图片输入
    •        支持文件选择、拖拽、粘贴、屏幕截图等多种方式加载图片。
  • OCR识别
    •        调用 PaddleOCR 进行文字识别,支持中文、英文等多语种。
    •        识别结果实时显示,支持区域高亮和文字复制。
  • 用户体验优化
    •        首次模型下载时,异步回调 UI,友好提示“正在初始化OCR模型,请稍候...”,避免用户等待时无响应。
    •        识别过程有进度提示,提升交互体验。
  • 结果交互
    •        支持鼠标框选图片区域,提取并复制选中区域的文字。
    •        右键点击可复制单个识别文本。
关键实现思路

•        AI驱动开发:通过 Copilot 自动生成代码骨架、方法实现和注释,大幅提升开发效率。
•        异步与回调:模型下载和识别过程均为异步,UI 通过回调及时反馈进度和状态。
•        图像与坐标映射:实现了图片与控件坐标的精准映射,保证高亮和选区准确。
源码

https://github.com/densen2014/Blazor100/tree/master/AI/MiOcr
首先创建wpf工程
  1. <Project Sdk="Microsoft.NET.Sdk">
  2.   <PropertyGroup>
  3.     <OutputType>WinExe</OutputType>
  4.     <TargetFramework>net9.0-windows</TargetFramework>
  5.     <Nullable>enable</Nullable>
  6.     <ImplicitUsings>enable</ImplicitUsings>
  7.     <UseWPF>true</UseWPF>
  8.   </PropertyGroup>
  9.     <ItemGroup>
  10.         <PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" />
  11.         <PackageReference Include="Sdcb.OpenVINO.PaddleOCR" Version="0.6.8" />
  12.         <PackageReference Include="Sdcb.OpenVINO.PaddleOCR.Models.Online" Version="0.6.2" />
  13.         <PackageReference Include="Sdcb.OpenVINO.runtime.win-x64" Version="2025.0.0" />
  14.     </ItemGroup>
  15. </Project>
复制代码
简单放置一个本地ocr服务
  1. using OpenCvSharp;
  2. using Sdcb.OpenVINO.PaddleOCR;
  3. using Sdcb.OpenVINO.PaddleOCR.Models;
  4. using Sdcb.OpenVINO.PaddleOCR.Models.Online;
  5. using System.Diagnostics;
  6. using System.Net.Http;
  7. namespace JovenApi;
  8. public class PaddleOCRService
  9. {
  10.     public static bool IsUrl(string filename)
  11.     {
  12.         return Uri.TryCreate(filename, UriKind.Absolute, out var uriResult)
  13.                && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
  14.     }
  15.     public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(string filename)
  16.     {
  17.         Mat src;
  18.         if (string.IsNullOrEmpty(filename))
  19.         {
  20.             throw new ArgumentNullException(nameof(filename));
  21.         }
  22.         if (IsUrl(filename))
  23.         {
  24.             src = Cv2.ImDecode(await new HttpClient().GetByteArrayAsync(filename), ImreadModes.Color);
  25.         }
  26.         else
  27.         {
  28.             src = Cv2.ImRead(filename);
  29.         }
  30.         return await StartOCR(src);
  31.     }
  32.     public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(byte[] imageData)
  33.     {
  34.         Mat src;
  35.         ArgumentNullException.ThrowIfNull(imageData);
  36.         src = Cv2.ImDecode(imageData, ImreadModes.Color);
  37.         return await StartOCR(src);
  38.     }
  39.     public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(Mat src)
  40.     {
  41.         PaddleOcrResult result;
  42.         var resultText = new List<string>();
  43.         FullOcrModel model = await OnlineFullModels.ChineseV3.DownloadAsync();
  44.         using (PaddleOcrAll all = new(model)
  45.         {
  46.             AllowRotateDetection = true,
  47.             Enable180Classification = true,
  48.         })
  49.         {
  50.             Stopwatch sw = Stopwatch.StartNew();
  51.             result = all.Run(src);
  52.             Console.WriteLine($"elapsed={sw.ElapsedMilliseconds} ms");
  53.             Console.WriteLine("Detected all texts: \n" + result.Text);
  54.             foreach (PaddleOcrResultRegion region in result.Regions)
  55.             {
  56.                 Console.WriteLine($"Text: {region.Text}, Score: {region.Score}, RectCenter: {region.Rect.Center}, RectSize:    {region.Rect.Size}, Angle: {region.Rect.Angle}");
  57.                 resultText.Add(region.Text);
  58.             }
  59.         }
  60.         src.Dispose();
  61.         return (resultText, result);
  62.     }
  63. }
复制代码
A. 提示词 "实现拖放,粘贴图片或者点击打开选择文件获取一张图片, 并且调用ocr服务,将结果绘图到原图上, 右边显示相应的文本"

下面是一个完整的 WPF 方案,支持拖放图片、粘贴图片、点击选择图片,并调用 OCR 服务,将识别结果绘制到原图上,右侧显示文本。代码已适配你的 PaddleOCRService,并假设其 StartOCR(byte[] image) 返回识别结果(如文本框列表)。

  • MainWindow.xaml(UI布局)
  1. <Window x:
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.         Title="MiOcr" Height="600" Width="900"
  5.         AllowDrop="True"
  6.         KeyDown="Window_KeyDown"
  7.         Drop="Window_Drop"
  8.         >
  9.     <Grid>
  10.         <Grid.ColumnDefinitions>
  11.             <ColumnDefinition Width="2*"/>
  12.             <ColumnDefinition Width="*"/>
  13.         </Grid.ColumnDefinitions>
  14.         <Border Grid.Column="0" Margin="10" BorderBrush="Gray" BorderThickness="1" Background="#FFF"
  15.                 MouseLeftButtonUp="ImageBorder_MouseLeftButtonUp"
  16.                 AllowDrop="True"
  17.                 Drop="ImageBorder_Drop"
  18.                 >
  19.             <Image x:Name="MainImage" Stretch="Uniform" />
  20.         </Border>
  21.         <ScrollViewer Grid.Column="1" Margin="10">
  22.             <TextBox x:Name="OcrTextBox" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" TextWrapping="Wrap" FontSize="16"/>
  23.         </ScrollViewer>
  24.         <Button Content="选择图片" Width="100" Height="30" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20"
  25.                 Click="SelectImageButton_Click"/>
  26.     </Grid>
  27. </Window>
复制代码

  • MainWindow.xaml.cs(核心逻辑)
  1. using JovenApi;
  2. using Microsoft.Win32;
  3. using System;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Input;
  9. using System.Windows.Media.Imaging;
  10. using System.Windows.Media;
  11. using System.Windows.Controls;
  12. using System.Collections.Generic;
  13. namespace MiOcr;
  14. public partial class MainWindow : Window
  15. {
  16.     private BitmapImage? _currentImage;
  17.     private byte[]? _currentImageBytes;
  18.     public MainWindow()
  19.     {
  20.         InitializeComponent();
  21.         this.PreviewDragOver += (s, e) => e.Handled = true; // 允许拖放
  22.         this.PreviewKeyDown += Window_KeyDown;
  23.     }
  24.     // 拖放图片
  25.     private async void Window_Drop(object sender, DragEventArgs e)
  26.     {
  27.         if (e.Data.GetDataPresent(DataFormats.FileDrop))
  28.         {
  29.             var files = (string[])e.Data.GetData(DataFormats.FileDrop);
  30.             if (files.Length > 0 && IsImageFile(files[0]))
  31.             {
  32.                 await LoadAndOcrImage(files[0]);
  33.             }
  34.         }
  35.     }
  36.     // Border区域拖放
  37.     private async void ImageBorder_Drop(object sender, DragEventArgs e)
  38.     {
  39.         await Window_Drop(sender, e);
  40.     }
  41.     // 粘贴图片
  42.     private async void Window_KeyDown(object sender, KeyEventArgs e)
  43.     {
  44.         if (e.Key == Key.V && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
  45.         {
  46.             if (Clipboard.ContainsImage())
  47.             {
  48.                 var img = Clipboard.GetImage();
  49.                 if (img != null)
  50.                 {
  51.                     var bmp = BitmapFromClipboard(img);
  52.                     using var ms = new MemoryStream();
  53.                     var encoder = new PngBitmapEncoder();
  54.                     encoder.Frames.Add(BitmapFrame.Create(bmp));
  55.                     encoder.Save(ms);
  56.                     await LoadAndOcrImage(ms.ToArray());
  57.                 }
  58.             }
  59.         }
  60.     }
  61.     // 点击选择图片
  62.     private async void SelectImageButton_Click(object sender, RoutedEventArgs e)
  63.     {
  64.         var dlg = new OpenFileDialog
  65.         {
  66.             Filter = "图片文件|*.jpg;*.jpeg;*.png;*.bmp;*.gif"
  67.         };
  68.         if (dlg.ShowDialog() == true)
  69.         {
  70.             await LoadAndOcrImage(dlg.FileName);
  71.         }
  72.     }
  73.     // 点击图片区域也可选择图片
  74.     private async void ImageBorder_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  75.     {
  76.         await SelectImageButton_Click(sender, e);
  77.     }
  78.     // 加载图片并调用OCR
  79.     private async Task LoadAndOcrImage(string filePath)
  80.     {
  81.         _currentImageBytes = await File.ReadAllBytesAsync(filePath);
  82.         _currentImage = new BitmapImage(new Uri(filePath));
  83.         MainImage.Source = _currentImage;
  84.         await RunOcrAndDraw(_currentImageBytes);
  85.     }
  86.     // 加载图片并调用OCR(字节流)
  87.     private async Task LoadAndOcrImage(byte[] imageBytes)
  88.     {
  89.         _currentImageBytes = imageBytes;
  90.         using var ms = new MemoryStream(imageBytes);
  91.         var bmp = new BitmapImage();
  92.         bmp.BeginInit();
  93.         bmp.CacheOption = BitmapCacheOption.OnLoad;
  94.         bmp.StreamSource = ms;
  95.         bmp.EndInit();
  96.         bmp.Freeze();
  97.         _currentImage = bmp;
  98.         MainImage.Source = _currentImage;
  99.         await RunOcrAndDraw(imageBytes);
  100.     }
  101.     // OCR并绘制
  102.     private async Task RunOcrAndDraw(byte[] imageBytes)
  103.     {
  104.         OcrTextBox.Text = "识别中...";
  105.         var ocr = new PaddleOCRService();
  106.         var results = await ocr.StartOCR(imageBytes);
  107.         if (results == null || results.Count == 0)
  108.         {
  109.             OcrTextBox.Text = "未识别到文本";
  110.             return;
  111.         }
  112.         // 假设results为List<OcrResult>,每个OcrResult有Text和Box(左上、右下坐标)
  113.         // 你需要根据PaddleOCRService的返回类型适配
  114.         OcrTextBox.Text = string.Join(Environment.NewLine, results.Select(r => r.Text));
  115.         // 绘制识别框和文字
  116.         var drawed = DrawOcrResultsOnImage(_currentImage, results);
  117.         MainImage.Source = drawed;
  118.     }
  119.     // 判断文件是否为图片
  120.     private bool IsImageFile(string file)
  121.     {
  122.         var ext = System.IO.Path.GetExtension(file).ToLower();
  123.         return ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp" || ext == ".gif";
  124.     }
  125.     // 从ClipboardBitmapSource转BitmapSource
  126.     private BitmapSource BitmapFromClipboard(BitmapSource src)
  127.     {
  128.         var encoder = new PngBitmapEncoder();
  129.         encoder.Frames.Add(BitmapFrame.Create(src));
  130.         using var ms = new MemoryStream();
  131.         encoder.Save(ms);
  132.         ms.Position = 0;
  133.         var bmp = new BitmapImage();
  134.         bmp.BeginInit();
  135.         bmp.CacheOption = BitmapCacheOption.OnLoad;
  136.         bmp.StreamSource = ms;
  137.         bmp.EndInit();
  138.         bmp.Freeze();
  139.         return bmp;
  140.     }
  141.     // 绘制OCR结果到图片
  142.     private BitmapSource DrawOcrResultsOnImage(BitmapImage? image, List<OcrResult> results)
  143.     {
  144.         if (image == null) return null!;
  145.         int width = image.PixelWidth;
  146.         int height = image.PixelHeight;
  147.         var visual = new DrawingVisual();
  148.         using (var dc = visual.RenderOpen())
  149.         {
  150.             dc.DrawImage(image, new Rect(0, 0, width, height));
  151.             var pen = new Pen(Brushes.Red, 2);
  152.             var typeface = new Typeface("Arial");
  153.             foreach (var r in results)
  154.             {
  155.                 // 假设 r.Box 是 int[4] 或 Point[],请根据实际类型适配
  156.                 var box = r.Box; // 例如: [x1, y1, x2, y2]
  157.                 dc.DrawRectangle(null, pen, new Rect(box[0], box[1], box[2] - box[0], box[3] - box[1]));
  158.                 var formattedText = new FormattedText(
  159.                     r.Text,
  160.                     System.Globalization.CultureInfo.CurrentCulture,
  161.                     FlowDirection.LeftToRight,
  162.                     typeface,
  163.                     20,
  164.                     Brushes.Yellow,
  165.                     1.25);
  166.                 dc.DrawText(formattedText, new Point(box[0], box[1] - 24));
  167.             }
  168.         }
  169.         var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
  170.         bmp.Render(visual);
  171.         bmp.Freeze();
  172.         return bmp;
  173.     }
  174. }
  175. // 假设OCR结果类型如下,实际请根据PaddleOCRService返回类型调整
  176. public class OcrResult
  177. {
  178.     public string Text { get; set; }
  179.     public int[] Box { get; set; } // [x1, y1, x2, y2]
  180. }
复制代码
说明
•        拖放:支持将图片文件拖到窗口或图片区域。
•        粘贴:Ctrl+V 粘贴剪贴板图片。
•        点击选择:点击图片区域或按钮弹出文件选择对话框。
•        OCR调用:调用 PaddleOCRService.StartOCR(byte[]),并假设返回 List,每个包含 Text 和 Box。
•        绘图:在图片上绘制识别框和文字,右侧显示所有识别文本。
•        UI:左侧图片,右侧文本,顶部有“选择图片”按钮。
如 PaddleOCRService 返回类型不同,请补充其定义,我可帮你适配。
如需支持多种图片格式或更复杂的坐标,请补充说明。
到这里,一个基本框架居然就完成了. 运行起来也差不多达到预期. 真是惊为天人.
修正一下,提示词 "返回的 results.result 类型为 PaddleOcrResult"

非常好,PaddleOCRService.StartOCR 返回的 results.result 类型为 PaddleOcrResult。
PaddleOcrResult 结构如下(基于 Sdcb.OpenVINO.PaddleOCR 官方文档和源码):
  1. public class PaddleOcrResult
  2. {
  3.     public List<PaddleOcrResultRegion> Regions { get; set; }
  4. }
  5. public class PaddleOcrResultRegion
  6. {
  7.     public string Text { get; set; }
  8.     public float Score { get; set; }
  9.     public RotatedRect Rect { get; set; }
  10.     // 还有 PolyPoints、Label 等属性
  11. }
复制代码
•        Rect 是 RotatedRect,包含中心点、尺寸、角度。
•        PolyPoints 是多边形点(通常为4个点,顺时针/逆时针),可用于绘制识别框。
你的代码应这样处理:

  • 显示文本
    OcrTextBox.Text = string.Join(Environment.NewLine, results.result.Regions.Select(r => r.Text));
  • 绘制识别框和文字
    建议用 PolyPoints 绘制多边形框,Rect.Center 作为文字起点。
  1. private BitmapSource DrawOcrResultsOnImage(BitmapImage? image, PaddleOcrResult result)
  2. {
  3.     if (image == null) return null!;
  4.     int width = image.PixelWidth;
  5.     int height = image.PixelHeight;
  6.     var visual = new DrawingVisual();
  7.     using (var dc = visual.RenderOpen())
  8.     {
  9.         dc.DrawImage(image, new Rect(0, 0, width, height));
  10.         var pen = new Pen(Brushes.Red, 2);
  11.         var typeface = new Typeface("Arial");
  12.         foreach (var region in result.Regions)
  13.         {
  14.             // 绘制多边形框
  15.             if (region.PolyPoints != null && region.PolyPoints.Length >= 4)
  16.             {
  17.                 var points = region.PolyPoints.Select(p => new System.Windows.Point(p.X, p.Y)).ToArray();
  18.                 var polygon = new System.Windows.Media.PolyLineSegment(points, true);
  19.                 var figure = new PathFigure(points[0], new[] { polygon }, true);
  20.                 var geometry = new PathGeometry(new[] { figure });
  21.                 dc.DrawGeometry(null, pen, geometry);
  22.             }
  23.             // 绘制文字
  24.             var formattedText = new FormattedText(
  25.                 region.Text,
  26.                 System.Globalization.CultureInfo.CurrentCulture,
  27.                 FlowDirection.LeftToRight,
  28.                 typeface,
  29.                 20,
  30.                 Brushes.Yellow,
  31.                 1.25);
  32.             dc.DrawText(formattedText, new System.Windows.Point(region.Rect.Center.X, region.Rect.Center.Y));
  33.         }
  34.     }
  35.     var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
  36.     bmp.Render(visual);
  37.     bmp.Freeze();
  38.     return bmp;
  39. }
复制代码

  • 调用方式
  1. var drawed = DrawOcrResultsOnImage(_currentImage, results.result);
  2. MainImage.Source = drawed;
复制代码
总结
•        results.result 是 PaddleOcrResult,其 Regions 为每个识别区域。
•        推荐用 PolyPoints 绘制多边形框,region.Text 作为识别文本。
•        右侧文本框用 string.Join(Environment.NewLine, results.result.Regions.Select(r => r.Text))。
继续修正,提示词 "绘制多边形框方法有点错误, 根据 Console.WriteLine($"Text: {region.Text}, Score: {region.Score}, RectCenter: {region.Rect.Center}, RectSize:    {region.Rect.Size}, Angle: {region.Rect.Angle}"); 修正一下"

你当前的绘制方法有误,因为 region.Rect 是 RotatedRect,它不是点集合,而是一个带有中心点、尺寸和角度的旋转矩形。
正确做法是:使用 region.Rect.GetVertices() 获取多边形的四个顶点,然后用这些点绘制多边形框。
请将 DrawOcrResultsOnImage 方法中的多边形绘制部分修正如下:
  1. private BitmapSource DrawOcrResultsOnImage(BitmapImage? image, PaddleOcrResult result)
  2. {
  3.     if (image == null) return null!;
  4.     int width = image.PixelWidth;
  5.     int height = image.PixelHeight;
  6.     var visual = new DrawingVisual();
  7.     using (var dc = visual.RenderOpen())
  8.     {
  9.         dc.DrawImage(image, new Rect(0, 0, width, height));
  10.         var pen = new Pen(Brushes.Red, 2);
  11.         var typeface = new Typeface("Arial");
  12.         foreach (var region in result.Regions)
  13.         {
  14.             // 正确获取四个顶点
  15.             var vertices = region.Rect.GetVertices(); // 返回 PointF[],长度为4
  16.             var points = vertices.Select(p => new System.Windows.Point(p.X, p.Y)).ToArray();
  17.             // 绘制多边形
  18.             var figure = new PathFigure(points[0], new[] { new PolyLineSegment(points.Skip(1), true) }, true);
  19.             var geometry = new PathGeometry(new[] { figure });
  20.             dc.DrawGeometry(null, pen, geometry);
  21.             // 绘制文字(以中心点为准)
  22.             var formattedText = new FormattedText(
  23.                 region.Text,
  24.                 System.Globalization.CultureInfo.CurrentCulture,
  25.                 FlowDirection.LeftToRight,
  26.                 typeface,
  27.                 20,
  28.                 Brushes.Yellow,
  29.                 1.25);
  30.             dc.DrawText(formattedText, new System.Windows.Point(region.Rect.Center.X, region.Rect.Center.Y));
  31.         }
  32.     }
  33.     var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
  34.     bmp.Render(visual);
  35.     bmp.Freeze();
  36.     return bmp;
  37. }
复制代码
关键修正说明:
•        用 region.Rect.GetVertices() 获取四个顶点,而不是直接遍历 Rect。
•        用 PolyLineSegment 和 PathFigure 绘制闭合多边形。
•        文字仍然以 region.Rect.Center 为基准绘制。
这个时候还是有错误, AI需要继续引导一下 , 提示词 "region.Rect 的定义是 Point2f center, Size2f size, float angle"

感谢补充,region.Rect 是 RotatedRect,其定义为:
  1. public struct RotatedRect
  2. {
  3.     public Point2f Center; // 中心点
  4.     public Size2f Size;    // 宽高
  5.     public float Angle;    // 旋转角度(度)
  6. }
复制代码
正确绘制多边形框的方法是:
•        使用 region.Rect.GetVertices() 获取四个顶点(Point2f[],顺序为左上、右上、右下、左下)。
•        用这些点绘制闭合多边形。
你的代码如下即可:
  1. private BitmapSource DrawOcrResultsOnImage(BitmapImage? image, PaddleOcrResult result)
  2. {
  3.     if (image == null) return null!;
  4.     int width = image.PixelWidth;
  5.     int height = image.PixelHeight;
  6.     var visual = new DrawingVisual();
  7.     using (var dc = visual.RenderOpen())
  8.     {
  9.         dc.DrawImage(image, new Rect(0, 0, width, height));
  10.         var pen = new Pen(Brushes.Red, 2);
  11.         var typeface = new Typeface("Arial");
  12.         foreach (var region in result.Regions)
  13.         {
  14.             // 获取四个顶点
  15.             var vertices = region.Rect.GetVertices(); // Point2f[],长度4
  16.             var points = vertices.Select(p => new System.Windows.Point(p.X, p.Y)).ToArray();
  17.             // 绘制多边形
  18.             var figure = new PathFigure(points[0], new[] { new PolyLineSegment(points.Skip(1), true) }, true);
  19.             var geometry = new PathGeometry(new[] { figure });
  20.             dc.DrawGeometry(null, pen, geometry);
  21.             // 绘制文字(以中心点为准)
  22.             var formattedText = new FormattedText(
  23.                 region.Text,
  24.                 System.Globalization.CultureInfo.CurrentCulture,
  25.                 FlowDirection.LeftToRight,
  26.                 typeface,
  27.                 20,
  28.                 Brushes.Yellow,
  29.                 1.25);
  30.             dc.DrawText(formattedText, new System.Windows.Point(region.Rect.Center.X, region.Rect.Center.Y));
  31.         }
  32.     }
  33.     var bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
  34.     bmp.Render(visual);
  35.     bmp.Freeze();
  36.     return bmp;
  37. }
复制代码
要点说明:
•        region.Rect.GetVertices() 是最安全、最准确的方式获取旋转矩形的四个顶点。
•        直接用 Center、Size、Angle 计算顶点较为繁琐,库已封装好。
•        文字建议以 region.Rect.Center 为锚点绘制。
这样即可正确绘制 PaddleOCR 识别区域的旋转矩形框和文本。
本节成果

1.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册