pyxllib 0.3.96__py3-none-any.whl → 0.3.197__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. pyxllib/algo/geo.py +12 -0
  2. pyxllib/algo/intervals.py +1 -1
  3. pyxllib/algo/matcher.py +78 -0
  4. pyxllib/algo/pupil.py +187 -19
  5. pyxllib/algo/specialist.py +2 -1
  6. pyxllib/algo/stat.py +38 -2
  7. {pyxlpr → pyxllib/autogui}/__init__.py +1 -1
  8. pyxllib/autogui/activewin.py +246 -0
  9. pyxllib/autogui/all.py +9 -0
  10. pyxllib/{ext/autogui → autogui}/autogui.py +40 -11
  11. pyxllib/autogui/uiautolib.py +362 -0
  12. pyxllib/autogui/wechat.py +827 -0
  13. pyxllib/autogui/wechat_msg.py +421 -0
  14. pyxllib/autogui/wxautolib.py +84 -0
  15. pyxllib/cv/slidercaptcha.py +137 -0
  16. pyxllib/data/echarts.py +123 -12
  17. pyxllib/data/jsonlib.py +89 -0
  18. pyxllib/data/pglib.py +514 -30
  19. pyxllib/data/sqlite.py +231 -4
  20. pyxllib/ext/JLineViewer.py +14 -1
  21. pyxllib/ext/drissionlib.py +277 -0
  22. pyxllib/ext/kq5034lib.py +0 -1594
  23. pyxllib/ext/robustprocfile.py +497 -0
  24. pyxllib/ext/unixlib.py +6 -5
  25. pyxllib/ext/utools.py +108 -95
  26. pyxllib/ext/webhook.py +32 -14
  27. pyxllib/ext/wjxlib.py +88 -0
  28. pyxllib/ext/wpsapi.py +124 -0
  29. pyxllib/ext/xlwork.py +9 -0
  30. pyxllib/ext/yuquelib.py +1003 -71
  31. pyxllib/file/docxlib.py +1 -1
  32. pyxllib/file/libreoffice.py +165 -0
  33. pyxllib/file/movielib.py +9 -0
  34. pyxllib/file/packlib/__init__.py +112 -75
  35. pyxllib/file/pdflib.py +1 -1
  36. pyxllib/file/pupil.py +1 -1
  37. pyxllib/file/specialist/dirlib.py +1 -1
  38. pyxllib/file/specialist/download.py +10 -3
  39. pyxllib/file/specialist/filelib.py +266 -55
  40. pyxllib/file/xlsxlib.py +205 -50
  41. pyxllib/file/xlsyncfile.py +341 -0
  42. pyxllib/prog/cachetools.py +64 -0
  43. pyxllib/prog/filelock.py +42 -0
  44. pyxllib/prog/multiprogs.py +940 -0
  45. pyxllib/prog/newbie.py +9 -2
  46. pyxllib/prog/pupil.py +129 -60
  47. pyxllib/prog/specialist/__init__.py +176 -2
  48. pyxllib/prog/specialist/bc.py +5 -2
  49. pyxllib/prog/specialist/browser.py +11 -2
  50. pyxllib/prog/specialist/datetime.py +68 -0
  51. pyxllib/prog/specialist/tictoc.py +12 -13
  52. pyxllib/prog/specialist/xllog.py +5 -5
  53. pyxllib/prog/xlosenv.py +7 -0
  54. pyxllib/text/airscript.js +744 -0
  55. pyxllib/text/charclasslib.py +17 -5
  56. pyxllib/text/jiebalib.py +6 -3
  57. pyxllib/text/jinjalib.py +32 -0
  58. pyxllib/text/jsa_ai_prompt.md +271 -0
  59. pyxllib/text/jscode.py +159 -4
  60. pyxllib/text/nestenv.py +1 -1
  61. pyxllib/text/newbie.py +12 -0
  62. pyxllib/text/pupil/common.py +26 -0
  63. pyxllib/text/specialist/ptag.py +2 -2
  64. pyxllib/text/templates/echart_base.html +11 -0
  65. pyxllib/text/templates/highlight_code.html +17 -0
  66. pyxllib/text/templates/latex_editor.html +103 -0
  67. pyxllib/text/xmllib.py +76 -14
  68. pyxllib/xl.py +2 -1
  69. pyxllib-0.3.197.dist-info/METADATA +48 -0
  70. pyxllib-0.3.197.dist-info/RECORD +126 -0
  71. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +1 -2
  72. pyxllib/ext/autogui/__init__.py +0 -8
  73. pyxllib-0.3.96.dist-info/METADATA +0 -51
  74. pyxllib-0.3.96.dist-info/RECORD +0 -333
  75. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  76. pyxlpr/ai/__init__.py +0 -5
  77. pyxlpr/ai/clientlib.py +0 -1281
  78. pyxlpr/ai/specialist.py +0 -286
  79. pyxlpr/ai/torch_app.py +0 -172
  80. pyxlpr/ai/xlpaddle.py +0 -655
  81. pyxlpr/ai/xltorch.py +0 -705
  82. pyxlpr/data/__init__.py +0 -11
  83. pyxlpr/data/coco.py +0 -1325
  84. pyxlpr/data/datacls.py +0 -365
  85. pyxlpr/data/datasets.py +0 -200
  86. pyxlpr/data/gptlib.py +0 -1291
  87. pyxlpr/data/icdar/__init__.py +0 -96
  88. pyxlpr/data/icdar/deteval.py +0 -377
  89. pyxlpr/data/icdar/icdar2013.py +0 -341
  90. pyxlpr/data/icdar/iou.py +0 -340
  91. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  92. pyxlpr/data/imtextline.py +0 -473
  93. pyxlpr/data/labelme.py +0 -866
  94. pyxlpr/data/removeline.py +0 -179
  95. pyxlpr/data/specialist.py +0 -57
  96. pyxlpr/eval/__init__.py +0 -85
  97. pyxlpr/paddleocr.py +0 -776
  98. pyxlpr/ppocr/__init__.py +0 -15
  99. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  100. pyxlpr/ppocr/data/__init__.py +0 -135
  101. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  102. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  103. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  104. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  105. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  106. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  107. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  108. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  109. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  110. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  111. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  112. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  113. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  114. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  115. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  116. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  117. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  118. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  119. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  120. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  121. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  122. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  123. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  124. pyxlpr/ppocr/losses/__init__.py +0 -61
  125. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  126. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  127. pyxlpr/ppocr/losses/center_loss.py +0 -88
  128. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  129. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  130. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  131. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  132. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  133. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  134. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  135. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  136. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  137. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  138. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  139. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  140. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  141. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  142. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  143. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  144. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  145. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  146. pyxlpr/ppocr/metrics/__init__.py +0 -44
  147. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  148. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  149. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  150. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  151. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  152. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  153. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  154. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  155. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  156. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  157. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  158. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  159. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  160. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  161. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  162. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  163. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  164. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  165. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  166. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  167. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  168. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  169. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  170. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  171. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  172. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  173. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  174. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  175. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  176. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  177. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  178. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  179. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  180. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  181. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  182. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  183. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  184. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  185. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  186. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  187. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  188. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  189. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  190. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  191. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  192. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  193. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  194. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  195. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  196. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  197. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  198. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  199. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  200. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  201. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  202. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  203. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  204. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  205. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  206. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  207. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  208. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  209. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  210. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  211. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  212. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  213. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  214. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  215. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  216. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  217. pyxlpr/ppocr/tools/__init__.py +0 -14
  218. pyxlpr/ppocr/tools/eval.py +0 -83
  219. pyxlpr/ppocr/tools/export_center.py +0 -77
  220. pyxlpr/ppocr/tools/export_model.py +0 -129
  221. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  222. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  223. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  224. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  225. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  226. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  227. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  228. pyxlpr/ppocr/tools/infer_det.py +0 -134
  229. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  230. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  231. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  232. pyxlpr/ppocr/tools/infer_table.py +0 -107
  233. pyxlpr/ppocr/tools/program.py +0 -596
  234. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  235. pyxlpr/ppocr/tools/train.py +0 -163
  236. pyxlpr/ppocr/tools/xlprog.py +0 -748
  237. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  238. pyxlpr/ppocr/utils/__init__.py +0 -24
  239. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  240. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  241. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  242. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  243. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  244. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  245. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  246. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  247. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  248. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  249. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  250. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  251. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  252. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  253. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  254. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  255. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  256. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  257. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  258. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  259. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  260. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  261. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  262. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  263. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  264. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  265. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  266. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  267. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  268. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  269. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  270. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  271. pyxlpr/ppocr/utils/dict90.txt +0 -90
  272. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  273. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  274. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  275. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  276. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  277. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  278. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  279. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  280. pyxlpr/ppocr/utils/gen_label.py +0 -81
  281. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  282. pyxlpr/ppocr/utils/iou.py +0 -54
  283. pyxlpr/ppocr/utils/logging.py +0 -69
  284. pyxlpr/ppocr/utils/network.py +0 -84
  285. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  286. pyxlpr/ppocr/utils/profiler.py +0 -110
  287. pyxlpr/ppocr/utils/save_load.py +0 -150
  288. pyxlpr/ppocr/utils/stats.py +0 -72
  289. pyxlpr/ppocr/utils/utility.py +0 -80
  290. pyxlpr/ppstructure/__init__.py +0 -13
  291. pyxlpr/ppstructure/predict_system.py +0 -187
  292. pyxlpr/ppstructure/table/__init__.py +0 -13
  293. pyxlpr/ppstructure/table/eval_table.py +0 -72
  294. pyxlpr/ppstructure/table/matcher.py +0 -192
  295. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  296. pyxlpr/ppstructure/table/predict_table.py +0 -221
  297. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  298. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  299. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  300. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  301. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  302. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  303. pyxlpr/ppstructure/utility.py +0 -71
  304. pyxlpr/xlai.py +0 -10
  305. /pyxllib/{ext/autogui → autogui}/virtualkey.py +0 -0
  306. {pyxllib-0.3.96.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxlpr/ai/clientlib.py DELETED
@@ -1,1281 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2020/05/30 21:14
6
-
7
- """
8
- 百度人工智能API,常用URL
9
- 使用文档:https://cloud.baidu.com/doc/OCR/s/Ek3h7xypm
10
- 调用次数:https://console.bce.baidu.com/ai/?_=1653139065257#/ai/ocr/overview/index
11
-
12
-
13
- """
14
- import os
15
- import base64
16
- import json
17
- import pprint
18
- import statistics
19
- import time
20
-
21
- import cv2
22
- import numpy as np
23
- import requests
24
-
25
- from pyxllib.prog.newbie import round_int
26
- from pyxllib.prog.pupil import check_install_package, is_url
27
- from pyxllib.prog.xlosenv import XlOsEnv
28
- from pyxllib.algo.geo import xywh2ltrb, rect_bounds
29
- from pyxllib.file.specialist import XlPath, get_etag
30
- from pyxllib.cv.expert import xlcv
31
-
32
-
33
- def __1_转类labelme标注():
34
- """ 将百度api获得的各种业务结果,转成类似labelme的标注格式
35
-
36
- 统一成一种风格,易分析的结构
37
- """
38
-
39
-
40
- def loc2points(loc, ratio=1):
41
- """ 百度的location格式转为 {'points': [[a, b], [c, d]], 'shape_type: 'rectangle'} """
42
- # 目前关注到的都是矩形,不知道有没可能有其他格式
43
- ltrb = xywh2ltrb([loc['left'], loc['top'], loc['width'], loc['height']])
44
- if ratio != 1:
45
- ltrb = [x * ratio for x in ltrb]
46
- l, t, r, b = round_int(ltrb, ndim=1)
47
- return {'points': [[l, t], [r, b]],
48
- 'shape_type': 'rectangle'}
49
-
50
-
51
- def loc2points2(loc, ratio=1):
52
- """ 百度的location格式转为 {'points': [[a, b], [c, d]], 'shape_type: 'rectangle'} """
53
- # 目前关注到的都是矩形,不知道有没可能有其他格式
54
- ltrb = xywh2ltrb([loc['left'], loc['top'], loc['right'] - loc['left'], loc['bottom'] - loc['top']])
55
- if ratio != 1:
56
- ltrb = [x * ratio for x in ltrb]
57
- l, t, r, b = round_int(ltrb, ndim=1)
58
- return {'points': [[l, t], [r, b]],
59
- 'shape_type': 'rectangle'}
60
-
61
-
62
- def polygon2rect(pts):
63
- l, t, r, b = rect_bounds(pts)
64
- return [[l, t], [r, b]]
65
-
66
-
67
- def zoom_point(pt, ratio):
68
- """ 返回的还是{x: 123, y: 456}的字典结构 """
69
- if ratio != 1:
70
- return {k: round_int(v * ratio) for k, v in pt.items()}
71
- return pt
72
-
73
-
74
- def zoom_point2(pt, ratio):
75
- """ 输入x,y的字典坐标,返回的还是[123, 456]的list结构 """
76
- pt = [pt['x'], pt['y']]
77
- if ratio != 1:
78
- return [round_int(v * ratio) for v in pt]
79
- return pt
80
-
81
-
82
- def zoom_labelme(d, ratio):
83
- """ 对labelme的标注进行缩放 """
84
- ratio2 = 1 / ratio
85
- for sp in d['shapes']:
86
- sp['points'] = [[round_int(p[0] * ratio2), round_int(p[1] * ratio2)] for p in sp['points']]
87
- d['imageHeight'] = round_int(d['imageHeight'] * ratio2)
88
- d['imageWidth'] = round_int(d['imageWidth'] * ratio2)
89
- return d
90
-
91
-
92
- def labelmelike_extend_args(core_func):
93
- """ 扩展 main_func 函数,支持一些通用上下游切面功能
94
-
95
- 支持 main_keys,主要数据字段名称
96
- 支持 remove_keys,批量移除一些不需要的参数值
97
- """
98
-
99
- def wrapper(data, ratio, main_key, remove_keys=None, *, clear_empty_shape=False):
100
- """
101
- :param clear_empty_shape: 在考虑要不要把清除空shape,四边形转矩形等功能写到这里,暂时还未写对应功能
102
- """
103
- # 1 转主要的数据结构
104
- if main_key in data:
105
- data['shapes'] = core_func(data[main_key], ratio)
106
-
107
- # 2 删除不需要的键值
108
- if remove_keys is None:
109
- _remove_keys = set()
110
- elif not isinstance(remove_keys, (list, tuple, set)):
111
- _remove_keys = [remove_keys]
112
- else:
113
- _remove_keys = remove_keys
114
- for k in ({main_key} | set(_remove_keys)):
115
- if k in data:
116
- del data[k]
117
-
118
- return data
119
-
120
- return wrapper
121
-
122
-
123
- class ToLabelmeLike:
124
- """ 注意配合装饰器的使用
125
- 这里每个函数,只要实现输入核心识别结果清单,返回shapes的代码即可。上下游一些切面操作是统一的。
126
- """
127
-
128
- @staticmethod
129
- @labelmelike_extend_args
130
- def list_word(ls, ratio):
131
- shapes = []
132
- for w in ls:
133
- shape = {}
134
- if 'location' in w:
135
- shape.update(loc2points(w['location'], ratio))
136
- if 'words' in w:
137
- # shape['label'] = json.dumps({'text': x['words']}, ensure_ascii=False)
138
- shape['label'] = {'text': w['words']} # 不转字符串
139
- shapes.append(shape)
140
- return shapes
141
-
142
- @staticmethod
143
- @labelmelike_extend_args
144
- def list_word2(ls, ratio):
145
- shapes = []
146
- for w in ls:
147
- shape = {}
148
- shape.update(loc2points(w['words']['words_location'], ratio))
149
- shape['label'] = {'text': w['words']['word'], 'category': w['words_type']}
150
- shapes.append(shape)
151
- return shapes
152
-
153
- @staticmethod
154
- @labelmelike_extend_args
155
- def dict_word(d, ratio):
156
- shapes = []
157
- for k, w in d.items():
158
- shape = {'label': {'category': k}}
159
- if 'location' in w:
160
- shape.update(loc2points(w['location'], ratio))
161
- if 'words' in w:
162
- shape['label']['text'] = w['words']
163
- shapes.append(shape)
164
- return shapes
165
-
166
- @staticmethod
167
- @labelmelike_extend_args
168
- def dict_str(d, ratio):
169
- return [{'label': {'text': v, 'category': k}} for k, v in d.items()]
170
-
171
- @staticmethod
172
- @labelmelike_extend_args
173
- def dict_strs(d, ratio):
174
- shapes = []
175
- for k, texts in d.items():
176
- shapes.append({'label': {'category': k, 'text': ','.join(texts)}})
177
- return shapes
178
-
179
-
180
- def __2_定制不同输出格式():
181
- pass
182
-
183
-
184
- class XlAiClient:
185
- """
186
- 封装该类
187
- 目的1:合并输入文件和url的识别
188
- 目的2:带透明底的png百度api识别不了,要先转成RGB格式
189
- """
190
- META_OPTS_NAME = {'record_files', # 是否记录传入的图片等数据
191
- 'record_xlapi', # 是否记录api处理结果
192
- 'use_exists_xlapi', # 是否使用已有的api结果
193
- 'record_xlserver', # 是否记录调用纪录
194
- }
195
-
196
- def __init__(self, auto_login=True, check=True):
197
- # 1 默认值
198
- self.db = None
199
-
200
- self._aipocr = None
201
-
202
- self._mathpix_header = None
203
-
204
- self._priu_header = None
205
- self._priu_host = None
206
-
207
- # 2 如果环境变量预存了账号信息,自动加载使用
208
- accounts = XlOsEnv.get('XlAiAccounts', decoding=True)
209
- if accounts and auto_login:
210
- if 'aipocr' in accounts:
211
- self.login_aipocr(**accounts['aipocr'])
212
- if 'mathpix' in accounts:
213
- self.login_mathpix(**accounts['mathpix'])
214
- if 'priu' in accounts:
215
- self.login_priu(**accounts['priu'], check=check)
216
-
217
- def __A1_登录账号(self):
218
- pass
219
-
220
- def set_database(self, db):
221
- """ 是否关联数据库,查找已运行过的结果,或存储运行结果
222
-
223
- from pyxllib.data.pglib import XlprDb
224
- db = XlprDb.connect()
225
- self.setup_database(db)
226
- """
227
- self.db = db
228
-
229
- def login_aipocr(self, app_id, api_key, secret_key):
230
- """
231
- 注:带透明底的png百度api识别不了,要先转成RGB格式
232
- """
233
- check_install_package('aip', 'baidu-aip')
234
- import aip
235
-
236
- self._aipocr = aip.AipOcr(str(app_id), api_key, secret_key)
237
-
238
- def login_mathpix(self, app_id, app_key):
239
- self._mathpix_header = {'Content-type': 'application/json'}
240
- self._mathpix_header.update({'app_id': app_id, 'app_key': app_key})
241
-
242
- def login_priu(self, token, host=None, *, check=True):
243
- """ 福建省模式识别与图像理解重点实验室
244
-
245
- :param str|list[str] host:
246
- str, 主机IP,比如'118.195.202.82'
247
- list, 也可以输入一个列表,会从左到右依次尝试链接,使用第一个能成功链接的ip,常用语优先选用局域网,找不到再使用公网接口
248
- """
249
- self._priu_header = {'Content-type': 'application/json'}
250
- self._priu_header.update({'Token': token})
251
-
252
- if host is None:
253
- # 优先尝试局域网链接,如果失败则尝试公网链接。公网默认用https,如果需要效率可以自己输入http的host。
254
- hosts = ['172.16.170.136', 'https://xmutpriu.com']
255
- elif isinstance(host, str):
256
- hosts = [host]
257
- elif isinstance(host, (list, tuple)):
258
- hosts = host
259
- else:
260
- raise TypeError
261
-
262
- # 确保都有http或https前缀
263
- for i, host in enumerate(hosts):
264
- if 'http' not in host:
265
- hosts[i] = f'http://{host}'
266
-
267
- if check:
268
- connent = False
269
- for host in hosts:
270
- try:
271
- if '欢迎来到 厦门理工' in requests.get(f'{host}/test_page', timeout=5).text:
272
- self._priu_host = host
273
- connent = True
274
- break
275
- except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
276
- continue
277
-
278
- if not connent:
279
- raise requests.exceptions.ConnectionError('PRIU接口登录失败')
280
- return connent
281
- else:
282
- self._priu_host = hosts[0] # 不检查的场景,一般都是局域网内使用
283
- return None
284
-
285
- def __A2_调整图片和关联数据库(self):
286
- pass
287
-
288
- @classmethod
289
- def adjust_image(cls, in_, flags=1, *, b64decode=True, to_buffer=True, b64encode=False,
290
- min_length=15, max_length=4096,
291
- limit_b64buffer_size=4 * 1024 ** 2):
292
- """ 这里是使用API接口,比较通用的一套图片处理操作
293
-
294
- :param in_: 可以是本地文件,也可以是图片url地址,也可以是Image对象
295
- 注意这个函数,输入是url,也会获取重置图片数据上传
296
- 如果为了效率明确只传url,可以用aip.AipOcr原生的相关url函数
297
- :param b64decode: 如果输入是bytes类型,是否要用b64解码,默认需要
298
- :return: 返回图片文件二进制值的buffer, 缩放系数(小余1是缩小,大于1是放大)
299
- """
300
- # 1 取不同来源的数据
301
- # 下面应该是比较通用的一套操作,如果有特殊接口,可以另外处理,不一定要通过该接口处理图片
302
- if isinstance(in_, bytes):
303
- im = xlcv.read_from_buffer(in_, flags, b64decode=b64decode)
304
- elif is_url(in_):
305
- im = xlcv.read_from_url(in_, flags)
306
- else:
307
- im = xlcv.read(in_, flags)
308
- origin_height = im.shape[0]
309
-
310
- if im.dtype == 'uint16':
311
- im = cv2.convertScaleAbs(im)
312
-
313
- # 2 图片尺寸不符合要求,要缩放
314
- if min_length or max_length:
315
- im = xlcv.adjust_shape(im, min_length, max_length)
316
-
317
- # 3 图片文件不能过大,要调整
318
- if limit_b64buffer_size:
319
- # b64后大小固定会变4/3,所以留给原文件的大小是要缩水,只有0.75;再以防万一总不能卡得刚刚好,所以设为0.74
320
- im = xlcv.reduce_filesize(im, limit_b64buffer_size * 0.74)
321
- current_height = im.shape[0]
322
-
323
- if to_buffer:
324
- im = xlcv.to_buffer(im, '.jpg', b64encode=b64encode)
325
- ratio = current_height / origin_height
326
- return im, ratio
327
-
328
- def _divide_options_with_meta(self, options, meta_opts):
329
- """ 会改变原始输入的两个字典值,但是新返回的才是真正有效的字典 """
330
- options = options or {} # 这里也要先处理,防止options是None类型
331
- meta_opts = meta_opts or {} # 这里也要先处理,防止是None类型
332
-
333
- for name in self.META_OPTS_NAME:
334
- if name in options:
335
- meta_opts[name] = options[name]
336
- del options[name]
337
-
338
- options = options or {}
339
- options = {k: options[k] for k in sorted(options.keys())} # 对参数进行排序,方便记录到数据库后标记唯一
340
-
341
- return options, meta_opts
342
-
343
- def run_aipocr_with_db(self, func, buffer, options=None, **meta_opts):
344
- """ 这一套主要是适配百度视觉api接口的情况,基本都是输入一个buffer和一个options
345
- 如果自己模型有特殊接口范式,可以额外定制,比如 run_textapi_with_db 等,可以拷贝这个函数后做定制微调
346
-
347
- :param func: 被封装的带执行的api函数
348
- :param buffer: 图片二进制数据
349
- :param options: 执行api功能的配套采纳数
350
- :param meta_opts:
351
- record_files: 是否保存数据文件
352
- False, 0,表示不存储
353
- True,表示存储,但默认只存储4MB以内的文件
354
- int,可以写一个整数,表示只存储多大Byte范围内的文件
355
- record_xlapi: 新结果是否记录到数据库
356
- use_exists_xlapi: 如果数据库里已有记录,是否直接复用
357
- 注意看函数实现,但这个机制生效已经获得res的时候,变不会显式执行record_files
358
- 这是出于效率的考虑,一般情况下,这种应该是已经存储过文件了
359
- 如果要怕以前执行过api但没存储files,可以关闭use_exists_xlapi,就可以显式重新执行保存一次
360
- mode_name: 可以指定存入数据库的功能名,默认用func的名称
361
- """
362
- # 1 参数处理
363
- options, meta_opts = self._divide_options_with_meta(options, meta_opts)
364
- if self.db is None: # 没有数据库的时候直接执行函数返回结果就好
365
- return func(buffer, options)
366
-
367
- # 以下元参数默认值,根据不同的api接口需求,可以设置不同的默认值
368
- record_files = meta_opts.get('record_files', True) # 是否保存数据文件
369
- if record_files is True:
370
- record_files = 4 * 1024 ** 2
371
- record_xlapi = meta_opts.get('record_xlapi', True)
372
- use_exists_xlapi = meta_opts.get('use_exists_xlapi', True)
373
- mode_name = meta_opts.get('mode_name', func.__name__)
374
-
375
- image_etag = None # 先不计算etag,只在需要的时候再计算
376
- res = None # api结果
377
-
378
- # 2 核心函数
379
- if use_exists_xlapi: # 定义如何获取已有的识别结果
380
- image_etag = image_etag or get_etag(buffer) # trick: 使用这个机制确保etag从头到尾不会重复计算,需要的话只会计算一次
381
- res = self.db.get_xlapi_record(mode=mode_name, image=image_etag, **options)
382
-
383
- # 否则调用百度的接口识别
384
- # TODO 这里使用协程逻辑最合理但配置麻烦,需要func底层等做协程的适配支持
385
- # 使用多线程测试了并没有更快,也发现主要耗时是post,数据库不会花太多时间,就先不改动了
386
- # 等以后数据库大了,看运行是否会慢,可以再测试是否有必要弄协程
387
- if res is None or 'error_code' in res:
388
- tt = time.time()
389
- res = func(buffer, options)
390
- elapse_ms = round_int(1000 * (time.time() - tt))
391
-
392
- if len(buffer) < record_files: # 定义数据的缓存方式
393
- image_etag = image_etag or get_etag(buffer)
394
- self.db.insert_row2files(buffer, etag=image_etag, name='.jpg')
395
-
396
- if record_xlapi: # 定义api的执行结果的保存方式
397
- image_etag = image_etag or get_etag(buffer)
398
- input = {'mode': mode_name, 'image': image_etag}
399
- if options:
400
- input.update(options)
401
- xlapi_id = self.db.insert_row2xlapi(input, res, elapse_ms, on_conflict='REPLACE')
402
- res['xlapi_id'] = xlapi_id
403
-
404
- # 3 收尾
405
- if 'log_id' in res: # 有xlapi_id的标记,就不用百度原本的log_id了
406
- del res['log_id']
407
-
408
- return res
409
-
410
- def __B1_通用(self):
411
- pass
412
-
413
- def general(self, image, **options):
414
- """ 通用文字识别(标准含位置版): https://cloud.baidu.com/doc/OCR/s/vk3h7y58v
415
- 500次/天赠送 + 超出按量计费
416
-
417
- 注意只要ratio!=1涉及到缩放的,偏移误差是会变大的~~
418
- """
419
- buffer, ratio = self.adjust_image(image)
420
- result_dict = self.run_aipocr_with_db(self._aipocr.general, buffer, options)
421
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
422
- return result_dict
423
-
424
- def basicGeneral(self, image, **options):
425
- """ 通用文字识别(标准版): https://cloud.baidu.com/doc/OCR/s/zk3h7xz52
426
- 5万次/天赠送 + 超出按量计费
427
- """
428
- buffer, ratio = self.adjust_image(image)
429
- result_dict = self.run_aipocr_with_db(self._aipocr.basicGeneral, buffer, options)
430
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
431
- return result_dict
432
-
433
- def accurate(self, image, **options):
434
- """ 通用文字识别(高精度含位置版): https://cloud.baidu.com/doc/OCR/s/tk3h7y2aq """
435
- sz = 10 * 1024 ** 2
436
- buffer, ratio = self.adjust_image(image, max_length=8192, limit_b64buffer_size=sz, to_buffer=True)
437
- result_dict = self.run_aipocr_with_db(self._aipocr.accurate, buffer, options, save_buffer_threshold_size=sz)
438
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
439
- return result_dict
440
-
441
- def basicAccurate(self, image, **options):
442
- """ 通用文字识别(高精度版): https://cloud.baidu.com/doc/OCR/s/1k3h7y3db """
443
- sz = 10 * 1024 ** 2
444
- buffer, ratio = self.adjust_image(image, max_length=8192, limit_b64buffer_size=sz, to_buffer=True)
445
- result_dict = self.run_aipocr_with_db(self._aipocr.basicAccurate, buffer, options,
446
- save_buffer_threshold_size=sz)
447
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
448
- return result_dict
449
-
450
- def webimageLoc(self, image, **options):
451
- """ 网络图片文字识别(含位置版): https://cloud.baidu.com/doc/OCR/s/Nkaz574we """
452
- buffer, ratio = self.adjust_image(image)
453
- result_dict = self.run_aipocr_with_db(self._aipocr.webimageLoc, buffer, options)
454
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
455
- return result_dict
456
-
457
- def webImage(self, image, **options):
458
- """ 网络图片文字识别: https://cloud.baidu.com/doc/OCR/s/Sk3h7xyad """
459
- buffer, ratio = self.adjust_image(image)
460
- result_dict = self.run_aipocr_with_db(self._aipocr.webImage, buffer, options)
461
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
462
- return result_dict
463
-
464
- def handwriting(self, image, **options):
465
- """ 手写文字识别: https://cloud.baidu.com/doc/OCR/s/hk3h7y2qq """
466
- buffer, ratio = self.adjust_image(image)
467
- result_dict = self.run_aipocr_with_db(self._aipocr.handwriting, buffer, options)
468
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
469
- return result_dict
470
-
471
- def numbers(self, image, **options):
472
- """ 数字识别: https://cloud.baidu.com/doc/OCR/s/Ok3h7y1vo """
473
- buffer, ratio = self.adjust_image(image)
474
- result_dict = self.run_aipocr_with_db(self._aipocr.numbers, buffer, options)
475
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
476
- return result_dict
477
-
478
- def qrcode(self, image, **options):
479
- """ 二维码识别: https://cloud.baidu.com/doc/OCR/s/qk3h7y5o7 """
480
- buffer, ratio = self.adjust_image(image)
481
- result_dict = self.run_aipocr_with_db(self._aipocr.qrcode, buffer, options)
482
-
483
- ls = result_dict['codes_result']
484
- shapes = []
485
- for x in ls:
486
- # assert len(x['text']) == 1, '取到的文本list长度大于1'
487
- shapes.append({'label': {'text': ','.join(x['text']), 'category': x['type']}})
488
- result_dict['shapes'] = shapes
489
-
490
- del result_dict['codes_result']
491
- del result_dict['codes_result_num']
492
- return result_dict
493
-
494
- def form(self, image, **options):
495
- """ 表格文字识别(同步接口): https://cloud.baidu.com/doc/OCR/s/ik3h7xyxf
496
-
497
- 备注:
498
- 0、有异步接口
499
- 1、返回是四边形结构,实际值都是矩形,先转成矩形处理了
500
- 2、因为矩形,这个不能处理倾斜的表格
501
- 3、看起来支持多表格
502
- """
503
- from bisect import bisect_left
504
- from pyxllib.file.xlsxlib import XlWorkbook
505
-
506
- # 1 单表处理功能
507
- def zoom_coords(table, ratio):
508
- for k, xs in table.items():
509
- if k == 'vertexes_location':
510
- table[k] = [zoom_point(pt, ratio) for pt in table[k]]
511
- else:
512
- for x in table[k]:
513
- x['vertexes_location'] = [zoom_point(pt, ratio) for pt in x['vertexes_location']]
514
-
515
- def parse_split_points(cells, cls='row', name='y'):
516
- # 计算行列数量,初始化空list
517
- n = max([x[cls] for x in cells]) + 2
518
- spans = [[] for i in range(n)]
519
- # 加入所有行列数据
520
- for x in cells:
521
- spans[x[cls]].append(x['vertexes_location'][0][name])
522
- spans[-1].append(table['vertexes_location'][2][name])
523
- # 计算平均行列位置
524
- spans = [statistics.mean(vs) for vs in spans if vs]
525
- # 合并临近的行列
526
- min_gap = 3 # 两行/列间距小余min_gap像素,合并为1行/列
527
- spans = [v for a, v in zip([0] + spans, spans) if v - a > min_gap]
528
- # 计算分割线(以中间为准)
529
- spans = [round_int((a + b) / 2) for a, b in zip(spans, spans[1:])]
530
- return spans
531
-
532
- def location2rowcol(loc, rowspan, colspan):
533
- p1, p2 = loc[0], loc[2]
534
- pos = {
535
- 'row': bisect_left(rowspan, p1['y']) + 1, # 我的数据改成从1开始编号了
536
- 'column': bisect_left(colspan, p1['x']) + 1,
537
- 'end_row': bisect_left(rowspan, p2['y']),
538
- 'end_column': bisect_left(colspan, p2['x']),
539
- }
540
- return pos
541
-
542
- def parse_table(table):
543
- # 1 计算行、列分界线
544
- rowspan = parse_split_points(table['body'], 'row', 'y')
545
- colspan = parse_split_points(table['body'], 'column', 'x')
546
-
547
- # 2 shapes
548
- shapes = []
549
- # 这个表格暂时转labelme格式,但泛用来说,其实不应该转labelme,它有其特殊性
550
- # location虽然给的是四边形,但看目前数据其实就是矩形
551
- for k, xs in table.items():
552
- if k == 'vertexes_location':
553
- sp = {'label': {'text': '', 'category': 'table'},
554
- 'points': polygon2rect([(p['x'], p['y']) for p in table['vertexes_location']]),
555
- 'shape_type': 'rectangle'}
556
- shapes.append(sp)
557
- else:
558
- for x in xs:
559
- label = {'text': x['words'], 'probability': x['probability'], 'category': k}
560
- label.update(location2rowcol(x['vertexes_location'], rowspan, colspan))
561
- sp = {'label': label,
562
- 'points': polygon2rect([(p['x'], p['y']) for p in x['vertexes_location']]),
563
- 'shape_type': 'rectangle'}
564
- shapes.append(sp)
565
-
566
- # 3 tables
567
- # 3.1 表格主体内容
568
- wb = XlWorkbook()
569
- ws = wb.active
570
- for sp in shapes:
571
- x = sp['label']
572
- if x['category'] != 'body':
573
- continue
574
- # ws.cell(x['row'], x['column'], x['text'])
575
- cel = ws.cell(x['row'], x['column']).mcell()
576
- cel.value = (cel.value or '') + x['text']
577
- if x['end_row'] - x['row'] + x['end_column'] - x['column'] > 0:
578
- ws.merge_cells(start_row=x['row'], start_column=x['column'],
579
- end_row=x['end_row'], end_column=x['end_column'])
580
- # wb.save('/home/chenkunze/data/aipocr_test/a.xlsx') # debug: 保存中间结果查看
581
-
582
- # 3.2 其他内容整体性拼接
583
- htmltable = ['<div>']
584
- header = '<br/>'.join([x['words'] for x in table['header']])
585
- footer = '<br/>'.join([x['words'] for x in table['footer']])
586
- if header:
587
- htmltable.append(f'<p>{header}</p>')
588
- htmltable.append(ws.to_html())
589
- if footer:
590
- htmltable.append(f'<p>{footer}</p>')
591
- htmltable.append('</div>')
592
-
593
- return shapes, '\n'.join(htmltable)
594
-
595
- # 2 主体解析功能
596
-
597
- buffer, ratio = self.adjust_image(image)
598
- result_dict = self.run_aipocr_with_db(self._aipocr.form, buffer, options)
599
- shapes = []
600
- htmltables = []
601
- for table in result_dict['forms_result']:
602
- if ratio != 1:
603
- zoom_coords(table, 1 / ratio)
604
- _shapes, htmltable = parse_table(table)
605
- shapes += _shapes
606
- htmltables.append(htmltable)
607
-
608
- # 3 收尾
609
- result_dict['shapes'] = shapes
610
- result_dict['htmltables'] = htmltables
611
- del result_dict['forms_result']
612
- del result_dict['forms_result_num']
613
- return result_dict
614
-
615
- def doc_analysis_office(self, image, **options):
616
- """ 办公文档识别: https://cloud.baidu.com/doc/OCR/s/ykg9c09ji """
617
- buffer, ratio = self.adjust_image(image)
618
- result_dict = self.run_aipocr_with_db(self._aipocr.doc_analysis_office, buffer, options)
619
- result_dict = ToLabelmeLike.list_word2(result_dict, 1 / ratio, 'results', 'results_num')
620
- return result_dict
621
-
622
- def seal(self, image, **options):
623
- """ 印章识别: https://cloud.baidu.com/doc/OCR/s/Mk3h7y47a """
624
- buffer, ratio = self.adjust_image(image)
625
- result_dict = self.run_aipocr_with_db(self._aipocr.seal, buffer, options)
626
-
627
- shapes = []
628
- for x in result_dict['result']:
629
- shape = {'label': {}}
630
- shape.update(loc2points(x['location'], 1 / ratio))
631
- shape['label']['text'] = x['major']['words']
632
- shape['label']['minor'] = ','.join(x['minor'])
633
- shape['label']['category'] = x['type']
634
- shapes.append(shape)
635
-
636
- result_dict['shapes'] = shapes
637
- del result_dict['result']
638
- del result_dict['result_num']
639
- return result_dict
640
-
641
- def __B2_卡证(self):
642
- pass
643
-
644
- def idcard(self, image, **options):
645
- """ 身份证识别: https://cloud.baidu.com/doc/OCR/s/rk3h7xzck """
646
-
647
- def idcard_front(image, options=None):
648
- return self._aipocr.idcard(image,
649
- options.get('id_card_side', 'front'), # 默认识别带照片一面
650
- options)
651
-
652
- buffer, ratio = self.adjust_image(image)
653
- result_dict = self.run_aipocr_with_db(idcard_front, buffer, options)
654
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result')
655
- return result_dict
656
-
657
- def idcard_back(self, image, **options):
658
- def func(image, options=None):
659
- return self._aipocr.idcard(image,
660
- options.get('id_card_side', 'back'), # 识别国徽一面
661
- options)
662
-
663
- buffer, ratio = self.adjust_image(image)
664
- result_dict = self.run_aipocr_with_db(func, buffer, options, mode_name='idcard_back')
665
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result')
666
- return result_dict
667
-
668
- def bankcard(self, image, **options):
669
- """ 银行卡识别: https://cloud.baidu.com/doc/OCR/s/ak3h7xxg3 """
670
- buffer, ratio = self.adjust_image(image)
671
- result_dict = self.run_aipocr_with_db(self._aipocr.bankcard, buffer, options)
672
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'result', 'words_result_num')
673
- return result_dict
674
-
675
- def businessLicense(self, image, **options):
676
- """ 营业执照识别: https://cloud.baidu.com/doc/OCR/s/sk3h7y3zs """
677
- buffer, ratio = self.adjust_image(image)
678
- result_dict = self.run_aipocr_with_db(self._aipocr.businessLicense, buffer, options)
679
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
680
- return result_dict
681
-
682
- def businessCard(self, image, **options):
683
- """ 名片识别: https://cloud.baidu.com/doc/OCR/s/5k3h7xyi2 """
684
- buffer, ratio = self.adjust_image(image)
685
- result_dict = self.run_aipocr_with_db(self._aipocr.businessCard, buffer, options)
686
- result_dict = ToLabelmeLike.dict_strs(result_dict, 1 / ratio, 'words_result')
687
- return result_dict
688
-
689
- def passport(self, image, **options):
690
- """ 护照识别: https://cloud.baidu.com/doc/OCR/s/Wk3h7y1gi """
691
- buffer, ratio = self.adjust_image(image)
692
- result_dict = self.run_aipocr_with_db(self._aipocr.passport, buffer, options)
693
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
694
- return result_dict
695
-
696
- def HKMacauExitentrypermit(self, image, **options):
697
- """ 港澳通行证识别: https://cloud.baidu.com/doc/OCR/s/4k3h7y0ly """
698
- buffer, ratio = self.adjust_image(image)
699
- result_dict = self.run_aipocr_with_db(self._aipocr.HKMacauExitentrypermit, buffer, options)
700
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
701
- return result_dict
702
-
703
- def taiwanExitentrypermit(self, image, **options):
704
- """ 台湾通行证识别: https://cloud.baidu.com/doc/OCR/s/kk3h7y2yc """
705
- buffer, ratio = self.adjust_image(image)
706
- result_dict = self.run_aipocr_with_db(self._aipocr.taiwanExitentrypermit, buffer, options)
707
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
708
- return result_dict
709
-
710
- def householdRegister(self, image, **options):
711
- """ 户口本识别: https://cloud.baidu.com/doc/OCR/s/ak3h7xzk7 """
712
- buffer, ratio = self.adjust_image(image)
713
- result_dict = self.run_aipocr_with_db(self._aipocr.householdRegister, buffer, options)
714
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
715
- return result_dict
716
-
717
- def birthCertificate(self, image, **options):
718
- """ 出生医学证明识别: https://cloud.baidu.com/doc/OCR/s/mk3h7y1o6 """
719
- buffer, ratio = self.adjust_image(image)
720
- result_dict = self.run_aipocr_with_db(self._aipocr.birthCertificate, buffer, options)
721
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
722
- return result_dict
723
-
724
- def __B3_交通(self):
725
- pass
726
-
727
- def vehicleLicense(self, image, **options):
728
- """ 行驶证识别: https://cloud.baidu.com/doc/OCR/s/yk3h7y3ks """
729
- buffer, ratio = self.adjust_image(image)
730
- result_dict = self.run_aipocr_with_db(self._aipocr.vehicleLicense, buffer, options)
731
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
732
- return result_dict
733
-
734
- def drivingLicense(self, image, **options):
735
- """ 驾驶证识别: https://cloud.baidu.com/doc/OCR/s/Vk3h7xzz7 """
736
- buffer, ratio = self.adjust_image(image)
737
- result_dict = self.run_aipocr_with_db(self._aipocr.drivingLicense, buffer, options)
738
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
739
- return result_dict
740
-
741
- def licensePlate(self, image, **options):
742
- """ 车牌识别: https://cloud.baidu.com/doc/OCR/s/ck3h7y191 """
743
- buffer, ratio = self.adjust_image(image)
744
- result_dict = self.run_aipocr_with_db(self._aipocr.licensePlate, buffer, options)
745
-
746
- if 'words_result' in result_dict:
747
- d = result_dict['words_result']
748
- else:
749
- d = {'number': '', 'color': '', 'probability': [0], 'vertexes_location': []}
750
-
751
- # 近似key_text,但有点不太一样
752
- # 已测试过 车牌 只能识别一个
753
- label = {'text': d['number'],
754
- 'color': d['color']}
755
- shape = {'label': label,
756
- 'score': sum(d['probability']) / len(d['probability']),
757
- 'points': [zoom_point2(pt, 1 / ratio) for pt in d['vertexes_location']],
758
- 'shape_type': 'polygon'}
759
- result_dict['shapes'] = [shape]
760
-
761
- if 'words_result' in result_dict:
762
- del result_dict['words_result']
763
- return result_dict
764
-
765
- def vinCode(self, image, **options):
766
- """ VIN码识别: https://cloud.baidu.com/doc/OCR/s/zk3h7y51e """
767
- buffer, ratio = self.adjust_image(image)
768
- result_dict = self.run_aipocr_with_db(self._aipocr.vinCode, buffer, options)
769
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
770
- return result_dict
771
-
772
- def vehicleInvoice(self, image, **options):
773
- """ 机动车销售发票识别: https://cloud.baidu.com/doc/OCR/s/vk3h7y4tx """
774
- buffer, ratio = self.adjust_image(image)
775
- result_dict = self.run_aipocr_with_db(self._aipocr.vehicleInvoice, buffer, options)
776
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
777
- return result_dict
778
-
779
- def vehicleCertificate(self, image, **options):
780
- """ 车辆合格证识别: https://cloud.baidu.com/doc/OCR/s/yk3h7y3sc """
781
- buffer, ratio = self.adjust_image(image)
782
- result_dict = self.run_aipocr_with_db(self._aipocr.vehicleCertificate, buffer, options)
783
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
784
- return result_dict
785
-
786
- def raw_mixed_multi_vehicle(self, image, **options):
787
- """ 车辆证照混贴识别: https://cloud.baidu.com/doc/OCR/s/Kksfsbngb """
788
- buffer, ratio = self.adjust_image(image)
789
- result_dict = self.run_aipocr_with_db(self._aipocr.mixed_multi_vehicle, buffer, options)
790
-
791
- if ratio != 1:
792
- for x in result_dict['words_result']:
793
- x['location'] = {k: round_int(v / ratio) for k, v in x['location'].items()}
794
-
795
- return result_dict
796
-
797
- def mixed_multi_vehicle(self, image, **options):
798
- result_dict = self.raw_mixed_multi_vehicle(image, **options)
799
- pprint.pprint(result_dict)
800
-
801
- shapes = []
802
- for x in result_dict.get('words_result', []):
803
- shape = {'label': {}}
804
- shape.update(loc2points(x['location']))
805
- shape['label']['text'] = json.dumps({w['word_name']: w['word'] for w in x['license_info']},
806
- ensure_ascii=False)
807
- shape['label']['category'] = x['card_type']
808
- shape['label']['score'] = x['probability']
809
- shapes.append(shape)
810
-
811
- result_dict['shapes'] = shapes
812
- if 'words_result' in result_dict:
813
- del result_dict['words_result']
814
- if 'words_result_num' in result_dict:
815
- del result_dict['words_result_num']
816
- return result_dict
817
-
818
- def vehicle_registration_certificate(self, image, **options):
819
- """ 机动车登记证书识别: https://cloud.baidu.com/doc/OCR/s/qknzs5zzo """
820
- buffer, ratio = self.adjust_image(image)
821
- result_dict = self.run_aipocr_with_db(self._aipocr.vehicle_registration_certificate, buffer, options)
822
- result_dict = ToLabelmeLike.dict_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
823
- return result_dict
824
-
825
- def weightNote(self, image, **options):
826
- """ 磅单识别: https://cloud.baidu.com/doc/OCR/s/Uksfp9far """
827
- buffer, ratio = self.adjust_image(image)
828
- result_dict = self.run_aipocr_with_db(self._aipocr.weightNote, buffer, options)
829
-
830
- shapes = []
831
- for x in result_dict['words_result']:
832
- for k, vs in x.items():
833
- shape = {'category': k,
834
- 'label': ''.join([v['word'] for v in vs])}
835
- shapes.append(shape)
836
-
837
- result_dict['shapes'] = shapes
838
- del result_dict['words_result']
839
- del result_dict['words_result_num']
840
- return result_dict
841
-
842
- def __B4_财务(self):
843
- pass
844
-
845
- def multipleInvoice(self, image, **options):
846
- """ 智能财务票据识别: https://cloud.baidu.com/doc/OCR/s/7ktb8md0j """
847
- buffer, ratio = self.adjust_image(image)
848
- result_dict = self.run_aipocr_with_db(self._aipocr.multipleInvoice, buffer, options)
849
-
850
- shapes = []
851
- for x in result_dict['words_result']:
852
- shape = {'category': x['type'], 'label': {'text': ''}}
853
- shape.update(loc2points(x, 1 / ratio))
854
- shapes.append(shape)
855
-
856
- result_dict['shapes'] = shapes
857
- del result_dict['words_result']
858
- del result_dict['words_result_num']
859
- return result_dict
860
-
861
- def quotaInvoice(self, image, **options):
862
- """ 定额发票识别: https://cloud.baidu.com/doc/OCR/s/lk3h7y4ev """
863
- buffer, ratio = self.adjust_image(image)
864
- result_dict = self.run_aipocr_with_db(self._aipocr.quotaInvoice, buffer, options)
865
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
866
- return result_dict
867
-
868
- def invoice(self, image, **options):
869
- """ 通用机打发票识别: https://cloud.baidu.com/doc/OCR/s/Pk3h7y06q """
870
- buffer, ratio = self.adjust_image(image)
871
- result_dict = self.run_aipocr_with_db(self._aipocr.invoice, buffer, options)
872
-
873
- shapes = []
874
- for k, v in result_dict['words_result'].items():
875
- shape = {'label': {'category': k}}
876
- if isinstance(v, list):
877
- shape['label']['text'] = '\n'.join([w['word'] for w in v])
878
- else:
879
- shape['label']['text'] = v
880
- shapes.append(shape)
881
-
882
- result_dict['shapes'] = shapes
883
- del result_dict['words_result']
884
- del result_dict['words_result_num']
885
- return result_dict
886
-
887
- def trainTicket(self, image, **options):
888
- """ 火车票识别: https://cloud.baidu.com/doc/OCR/s/Ok3h7y35u """
889
- buffer, ratio = self.adjust_image(image)
890
- result_dict = self.run_aipocr_with_db(self._aipocr.trainTicket, buffer, options)
891
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
892
- return result_dict
893
-
894
- def taxiReceipt(self, image, **options):
895
- """ 出租车票识别: https://cloud.baidu.com/doc/OCR/s/Zk3h7xxnn """
896
- buffer, ratio = self.adjust_image(image)
897
- result_dict = self.run_aipocr_with_db(self._aipocr.taxiReceipt, buffer, options)
898
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
899
- return result_dict
900
-
901
- def airTicket(self, image, **options):
902
- """ 飞机行程单识别: https://cloud.baidu.com/doc/OCR/s/Qk3h7xzro """
903
- buffer, ratio = self.adjust_image(image)
904
- result_dict = self.run_aipocr_with_db(self._aipocr.airTicket, buffer, options)
905
- result_dict = ToLabelmeLike.dict_str(result_dict, 1 / ratio, 'words_result', 'words_result_num')
906
- return result_dict
907
-
908
- def onlineTaxiItinerary(self, image, **options):
909
- """ 网约车行程单识别: https://cloud.baidu.com/doc/OCR/s/Bkocoyu9n """
910
-
911
- def func(image, options=None):
912
- return self._aipocr.onlineTaxiItinerary(image)
913
-
914
- buffer, ratio = self.adjust_image(image)
915
- result_dict = self.run_aipocr_with_db(func, buffer, options, mode_name='onlineTaxiItinerary')
916
-
917
- shapes = []
918
- for k, v in result_dict['words_result'].items():
919
- if k == 'items':
920
- shape = {'label': {'category': 'item'}}
921
- shape['label']['text'] = json.dumps(v, ensure_ascii=False)
922
- else:
923
- shape = {'label': {'category': k}}
924
- shape['label']['text'] = v
925
- shapes.append(shape)
926
-
927
- result_dict['shapes'] = shapes
928
- del result_dict['words_result']
929
- del result_dict['words_result_num']
930
- return result_dict
931
-
932
- def receipt(self, image, **options):
933
- """ receipt: https://cloud.baidu.com/doc/OCR/s/6k3h7y11b """
934
- buffer, ratio = self.adjust_image(image)
935
- result_dict = self.run_aipocr_with_db(self._aipocr.receipt, buffer, options)
936
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
937
- return result_dict
938
-
939
- def __B5_医疗(self):
940
- pass
941
-
942
- def raw_medicalInvoice(self, image, **options):
943
- """ 医疗发票识别: https://cloud.baidu.com/doc/OCR/s/yke30j1hq """
944
- buffer, ratio = self.adjust_image(image)
945
- result_dict = self.run_aipocr_with_db(self._aipocr.medicalInvoice, buffer, options)
946
- return result_dict
947
-
948
- def raw_medicalDetail(self, image, **options):
949
- """ 医疗费用明细识别: https://cloud.baidu.com/doc/OCR/s/Bknjnwlyj """
950
- buffer, ratio = self.adjust_image(image)
951
- result_dict = self.run_aipocr_with_db(self._aipocr.medicalDetail, buffer, options)
952
- return result_dict
953
-
954
- def raw_insuranceDocuments(self, image, **options):
955
- """ 保险单识别: https://cloud.baidu.com/doc/OCR/s/Wk3h7y0eb """
956
- buffer, ratio = self.adjust_image(image)
957
- result_dict = self.run_aipocr_with_db(self._aipocr.insuranceDocuments, buffer, options)
958
- return result_dict
959
-
960
- def __B6_教育(self):
961
- pass
962
-
963
- def docAnalysis(self, image, **options):
964
- """ 试卷分析与识别: https://cloud.baidu.com/doc/OCR/s/jk9m7mj1l
965
- 总量1000次
966
- """
967
- buffer, ratio = self.adjust_image(image)
968
- result_dict = self.run_aipocr_with_db(self._aipocr.docAnalysis, buffer, options)
969
-
970
- shapes = []
971
- for x in result_dict['results']:
972
- shape = loc2points(x['words']['words_location'], 1 / ratio)
973
- # 有line_probability字段,但实际并没有返回置信度~
974
- shape['label'] = {'category': x['words_type'], 'text': x['words']['word']}
975
- shapes.append(shape)
976
-
977
- result_dict['shapes'] = shapes
978
- del result_dict['results']
979
- del result_dict['results_num']
980
- return result_dict
981
-
982
- def formula(self, image, **options):
983
- """ 公式识别: https://cloud.baidu.com/doc/OCR/s/Ok3h7xxva
984
- 总共1000次
985
- """
986
- buffer, ratio = self.adjust_image(image)
987
- result_dict = self.run_aipocr_with_db(self._aipocr.formula, buffer, options)
988
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
989
- return result_dict
990
-
991
- def __B7_其他(self):
992
- pass
993
-
994
- def meter(self, image, **options):
995
- """ 仪器仪表盘读数识别: https://cloud.baidu.com/doc/OCR/s/Jkafike0v """
996
- buffer, ratio = self.adjust_image(image)
997
- result_dict = self.run_aipocr_with_db(self._aipocr.meter, buffer, options)
998
- result_dict = ToLabelmeLike.list_word(result_dict, 1 / ratio, 'words_result', 'words_result_num')
999
- return result_dict
1000
-
1001
- def raw_facade(self, image, **options):
1002
- """ 门脸文字识别: https://cloud.baidu.com/doc/OCR/s/wk5hw3cvo """
1003
- buffer, ratio = self.adjust_image(image)
1004
- result_dict = self.run_aipocr_with_db(self._aipocr.facade, buffer, options)
1005
- return result_dict
1006
-
1007
- def facade(self, image, **options):
1008
- result_dict = self.raw_facade(image, options)
1009
-
1010
- shapes = []
1011
- for x in result_dict['words_result']:
1012
- shape = {'label': {'text': x['words'], 'score': x['score']}}
1013
- shapes.append(shape)
1014
-
1015
- result_dict['shapes'] = shapes
1016
- del result_dict['words_result']
1017
- del result_dict['words_result_num']
1018
- return result_dict
1019
-
1020
- def __C_其他三方api接口(self):
1021
- pass
1022
-
1023
- def mathpix_latex(self, image, **options):
1024
- """ 调用mathpix识别单张图片的公式
1025
-
1026
- 【return】
1027
- {'auto_rotate_confidence': 0.0003554748584377876,
1028
- 'auto_rotate_degrees': 0,
1029
- 'detection_list': ['is_printed'],
1030
- 'detection_map': {'contains_chart': 0,
1031
- 'contains_diagram': 0,
1032
- 'contains_graph': 0,
1033
- 'contains_table': 0,
1034
- 'is_blank': 0,
1035
- 'is_inverted': 0,
1036
- 'is_not_math': 0,
1037
- 'is_printed': 0.9996553659439087},
1038
- 'error': '',
1039
- 'latex': '\\left. \\begin{array} { l } { \\alpha / / \\beta } \\\\ { \\gamma '
1040
- '\\cap \\alpha = a } \\\\ { \\gamma \\cap \\beta = b } \\end{array} '
1041
- '\\right\\} \\Rightarrow a / / b',
1042
- 'latex_confidence': 0.9824940343387425,
1043
- 'latex_confidence_rate': 0.9994673295454546,
1044
- 'latex_list': [],
1045
- 'position': {'height': 160, 'top_left_x': 0, 'top_left_y': 0, 'width': 266},
1046
- 'request_id': '2022_05_28_762803ab687ff5ba1d80g'}
1047
- """
1048
-
1049
- def func(buffer, options):
1050
- image_uri = f'data:image/jpg;base64,' + base64.b64encode(buffer).decode()
1051
- for i in range(3): # 尝试3次
1052
- try:
1053
- r = requests.post('https://api.mathpix.com/v3/latex',
1054
- data=json.dumps({'src': image_uri}),
1055
- headers=self._mathpix_header)
1056
- return json.loads(r.text)
1057
- except requests.exceptions.ConnectionError:
1058
- pass
1059
- else:
1060
- return {'latex': '', 'error': 'requests.exceptions.ConnectionError'}
1061
-
1062
- # mathpix的接口没有说限制图片大小,但我还是按照百度的规范处理下更好
1063
- buffer, ratio = self.adjust_image(image)
1064
- result_dict = self.run_aipocr_with_db(func, buffer, options, mode_name='mathpix_latex')
1065
- if 'position' in result_dict:
1066
- result_dict['position'] = zoom_point(result_dict['position'], ratio)
1067
- return result_dict
1068
-
1069
- def __D_福建省模式识别与图像理解重点实验室(self):
1070
- pass
1071
-
1072
- def _priu_read_image(self, image, min_length=None, max_length=None,
1073
- limit_b64buffer_size=None, b64encode=None, **kwargs):
1074
- # 默认不进行尺寸、文件大小压缩,这样会由平台上负责进行ratio缩放计算
1075
- buffer, ratio = self.adjust_image(image,
1076
- min_length=min_length, max_length=max_length,
1077
- limit_b64buffer_size=limit_b64buffer_size,
1078
- b64encode=True, **kwargs)
1079
- assert ratio == 1, f'本地不做缩放,由服务器进行缩放处理'
1080
- return buffer.decode()
1081
-
1082
- def priu_api(self, mode, image=None, texts=None, options=None, timeout=30, **meta_opts):
1083
- """ 借助服务器来调用该类中含有的其他函数接口
1084
-
1085
- 使用该接口的时候,因为服务器一般会有图片等的备份,所以本接口默认不对图片进行备份
1086
- 另外因为可能会调用自定义的模型功能,自定义的模型可能迭代较快,不适合在数据库缓存结果,所以也不记录json结果
1087
-
1088
- :param mode: 使用的api接口名称
1089
- :param image: image、texts、options都是比较常用的几个参数键值,所以显式地写出来
1090
- :param options: 有时候为了严谨,api相关的参数可能要单独打包
1091
- 但其实这里的参数混入meta_opts也没事,底层会有divide拆分函数,能区分开不同的参数类型
1092
- :param timeout: 超时30秒的限制,有些特殊情况可以设置的更短
1093
- :param meta_opts: 也可以自己额外扩展一些键值,兼容一些特殊的输入范式的api接口
1094
- use_exists_xlapi: 是否复用已有的识别结果,避免重复运算,节约api成本
1095
- record_files: 是否保存图片等数据
1096
- record_xlapi: 是否保存api执行结果
1097
- record_xlserver: 是否保存本次调用纪录
1098
- 根据不同的api,这些参数有不同的默认值,但一般都是True
1099
- """
1100
- # 1 统一输入参数的范式
1101
- data = {}
1102
- if image is not None:
1103
- data['image'] = self._priu_read_image(image, b64encode=True)
1104
- if texts is not None:
1105
- data['texts'] = texts
1106
- if options: # 支持对算法的参数打包,但其实也支持从kwargs直接零散输入
1107
- data['options'] = options
1108
- if meta_opts:
1109
- data.update(meta_opts)
1110
-
1111
- try:
1112
- r = requests.post(f'{self._priu_host}/api/{mode}',
1113
- json=data, headers=self._priu_header, timeout=timeout)
1114
- except requests.exceptions.Timeout:
1115
- # 为了方便处理,统一也返回ConnectionError
1116
- raise requests.exceptions.ConnectionError()
1117
- except Exception:
1118
- raise requests.exceptions.ConnectionError()
1119
-
1120
- if r.status_code == 200:
1121
- res = json.loads(r.text)
1122
- else: # TODO 正常状态码不只200,可能还有重定向等某些不一定是错误的状态
1123
- raise requests.exceptions.ConnectionError(r.text)
1124
-
1125
- # 2 统一返回值的范式,默认都是dict。 有些特殊格式表示是图片,这里会自动做后处理解析。
1126
- if isinstance(res, dict) and len(res) == 1 and 'imageData' in res:
1127
- # 只有一张图片情况的数据,直接返回图片
1128
- return xlcv.read_from_buffer(res['imageData'], b64decode=True)
1129
- else:
1130
- return res
1131
-
1132
- def common_ocr(self, image, **options):
1133
- """ 通用文字识别 """
1134
- options.update({'image': self._priu_read_image(image, b64encode=True)})
1135
- r = requests.post(f'{self._priu_host}/api/common_ocr', headers=self._priu_header,
1136
- json=options)
1137
- res = json.loads(r.text)
1138
- return res
1139
-
1140
- def ocr2texts(self, image, mode='common_ocr', **options):
1141
- """ 通用的识别一张图的所有文本
1142
-
1143
- >> xlapi.ocr2texts(im)
1144
- ['OCR文字识别软件', '如何将图片转换成Word', '文章来源:OCR文字识别软件', 'OCR']
1145
- """
1146
- texts = [] # 因图片太小等各种原因,没有识别到结果,默认就设空值
1147
- try:
1148
- d = self.priu_api(mode, image, options=options)
1149
- if 'shapes' in d:
1150
- texts = [sp['label']['text'] for sp in d['shapes']]
1151
- except requests.exceptions.ConnectionError:
1152
- pass
1153
- return texts
1154
-
1155
- def rec_singleline(self, image, mode='common_ocr', **options):
1156
- """ 通用的识别一张图的所有文本,并拼接到一起 """
1157
- texts = self.ocr2texts(image, mode, **options)
1158
- return ' '.join(texts)
1159
-
1160
- def hesuan_layout(self, image):
1161
- """ 核酸版面分析 """
1162
- r = requests.post(f'{self._priu_host}/api/hesuan/layout', headers=self._priu_header,
1163
- json={'image': self._priu_read_image(image)})
1164
- return json.loads(r.text)
1165
-
1166
- def lexical_analysis(self, texts, options=None, return_mode=None):
1167
- """ 词法分析,有分词的效果
1168
-
1169
- >> lexical_analysis(["今天是个好日子", "天气预报说今天要下雨"])
1170
- [['今天', '是', '个', '好日子'], ['天气预报', '说', '今天', '要', '下雨']]
1171
- """
1172
- # TODO 可以增加接口参数,配置返回值类型。其他api同理。
1173
- data = {'texts': texts}
1174
- if options:
1175
- data['options'] = options
1176
- r = requests.post(f'{self._priu_host}/api/lac', json=data, headers=self._priu_header)
1177
- res = json.loads(r.text)
1178
-
1179
- if return_mode == 'raw':
1180
- pass
1181
- else:
1182
- res = [x['word'] for x in res]
1183
- return res
1184
-
1185
- def sentiment_classify(self, texts, options=None):
1186
- """ 情感分析
1187
-
1188
- 返回的值越大表示句子的情感越积极
1189
- >> sentiment_classify(['今天是个好日子', '天气预报说今天要下雨'])
1190
- [0.4555, 0.2513]
1191
- """
1192
- data = {'texts': texts}
1193
- if options:
1194
- data['options'] = options
1195
- r = requests.post(f'{self._priu_host}/api/senta_bilstm', json=data, headers=self._priu_header)
1196
- return json.loads(r.text)
1197
-
1198
- def humanseg(self, image):
1199
- """ 人像抠图
1200
-
1201
- TODO 还没测过特大、特小图会不会有问题~
1202
- """
1203
- im, _ = self.adjust_image(image, 1, to_buffer=False) # 不能用xlcv.read,因为也可能作为服务端接口,输入buffer格式
1204
- mask = self.priu_api('deeplabv3p_xception65_humanseg', im)
1205
- new_im = np.concatenate([im, np.expand_dims(mask, axis=2)], axis=2) # 变成BGRA格式图片
1206
- return new_im
1207
-
1208
- def det_face(self, image):
1209
- """ 人脸识别 """
1210
- lmdict = self.priu_api('ultra_light_fast_generic_face_detector_1mb_640', image)
1211
- return lmdict
1212
-
1213
- def super_resolution(self, image, times=1):
1214
- """ 超分辨率(把图片变高清)
1215
-
1216
- :param times: 放大多少次,每次放大2倍(长宽个放大2倍)
1217
- """
1218
- for _ in range(times):
1219
- image = self.priu_api('falsr_c', image)
1220
- return image
1221
-
1222
- def rec_speech(self, audio_file):
1223
- """ 语音识别 """
1224
- if os.path.isfile(audio_file):
1225
- audio = base64.b64encode(XlPath(audio_file).read_bytes()).decode()
1226
- else:
1227
- raise NotImplementedError
1228
- text = self.priu_api('u2_conformer_wenetspeech', audio=audio)[0]
1229
- return text
1230
-
1231
-
1232
- def demo_aipocr():
1233
- import pprint
1234
- import re
1235
-
1236
- from pyxlpr.data.labelme import LabelmeDict
1237
-
1238
- xlapi = XlAiClient()
1239
- # xlapi.setup_database()
1240
- xlapi._priu_host = 'http://localhost:5003'
1241
-
1242
- mode = 'general'
1243
-
1244
- _dir = XlPath("/home/chenkunze/data/aipocr_test")
1245
- fmode = re.sub(r'^raw_', r'', mode)
1246
- fmode = {'basicAccurate': 'accurate',
1247
- 'basicGeneral': 'general',
1248
- 'webimageLoc': 'webImage',
1249
- 'idcard_back': 'idcard',
1250
- 'vat_invoice_verification': 'vatInvoice',
1251
- 'mathpix_latex': 'formula',
1252
- }.get(fmode, fmode)
1253
- files = _dir.glob_images(f'*/{fmode}/**/*')
1254
-
1255
- for f in list(files):
1256
- # 1 处理指定图片
1257
- # if f.stem != '824ef2b9a5f05422199107721d299f30':
1258
- # continue
1259
-
1260
- # 2 检查字典
1261
- print(f.as_posix())
1262
- d = getattr(xlapi, mode)(f.as_posix())
1263
-
1264
- # browser.html(d['htmltables'][0])
1265
- print()
1266
- pprint.pprint(d)
1267
- print('- ' * 20)
1268
-
1269
- # 3 前置的错误图可以删除;有shapes的可以转labelme;非labelme格式上面print后直接退出
1270
- if 'error_code' in d:
1271
- f.delete()
1272
- elif d.get('shapes', 0):
1273
- # tolabelme
1274
- lmdata = LabelmeDict.gen_data(f)
1275
- lmdata['shapes'] = d['shapes']
1276
- for sp in lmdata['shapes']:
1277
- sp['label'] = json.dumps(sp['label'], ensure_ascii=False)
1278
- f.with_suffix('.json').write_json(lmdata)
1279
- break
1280
- else:
1281
- break