Files
ctbjrj/成套报价软件/扒图/UmiOcrClient.cs
T

144 lines
6.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// UmiOcrClient:Umi-OCR 本地 OCR 引擎(hiroi-sora/Umi-OCR,离线免费)
// · 前提:用户在本机运行 Umi-OCR 主程序,全局设置里开启 HTTP 服务(默认开启,端口 1224)
// · POST {BaseUrl}/api/ocr application/json:
// { "base64": "<纯base64无前缀>", "options": { "data.format": "dict" } }
// · 响应: code=100 成功;data=[{ text, score, box:[[x,y]×4 左上/右上/右下/左下], end }]
// code=101 无文本(data 为原因字符串);其余 code 失败
// · 地址存 baidu_ocr.json 的 umi_url(OCR设置窗口写,默认 http://127.0.0.1:1224)
// · 本地引擎无需 API Key,IsConfigured 恒 true
// ============================================================
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Web.Script.Serialization;
namespace 成套报价软件
{
/// <summary>Umi-OCR 本地引擎客户端(单实例)。</summary>
internal sealed class UmiOcrClient
{
private readonly JavaScriptSerializer _json = new JavaScriptSerializer();
private string _baseUrl = "http://127.0.0.1:1224";
/// <summary>状态提示,接 OcrClient.StatusChanged。</summary>
public event Action<string> StatusChanged;
public bool IsConfigured
{
get { return true; } // 本地引擎无需 Key
}
/// <summary>服务地址由 OcrClient 从 baidu_ocr.json 的 umi_url 注入(热更新)。</summary>
public void SetBaseUrl(string url)
{
url = (url == null ? "" : url.Trim());
if (url.Length == 0) url = "http://127.0.0.1:1224";
if (!url.StartsWith("http")) url = "http://" + url;
_baseUrl = url.TrimEnd('/');
}
/// <summary>识别一块位图 → 文本行列表(带位置)。JSON POST 到本机 Umi-OCR。</summary>
public Task<List<OcrItem>> RecognizeAsync(Bitmap crop, int timeoutMs)
{
if (crop == null || crop.Width <= 0 || crop.Height <= 0)
return Task.FromResult(new List<OcrItem>());
return Task.Factory.StartNew(() =>
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var result = RequestAndParse(crop, timeoutMs);
Notify("Umi-OCR·" + result.Count + "条 " + sw.ElapsedMilliseconds + "ms");
return result;
});
}
private List<OcrItem> RequestAndParse(Bitmap crop, int timeoutMs)
{
// PNG base64(无 data: 前缀);PNG 保坐标精度,本地服务无 4MB 限制
string b64;
using (var ms = new MemoryStream())
{
crop.Save(ms, ImageFormat.Png);
b64 = Convert.ToBase64String(ms.ToArray());
}
string req = _json.Serialize(new Dictionary<string, object>
{
{ "base64", b64 },
// ★ ocr.cls=true:纠正文本方向(PaddleOCR 引擎方向分类,即截图OCR界面的"纠正文本方向";
// 界面设置不会带给 HTTP 调用,必须显式传——否则旋转90°的横排文字识别必乱)
{ "options", new Dictionary<string, object> { { "data.format", "dict" }, { "ocr.cls", true } } }
});
string body;
using (var wc = new WebClient())
{
wc.Encoding = Encoding.UTF8;
wc.Headers["Content-Type"] = "application/json";
wc.Proxy = null; // 本地地址不走系统代理(配了代理会把 127.0.0.1 也代理走)
body = wc.UploadString(_baseUrl + "/api/ocr", "POST", req);
}
var resp = _json.Deserialize<Dictionary<string, object>>(body);
if (resp == null || !resp.ContainsKey("code"))
throw new Exception("Umi-OCR 返回解析失败");
int code;
int.TryParse(Convert.ToString(resp["code"]), out code);
if (code == 101)
return new List<OcrItem>(); // 无文本,不算错误
if (code != 100)
throw new Exception("Umi-OCR 错误 code=" + code + ": " +
Convert.ToString(resp.ContainsKey("data") ? resp["data"] : ""));
var items = new List<OcrItem>();
var arr = resp.ContainsKey("data") ? resp["data"] as System.Collections.ArrayList : null;
if (arr != null)
{
foreach (Dictionary<string, object> w in arr)
{
if (w == null) continue;
var boxArr = w.ContainsKey("box") ? w["box"] as System.Collections.ArrayList : null;
if (boxArr == null || boxArr.Count < 4) continue;
var pts = new PointF[4];
for (int i = 0; i < 4; i++)
{
var xy = boxArr[i] as System.Collections.ArrayList;
float x, y;
float.TryParse(Convert.ToString(xy != null && xy.Count > 0 ? xy[0] : "0"), out x);
float.TryParse(Convert.ToString(xy != null && xy.Count > 1 ? xy[1] : "0"), out y);
pts[i] = new PointF(x, y);
}
float score = 0.95f;
if (w.ContainsKey("score"))
{
float sv;
if (float.TryParse(Convert.ToString(w["score"]),
System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out sv)) score = sv;
}
items.Add(new OcrItem
{
Text = Convert.ToString(w.ContainsKey("text") ? w["text"] : ""),
Box = pts, // box 即发送图像素坐标(顺时针四点),与 OcrItem 语义一致
Score = score
});
}
}
return items;
}
private void Notify(string msg)
{
var h = StatusChanged;
if (h != null) h(msg);
}
}
}