C# 结合PaddleOCRSharp搭建Http网络服务
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);}
}
起服务后:
测试:
相关文章:

C# 结合PaddleOCRSharp搭建Http网络服务
Windows打开端口: 控制面板 > 系统和安全 > 防火墙> 高级设置 → 入站规则 → 右侧选择 → 新建规则 → 端口 → 协议类型 TCP→ 端口 using System; using System.Drawing; using System.IO; using System.Net; using System.Text; using System.Threadi…...

【连接器专题】SD卡座规格书审查需要审哪些方面?
在审查SD卡座规格书时,我们需要考虑哪些方面? 首先在拿到一份SD卡座的详细规格书时,一般供应商给到的规格书中包括了一些基础信息、产品图纸信息、技术参数信息,同时有些供应商会给出产品可靠性测试报告。因此我们会从这几个要素去看规格书。 基础信息 基础信息一般会给变更…...
JS手写代码篇---手写节流函数
8、节流函数 什么是节流函数? 指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。 与防抖函数有什么区别? 防抖函数是延…...
UE5 C++动态调用函数方法、按键输入绑定 ,地址前加修饰符
UE5 C动态调用函数方法、按键输入绑定 ,地址前加修饰符&,这个符号忘记输入的话,编译一直报错不通过 void ASnakeHead::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerIn…...

eBest智能价格引擎系统 助力屈臣氏饮料落地「价格大脑」+「智慧通路」数字基建
从价格策略到终端执行,数字化正在重构饮料行业竞争壁垒! 近日,eBest为屈臣氏饮料提供的智能价格引擎系统已正式上线并投入运营。同时,基于eBest SFA方案且与屈臣氏饮料业务场景深度耦合的Smart Field Operation智慧通路项目正式启…...
ubuntu mysql 8.0.42 基于二进制日志文件位置和GTID主从复制配置
目录 1 操作系统信息 2 MySql数据库版本 3 主机列表 4 MySQL服务器都安装依赖 5 主库服务器安装mysql软件步骤: 6 从服务器安装mysql软件步骤 7 基于二进制日志文件位置的主从复制配置 8 使用全局事务标识符进行主从复制(GTID) 9 部署过程遇到问题 1 操作系…...

Kettle 远程mysql 表导入到 hadoop hive
kettle 远程mysql 表导入到 hadoop hive (教学用 ) 文章目录 kettle 远程mysql 表导入到 hadoop hive创建 对象 执行 SQL 语句 -mysql 导出 CSV格式CSV 文件远程上传到 HDFS运行 SSH 命令远程登录 run SSH 并执行 hadoop fs -put 建表和加载数据总结 创…...

完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤
完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤 一、前言 在使用 Linux 操作系统进行内核开发或者系统维护时,内核 panic 是最常见的系统崩溃环节。如果想要在内核崩溃后立即分析环境和输出内核内存 dump,Kdump crashkernel 是最接近完美的解…...

菜鸟之路Day36一一Web开发综合案例(部门管理)
菜鸟之路Day36一一Web开发综合案例(部门管理) 作者:blue 时间:2025.5.28 文章目录 菜鸟之路Day36一一Web开发综合案例(部门管理)一.环境搭建二.开发规范三.部门管理3.1查询3.2删除3.3新增3.3修改根据id来…...
LangChain实战:MMR和相似性搜索技术应用
导读:在当今大数据和人工智能快速发展的背景下,向量数据库的搜索技术正成为技术人员必须掌握的核心技能。本文将深入探讨LangChain框架与Milvus向量数据库的整合实践,重点对比分析相似度搜索与最大边际相关性(MMR)搜索…...

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

SQL查询——大厂面试真题
前言 本文总结了SQLite数据库的核心操作要点:1. 基础语法:SQL语句不区分大小写,多语句需用分号分隔,支持多种注释方式2. 表操作:包括创建表(定义主键、非空约束等)、插入/更新/删除数据、添加/…...

Linux-pcie ranges介绍
参考链接:https://elinux.org/Device_Tree_Usage#PCI_Host_Bridge pcie bar高低端BAR起始地址介绍 pcie设备树节点 / {compatible "rockchip,rk3588";interrupt-parent <&gic>;#address-cells <2>;#size-cells <2>;pcie3x4: p…...

⭐ Unity AVProVideo插件自带播放器 脚本重构 实现视频激活重置功能
一、功能概述 本笔记记录直接修改插件自带的场景播放其中 原始的 MediaPlayerUI 脚本,实现激活时自动重置播放器的功能。 我用的插件版本是 AVPro Video - Ultra Edition 2.7.3 修改后的脚本将具备以下特性: 激活 GameObject 时自动重置播放位置到开头 可配置是否在重置后自…...
互联网大厂Java求职面试:云原生微服务架构设计与AI大模型集成实战
互联网大厂Java求职面试:云原生微服务架构设计与AI大模型集成实战 面试场景设定 人物设定: 李明(技术总监):拥有15年分布式系统架构经验,主导过多个亿级用户系统的重构,对云原生和AI融合有深…...

详解K8s API Server 如何处理请求的?
详解K8s API Server 如何处理请求的? Kubernetes(K8s)是一个强大的容器编排系统,而API Server(kube-apiserver) 是它的核心组件之一。 如果把 K8s 比作一个国家,API Server 就是政府机构,所有资源的创建、修改、删除都要经过它审批! 🎯 API Server 的作用 📌 A…...

微调数据处理
1. 数据爬取 我们将爬取的1G文件都保存到all_m_files目录下 查看原始数据文件数量: find /root/all_m_files -type f | wc -l 2. 数据预处理 仅保留UTF-8 格式文件,且所有保留的代码文件长度必须大于20行 import os import pandas as pddef try_read…...
✨1.1.1 按位与运算替代求余运算优化场景
在计算机编程中,使用按位与运算(&)替代求余运算(%)可以提高效率的特殊场景是:当除数是 2 的整数次幂(即 ( b 2^n ),其中 ( n ) 为自然数)时。例如,( b …...

解决开发者技能差距:AI 在提升效率与技能培养中的作用
企业在开发者人才方面正面临双重挑战。一方面,IDC 预测,到2025年,全球全职开发者将短缺400万人;另一方面,一些行业巨头已暂停开发者招聘,转而倚重人工智能(AI)来满足开发需求。这不禁…...

XCTF-web-easyphp
解析 第一个条件( k e y 1 ): i s s e t ( key1):isset( key1):isset(a) && intval(KaTeX parse error: Expected EOF, got & at position 14: a) > 6000000 &̲& strl…...

Transformer 通关秘籍11:Word2Vec 及工具的使用
将文字文本转换为词向量(word embedding)的过程中,一个非常著名的算法模型应该就是 Word2Vec 了。 相信大家或多或少都听说过,本节就来简单介绍一下 Word2Vec 。 什么是 Word2Vec ? Word2Vec 可以非常有效的创建词嵌入向量&…...

【DAY34】GPU训练及类的call方法
内容来自浙大疏锦行python打卡训练营 浙大疏锦行 知识点: CPU性能的查看:看架构代际、核心数、线程数GPU性能的查看:看显存、看级别、看架构代际GPU训练的方法:数据和模型移动到GPU device上类的call方法:为什么定义前…...

Flutte ListView 列表组件
目录 1、垂直列表 1.1 实现用户中心的垂直列表 2、垂直图文列表 2.1 动态配置列表 2.2 for循环生成一个动态列表 2.3 ListView.builder配置列表 列表布局是我们项目开发中最常用的一种布局方式。Flutter中我们可以通过ListView来定义列表项,支持垂直和水平方向展示…...

muduo库的初步认识和基本使用,创建一个简单查询单词服务系统
小编在学习完muduo库之后,觉得对于初学者,muduo库还是有点不好理解,所以在此,小编来告诉大家muduo库的初步认识和基本使用,让初学者也可以更快的上手和使用muduo库。 Muduo由陈硕大佬开发,是⼀个基于 非阻塞…...
电脑如何保养才能用得更久
在这个数字化的时代,电脑已经成为了我们生活和工作中不可或缺的伙伴。无论是处理工作文档、追剧娱乐,还是进行创意设计,电脑都发挥着至关重要的作用。那么,如何让我们的电脑“健康长寿”,陪伴我们更久呢?今…...
Oracle的NVL函数
Oracle的NVL函数是一个常用的空值处理函数,主要用于在查询结果中将NULL值替换为指定的默认值。以下是关于NVL函数的详细说明: 基本语法 NVL(expr1, expr2) 如果expr1为NULL,则返回expr2如果expr1不为NULL,则返回expr1本身 …...

【HTML/CSS面经】
HTML/CSS面经 HTML1. script标签中的async和defer的区别2. H5新特性(1 标签语义化(2 表单功能增强(3 音频和视频标签(4 canvas和svg绘画(5 地理位置获取(6 元素拖动API(7 Web Worker(…...

git查看commit属于那个tag
1. 快速确认commit原始分支及合入tag # git describe 213b4b3bbef2771f7a1b8166f6e6989442ca67c8 查看commit合入tag # git describe 213b4b3bbef2771f7a1b8166f6e6989442ca67c8 --all 查看commit原始分支 2.查看分支与master关系 # git show --all 0.5.67_0006 --stat 以缩…...
如何从ISO镜像直接制作Docker容器基础镜像
引言 这段最值得总结的经验知识,就是如何在ISO镜像的基础上,直接做出docker base image,无需联网! 特别地,对于一些老旧OS系统,都能依此做出docker base image! 例如,某些老旧系统,CentOS 6,…...
网站缓存入门与实战:浏览器与Nginx/Apache服务器端缓存,让网站速度起飞!(2025)
更多服务器知识,尽在hostol.com 嘿,各位站长和网站管理员朋友们!咱们精心打造的网站,内容再好,设计再炫,如果用户打开它的时候,加载速度慢得像“老牛拉破车”,那体验可就大打折扣了…...