## 主要变更 ### OCR 架构 - 新增多提供商 OCR 系统 (Tesseract.js, Baidu OCR, RapidOCR) - 添加 Provider 基类接口和工厂模式 - 支持 provider 自动选择和降级处理 - 新增 RapidOCR Python HTTP 服务 (端口 8080) ### 路径修复 - 修复 Windows 平台路径解析问题 - 统一路径处理工具 (lib/path.ts) - 修复 uploads 目录定位问题 ### 设置页面重构 - 三个标签页:API 配置、OCR 配置、AI 配置 - API 服务器地址配置 - OCR 服务商配置(Tesseract.js, RapidOCR, 百度 OCR) - AI 服务商配置(智谱 GLM, MiniMax, DeepSeek, Kimi, OpenAI, Anthropic) ### 端口配置 - 前端端口: 13056 - 后端端口: 13057 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
"""
|
|
RapidOCR HTTP Server
|
|
为 Node.js 后端提供 OCR API 服务
|
|
"""
|
|
|
|
from rapidocr_onnxruntime import RapidOCR
|
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
import json
|
|
import base64
|
|
import numpy as np
|
|
from io import BytesIO
|
|
from PIL import Image
|
|
import cv2
|
|
|
|
# 初始化 RapidOCR
|
|
ocr = RapidOCR()
|
|
|
|
class OCRHandler(BaseHTTPRequestHandler):
|
|
def _set_cors(self):
|
|
"""设置 CORS"""
|
|
self.send_header('Access-Control-Allow-Origin', '*')
|
|
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
|
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
|
|
|
def _send_json(self, data, status=200):
|
|
"""发送 JSON 响应"""
|
|
self.send_response(status)
|
|
self.send_header('Content-Type', 'application/json')
|
|
self._set_cors()
|
|
self.end_headers()
|
|
self.wfile.write(json.dumps(data, ensure_ascii=False).encode('utf-8'))
|
|
|
|
def do_OPTIONS(self):
|
|
"""处理 OPTIONS 请求"""
|
|
self.send_response(200)
|
|
self._set_cors()
|
|
self.end_headers()
|
|
|
|
def do_GET(self):
|
|
"""健康检查"""
|
|
if self.path == '/health':
|
|
self._send_json({"status": "ok", "service": "rapidocr"})
|
|
else:
|
|
self._send_json({"error": "Not found"}, 404)
|
|
|
|
def do_POST(self):
|
|
"""处理 OCR 请求"""
|
|
if self.path == '/ocr':
|
|
try:
|
|
content_length = int(self.headers['Content-Length'])
|
|
post_data = self.rfile.read(content_length)
|
|
data = json.loads(post_data.decode('utf-8'))
|
|
|
|
# 获取图片数据
|
|
images = data.get('images', [])
|
|
if not images:
|
|
self._send_json({"error": "No images provided"}, 400)
|
|
return
|
|
|
|
# 处理第一张图片
|
|
image_base64 = images[0]
|
|
image_data = base64.b64decode(image_base64)
|
|
|
|
# 转换为 OpenCV 格式
|
|
nparr = np.frombuffer(image_data, np.uint8)
|
|
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
|
|
|
|
if img is None:
|
|
self._send_json({"error": "Failed to decode image"}, 400)
|
|
return
|
|
|
|
# 执行 OCR
|
|
result, _ = ocr(img)
|
|
|
|
# 格式化结果
|
|
ocr_results = []
|
|
for line in result:
|
|
ocr_results.append({
|
|
"text": line[0],
|
|
"score": float(line[1]),
|
|
"box": line[2]
|
|
})
|
|
|
|
# 计算平均置信度
|
|
avg_confidence = 0
|
|
if ocr_results:
|
|
avg_confidence = sum(r["score"] for r in ocr_results) / len(ocr_results)
|
|
|
|
self._send_json({
|
|
"code": 200,
|
|
"msg": "success",
|
|
"data": ocr_results
|
|
})
|
|
|
|
except Exception as e:
|
|
self._send_json({"error": str(e)}, 500)
|
|
else:
|
|
self._send_json({"error": "Not found"}, 404)
|
|
|
|
def log_message(self, format, *args):
|
|
"""减少日志输出"""
|
|
pass
|
|
|
|
def run_server(port=8080):
|
|
"""启动服务器"""
|
|
server_address = ('', port)
|
|
httpd = HTTPServer(server_address, OCRHandler)
|
|
print(f"RapidOCR Server running on port {port}")
|
|
print(f"Health check: http://localhost:{port}/health")
|
|
print(f"OCR endpoint: http://localhost:{port}/ocr")
|
|
httpd.serve_forever()
|
|
|
|
if __name__ == '__main__':
|
|
run_server()
|