C# 结合PaddleOCRSharp搭建Http网络服务

article/2025/7/28 0:53:03

Windows打开端口:
控制面板 > 系统和安全 > 防火墙> 高级设置 → 入站规则 → 右侧选择 → 新建规则 → 端口 → 协议类型 TCP→ 端口

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Linq;
using PaddleOCRSharp;
using HttpMultipartParser;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Drawing.Imaging;
using OpenCvSharp;
using System.Net.Http;class Program
{static void Main(string[] args){//string remoteUrl = "http://10.50.7.210:9011/profile/upload/2025/05/21/image_20250521093746A004.jpg";// curl "http://localhost:8082/image-ocr?templateCode=abc123&path=E://3.png"// 打开端口:控制面板 > 系统和安全 >  防火墙> 高级设置 → 入站规则 → 右侧选择 → 新建规则 → 端口  → 协议类型 TCP→ 端口string baseUrl = "http://127.0.0.1:8082/image-ocr/";//string baseUrl = "http://*:8082/image-ocr/";var server = new OCRHttpServer(baseUrl);Console.CancelKeyPress += (sender, e) =>{e.Cancel = true;server.Stop();};server.Start();Console.WriteLine("Press CTRL+C to stop the server...");Console.WriteLine("curl \"http://localhost:8082/image-ocr?templateCode=n&path=imagePath\"");while (true){Thread.Sleep(100);}}}class OCRHttpServer
{private readonly HttpListener _listener;private readonly string _baseUrl;private PaddleOCREngine engine;private PaddleStructureEngine structengine;public OCRHttpServer(string baseUrl){_baseUrl = baseUrl;_listener = new HttpListener();_listener.Prefixes.Add(baseUrl);}public void OCRModel_Load(){string outpath = Path.Combine(Environment.CurrentDirectory, "out");if (!Directory.Exists(outpath)){ Directory.CreateDirectory(outpath); }自带轻量版中英文模型V3模型//OCRModelConfig config = null;//服务器中英文模型//OCRModelConfig config = new OCRModelConfig();//string root = System.IO.Path.GetDirectoryName(typeof(OCRModelConfig).Assembly.Location);//string modelPathroot = root + @"\inferenceserver";//config.det_infer = modelPathroot + @"\ch_ppocr_server_v2.0_det_infer";//config.cls_infer = modelPathroot + @"\ch_ppocr_mobile_v2.0_cls_infer";//config.rec_infer = modelPathroot + @"\ch_ppocr_server_v2.0_rec_infer";//config.keys = modelPathroot + @"\ppocr_keys.txt";//英文和数字模型V3OCRModelConfig config = new OCRModelConfig();string root = System.IO.Path.GetDirectoryName(typeof(OCRModelConfig).Assembly.Location);string modelPathroot = root + @"\en_v3";config.det_infer = modelPathroot + @"\en_PP-OCRv3_det_infer";config.cls_infer = modelPathroot + @"\ch_ppocr_mobile_v2.0_cls_infer";config.rec_infer = modelPathroot + @"\en_PP-OCRv3_rec_infer";config.keys = modelPathroot + @"\en_dict.txt";//OCR参数OCRParameter oCRParameter = new OCRParameter();oCRParameter.cpu_math_library_num_threads = 10;//预测并发线程数oCRParameter.enable_mkldnn = true;//web部署该值建议设置为0,否则出错,内存如果使用很大,建议该值也设置为0.oCRParameter.cls = false; //是否执行文字方向分类;默认falseoCRParameter.det = true;//是否开启方向检测,用于检测识别180旋转oCRParameter.use_angle_cls = false;//是否开启方向检测,用于检测识别180旋转oCRParameter.det_db_score_mode = true;//是否使用多段线,即文字区域是用多段线还是用矩形,//初始化OCR引擎engine = new PaddleOCREngine(config, oCRParameter);Console.Clear();//模型配置,使用默认值StructureModelConfig structureModelConfig = null;//表格识别参数配置,使用默认值StructureParameter structureParameter = new StructureParameter();structengine = new PaddleStructureEngine(structureModelConfig, structureParameter);Console.Clear();}public void Start(){_listener.Start();Console.WriteLine($"Server started at {_baseUrl}");OCRModel_Load();ThreadPool.QueueUserWorkItem((o) =>{try{while (_listener.IsListening){ThreadPool.QueueUserWorkItem((contextState) =>{var context = (HttpListenerContext)contextState;try{HandleRequest(context);}catch (Exception ex){Console.WriteLine($"Error handling request: {ex.Message}");SendErrorResponse(context, 500, "Internal Server Error");}finally{context.Response.Close();}}, _listener.GetContext());}}catch (Exception ex){Console.WriteLine($"Server error: {ex.Message}");}});}public void Stop(){_listener.Stop();_listener.Close();Console.WriteLine("Server stopped");}private void HandleRequest(HttpListenerContext context){HttpListenerRequest request = context.Request;HttpListenerResponse response = context.Response;if (request.HttpMethod == "POST"){HandlePostRequest(request, response);}else if (request.HttpMethod == "GET"){HandleGetRequest(request, response);}else{SendError(response, "Unsupported HTTP method");}response.OutputStream.Close();}private string HandleImageOCRRequest(string imagePath){string jsonResponse = string.Empty;try{if (string.IsNullOrEmpty(imagePath)){// 返回成功响应var response = new{Status = "Error",Message = "",ReceivedAt = DateTime.Now};jsonResponse = JsonSerializer.Serialize(response);return jsonResponse;}jsonResponse = ProgressImage(imagePath);return jsonResponse;}catch (Exception ex){Console.WriteLine($"Error processing string: {ex}");var response = new{Status = "Error",Message = "",ReceivedAt = DateTime.Now};jsonResponse = JsonSerializer.Serialize(response);return jsonResponse;}}//用OpenCV实现分块检测private string ProgressImage(string imagePath){string jsonResponse = string.Empty;string message = string.Empty;using (Mat src = Cv2.ImRead(imagePath, ImreadModes.Color)){if (src.Empty())throw new Exception("无法加载图像");Mat gray = new Mat();Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);//图片边缘裁掉多少像素的边缘int gap = 5;int height = src.Rows;int width = src.Cols;// 创建掩膜(矩形区域)Mat mask = new Mat(height, width, MatType.CV_8UC1, Scalar.All(0));Rect rectROI = new Rect(gap, gap, width - gap * 2, height - gap * 2);Mat roiMask = new Mat(mask, rectROI);roiMask.SetTo(Scalar.All(255));// 阈值分割Mat thresh = new Mat();Cv2.Threshold(gray, thresh, 254, 255, ThresholdTypes.Binary);// 与掩膜进行 AND 操作Mat maskedThresh = new Mat();Cv2.BitwiseAnd(thresh, mask, maskedThresh);// 填充孔洞Mat filled = new Mat();maskedThresh.CopyTo(filled);// 创建FloodFill所需的mask(比原图大2像素)Mat floodFillMask = new Mat(filled.Rows + 2, filled.Cols + 2, MatType.CV_8UC1, Scalar.All(0));// 执行floodfill从边界开始填充背景Cv2.FloodFill(filled, floodFillMask, new OpenCvSharp.Point(0, 0), new Scalar(255),out Rect rect,new Scalar(), new Scalar(),FloodFillFlags.Link8);// 反转图像以获取填充后的对象Cv2.BitwiseNot(filled, filled);// 查找轮廓(相当于连接区域)OpenCvSharp.Point[][] contours;HierarchyIndex[] hierarchy;Cv2.FindContours(filled, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxSimple);Console.WriteLine(imagePath);// 遍历每个轮廓for (int i = 0; i < contours.Length; i++){Rect boundingRect = Cv2.BoundingRect(contours[i]);// 裁剪图像Mat cropped = new Mat(src, boundingRect);// 保存裁剪图像到临时路径string tempImagePath = Path.Combine("E:/File/", $"{i + 1}.png");Cv2.ImWrite(tempImagePath, cropped);// 转换为 byte[]byte[] imageBytes = cropped.ToBytes();// OCR 识别OCRResult ocrResult = engine.DetectText(imageBytes); // 假设 engine 是已初始化的 OCR 引擎string outMessage = "";string printMessage = "";foreach (var item in ocrResult.TextBlocks){string input = item.ToString(); // 根据实际结构调整var idMatch = Regex.Match(input, @"^([^,]+)");string text = idMatch.Success ? idMatch.Groups[1].Value.Trim() : "";var coordinatesMatch = Regex.Match(input, @"\[(\([^)]*\)(?:,\([^)]*\))*)\]");string coordsStr = coordinatesMatch.Success ? coordinatesMatch.Groups[1].Value.Trim() : "";outMessage += text + ":" + coordsStr + ";";printMessage += text + "    ";}message += $"Rectangle{i + 1}:{{{outMessage}}}";Console.WriteLine($"Rectangle {i+1}");Console.WriteLine($"OCR Result: {printMessage}");}}// 14. 返回 JSON 结果var response = new{Status = "Success",Message = message,ReceivedAt = DateTime.Now};jsonResponse = JsonSerializer.Serialize(response);return jsonResponse;}// 处理 GET 请求,解析 query string 中的 templateCode 和 pathprivate void HandleGetRequest(HttpListenerRequest request, HttpListenerResponse response){// 使用 HttpUtility.ParseQueryString 来解析查询字符串Uri url = request.Url;if (url == null){SendError(response, "Invalid request URL");return;}NameValueCollection queryParams = System.Web.HttpUtility.ParseQueryString(url.Query);string templateCode = queryParams["templateCode"];string path = queryParams["path"];if (string.IsNullOrEmpty(templateCode) || string.IsNullOrEmpty(path)){SendError(response, "Missing required parameters: templateCode and path");return;}string responseBody = "";responseBody = HandleImageOCRRequest(path);byte[] buffer = Encoding.UTF8.GetBytes(responseBody);response.ContentType = "text/plain";response.ContentLength64 = buffer.Length;response.OutputStream.Write(buffer, 0, buffer.Length);}// 处理 POST multipart/form-data 文件上传private void HandlePostRequest(HttpListenerRequest request, HttpListenerResponse response){string boundary = request.ContentType?.Split('=')[1];if (string.IsNullOrEmpty(boundary)){SendError(response, "Invalid Content-Type");return;}using (Stream input = request.InputStream){Encoding encoding = Encoding.UTF8;string formData = ReadMultipartFormData(input, encoding, boundary);string responseBody = $"Received POST:\nFile Content:\n{formData}";byte[] buffer = encoding.GetBytes(responseBody);response.ContentType = "text/plain";response.ContentLength64 = buffer.Length;response.OutputStream.Write(buffer, 0, buffer.Length);}}// 发送错误信息private void SendError(HttpListenerResponse response, string message){byte[] buffer = Encoding.UTF8.GetBytes(message);response.StatusCode = (int)HttpStatusCode.BadRequest;response.ContentType = "text/plain";response.ContentLength64 = buffer.Length;response.OutputStream.Write(buffer, 0, buffer.Length);}// 读取 multipart/form-data 内容private string ReadMultipartFormData(Stream inputStream, Encoding encoding, string boundary){StreamReader reader = new StreamReader(inputStream, encoding);string boundaryLine;StringBuilder result = new StringBuilder();while ((boundaryLine = reader.ReadLine()) != null){if (boundaryLine.Contains(boundary)) continue;// 跳过 headersstring line;while (!(string.IsNullOrEmpty(line = reader.ReadLine()))) { }// Read content until next boundarystring content;while ((content = reader.ReadLine()) != null && !content.Contains(boundary)){result.AppendLine(content);}break; // 只处理第一个 part}return result.ToString().Trim();}private void SendResponse(HttpListenerContext context, string responseString){try{byte[] buffer = Encoding.UTF8.GetBytes(responseString);context.Response.ContentLength64 = buffer.Length;context.Response.OutputStream.Write(buffer, 0, buffer.Length);}catch (Exception){}}private void SendErrorResponse(HttpListenerContext context, int statusCode, string message){context.Response.StatusCode = statusCode;SendResponse(context, message);}
}

起服务后:
在这里插入图片描述
测试:

在这里插入图片描述

在这里插入图片描述


http://www.hkcw.cn/article/cVZkxkPufY.shtml

相关文章

Real SQL Programming

目录 SQL in Real Programs Options Stored Procedures Advantages of Stored Procedures Parameters in PSM SQL in Real Programs We have seen only how SQL is used at the generic query interface --- an environment where we sit at a terminal and ask queries …

华为OD机试真题——跳格子3(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《跳格子3》: 目录 题目名称…

UE5蓝图暴露变量,类似Unity中public一个变量,在游戏运行时修改变量实时变化和看向目标跟随目标Find Look at Rotation

UE5蓝图中暴露变量&#xff0c;类似Unity中public一个变量&#xff0c;在游戏运行时修改变量实时变化 1&#xff0c;添加变量 2&#xff0c;设置变量的值 3&#xff0c;点开小眼睛&#xff0c;此变量显示在编辑器中&#xff0c;可以运行时修改 看向目标跟随目标Find Look at R…

第 1 章:学习起步

1. React Native前置知识要求 在开始学习React Native之前&#xff0c;有一些前置知识你需要了解。不过别担心&#xff0c;我会带你逐步掌握这些内容&#xff0c;让你顺利入门。 1.1. JavaScript是必须掌握的 学习React Native&#xff0c;JavaScript是基础。你需要了解Java…

BERT***

​​1.预训练&#xff08;Pre-training&#xff09;​​ 是深度学习中的一种训练策略&#xff0c;指在大规模无标注数据上预先训练模型&#xff0c;使其学习通用的特征表示&#xff0c;再通过​​微调&#xff08;Fine-tuning&#xff09;​​ 适配到具体任务 2.sentence-lev…

在Mathematica中使用WhenEvent求解微分方程

WhenEvent[event,action]指定当事件event触发时&#xff0c;方程在 NDSolve 及相关函数中执行的操作action。 模拟一个每次弹起后保持95%速度的弹跳球 NDSolve[{y[t] -9.81, y[0] 5, y[0] 0, WhenEvent[y[t] 0, y[t] -> -0.95 y[t]]}, y, {t, 0, 10}]; Plot[y[t] /. %…

Nature:多模态大模型LLMs如何驱动多组学与生命科学研究新范式?

高通量组学技术的快速进步引发了生物数据的爆炸式增长&#xff0c;远超当前对分子层面规律的解析能力。在自然语言处理领域&#xff0c;大语言模型&#xff08;LLMs&#xff09;通过整合海量数据构建统一模型&#xff0c;已显现突破数据困境的潜力。 Nature的这篇文章中&#x…

ubuntu20.04安装教程(图文详解)

Ubuntu 24.04 LTS&#xff0c;代号 Noble Numbat&#xff0c;于 2024 年 4 月 25 日发布&#xff0c;现在可以从 Ubuntu 官方网站及其镜像下载。此版本将在 2029 年 4 月之前接收为期五年的官方安全和维护更新。 关于 Ubuntu 24.04 LTS 的一些关键点&#xff1a; 发布日期&am…

Linux中Shell脚本的常用命令

一、设置主机名称 1、通过修改系统文件来修改主机名称 [rootsakura1 桌面]# vim /etc/hostname sakura /etc/hostname&#xff1a;Linux 系统中存储主机名的配置文件。修改完文件后&#xff0c;在当前的shell中是不生效的&#xff0c;需要关闭当前shell后重新开启才能看到效…

Redisson学习专栏(二):核心功能深入学习(分布式锁,分布式集合,原子操作与计数器,事件与监听)

本文是“Redisson学习专栏”第二篇&#xff0c;聚焦其核心分布式功能实现原理与最佳实践 文章目录 前言&#xff1a;分布式系统核心能力实践一、分布式锁&#xff1a;高并发下的守卫者1.1 可重入锁 (Reentrant Lock)1.2 公平锁 (Fair Lock)1.3 联锁 (MultiLock)1.4 红锁 (RedLo…

学习路之PHP--easyswoole_panel安装使用

学习路之PHP--easyswoole_panel安装使用 一、新建文件夹二、安装三、改配置地址四、访问 IP:Port 自动进入index.html页面 一、新建文件夹 /www/wwwroot/easyswoole_panel 及配置ftp 解压easyswoole_panel源码 https://github.com/easyswoole-panel/easyswoole_panel 二、安…

基于分布式状态机的集装箱智能道口软件架构方法

集装箱码头对进出场道口的通过能力始终是要求最高的&#xff0c;衡量道口的直接指标为道口通行效率&#xff0c;道口通行效率直接体现了集装箱码头的作业效率以及对外服务水平&#xff0c;进而直接影响到码头的综合能力。所以&#xff0c;码头普遍使用智能道口实现24小时无人值…

2014药柜设计问题

1 题目描述 D题 储药柜的设计 储药柜的结构类似于书橱&#xff0c;通常由若干个横向隔板和竖向隔板将储药柜分割成若干个储药槽(如图1所示)。为保证药品分拣的准确率&#xff0c;防止发药错误&#xff0c;一个储药槽内只能摆放同一种药品。药品在储药槽中的排列方式如图2所示。…

c# 获取电脑 分辨率 及 DPI 设置

using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices;/// <summary> /// 这个可以 /// </summary> class Program {static void Main(){//设置DPI感知try{SetProcessDpiAwareness(…

2025年渗透测试面试题总结-匿名[校招]红队攻防工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 匿名[校招]红队攻防工程师 1. 00截断的原理 2. Java回显通用思路及JDK差异 3. Redis利用姿势及环境差异 …

高级数据结构与算法期末考试速成记录

高级数据结构与算法期末考试速成记录 0.分治中的一些知识点 Master公式&#xff08;又称主定理&#xff0c;Master Theorem&#xff09;是一种用于快速求解分治递归算法时间复杂度 的数学工具&#xff0c;适用于递归式形如以下形式的算法&#xff1a; T ( n ) a T ( n b ) …

Telerik生态整合:Kendo UI for Angular组件在WinForms应用中的深度嵌入(一)

Telerik DevCraft包含一个完整的产品栈来构建您下一个Web、移动和桌面应用程序。它使用HTML和每个.NET平台的UI库&#xff0c;加快开发速度。Telerik DevCraft提供完整的工具箱&#xff0c;用于构建现代和面向未来的业务应用程序&#xff0c;目前提供UI for ASP.NET MVC、Kendo…

深入浅出程序设计竞赛(洛谷基础篇) 第十四章 搜索

文章目录 前言例14-1 四阶数独前置知识&#xff1a; 例14-2八皇后例14-3 kkksc03考前临时抱佛脚例14-4 马的遍历前置知识 例14-5 奇怪的电梯例14-6 Meteor Shower S习题14-1.1 选数例14-1 四阶数独前置知识&#xff1a; 例14-2八皇后例14-3 kkksc03考前临时抱佛脚例14-4 马的遍…

图书管理系统的设计与实现

湖南软件职业技术大学 本科毕业设计(论文) 设计(论文)题目 图书管理系统的设计与实现 学生姓名 学生学号 所在学院 专业班级 毕业设计(论文)真实性承诺及声明 学生对毕业设计(论文)真实性承诺 本人郑重声明:所提交的毕业设计(论文)作品是本人在指导教师的指导下,独…

【Java基础-环境搭建-创建项目】IntelliJ IDEA创建Java项目的详细步骤

在Java开发的世界里&#xff0c;选择一个强大的集成开发环境&#xff08;IDE&#xff09;是迈向高效编程的第一步。而IntelliJ IDEA无疑是Java开发者中最受欢迎的选择之一。它以其强大的功能、智能的代码辅助和简洁的用户界面&#xff0c;帮助无数开发者快速构建和部署Java项目…