共计 3657 个字符,预计需要花费 10 分钟才能阅读完成。
背景痛点分析
工作中经常遇到需要从图片中提取文字的场景,比如截图识别、证件信息采集等。传统手动录入效率低下,而接入 OCR 技术时往往会遇到几个头疼的问题:

- 鉴权流程复杂:百度 OCR 的 AccessToken 需要定期刷新,新手容易忽略过期机制
- 图像格式限制:原始 API 只支持特定格式(如 JPG/PNG),实际业务中常遇到 BMP 等格式
- 并发瓶颈:免费版 QPS 限制为 2,稍不注意就会触发限流
技术方案选型
对比主流 OCR 方案,我们选择了百度 OCR 是因为:
- 准确率高:特别是对中文印刷体识别率可达 98% 以上
- 接口丰富:支持身份证、银行卡等特殊场景的结构化识别
- 开发友好:提供标准的 RESTful 接口
与开源的 Tesseract 对比:
| 特性 | 百度 OCR | Tesseract |
|---|---|---|
| 中文识别精度 | ★★★★★ | ★★★☆☆ |
| 部署成本 | 云服务即用 | 需要本地训练 |
| 特殊场景支持 | 证件 / 票据专用 | 通用场景 |
核心实现步骤
1. 准备工作
首先需要在 百度 AI 开放平台 申请 API Key:
- 注册百度云账号
- 创建文字识别应用
- 获取 API Key 和 Secret Key
2. 基础封装类
创建 BaiduOcrService.cs 核心类,使用 HttpClient 封装带重试机制的请求:
public class BaiduOcrService
{
private readonly HttpClient _httpClient;
private readonly ILogger<BaiduOcrService> _logger;
#region 配置参数
private const string AuthUrl = "https://aip.baidubce.com/oauth/2.0/token";
private const string GeneralUrl = "https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic";
private int _maxRetry = 3; // 最大重试次数
#endregion
public BaiduOcrService(HttpClient httpClient, ILogger<BaiduOcrService> logger)
{
_httpClient = httpClient;
_logger = logger;
}
// 获取 AccessToken(带缓存机制)private async Task<string> GetAccessTokenAsync(string apiKey, string secretKey)
{// 实现代码...}
// 带重试机制的通用请求方法
private async Task<string> SendWithRetryAsync(HttpRequestMessage request, int retryCount = 0)
{
try {var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();}
catch(Exception ex) when (retryCount < _maxRetry)
{_logger.LogWarning($"请求失败,正在进行第 {retryCount+1} 次重试. Error: {ex.Message}");
await Task.Delay(1000 * (retryCount + 1)); // 指数退避
return await SendWithRetryAsync(request, retryCount + 1);
}
}
}
3. 图像处理模块
使用 System.Drawing 进行图像预处理:
public static class ImageHelper
{
// 将截图转换为符合要求的 JPG 格式
public static byte[] ProcessImage(Image image, int quality = 75)
{using var ms = new MemoryStream();
// 自动旋转校正
if (Array.IndexOf(image.PropertyIdList, 274) > -1)
{var orientation = (int)image.GetPropertyItem(274).Value[0];
image = RotateImageByExifOrientation(image, orientation);
}
// 质量压缩
var encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
// 转换为 RGB 格式(兼容百度 OCR)using var bitmap = new Bitmap(image);
bitmap.Save(ms, GetEncoder(ImageFormat.Jpeg), encoderParams);
return ms.ToArray();}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{// 获取 JPG 编码器...}
}
4. 完整调用示例
组合各模块实现端到端识别流程:
public async Task<List<string>> RecognizeTextAsync(byte[] imageBytes)
{
// 1. 获取 AccessToken
var token = await GetAccessTokenAsync(_config.ApiKey, _config.SecretKey);
// 2. 准备请求体
var content = new MultipartFormDataContent();
content.Add(new StringContent(token), "access_token");
content.Add(new StringContent(Convert.ToBase64String(imageBytes)), "image");
// 3. 发送请求
var request = new HttpRequestMessage(HttpMethod.Post, GeneralUrl) {Content = content};
var response = await SendWithRetryAsync(request);
// 4. 解析结果
var result = JsonConvert.DeserializeObject<BaiduOcrResult>(response);
return result.words_result?.Select(x => x.words).ToList() ?? new List<string>();
}
生产环境建议
QPS 控制策略
// 使用 Polly 实现速率限制
var ratePolicy = Policy.RateLimitAsync(
numberOfExecutions: 2,
perTimeSpan: TimeSpan.FromSeconds(1),
maxBurst: 1);
// 结合重试策略
var policyWrap = Policy.WrapAsync(retryPolicy, ratePolicy);
敏感数据处理
在存储日志时添加脱敏处理:
private string SanitizeInput(string input)
{
// 身份证号脱敏
if (Regex.IsMatch(input, @"^[1-9]\d{5}\d{4}"))
return Regex.Replace(input, @"(\d{6})\d{8}(\w{4})", "$1********$2");
// 银行卡号脱敏
if (Regex.IsMatch(input, @"^[1-9]\d{12,18}$"))
return Regex.Replace(input, @"(\d{6})\d+(\d{4})", "$1******$2");
return input;
}
延伸开发
基于上述核心模块,可以快速扩展出实用工具:
- 截图识别工具:
- 使用 Graphics.CopyFromScreen 获取屏幕区域
-
通过热键触发识别流程
-
WPF 可视化应用:
<Window> <Grid> <Button Content="截图识别" Click="OnCaptureClick"/> <Image x:Name="PreviewImage"/> <TextBox x:Name="ResultText" AcceptsReturn="True"/> </Grid> </Window>
完整代码示例已上传 Gist:点击查看完整项目
开发心得
经过这次实践,最大的收获是认识到完善的错误处理机制比核心功能更重要。特别是在网络请求场景中,重试策略、限流控制和降级方案缺一不可。建议大家在开发类似工具时:
- 早期就建立完善的日志系统
- 对第三方 API 做必要的抽象隔离
- 在 UI 层提供明确的操作反馈
这些经验让我后续开发效率提升了至少 30%,希望对你也有所帮助。
正文完
