echodict/ppv5
0220
1
2# see huggingface/project/flask_auto_selection.py
3
4# see huggingface_echodict/typst-app-clone/tools/extract_pdf_images.py
5
6# C:\Users\echod\.paddlex\official_models\PP-OCRv5_server_det
7# c:\Users\echod\.conda\envs\ppv5\lib\site-packages\paddle\utils\cpp_extension\extension_utils.py 看模型加载的代码在哪
8# PP-OCRv5_server_det PP-OCRv5_server_det.yaml 搜这两个
9# paddlex/configs/modules/text_detection/PP-OCRv5_server_det.yaml,sha256=_cS2Eaqb1IJdN0jXPqtc8wsC-gHY0BdS3oOzZfVINCI,1100
10# C:\Users\echod\.conda\envs\ppv5\Lib\site-packages\paddlex\inference\models\text_detection\predictor.py 实际建模好像是这里
11# Model files already exist. 搜这个
12# C:\Users\echod\.conda\envs\ppv5\Lib\site-packages\paddlex\inference\utils\official_models.py self._save_dir 'C:/Users/echod/.paddlex/official_models'
13# 要改的是这个目录路径
14# _save_dir = Path(CACHE_DIR) / "official_models" 改 CACHE_DIR 为 相对路径就可以了吧
15
16"""
17import os,sys
18from pathlib import Path
19
20# 获取python.exe所在目录
21python_dir = Path(sys.executable).parent
22os.chdir(python_dir)
23
24abs_path = Path(".paddlex").resolve()
25
26DEFAULT_CACHE_DIR = osp.abspath(osp.join(os.path.expanduser("~"), ".paddlex"))
27CACHE_DIR = os.environ.get("PADDLE_PDX_CACHE_HOME", DEFAULT_CACHE_DIR)
28CACHE_DIR = abs_path
29"""
30
31
32"""
33
34conda create -n ppv5 python==3.10 pip \
35 && conda activate ppv5 \
36 && python -m pip install paddlepaddle-gpu==3.1.1 -i https://www.paddlepaddle.org.cn/packages/stable/cu118 \
37 && pip install paddleocr
38 # python -m pip install paddlepaddle==3.1.1 -i https://www.paddlepaddle.org.cn/packages/stable/cpu/
39 # cpu 就这样
40
41# pip install numpy==2.2.4 pillow==11.1.0 protobuf==6.30.2 flask==3.1.2 opencv-python==4.12.0.88 paddlepaddle==3.1.1 paddleocr==3.2.0 --proxy=http://127.0.0.1:7897
42 # -i https://mirrors.aliyun.com/pypi/simple/
43
44"""
45
46is_debug = False
47
48dic_cache = {}
49
50from flask import Flask, request, jsonify
51import threading
52import platform
53
54app = Flask(__name__)
55
56import json
57import decimal
58import datetime
59import base64
60import numpy as np
61import cv2
62
63from collections import OrderedDict
64
65class DecimalEncoder(json.JSONEncoder):
66 def default(self, o):
67 if isinstance(o, decimal.Decimal):
68 return float(o)
69 elif isinstance(o, datetime.datetime):
70 return str(o)
71 super(DecimalEncoder, self).default(o)
72
73def save_json(filename, dics):
74 with open(filename, 'w', encoding='utf-8') as fp:
75 json.dump(dics, fp, indent=4, cls=DecimalEncoder, ensure_ascii=False)
76 fp.close()
77
78def load_json(filename):
79 with open(filename, encoding='utf-8') as fp:
80 js = json.load(fp)
81 fp.close()
82 return js
83
84def base64_to_mat(base64_str):
85 """
86 将 Base64 字符串转换为 OpenCV Mat 对象(NumPy 数组)
87
88 参数:
89 base64_str (str): Base64 编码的图片字符串(不可以含前缀如 "data:image/jpeg;base64,")
90
91 返回:
92 Mat: OpenCV 图像对象(NumPy 数组),格式为 BGR
93 """
94 # 处理可能存在的 Base64 前缀(如 "data:image/jpeg;base64,")
95 # if ',' in base64_str:
96 # base64_data = base64_str.split(',')[1] # 提取纯 Base64 数据部分
97 # else:
98 # base64_data = base64_str
99
100 # 解码 Base64 字符串为二进制字节流
101 image_bytes = base64.b64decode(base64_str)
102
103 # 将字节流转换为 NumPy 数组(数据类型 uint8)
104 nparr = np.frombuffer(image_bytes, np.uint8)
105
106 # 使用 OpenCV 解码为 Mat 对象(BGR 格式)
107 mat = cv2.imdecode(nparr, cv2.IMREAD_COLOR_BGR) # cv2.IMREAD_COLOR 保留色彩通道
108
109 return mat
110
111
112from paddleocr import PaddleOCR
113
114ocr = PaddleOCR(
115 use_doc_orientation_classify=False,
116 use_doc_unwarping=False,
117 use_textline_orientation=False)
118
119def ppresult_tojson(img, result):
120 global is_debug
121
122 jn = OrderedDict()
123 prism_wordsInfo = []
124 jn["prism_wordsInfo"] = prism_wordsInfo
125 jn["height"] = img.shape[0]
126 jn["width"] = img.shape[1]
127
128 for res in result:
129 output_img = res['doc_preprocessor_res']['output_img'] # 这是预处理后的图片,坐标可能是这张图的坐标,而且还原不回去
130 # img = output_img
131 jsn = res.json['res']
132 text_word = jsn['text_word']
133 text_word_boxes = jsn['text_word_boxes']
134 rec_texts = jsn['rec_texts']
135 rec_boxes = jsn['rec_boxes']
136
137 for idx_line, (words, boxs) in enumerate(zip(text_word, text_word_boxes)):
138 text_line = rec_texts[idx_line]
139 text_box = rec_boxes[idx_line]
140
141 j = OrderedDict()
142 prism_wordsInfo.append( j )
143
144 lu = OrderedDict(x=text_box[0], y=text_box[1])
145 ru = OrderedDict(x=text_box[2], y=text_box[1])
146 rd = OrderedDict(x=text_box[2], y=text_box[3])
147 ld = OrderedDict(x=text_box[0], y=text_box[3])
148
149 j["word"] = text_line
150 j["pos"] = [ lu, ru, rd, ld ]
151
152
153 charInfo = []
154 j['charInfo'] = charInfo
155 j['angle'] = -1
156 j["x"] = lu["x"]
157 j["y"] = lu["y"]
158 j["width"] = ( max(ru["x"], rd["x"])) - ( min(lu["x"], ld["x"]) )
159 j["height"] = ( max(ld["y"], rd["y"])) - ( min(lu["y"], ru["y"]) )
160
161
162 img = cv2.rectangle(img, (lu['x'], lu['y']), (rd['x'], rd['y']), (255, 0, 0), 2)
163 if platform.system() == "Windows":
164 if is_debug:
165 cv2.imshow('orig', img)
166 cv2.waitKey(0)
167 pass
168 for idx_word, (word, box) in enumerate(zip(words, boxs)):
169
170 if (len(word) == 1):
171 info = OrderedDict()
172 charInfo.append( info )
173 info["word"] = word
174 info["x"] = box[0]
175 info["y"] = box[1]
176 info["w"] = box[2] - box[0]
177 info["h"] = box[3] - box[1]
178 elif (len(word) > 1):
179 for w in word:
180 info = OrderedDict()
181 charInfo.append( info )
182 info["word"] = w
183 info["x"] = box[0]
184 info["y"] = box[1]
185 info["w"] = box[2] - box[0]
186 info["h"] = box[3] - box[1]
187
188 # print(word)
189 img = cv2.rectangle(img, (box[0], box[1]), (box[2], box[3]), (0, 255, 0), 2) # 矩形的左上角, 矩形的右下角
190 if platform.system() == "Windows":
191 if is_debug:
192 cv2.imshow('orgin', img)
193 cv2.waitKey(0)
194 pass
195
196 # save_json('out.json', jn)
197 break # 只处理第一张图的结果
198
199 return jn
200
201# 限制同时处理的请求数量为1
202ocr_semaphore = threading.Semaphore(1)
203@app.route('/ppocr', methods=['post'])
204def autoselection():
205 # request.json 只能够接受方法为POST、Body为raw,header 内容为 application/json类型的数据
206 # print(request.json, type(request.json))
207
208 # 使用 request.form 来接受 x-www-form-urlencoded 格式的数据
209 # print(request.form, type(request.form))
210
211 # form_data = request.form.to_dict()
212 # if "img" not in form_data:
213 # return jsonify([])
214
215 # base64_str = form_data["img"]
216
217 # 非阻塞方式获取信号量
218 if not ocr_semaphore.acquire(blocking=False):
219 return jsonify({"warning": "wait pre task done."})
220
221 try:
222
223 base64_str = request.json['img']
224
225 img = base64_to_mat(base64_str)
226
227 result = ocr.predict(
228 input = img,
229 return_word_box=True
230 )
231
232 jn = ppresult_tojson(img.copy(), result)
233
234 return jsonify(jn)
235
236 except Exception as e:
237 return jsonify({"error": str(e)})
238 finally:
239 ocr_semaphore.release()
240
241if __name__ == '__main__':
242
243 if is_debug:
244 pth_img = "data/0025.jpg" # "data/第一单元.jpg"
245
246 imgData = np.fromfile(pth_img, dtype=np.uint8)
247 img = cv2.imdecode(imgData, cv2.IMREAD_COLOR_BGR)
248
249 # cv2.imshow('orgin', img)
250 # cv2.waitKey(0)
251
252 result = ocr.predict(
253 input = img, # pth_img, # "data/无标点符号.jpg",
254 return_word_box=True
255 )
256
257 jn =ppresult_tojson(img.copy(), result)
258 save_json('out.json', jn)
259
260 # res.print()
261 # res.save_to_img("output")
262 # res.save_to_json("output")
263 else:
264 app.run(host="0.0.0.0", port=8889, debug=True)
265 