GLM-OCR在.NET项目中的集成应用C#调用示例详解最近在做一个内部文档管理系统需要从上传的图片里自动提取文字信息。手动录入效率太低。找了一圈OCR方案要么识别率不理想要么集成起来太复杂。后来试了试GLM-OCR发现效果和易用性都挺不错最关键的是它提供了清晰的API接口用C#调用起来非常顺手。如果你也在用.NET技术栈不管是WinForms、WPF还是ASP.NET Core想给应用加个“看图识字”的功能那这篇文章就是为你准备的。我会用一个完整的C#示例带你走通从图片上传到文字展示的全过程代码可以直接拿去用。1. 场景与价值为什么要在.NET里集成OCR先说说我们当时遇到的具体问题。用户上传的可能是扫描的合同、会议白板照片或者手机拍的文档我们需要把这些图片里的文字变成可编辑、可搜索的文本。传统的做法有几个痛点。一是本地OCR引擎部署麻烦依赖一堆运行时库还得分发。二是很多云服务虽然识别准但按调用次数收费内部系统用量一大成本就上去了。三是集成复杂度有的SDK文档写得云里雾里调试起来费时费力。GLM-OCR的API接口方式就简单多了。它把复杂的模型推理放在服务端我们客户端只需要做三件事把图片传过去、等结果、把结果拿回来处理。这种模式对.NET开发者特别友好因为我们最擅长的就是处理网络请求、数据序列化和界面绑定。集成后带来的价值是实实在在的。我们那个文档系统人工录入一份两三页的文档平均要10分钟还容易出错。接入后上传图片秒级出结果人工只需要做简单的核对和格式调整效率提升非常明显。而且文字变成了结构化数据后续的检索、分析都方便多了。2. 核心流程与准备工作在开始写代码之前我们先理清整个调用流程心里有个谱。整个过程就像寄快递和收快递。你的应用客户端需要做的是准备包裹把要识别的图片转换成一种可以通过网络发送的格式Base64编码。填写运单按照GLM-OCR API的要求组装一个包含图片信息和你期望参数的请求数据包JSON格式。寄出包裹通过HTTP协议将这个数据包发送到指定的API地址。接收回执等待并接收服务器返回的响应也是JSON格式。拆包验货解析响应数据提取出识别出来的文字、位置等信息。展示成果把提取的文字和对应的图片位置在你的软件界面上展示出来。GLM-OCR服务端则负责最重的活接收图片、调用模型进行文字识别、分析文字位置、然后把结果打包好送回来。对于准备工作你只需要确保你的.NET项目.NET Framework 4.6.1 或 .NET Core/.NET 5能够访问到GLM-OCR的API服务地址。通常你需要从服务提供商那里获取一个API端点URL和可能的认证密钥。本文的示例将使用一个假设的公共端点你需要替换成你自己的。3. 分步实现从图片到文字的C#代码接下来我们一步步用C#代码实现上述流程。我会创建一个控制台应用示例但其中的核心类库代码可以无缝迁移到WinForms或WPF项目中。3.1 定义数据模型首先定义我们和API通信时用到的数据结构。这能让我们的代码更清晰也方便使用System.Text.Json或Newtonsoft.Json进行序列化和反序列化。// 这个类代表我们发送给API的请求 public class OcrRequest { // 图片的Base64编码字符串不包含“data:image/...;base64,”前缀 public string ImageBase64 { get; set; } // 可选参数例如是否返回文字位置框、语言类型等 public Dictionarystring, object Parameters { get; set; } new Dictionarystring, object(); } // 这个类代表API返回的响应中的单个文字块 public class TextBlock { // 识别出的文字内容 public string Text { get; set; } // 文字块在图片中的位置坐标 [左上角x, 左上角y, 右下角x, 右下角y] public Listfloat BoundingBox { get; set; } // 识别置信度值越高越可信 public float Confidence { get; set; } } // 这个类代表API返回的完整响应 public class OcrResponse { // 识别出的所有文字块列表 public ListTextBlock TextBlocks { get; set; } new ListTextBlock(); // 整个识别过程是否成功 public bool Success { get; set; } // 如果失败这里会有错误信息 public string Message { get; set; } }3.2 图片处理与Base64编码OCR API通常接收Base64编码的图片字符串。我们需要一个方法把本地图片文件或者内存中的图片转换成这种格式。using System; using System.IO; public static class ImageHelper { /// summary /// 将图片文件转换为Base64字符串 /// /summary /// param nameimagePath图片文件路径/param /// returns纯Base64编码字符串/returns public static string ConvertImageToBase64(string imagePath) { try { byte[] imageBytes File.ReadAllBytes(imagePath); string base64String Convert.ToBase64String(imageBytes); return base64String; } catch (Exception ex) { Console.WriteLine($转换图片失败: {ex.Message}); return null; } } /// summary /// 对于WinForms/WPF可以从Bitmap或BitmapImage转换 /// /summary public static string ConvertBitmapToBase64(System.Drawing.Bitmap bitmap) { using (MemoryStream ms new MemoryStream()) { bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); byte[] imageBytes ms.ToArray(); return Convert.ToBase64String(imageBytes); } } }3.3 调用OCR API的核心方法这是最关键的部分我们使用HttpClient来发送请求和接收响应。注意在实际项目中建议将HttpClient实例化一次并重复使用遵循单例或依赖注入模式而不是每次调用都创建新的。using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; public class GlmOcrClient { private readonly HttpClient _httpClient; private readonly string _apiEndpoint; // 例如: https://api.example.com/v1/ocr public GlmOcrClient(string apiEndpoint) { _apiEndpoint apiEndpoint ?? throw new ArgumentNullException(nameof(apiEndpoint)); _httpClient new HttpClient(); // 可以在这里设置默认请求头如认证信息、超时时间等 // _httpClient.DefaultRequestHeaders.Add(Authorization, Bearer YOUR_API_KEY); _httpClient.Timeout TimeSpan.FromSeconds(30); } /// summary /// 调用OCR接口识别图片中的文字 /// /summary public async TaskOcrResponse RecognizeTextAsync(string imageBase64, Dictionarystring, object parameters null) { var request new OcrRequest { ImageBase64 imageBase64, Parameters parameters ?? new Dictionarystring, object() }; // 将请求对象序列化为JSON字符串 string jsonContent JsonSerializer.Serialize(request); var httpContent new StringContent(jsonContent, Encoding.UTF8, application/json); try { Console.WriteLine(正在发送OCR请求...); HttpResponseMessage response await _httpClient.PostAsync(_apiEndpoint, httpContent); if (response.IsSuccessStatusCode) { string responseJson await response.Content.ReadAsStringAsync(); var ocrResponse JsonSerializer.DeserializeOcrResponse(responseJson, new JsonSerializerOptions { PropertyNameCaseInsensitive true }); // 简单处理假设有文字块就认为成功 ocrResponse.Success ocrResponse?.TextBlocks?.Count 0; if (!ocrResponse.Success string.IsNullOrEmpty(ocrResponse.Message)) { ocrResponse.Message 识别成功但未检测到文字。; } return ocrResponse; } else { string errorBody await response.Content.ReadAsStringAsync(); Console.WriteLine($API请求失败状态码: {response.StatusCode}, 响应: {errorBody}); return new OcrResponse { Success false, Message $HTTP错误: {response.StatusCode} }; } } catch (HttpRequestException ex) { Console.WriteLine($网络请求异常: {ex.Message}); return new OcrResponse { Success false, Message $网络错误: {ex.Message} }; } catch (TaskCanceledException) { Console.WriteLine(请求超时。); return new OcrResponse { Success false, Message 请求超时请检查网络或稍后重试。 }; } catch (Exception ex) { Console.WriteLine($处理请求时发生未知异常: {ex.Message}); return new OcrResponse { Success false, Message $处理错误: {ex.Message} }; } } }3.4 整合测试一个完整的控制台示例让我们把上面的代码组合起来写一个可以跑起来的控制台程序。using System; using System.Collections.Generic; using System.Threading.Tasks; namespace GlmOcrDemo { class Program { static async Task Main(string[] args) { Console.WriteLine(GLM-OCR .NET 集成演示); Console.WriteLine(\n); // 1. 替换成你的真实图片路径 string imagePath C:\test_doc.png; // 2. 替换成你的真实API地址 string apiUrl https://your-glm-ocr-service.com/recognize; // 3. 转换图片 Console.WriteLine($正在处理图片: {imagePath}); string base64Image ImageHelper.ConvertImageToBase64(imagePath); if (string.IsNullOrEmpty(base64Image)) { Console.WriteLine(图片转换失败程序退出。); return; } Console.WriteLine(图片已转换为Base64格式。\n); // 4. 创建客户端并调用API var ocrClient new GlmOcrClient(apiUrl); // 可以设置一些可选参数例如要求返回详细的文本框位置 var parameters new Dictionarystring, object { { return_bbox, true }, { language, zh } // 假设支持中文识别 }; Console.WriteLine(开始调用OCR API...); var result await ocrClient.RecognizeTextAsync(base64Image, parameters); // 5. 处理并展示结果 Console.WriteLine(\n 识别结果 ); if (result.Success) { Console.WriteLine($识别成功共发现 {result.TextBlocks.Count} 个文字块。\n); for (int i 0; i result.TextBlocks.Count; i) { var block result.TextBlocks[i]; Console.WriteLine($[区块 {i 1}]); Console.WriteLine($ 文本: {block.Text}); if (block.BoundingBox ! null block.BoundingBox.Count 4) { Console.WriteLine($ 位置: [{block.BoundingBox[0]:F1}, {block.BoundingBox[1]:F1}, {block.BoundingBox[2]:F1}, {block.BoundingBox[3]:F1}]); } Console.WriteLine($ 置信度: {block.Confidence:P1}\n); } } else { Console.WriteLine($识别失败: {result.Message}); } Console.WriteLine(\n); Console.WriteLine(演示结束。); } } }运行这个程序如果一切配置正确你就能在控制台看到从图片中提取出来的文字内容、位置和可信度了。4. 进阶应用在WinForms/WPF界面中展示控制台看结果不够直观。在实际项目中我们更希望能在图形界面里直观地看到图片和识别出的文字框的对应关系。下面以WinForms为例展示一个简单的集成思路。首先设计一个简单的窗体包含一个PictureBox用于显示原图一个ListView或DataGridView用于展示识别出的文本列表以及一个按钮来触发识别。using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Threading.Tasks; using System.Windows.Forms; namespace WinFormsOcrApp { public partial class MainForm : Form { private GlmOcrClient _ocrClient; private string _currentImagePath; private ListTextBlock _currentTextBlocks; private Pen _highlightPen new Pen(Color.Red, 2); // 用于画框的笔 public MainForm() { InitializeComponent(); // 初始化OCR客户端API地址可以从配置文件中读取 _ocrClient new GlmOcrClient(https://your-glm-ocr-service.com/recognize); _currentTextBlocks new ListTextBlock(); } // “选择图片”按钮点击事件 private async void btnSelectImage_Click(object sender, EventArgs e) { using (OpenFileDialog openFileDialog new OpenFileDialog()) { openFileDialog.Filter 图片文件|*.jpg;*.jpeg;*.png;*.bmp; if (openFileDialog.ShowDialog() DialogResult.OK) { _currentImagePath openFileDialog.FileName; pictureBoxOriginal.Image Image.FromFile(_currentImagePath); listViewResults.Items.Clear(); // 清空旧结果 pictureBoxOriginal.Invalidate(); // 清除之前画的框 _currentTextBlocks.Clear(); // 可选这里可以自动触发识别 // await RecognizeAndDisplayAsync(); } } } // “开始识别”按钮点击事件 private async void btnRecognize_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(_currentImagePath) || pictureBoxOriginal.Image null) { MessageBox.Show(请先选择一张图片。); return; } btnRecognize.Enabled false; listViewResults.Items.Clear(); pictureBoxOriginal.Invalidate(); _currentTextBlocks.Clear(); try { string base64Image ImageHelper.ConvertImageToBase64(_currentImagePath); var result await _ocrClient.RecognizeTextAsync(base64Image); if (result.Success) { _currentTextBlocks result.TextBlocks; DisplayResultsInListView(result.TextBlocks); pictureBoxOriginal.Invalidate(); // 触发重绘画上文字框 } else { MessageBox.Show($识别失败: {result.Message}, 提示, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception ex) { MessageBox.Show($处理过程中发生错误: {ex.Message}, 错误, MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { btnRecognize.Enabled true; } } // 在ListView中展示识别结果 private void DisplayResultsInListView(ListTextBlock textBlocks) { listViewResults.BeginUpdate(); foreach (var block in textBlocks) { var item new ListViewItem(block.Text); item.SubItems.Add(block.Confidence.ToString(P1)); // 可以添加更多信息如坐标 listViewResults.Items.Add(item); } listViewResults.EndUpdate(); } // 在PictureBox上绘制识别出的文字框 private void pictureBoxOriginal_Paint(object sender, PaintEventArgs e) { if (_currentTextBlocks null || _currentTextBlocks.Count 0) return; Graphics g e.Graphics; foreach (var block in _currentTextBlocks) { if (block.BoundingBox ! null block.BoundingBox.Count 4) { // 注意API返回的坐标可能是归一化的0-1也可能是像素坐标。 // 这里假设是像素坐标。如果是归一化坐标需要乘以图片宽高。 float x1 block.BoundingBox[0]; float y1 block.BoundingBox[1]; float x2 block.BoundingBox[2]; float y2 block.BoundingBox[3]; RectangleF rect new RectangleF(x1, y1, x2 - x1, y2 - y1); g.DrawRectangle(_highlightPen, rect.X, rect.Y, rect.Width, rect.Height); // 可以在框旁边标上序号 using (var font new Font(Arial, 10)) using (var brush new SolidBrush(Color.Blue)) { g.DrawString((_currentTextBlocks.IndexOf(block) 1).ToString(), font, brush, x1, y1 - 15); } } } } // 点击ListView项时高亮对应的框可选功能 private void listViewResults_SelectedIndexChanged(object sender, EventArgs e) { if (listViewResults.SelectedIndices.Count 0) { int selectedIndex listViewResults.SelectedIndices[0]; // 可以在这里实现滚动到对应位置或改变框的颜色这里略去具体实现 pictureBoxOriginal.Invalidate(); } } } }这个WinForms示例展示了核心的集成模式选择图片 - 调用我们的GlmOcrClient- 在列表展示文本 - 在原图上绘制对应的文本框。WPF的实现思路类似主要区别在于UI控件的使用和绑定方式。5. 实践中的经验与建议在实际项目里跑通这个流程后我总结了几点经验可能对你有帮助。关于性能图片的Base64编码会让数据体积增大约33%。如果图片很大可以考虑先进行压缩或缩放比如把长边限制在2000像素以内这能在几乎不影响识别率的前提下显著提升传输速度。对于HttpClient一定要记得复用实例而不是用using语句包裹每次调用否则在高频请求下容易耗尽Socket连接。关于错误处理网络请求什么意外都可能发生。除了示例中的基本try-catch在生产环境里建议增加重试机制比如对网络超时重试2-3次并做好日志记录把请求和响应的关键信息不包含敏感图片数据记下来方便排查问题。关于用户体验在界面调用OCR时务必要给用户明确的反馈。比如在btnRecognize_Click方法里禁用按钮并显示一个“识别中...”的提示识别完成后再恢复。对于返回的文本可以提供一键复制、导出为TXT或Word等便捷功能。扩展可能性一旦你拿到了结构化的文本和位置信息能做的事情就多了。比如可以实现“点击图片上的文字在右侧编辑框中修改”然后反向将修改同步回图片这需要额外的渲染技术。或者对识别出的文本进行关键词提取、自动分类等后续处理。6. 总结回过头看在.NET项目里集成GLM-OCR这类服务技术门槛并不高。核心就是熟练运用HttpClient进行网络通信以及使用System.Text.Json处理数据格式。整个过程清晰明了准备数据、发送请求、解析结果、展示出来。本文提供的代码示例已经覆盖了从核心逻辑到界面展示的关键环节你可以直接复制到项目里替换掉API地址和图片处理逻辑很快就能跑起来。这种API化的AI能力接入方式让我们.NET开发者能够在不深入机器学习细节的情况下快速为应用增添实用的智能特性。如果你之前没怎么接触过HTTP API调用可能会觉得有点陌生但跟着步骤走一遍你会发现它和你调用其他第三方Web服务比如支付接口、地图接口没什么本质区别。关键是动手试遇到问题多看返回的错误信息大部分都能顺利解决。希望这个示例能帮你把“图片转文字”的功能轻松做出来。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。