.NET 实战:调用千问视觉模型实现 OCR(车票识别完整教程)

张开发
2026/4/17 14:08:24 15 分钟阅读

分享文章

.NET 实战:调用千问视觉模型实现 OCR(车票识别完整教程)
很多人第一次接入 AI OCR会踩几个典型坑❌ 直接调接口结果不稳定❌ AI 返回 JSON 不规范❌ 网络偶发失败没有兜底❌ 代码无法复用这篇直接带你做一件事写一套“可复用 稳定 可扩展”的 OCR 调用组件 一、核心思路先搞清本质千问视觉模型 ≠ 传统 OCR它本质是“看图 按你要求生成结构化文本”流程如下图片 → Base64 → 千问模型 → 文本(JSON) → 清洗 → DTO 二、请求结构必须理解请求体核心结构{ model: qwen-vl, messages: [ { role: user, content: [ { type: image_url, image_url: { url: data:image/jpeg;base64,xxx } }, { type: text, text: 你的Prompt } ] } ] } 重点图片必须 Base64Prompt 决定识别质量 三、示例车票 OCR Prompt请识别图片中的火车票信息并严格按JSON格式输出 { PassengerName: , TrainNumber: , FromStation: , ToStation: , DepartureTime: , Seat: , Price: } 要求 1. 只输出JSON 2. 不要解释 3. 金额只保留数字 4. 时间格式 yyyy-MM-dd HH:mm:ss 5. 缺失字段返回空字符串 四、核心设计工程级这一套你可以直接复用到发票水单提单合同✅ 关键设计点1. 通用泛型方法TaskT ExtractAsyncT() 一套代码支持所有 OCR 类型2. JSON 清洗必须处理中文引号尾逗号3. 重试机制防网络波动接口偶发失败4. DTO 强类型 不要返回 string必须结构化️ 五、稳定性策略精华你这套代码真正值钱的地方在这里✅ 重试机制3次✅ JSON 修复✅ 文件大小限制✅ 临时文件自动清理✅ 反序列化兜底⚠️ 六、生产环境建议 Key 管理不要写死放环境变量配置文件⚡ 并发控制OCR 是重资源操作 建议限流SemaphoreSlim 日志建议记录原始返回清洗后 JSON 七、完整可运行代码核心部分下面这份代码是精简 工程可用版本 1. OCR Helperusing System; using System.IO; using System.Net.Http; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; public static class OcrHelper { private static readonly HttpClient _httpClient new HttpClient { Timeout TimeSpan.FromSeconds(60) }; private const string ApiUrl 你的API地址; private const string ApiKey 你的Key; private const string Model 你的模型名称; static OcrHelper() { _httpClient.DefaultRequestHeaders.Add(Authorization, $Bearer {ApiKey}); } public static async TaskT ExtractAsyncT(string imagePath, string prompt) { var json await ExtractRawAsync(imagePath, prompt); return JsonSerializer.DeserializeT(json, new JsonSerializerOptions { PropertyNameCaseInsensitive true }); } public static async Taskstring ExtractRawAsync(string imagePath, string prompt) { byte[] bytes await File.ReadAllBytesAsync(imagePath); if (bytes.Length 5 * 1024 * 1024) throw new Exception(图片不能超过5MB); var base64 Convert.ToBase64String(bytes); var requestBody new { model Model, messages new object[] { new { role user, content new object[] { new { type image_url, image_url new { url $data:image/jpeg;base64,{base64} } }, new { type text, text prompt } } } }, stream false, extra_body new { enable_thinking false } }; var content new StringContent( JsonSerializer.Serialize(requestBody), Encoding.UTF8, application/json ); var response await SendWithRetryAsync(content); var message ExtractContent(response); return CleanJson(message); } private static async Taskstring SendWithRetryAsync(StringContent content) { for (int i 0; i 3; i) { try { var response await _httpClient.PostAsync(ApiUrl, content); var text await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) throw new Exception(text); return text; } catch when (i 2) { await Task.Delay(1000 * (i 1)); } } throw new Exception(OCR请求失败); } private static string ExtractContent(string responseText) { using var doc JsonDocument.Parse(responseText); return doc.RootElement .GetProperty(choices)[0] .GetProperty(message) .GetProperty(content) .GetString(); } private static string CleanJson(string json) { if (string.IsNullOrWhiteSpace(json)) return {}; json json .Replace(json, ) .Replace(, ) .Replace(“, \) .Replace(”, \) .Trim(); json Regex.Replace(json, ,\\s*}, }); json Regex.Replace(json, ,\\s*], ]); return json; } } 2. DTO车票public class TrainTicketDto { public string PassengerName { get; set; } public string TrainNumber { get; set; } public string FromStation { get; set; } public string ToStation { get; set; } public string DepartureTime { get; set; } public string Seat { get; set; } public string Price { get; set; } } 3. 调用示例var prompt 请识别图片中的火车票信息并按JSON输出 { PassengerName: , TrainNumber: , FromStation: , ToStation: , DepartureTime: , Seat: , Price: }; var result await OcrHelper.ExtractAsyncTrainTicketDto( test.jpg, prompt ); Console.WriteLine(result.PassengerName); 最后总结一句话AI OCR 不难难的是把“不稳定输出”变成“稳定系统能力”。

更多文章