☰
Github Copilot 实战: 从零开始用AI写一个OCR工具(1)——WPF+.NET桌面端识别流程拆解
2026/10/4 21:02:53 网站建设 项目流程

1. 从零搭一个 WPF OCR 工具,为什么值得用 Github Copilot 辅助

Github Copilot 在桌面端项目里最实用的场景,不是帮你写一个完整应用,而是帮你把「我知道要做什么,但懒得查 API」的那部分代码快速补全。WPF + .NET 做 OCR 工具就是典型例子:界面布局、拖拽事件、剪贴板处理、图像坐标映射,这些逻辑你脑子里有轮廓,但手写起来要翻不少文档。Copilot 能根据注释和上下文直接给出可运行的骨架,你只需要在关键位置修正类型和参数。

这篇文章要交付的是一个最小可用的 WPF OCR 工具:支持拖拽图片、Ctrl+V 粘贴、点击选择文件三种输入方式,调用 PaddleOCR 做中英文识别,把识别框绘制回原图,右侧同步显示文本。整个项目基于 .NET 9 + WPF,OCR 引擎用 Sdcb.OpenVINO.PaddleOCR,图像处理用 OpenCvSharp。跑通之后,我会把识别服务的调用通道切到 TaoToken 的统一 API 入口,这样后续换模型、加多模态能力时不用改一堆 endpoint。

适合谁看:有 C# 基础、想快速验证桌面端 OCR 可行性的开发者;已经在用 Github Copilot 但不知道怎么引导它生成正确类型代码的人;以及想把本地 OCR 和云端模型调用串起来的工程实践者。下面从项目结构开始,每一步都给可复制的配置和命令。

2. 项目结构与依赖配置:WPF + .NET 9 的 csproj 怎么写

先建一个 WPF 工程,目标框架选 net9.0-windows。用命令行或者 Visual Studio 新建都行,关键是 csproj 里的包引用要一次到位。我试过把 OpenCvSharp 和 PaddleOCR 的运行时包分开装,结果在 x64 环境下缺 native dll,后来统一用 runtime.win 包才稳定。

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net9.0-windows</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <UseWPF>true</UseWPF> <Platforms>x64</Platforms> </PropertyGroup> <ItemGroup> <PackageReference Include="OpenCvSharp4.runtime.win" Version="4.11.0.20250507" /> <PackageReference Include="Sdcb.OpenVINO.PaddleOCR" Version="0.6.8" /> <PackageReference Include="Sdcb.OpenVINO.PaddleOCR.Models.Online" Version="0.6.2" /> <PackageReference Include="Sdcb.OpenVINO.runtime.win-x64" Version="2025.0.0" /> </ItemGroup> </Project>

这里有个坑要注意:Sdcb.OpenVINO.runtime.win-x64必须显式引用,否则运行时会报找不到openvino.dll。另外Platforms建议锁 x64,因为 OpenVINO 的 native 库目前对 AnyCPU 支持不好。

项目目录结构建议这样组织:

MiOcr/ ├── MiOcr.csproj ├── App.xaml ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Services/ │ └── PaddleOCRService.cs └── Models/ └── OcrRegion.cs

把 OCR 服务单独放 Services 目录,后面要换成 TaoToken 通道时只改这一个文件。Models 里放识别结果的轻量封装,避免 UI 层直接依赖 PaddleOCR 的类型。

Copilot 在这里的用法:在 csproj 里敲完PackageReference的前几个字符,它会自动补全版本号;在 Services 目录新建类时,输入// 调用 PaddleOCR 识别图片,返回文本列表和区域信息,它会生成方法签名和基本调用链。但版本号一定要自己核对,Copilot 有时会给过时的版本。

3. 可复制的 OCR 服务配置:从本地 PaddleOCR 到 TaoToken 统一通道

先写本地 OCR 服务,把识别链路跑通。这个类负责三件事:把输入(文件路径、URL、byte[]、Mat)统一转成 Mat,调用 PaddleOCR 模型,返回文本列表和区域结果。

using OpenCvSharp; using Sdcb.OpenVINO.PaddleOCR; using Sdcb.OpenVINO.PaddleOCR.Models; using Sdcb.OpenVINO.PaddleOCR.Models.Online; using System.Diagnostics; namespace MiOcr.Services; public class PaddleOCRService { public static bool IsUrl(string filename) { return Uri.TryCreate(filename, UriKind.Absolute, out var uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); } public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(string filename) { if (string.IsNullOrEmpty(filename)) throw new ArgumentNullException(nameof(filename)); Mat src; if (IsUrl(filename)) { using var http = new HttpClient(); var bytes = await http.GetByteArrayAsync(filename); src = Cv2.ImDecode(bytes, ImreadModes.Color); } else { src = Cv2.ImRead(filename); } return await StartOCR(src); } public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(byte[] imageData) { ArgumentNullException.ThrowIfNull(imageData); var src = Cv2.ImDecode(imageData, ImreadModes.Color); return await StartOCR(src); } public async Task<(List<string> strings, PaddleOcrResult result)> StartOCR(Mat src) { var resultText = new List<string>(); FullOcrModel model = await OnlineFullModels.ChineseV3.DownloadAsync(); using var all = new PaddleOcrAll(model) { AllowRotateDetection = true, Enable180Classification = true, }; var sw = Stopwatch.StartNew(); var result = all.Run(src); Console.WriteLine($"elapsed={sw.ElapsedMilliseconds} ms"); foreach (PaddleOcrResultRegion region in result.Regions) { resultText.Add(region.Text); } src.Dispose(); return (resultText, result); } }

模型下载是异步的,第一次运行会拉取 ChineseV3 模型,大概几十 MB。建议在 UI 上加一个「正在初始化 OCR 模型」的提示,否则用户会以为程序卡死。

现在说 TaoToken 通道的接入。TaoToken 提供统一的 API 入口,Base URL 是https://taotoken.net/api,你可以在控制台生成 Key,然后在代码里把模型调用指向这个 endpoint。对于 OCR 场景,如果你想把识别后的文本再交给大模型做结构化提取(比如从发票图片里抽金额、日期),就可以在本地 OCR 之后追加一次模型调用。

配置方式有两种。第一种是环境变量,适合本地开发:

set TAOTOKEN_API_KEY=sk-你的Key set TAOTOKEN_BASE_URL=https://taotoken.net/api

第二种是配置文件,适合团队协作。在项目根目录建appsettings.json:

{ "TaoToken": { "BaseUrl": "https://taotoken.net/api", "ApiKey": "sk-你的Key", "ModelId": "claude-sonnet-4-20250514" } }

然后在代码里读取:

using System.Text.Json; public class TaoTokenConfig { public string BaseUrl { get; set; } = "https://taotoken.net/api"; public string ApiKey { get; set; } = ""; public string ModelId { get; set; } = "claude-sonnet-4-20250514"; public static TaoTokenConfig Load(string path = "appsettings.json") { var json = File.ReadAllText(path); using var doc = JsonDocument.Parse(json); var root = doc.RootElement.GetProperty("TaoToken"); return new TaoTokenConfig { BaseUrl = root.GetProperty("BaseUrl").GetString()!, ApiKey = root.GetProperty("ApiKey").GetString()!, ModelId = root.GetProperty("ModelId").GetString()!, }; } }

三件套必须齐全:Base URL 指向https://taotoken.net/api,Key 从控制台生成,Model ID 按你实际要用的模型填。缺任何一个都会在请求时返回 401 或 model not found。

如果你用的是 Claude Code 或者 Cline 这类工具做辅助开发,配置逻辑是一样的:Base URL 填 TaoToken 的 API 地址,Key 填生成的 Key,Model ID 填对应模型。CC Switch 里切换配置时,注意把这三项一起改,不要只改 Key。

4. 验证请求与成功结果:跑通最小识别链路

配置写好后,先验证 OCR 服务本身能跑通。在 MainWindow 的构造函数里加一个测试调用,或者单独写个控制台入口:

var service = new PaddleOCRService(); var (texts, result) = await service.StartOCR(@"C:\test\sample.png"); Console.WriteLine($"识别到 {texts.Count} 个文本区域"); foreach (var t in texts) { Console.WriteLine(t); }

第一次运行会触发模型下载,控制台会输出下载进度。下载完成后,你会看到类似这样的输出:

elapsed=342 ms 识别到 5 个文本区域 发票代码 1234567890 开票日期 2025-01-15 金额 ¥1,280.00

如果识别结果为空,先检查图片路径是否正确、图片是否真的包含文字。PaddleOCR 对纯色背景上的小字识别率一般,建议用截图工具截一块有明显文字的屏幕区域做测试。

接下来验证 TaoToken 通道。写一个简单的 HTTP 请求,把 OCR 识别出的文本发给模型做结构化提取:

using System.Net.Http.Headers; using System.Text; using System.Text.Json; public async Task<string> ExtractWithTaoToken(string ocrText) { var config = TaoTokenConfig.Load(); using var http = new HttpClient(); http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", config.ApiKey); var payload = new { model = config.ModelId, messages = new[] { new { role = "user", content = $"从以下OCR文本中提取金额和日期,返回JSON:\n{ocrText}" } }, max_tokens = 512 }; var json = JsonSerializer.Serialize(payload); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await http.PostAsync($"{config.BaseUrl}/v1/messages", content); var body = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) throw new Exception($"TaoToken 请求失败: {response.StatusCode} {body}"); return body; }

成功时返回的 JSON 里会有content数组,里面是模型生成的文本。你可以用JsonDocument解析出content[0].text。

验证顺序建议:先本地 OCR 跑通,再单独测 TaoToken 请求,最后把两者串起来。这样出问题时能快速定位是 OCR 环节还是网络环节。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

401 Unauthorized:Key 没填对,或者 Base URL 写成了https://taotoken.net(少了/api)。检查appsettings.json里的ApiKey是否以sk-开头,BaseUrl是否是https://taotoken.net/api。如果用的是环境变量,确认set之后重启了终端或 IDE。

local proxy failed / connection refused:通常是本地网络配置问题。先确认能正常访问https://taotoken.net/api,用curl测一下:

curl -X POST https://taotoken.net/api/v1/messages ^ -H "Authorization: Bearer sk-你的Key" ^ -H "Content-Type: application/json" ^ -d "{\"model\":\"claude-sonnet-4-20250514\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],\"max_tokens\":10}"

如果 curl 能通但代码不通,检查代码里有没有设置HttpClient的Proxy属性,或者系统代理是否干扰。

reading choices / index out of range:这个报错通常出现在解析模型返回时。TaoToken 返回的 JSON 结构里,content是一个数组,不是字符串。如果你直接content[0]当字符串用,会报类型错误。正确做法是先JsonDocument.Parse,再取root.GetProperty("content")[0].GetProperty("text").GetString()。

OAuth / authentication failed:如果你用的是 Claude Code 或 Cline 这类工具,OAuth 报错说明工具在尝试用账号登录而不是 API Key。需要在工具设置里切换到 API Key 模式,填入 TaoToken 的 Key,Base URL 填https://taotoken.net/api。CC Switch 里切换配置时,确认三件套(Base URL、Key、Model ID)都改了。

模型下载失败:PaddleOCR 第一次运行要下载模型,如果网络不稳定会超时。可以手动下载模型文件放到缓存目录,或者多试几次。控制台会输出下载 URL,用浏览器下好放到对应路径也行。

识别框位置偏移:如果绘制出来的框和文字对不上,检查图片的 DPI 和控件缩放。WPF 的Image控件用Stretch="Uniform"时,实际显示尺寸和原图尺寸不一致,需要做坐标映射。简单做法是绘制时用原图尺寸,显示时让Image控件自己缩放。

6. 继续扩展:把 OCR 结果交给模型做后处理

最小链路跑通后,你可以在这个骨架上加更多能力。比如识别完发票后,自动调用 TaoToken 的模型接口做字段提取;或者识别截图后,让模型判断这段文字的情绪倾向。TaoToken 的模型对话入口在https://taotoken.net/api,控制台可以生成和管理 Key,接入文档里有各语言的调用示例。

如果你打算长期做编码类项目,Coding Plan 适合把模型调用集成到日常开发流里;如果只是偶尔验证模型效果,直接用模型对话页面测试就行。API Keys 页面管理所有 Key,接入文档里有完整的 endpoint 列表和参数说明。

这个 WPF OCR 工具的完整代码结构就是上面这些:csproj 配好依赖,PaddleOCRService 封装识别逻辑,MainWindow 处理三种输入方式和结果绘制,TaoTokenConfig 管理模型通道配置。你可以先把本地 OCR 跑通,再把 TaoToken 的调用加进去,每一步都有可验证的输出。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询