pyxllib 0.3.96__py3-none-any.whl → 0.3.200__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 (358) hide show
  1. pyxllib/__init__.py +21 -21
  2. pyxllib/algo/__init__.py +8 -8
  3. pyxllib/algo/disjoint.py +54 -54
  4. pyxllib/algo/geo.py +541 -529
  5. pyxllib/algo/intervals.py +964 -964
  6. pyxllib/algo/matcher.py +389 -311
  7. pyxllib/algo/newbie.py +166 -166
  8. pyxllib/algo/pupil.py +629 -461
  9. pyxllib/algo/shapelylib.py +67 -67
  10. pyxllib/algo/specialist.py +241 -240
  11. pyxllib/algo/stat.py +494 -458
  12. pyxllib/algo/treelib.py +149 -149
  13. pyxllib/algo/unitlib.py +66 -66
  14. {pyxlpr → pyxllib/autogui}/__init__.py +5 -5
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/{ext/autogui → autogui}/autogui.py +852 -823
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/{ext/autogui → autogui}/virtualkey.py +102 -102
  20. pyxllib/autogui/wechat.py +827 -0
  21. pyxllib/autogui/wechat_msg.py +421 -0
  22. pyxllib/autogui/wxautolib.py +84 -0
  23. pyxllib/cv/__init__.py +5 -5
  24. pyxllib/cv/expert.py +267 -267
  25. pyxllib/cv/imfile.py +159 -159
  26. pyxllib/cv/imhash.py +39 -39
  27. pyxllib/cv/pupil.py +9 -9
  28. pyxllib/cv/rgbfmt.py +1525 -1525
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +251 -251
  31. pyxllib/cv/xlcvlib.py +1040 -1040
  32. pyxllib/cv/xlpillib.py +423 -423
  33. pyxllib/data/echarts.py +240 -129
  34. pyxllib/data/jsonlib.py +89 -0
  35. pyxllib/data/oss.py +72 -72
  36. pyxllib/data/pglib.py +1127 -643
  37. pyxllib/data/sqlite.py +568 -341
  38. pyxllib/data/sqllib.py +297 -297
  39. pyxllib/ext/JLineViewer.py +505 -492
  40. pyxllib/ext/__init__.py +6 -6
  41. pyxllib/ext/demolib.py +246 -246
  42. pyxllib/ext/drissionlib.py +277 -0
  43. pyxllib/ext/kq5034lib.py +12 -1606
  44. pyxllib/ext/old.py +663 -663
  45. pyxllib/ext/qt.py +449 -449
  46. pyxllib/ext/robustprocfile.py +497 -0
  47. pyxllib/ext/seleniumlib.py +76 -76
  48. pyxllib/ext/tk.py +173 -173
  49. pyxllib/ext/unixlib.py +827 -826
  50. pyxllib/ext/utools.py +351 -338
  51. pyxllib/ext/webhook.py +124 -101
  52. pyxllib/ext/win32lib.py +40 -40
  53. pyxllib/ext/wjxlib.py +88 -0
  54. pyxllib/ext/wpsapi.py +124 -0
  55. pyxllib/ext/xlwork.py +9 -0
  56. pyxllib/ext/yuquelib.py +1105 -173
  57. pyxllib/file/__init__.py +17 -17
  58. pyxllib/file/docxlib.py +761 -761
  59. pyxllib/file/gitlib.py +309 -309
  60. pyxllib/file/libreoffice.py +165 -0
  61. pyxllib/file/movielib.py +148 -139
  62. pyxllib/file/newbie.py +10 -10
  63. pyxllib/file/onenotelib.py +1469 -1469
  64. pyxllib/file/packlib/__init__.py +330 -293
  65. pyxllib/file/packlib/zipfile.py +2441 -2441
  66. pyxllib/file/pdflib.py +426 -426
  67. pyxllib/file/pupil.py +185 -185
  68. pyxllib/file/specialist/__init__.py +685 -685
  69. pyxllib/file/specialist/dirlib.py +799 -799
  70. pyxllib/file/specialist/download.py +193 -186
  71. pyxllib/file/specialist/filelib.py +2829 -2618
  72. pyxllib/file/xlsxlib.py +3131 -2976
  73. pyxllib/file/xlsyncfile.py +341 -0
  74. pyxllib/prog/__init__.py +5 -5
  75. pyxllib/prog/cachetools.py +64 -0
  76. pyxllib/prog/deprecatedlib.py +233 -233
  77. pyxllib/prog/filelock.py +42 -0
  78. pyxllib/prog/ipyexec.py +253 -253
  79. pyxllib/prog/multiprogs.py +940 -0
  80. pyxllib/prog/newbie.py +451 -444
  81. pyxllib/prog/pupil.py +1197 -1128
  82. pyxllib/prog/sitepackages.py +33 -33
  83. pyxllib/prog/specialist/__init__.py +391 -217
  84. pyxllib/prog/specialist/bc.py +203 -200
  85. pyxllib/prog/specialist/browser.py +497 -488
  86. pyxllib/prog/specialist/common.py +347 -347
  87. pyxllib/prog/specialist/datetime.py +199 -131
  88. pyxllib/prog/specialist/tictoc.py +240 -241
  89. pyxllib/prog/specialist/xllog.py +180 -180
  90. pyxllib/prog/xlosenv.py +108 -101
  91. pyxllib/stdlib/__init__.py +17 -17
  92. pyxllib/stdlib/tablepyxl/__init__.py +10 -10
  93. pyxllib/stdlib/tablepyxl/style.py +303 -303
  94. pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
  95. pyxllib/text/__init__.py +8 -8
  96. pyxllib/text/ahocorasick.py +39 -39
  97. pyxllib/text/airscript.js +744 -0
  98. pyxllib/text/charclasslib.py +121 -109
  99. pyxllib/text/jiebalib.py +267 -264
  100. pyxllib/text/jinjalib.py +32 -0
  101. pyxllib/text/jsa_ai_prompt.md +271 -0
  102. pyxllib/text/jscode.py +922 -767
  103. pyxllib/text/latex/__init__.py +158 -158
  104. pyxllib/text/levenshtein.py +303 -303
  105. pyxllib/text/nestenv.py +1215 -1215
  106. pyxllib/text/newbie.py +300 -288
  107. pyxllib/text/pupil/__init__.py +8 -8
  108. pyxllib/text/pupil/common.py +1121 -1095
  109. pyxllib/text/pupil/xlalign.py +326 -326
  110. pyxllib/text/pycode.py +47 -47
  111. pyxllib/text/specialist/__init__.py +8 -8
  112. pyxllib/text/specialist/common.py +112 -112
  113. pyxllib/text/specialist/ptag.py +186 -186
  114. pyxllib/text/spellchecker.py +172 -172
  115. pyxllib/text/templates/echart_base.html +11 -0
  116. pyxllib/text/templates/highlight_code.html +17 -0
  117. pyxllib/text/templates/latex_editor.html +103 -0
  118. pyxllib/text/vbacode.py +17 -17
  119. pyxllib/text/xmllib.py +747 -685
  120. pyxllib/xl.py +42 -38
  121. pyxllib/xlcv.py +17 -17
  122. pyxllib-0.3.200.dist-info/METADATA +48 -0
  123. pyxllib-0.3.200.dist-info/RECORD +126 -0
  124. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info}/WHEEL +1 -2
  125. {pyxllib-0.3.96.dist-info → pyxllib-0.3.200.dist-info/licenses}/LICENSE +190 -190
  126. pyxllib/ext/autogui/__init__.py +0 -8
  127. pyxllib-0.3.96.dist-info/METADATA +0 -51
  128. pyxllib-0.3.96.dist-info/RECORD +0 -333
  129. pyxllib-0.3.96.dist-info/top_level.txt +0 -2
  130. pyxlpr/ai/__init__.py +0 -5
  131. pyxlpr/ai/clientlib.py +0 -1281
  132. pyxlpr/ai/specialist.py +0 -286
  133. pyxlpr/ai/torch_app.py +0 -172
  134. pyxlpr/ai/xlpaddle.py +0 -655
  135. pyxlpr/ai/xltorch.py +0 -705
  136. pyxlpr/data/__init__.py +0 -11
  137. pyxlpr/data/coco.py +0 -1325
  138. pyxlpr/data/datacls.py +0 -365
  139. pyxlpr/data/datasets.py +0 -200
  140. pyxlpr/data/gptlib.py +0 -1291
  141. pyxlpr/data/icdar/__init__.py +0 -96
  142. pyxlpr/data/icdar/deteval.py +0 -377
  143. pyxlpr/data/icdar/icdar2013.py +0 -341
  144. pyxlpr/data/icdar/iou.py +0 -340
  145. pyxlpr/data/icdar/rrc_evaluation_funcs_1_1.py +0 -463
  146. pyxlpr/data/imtextline.py +0 -473
  147. pyxlpr/data/labelme.py +0 -866
  148. pyxlpr/data/removeline.py +0 -179
  149. pyxlpr/data/specialist.py +0 -57
  150. pyxlpr/eval/__init__.py +0 -85
  151. pyxlpr/paddleocr.py +0 -776
  152. pyxlpr/ppocr/__init__.py +0 -15
  153. pyxlpr/ppocr/configs/rec/multi_language/generate_multi_language_configs.py +0 -226
  154. pyxlpr/ppocr/data/__init__.py +0 -135
  155. pyxlpr/ppocr/data/imaug/ColorJitter.py +0 -26
  156. pyxlpr/ppocr/data/imaug/__init__.py +0 -67
  157. pyxlpr/ppocr/data/imaug/copy_paste.py +0 -170
  158. pyxlpr/ppocr/data/imaug/east_process.py +0 -437
  159. pyxlpr/ppocr/data/imaug/gen_table_mask.py +0 -244
  160. pyxlpr/ppocr/data/imaug/iaa_augment.py +0 -114
  161. pyxlpr/ppocr/data/imaug/label_ops.py +0 -789
  162. pyxlpr/ppocr/data/imaug/make_border_map.py +0 -184
  163. pyxlpr/ppocr/data/imaug/make_pse_gt.py +0 -106
  164. pyxlpr/ppocr/data/imaug/make_shrink_map.py +0 -126
  165. pyxlpr/ppocr/data/imaug/operators.py +0 -433
  166. pyxlpr/ppocr/data/imaug/pg_process.py +0 -906
  167. pyxlpr/ppocr/data/imaug/randaugment.py +0 -143
  168. pyxlpr/ppocr/data/imaug/random_crop_data.py +0 -239
  169. pyxlpr/ppocr/data/imaug/rec_img_aug.py +0 -533
  170. pyxlpr/ppocr/data/imaug/sast_process.py +0 -777
  171. pyxlpr/ppocr/data/imaug/text_image_aug/__init__.py +0 -17
  172. pyxlpr/ppocr/data/imaug/text_image_aug/augment.py +0 -120
  173. pyxlpr/ppocr/data/imaug/text_image_aug/warp_mls.py +0 -168
  174. pyxlpr/ppocr/data/lmdb_dataset.py +0 -115
  175. pyxlpr/ppocr/data/pgnet_dataset.py +0 -104
  176. pyxlpr/ppocr/data/pubtab_dataset.py +0 -107
  177. pyxlpr/ppocr/data/simple_dataset.py +0 -372
  178. pyxlpr/ppocr/losses/__init__.py +0 -61
  179. pyxlpr/ppocr/losses/ace_loss.py +0 -52
  180. pyxlpr/ppocr/losses/basic_loss.py +0 -135
  181. pyxlpr/ppocr/losses/center_loss.py +0 -88
  182. pyxlpr/ppocr/losses/cls_loss.py +0 -30
  183. pyxlpr/ppocr/losses/combined_loss.py +0 -67
  184. pyxlpr/ppocr/losses/det_basic_loss.py +0 -208
  185. pyxlpr/ppocr/losses/det_db_loss.py +0 -80
  186. pyxlpr/ppocr/losses/det_east_loss.py +0 -63
  187. pyxlpr/ppocr/losses/det_pse_loss.py +0 -149
  188. pyxlpr/ppocr/losses/det_sast_loss.py +0 -121
  189. pyxlpr/ppocr/losses/distillation_loss.py +0 -272
  190. pyxlpr/ppocr/losses/e2e_pg_loss.py +0 -140
  191. pyxlpr/ppocr/losses/kie_sdmgr_loss.py +0 -113
  192. pyxlpr/ppocr/losses/rec_aster_loss.py +0 -99
  193. pyxlpr/ppocr/losses/rec_att_loss.py +0 -39
  194. pyxlpr/ppocr/losses/rec_ctc_loss.py +0 -44
  195. pyxlpr/ppocr/losses/rec_enhanced_ctc_loss.py +0 -70
  196. pyxlpr/ppocr/losses/rec_nrtr_loss.py +0 -30
  197. pyxlpr/ppocr/losses/rec_sar_loss.py +0 -28
  198. pyxlpr/ppocr/losses/rec_srn_loss.py +0 -47
  199. pyxlpr/ppocr/losses/table_att_loss.py +0 -109
  200. pyxlpr/ppocr/metrics/__init__.py +0 -44
  201. pyxlpr/ppocr/metrics/cls_metric.py +0 -45
  202. pyxlpr/ppocr/metrics/det_metric.py +0 -82
  203. pyxlpr/ppocr/metrics/distillation_metric.py +0 -73
  204. pyxlpr/ppocr/metrics/e2e_metric.py +0 -86
  205. pyxlpr/ppocr/metrics/eval_det_iou.py +0 -274
  206. pyxlpr/ppocr/metrics/kie_metric.py +0 -70
  207. pyxlpr/ppocr/metrics/rec_metric.py +0 -75
  208. pyxlpr/ppocr/metrics/table_metric.py +0 -50
  209. pyxlpr/ppocr/modeling/architectures/__init__.py +0 -32
  210. pyxlpr/ppocr/modeling/architectures/base_model.py +0 -88
  211. pyxlpr/ppocr/modeling/architectures/distillation_model.py +0 -60
  212. pyxlpr/ppocr/modeling/backbones/__init__.py +0 -54
  213. pyxlpr/ppocr/modeling/backbones/det_mobilenet_v3.py +0 -268
  214. pyxlpr/ppocr/modeling/backbones/det_resnet_vd.py +0 -246
  215. pyxlpr/ppocr/modeling/backbones/det_resnet_vd_sast.py +0 -285
  216. pyxlpr/ppocr/modeling/backbones/e2e_resnet_vd_pg.py +0 -265
  217. pyxlpr/ppocr/modeling/backbones/kie_unet_sdmgr.py +0 -186
  218. pyxlpr/ppocr/modeling/backbones/rec_mobilenet_v3.py +0 -138
  219. pyxlpr/ppocr/modeling/backbones/rec_mv1_enhance.py +0 -258
  220. pyxlpr/ppocr/modeling/backbones/rec_nrtr_mtb.py +0 -48
  221. pyxlpr/ppocr/modeling/backbones/rec_resnet_31.py +0 -210
  222. pyxlpr/ppocr/modeling/backbones/rec_resnet_aster.py +0 -143
  223. pyxlpr/ppocr/modeling/backbones/rec_resnet_fpn.py +0 -307
  224. pyxlpr/ppocr/modeling/backbones/rec_resnet_vd.py +0 -286
  225. pyxlpr/ppocr/modeling/heads/__init__.py +0 -54
  226. pyxlpr/ppocr/modeling/heads/cls_head.py +0 -52
  227. pyxlpr/ppocr/modeling/heads/det_db_head.py +0 -118
  228. pyxlpr/ppocr/modeling/heads/det_east_head.py +0 -121
  229. pyxlpr/ppocr/modeling/heads/det_pse_head.py +0 -37
  230. pyxlpr/ppocr/modeling/heads/det_sast_head.py +0 -128
  231. pyxlpr/ppocr/modeling/heads/e2e_pg_head.py +0 -253
  232. pyxlpr/ppocr/modeling/heads/kie_sdmgr_head.py +0 -206
  233. pyxlpr/ppocr/modeling/heads/multiheadAttention.py +0 -163
  234. pyxlpr/ppocr/modeling/heads/rec_aster_head.py +0 -393
  235. pyxlpr/ppocr/modeling/heads/rec_att_head.py +0 -202
  236. pyxlpr/ppocr/modeling/heads/rec_ctc_head.py +0 -88
  237. pyxlpr/ppocr/modeling/heads/rec_nrtr_head.py +0 -826
  238. pyxlpr/ppocr/modeling/heads/rec_sar_head.py +0 -402
  239. pyxlpr/ppocr/modeling/heads/rec_srn_head.py +0 -280
  240. pyxlpr/ppocr/modeling/heads/self_attention.py +0 -406
  241. pyxlpr/ppocr/modeling/heads/table_att_head.py +0 -246
  242. pyxlpr/ppocr/modeling/necks/__init__.py +0 -32
  243. pyxlpr/ppocr/modeling/necks/db_fpn.py +0 -111
  244. pyxlpr/ppocr/modeling/necks/east_fpn.py +0 -188
  245. pyxlpr/ppocr/modeling/necks/fpn.py +0 -138
  246. pyxlpr/ppocr/modeling/necks/pg_fpn.py +0 -314
  247. pyxlpr/ppocr/modeling/necks/rnn.py +0 -92
  248. pyxlpr/ppocr/modeling/necks/sast_fpn.py +0 -284
  249. pyxlpr/ppocr/modeling/necks/table_fpn.py +0 -110
  250. pyxlpr/ppocr/modeling/transforms/__init__.py +0 -28
  251. pyxlpr/ppocr/modeling/transforms/stn.py +0 -135
  252. pyxlpr/ppocr/modeling/transforms/tps.py +0 -308
  253. pyxlpr/ppocr/modeling/transforms/tps_spatial_transformer.py +0 -156
  254. pyxlpr/ppocr/optimizer/__init__.py +0 -61
  255. pyxlpr/ppocr/optimizer/learning_rate.py +0 -228
  256. pyxlpr/ppocr/optimizer/lr_scheduler.py +0 -49
  257. pyxlpr/ppocr/optimizer/optimizer.py +0 -160
  258. pyxlpr/ppocr/optimizer/regularizer.py +0 -52
  259. pyxlpr/ppocr/postprocess/__init__.py +0 -55
  260. pyxlpr/ppocr/postprocess/cls_postprocess.py +0 -33
  261. pyxlpr/ppocr/postprocess/db_postprocess.py +0 -234
  262. pyxlpr/ppocr/postprocess/east_postprocess.py +0 -143
  263. pyxlpr/ppocr/postprocess/locality_aware_nms.py +0 -200
  264. pyxlpr/ppocr/postprocess/pg_postprocess.py +0 -52
  265. pyxlpr/ppocr/postprocess/pse_postprocess/__init__.py +0 -15
  266. pyxlpr/ppocr/postprocess/pse_postprocess/pse/__init__.py +0 -29
  267. pyxlpr/ppocr/postprocess/pse_postprocess/pse/setup.py +0 -14
  268. pyxlpr/ppocr/postprocess/pse_postprocess/pse_postprocess.py +0 -118
  269. pyxlpr/ppocr/postprocess/rec_postprocess.py +0 -654
  270. pyxlpr/ppocr/postprocess/sast_postprocess.py +0 -355
  271. pyxlpr/ppocr/tools/__init__.py +0 -14
  272. pyxlpr/ppocr/tools/eval.py +0 -83
  273. pyxlpr/ppocr/tools/export_center.py +0 -77
  274. pyxlpr/ppocr/tools/export_model.py +0 -129
  275. pyxlpr/ppocr/tools/infer/predict_cls.py +0 -151
  276. pyxlpr/ppocr/tools/infer/predict_det.py +0 -300
  277. pyxlpr/ppocr/tools/infer/predict_e2e.py +0 -169
  278. pyxlpr/ppocr/tools/infer/predict_rec.py +0 -414
  279. pyxlpr/ppocr/tools/infer/predict_system.py +0 -204
  280. pyxlpr/ppocr/tools/infer/utility.py +0 -629
  281. pyxlpr/ppocr/tools/infer_cls.py +0 -83
  282. pyxlpr/ppocr/tools/infer_det.py +0 -134
  283. pyxlpr/ppocr/tools/infer_e2e.py +0 -122
  284. pyxlpr/ppocr/tools/infer_kie.py +0 -153
  285. pyxlpr/ppocr/tools/infer_rec.py +0 -146
  286. pyxlpr/ppocr/tools/infer_table.py +0 -107
  287. pyxlpr/ppocr/tools/program.py +0 -596
  288. pyxlpr/ppocr/tools/test_hubserving.py +0 -117
  289. pyxlpr/ppocr/tools/train.py +0 -163
  290. pyxlpr/ppocr/tools/xlprog.py +0 -748
  291. pyxlpr/ppocr/utils/EN_symbol_dict.txt +0 -94
  292. pyxlpr/ppocr/utils/__init__.py +0 -24
  293. pyxlpr/ppocr/utils/dict/ar_dict.txt +0 -117
  294. pyxlpr/ppocr/utils/dict/arabic_dict.txt +0 -162
  295. pyxlpr/ppocr/utils/dict/be_dict.txt +0 -145
  296. pyxlpr/ppocr/utils/dict/bg_dict.txt +0 -140
  297. pyxlpr/ppocr/utils/dict/chinese_cht_dict.txt +0 -8421
  298. pyxlpr/ppocr/utils/dict/cyrillic_dict.txt +0 -163
  299. pyxlpr/ppocr/utils/dict/devanagari_dict.txt +0 -167
  300. pyxlpr/ppocr/utils/dict/en_dict.txt +0 -63
  301. pyxlpr/ppocr/utils/dict/fa_dict.txt +0 -136
  302. pyxlpr/ppocr/utils/dict/french_dict.txt +0 -136
  303. pyxlpr/ppocr/utils/dict/german_dict.txt +0 -143
  304. pyxlpr/ppocr/utils/dict/hi_dict.txt +0 -162
  305. pyxlpr/ppocr/utils/dict/it_dict.txt +0 -118
  306. pyxlpr/ppocr/utils/dict/japan_dict.txt +0 -4399
  307. pyxlpr/ppocr/utils/dict/ka_dict.txt +0 -153
  308. pyxlpr/ppocr/utils/dict/korean_dict.txt +0 -3688
  309. pyxlpr/ppocr/utils/dict/latin_dict.txt +0 -185
  310. pyxlpr/ppocr/utils/dict/mr_dict.txt +0 -153
  311. pyxlpr/ppocr/utils/dict/ne_dict.txt +0 -153
  312. pyxlpr/ppocr/utils/dict/oc_dict.txt +0 -96
  313. pyxlpr/ppocr/utils/dict/pu_dict.txt +0 -130
  314. pyxlpr/ppocr/utils/dict/rs_dict.txt +0 -91
  315. pyxlpr/ppocr/utils/dict/rsc_dict.txt +0 -134
  316. pyxlpr/ppocr/utils/dict/ru_dict.txt +0 -125
  317. pyxlpr/ppocr/utils/dict/ta_dict.txt +0 -128
  318. pyxlpr/ppocr/utils/dict/table_dict.txt +0 -277
  319. pyxlpr/ppocr/utils/dict/table_structure_dict.txt +0 -2759
  320. pyxlpr/ppocr/utils/dict/te_dict.txt +0 -151
  321. pyxlpr/ppocr/utils/dict/ug_dict.txt +0 -114
  322. pyxlpr/ppocr/utils/dict/uk_dict.txt +0 -142
  323. pyxlpr/ppocr/utils/dict/ur_dict.txt +0 -137
  324. pyxlpr/ppocr/utils/dict/xi_dict.txt +0 -110
  325. pyxlpr/ppocr/utils/dict90.txt +0 -90
  326. pyxlpr/ppocr/utils/e2e_metric/Deteval.py +0 -574
  327. pyxlpr/ppocr/utils/e2e_metric/polygon_fast.py +0 -83
  328. pyxlpr/ppocr/utils/e2e_utils/extract_batchsize.py +0 -87
  329. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_fast.py +0 -457
  330. pyxlpr/ppocr/utils/e2e_utils/extract_textpoint_slow.py +0 -592
  331. pyxlpr/ppocr/utils/e2e_utils/pgnet_pp_utils.py +0 -162
  332. pyxlpr/ppocr/utils/e2e_utils/visual.py +0 -162
  333. pyxlpr/ppocr/utils/en_dict.txt +0 -95
  334. pyxlpr/ppocr/utils/gen_label.py +0 -81
  335. pyxlpr/ppocr/utils/ic15_dict.txt +0 -36
  336. pyxlpr/ppocr/utils/iou.py +0 -54
  337. pyxlpr/ppocr/utils/logging.py +0 -69
  338. pyxlpr/ppocr/utils/network.py +0 -84
  339. pyxlpr/ppocr/utils/ppocr_keys_v1.txt +0 -6623
  340. pyxlpr/ppocr/utils/profiler.py +0 -110
  341. pyxlpr/ppocr/utils/save_load.py +0 -150
  342. pyxlpr/ppocr/utils/stats.py +0 -72
  343. pyxlpr/ppocr/utils/utility.py +0 -80
  344. pyxlpr/ppstructure/__init__.py +0 -13
  345. pyxlpr/ppstructure/predict_system.py +0 -187
  346. pyxlpr/ppstructure/table/__init__.py +0 -13
  347. pyxlpr/ppstructure/table/eval_table.py +0 -72
  348. pyxlpr/ppstructure/table/matcher.py +0 -192
  349. pyxlpr/ppstructure/table/predict_structure.py +0 -136
  350. pyxlpr/ppstructure/table/predict_table.py +0 -221
  351. pyxlpr/ppstructure/table/table_metric/__init__.py +0 -16
  352. pyxlpr/ppstructure/table/table_metric/parallel.py +0 -51
  353. pyxlpr/ppstructure/table/table_metric/table_metric.py +0 -247
  354. pyxlpr/ppstructure/table/tablepyxl/__init__.py +0 -13
  355. pyxlpr/ppstructure/table/tablepyxl/style.py +0 -283
  356. pyxlpr/ppstructure/table/tablepyxl/tablepyxl.py +0 -118
  357. pyxlpr/ppstructure/utility.py +0 -71
  358. pyxlpr/xlai.py +0 -10
pyxllib/algo/geo.py CHANGED
@@ -1,529 +1,541 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- # @Author : 陈坤泽
4
- # @Email : 877362867@qq.com
5
- # @Date : 2020/11/15 10:16
6
-
7
- """ 几何、数学运算
8
-
9
- specialist级别
10
- """
11
-
12
- from pyxllib.prog.pupil import check_install_package
13
-
14
- check_install_package('cv2', 'opencv-python')
15
-
16
- import copy
17
-
18
- import numpy as np
19
- import cv2
20
-
21
- from pyxllib.algo.intervals import Intervals
22
-
23
- ____base = """
24
-
25
- """
26
-
27
-
28
- def xywh2ltrb(p):
29
- return [p[0], p[1], p[0] + p[2], p[1] + p[3]]
30
-
31
-
32
- def ltrb2xywh(p):
33
- return [p[0], p[1], p[2] - p[0], p[3] - p[1]]
34
-
35
-
36
- def rect2polygon(src_pts):
37
- """ 矩形对角线两个点,转成四边形四个点的模式来表达
38
- (输入左上、右下两个顶点坐标)
39
-
40
- :param list|np.ndarray src_pts: size 2*2
41
- :rtype: list
42
-
43
- >>> rect2polygon([[0, 0], [10, 20]])
44
- [[0, 0], [10, 0], [10, 20], [0, 20]]
45
- >>> rect2polygon(np.array([[0, 0], [10, 20]]))
46
- [[0, 0], [10, 0], [10, 20], [0, 20]]
47
- >>> rect2polygon([[10, 0], [0, 20]])
48
- [[0, 0], [10, 0], [10, 20], [0, 20]]
49
- """
50
- [[x1, y1], [x2, y2]] = src_pts
51
- dst_pts = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
52
- dst_pts = resort_quad_points(dst_pts)
53
- return dst_pts
54
-
55
-
56
- def reshape_coords(coords, m, dtype=None):
57
- """ 重置坐标点的维度
58
-
59
- :param list coords: 这个函数主要还是封装了对list情况的处理
60
- 其实np.ndarray结构也行,但这种情况直接用np接口操作就行,不需要引用该函数
61
- :rtype: list
62
-
63
- # 转成 n*1 的矩阵
64
-
65
- >>> reshape_coords([(1, 2), (3, 4)], 1)
66
- [1, 2, 3, 4]
67
- >>> reshape_coords(np.array([[1, 2], [3, 4]]), 1)
68
- [1, 2, 3, 4]
69
- >>> reshape_coords([1, 2, 3, 4], 1)
70
- [1, 2, 3, 4]
71
-
72
- >>> reshape_coords([[1.5, 2], [3.5, 4]], 1)
73
- [1.5, 2.0, 3.5, 4.0]
74
-
75
- # 这种情况,[3,4]、[5,6,7]都是一个整体
76
- # VisibleDeprecationWarning
77
- >>> reshape_coords([1, 2, [3, 4], [5, 6, 7]], 1)
78
- [1, 2, [3, 4], [5, 6, 7]]
79
-
80
- >>> reshape_coords([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], 1)
81
- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
82
-
83
- # 变成 n*2 的矩阵
84
-
85
- >>> reshape_coords([1, 2, 3, 4], 2)
86
- [[1, 2], [3, 4]]
87
- >>> reshape_coords(np.array([1, 2, 3, 4]), 2)
88
- [[1, 2], [3, 4]]
89
- >>> reshape_coords([[1, 2], [3, 4]], 2)
90
- [[1, 2], [3, 4]]
91
- >>> reshape_coords([1.5, 2, 3.5, 4], 2)
92
- [[1.5, 2.0], [3.5, 4.0]]
93
- >>> reshape_coords([1.5, 2, 3.5, 4], 2, dtype=int) # 数据类型转换
94
- [[1, 2], [3, 4]]
95
- """
96
- if m == 1:
97
- return np.array(coords, dtype=dtype).reshape(-1).tolist()
98
- else:
99
- return np.array(coords, dtype=dtype).reshape((-1, m)).tolist()
100
-
101
-
102
- def rect_bounds(coords):
103
- """ 多边形的最大外接矩形
104
-
105
- :param coords: 支持list、np等类型,支持1d、2d两种维度表达方式
106
- :return: rect的两个点坐标,同时也是 [left, top, right, bottom]
107
- """
108
- pts = np.array(coords).reshape(-1).tolist() # tolist不能删,不然int类型就变了。比如int64不能json.dump
109
- p = [min(pts[::2]), min(pts[1::2]), max(pts[::2]), max(pts[1::2])]
110
- return [v for v in p]
111
-
112
-
113
- def resort_quad_points(src_pts):
114
- """ 重置四边形点集顺序,确保以左上角为起点,顺时针罗列点集
115
-
116
- 算法:先确保pt1、pt2在上面,然后再确保pt1在pt2左边
117
-
118
- :param list|tuple|np.ndarray src_pts: 点集
119
- :rtype: list|np.ndarray
120
-
121
- >>> pts = [[100, 50], [200, 0], [100, 0], [0, 50]]
122
- >>> resort_quad_points(pts)
123
- [[100, 0], [200, 0], [100, 50], [0, 50]]
124
- >>> pts # 原来的点不会被修改
125
- [[100, 50], [200, 0], [100, 0], [0, 50]]
126
-
127
- >>> pts = np.array([[100, 50], [200, 0], [100, 0], [0, 50]])
128
- >>> resort_quad_points(pts)
129
- array([[100, 0],
130
- [200, 0],
131
- [100, 0],
132
- [ 0, 50]])
133
- >>> pts # 原来的点不会被修改
134
- array([[100, 50],
135
- [200, 0],
136
- [100, 0],
137
- [ 0, 50]])
138
- """
139
- pts = copy.copy(src_pts)
140
- if isinstance(pts, np.ndarray):
141
- pts = pts.tolist()
142
- if pts[0][1] > pts[2][1]:
143
- pts[0], pts[2] = pts[2], pts[0]
144
- if pts[1][1] > pts[3][1]:
145
- pts[1], pts[3] = pts[3], pts[1]
146
- if pts[0][0] > pts[1][0]:
147
- pts[0], pts[1] = pts[1], pts[0]
148
- pts[2], pts[3] = pts[3], pts[2]
149
- return pts
150
-
151
-
152
- def ltrb_border(ltrb, border, size=None):
153
- """ 给原来的ltrb定位扩展border像素
154
-
155
- Args:
156
- ltrb:
157
- border: 可以一个数字,表示统一添加的像素值
158
- 也可以四个数字,表示每个维度分别加的像素值
159
- size:
160
- 原图的 (width, height),防止越界
161
- 可以不填,默认不考虑越界问题
162
- Returns: 新的ltrb坐标
163
- """
164
- if isinstance(border, int):
165
- border = [border] * 4
166
-
167
- l = max(0, ltrb[0] - border[0])
168
- t = max(0, ltrb[1] - border[1])
169
- r = min(size[0], ltrb[2] + border[2])
170
- b = min(size[1], ltrb[3] + border[3])
171
-
172
- return [l, t, r, b]
173
-
174
-
175
- ____warp_perspective = """
176
- 仿射、透视变换相关功能
177
-
178
- https://www.yuque.com/xlpr/pyxllib/warpperspective
179
- """
180
-
181
-
182
- def warp_points(pts, warp_mat):
183
- """ 透视等点集坐标转换
184
-
185
- :param list|tuple|np.ndarray pts: 支持1d、2d的维度
186
- 其实这个坐标变换就是一个简单的矩阵乘法,只是pts的数据结构往往比较特殊,
187
- 并不是一个n*3的矩阵结构,所以需要进行一些简单的格式转换
188
- 例如 [x1, y1, x2, y2, x3, y3] --> [[x1, x2, x3], [y1, y2, y3], [1, 1, 1]]
189
- :param list|tuple|np.ndarray warp_mat: 变换矩阵,一般是个3*3的矩阵,但是只输入2*3的矩阵也行,因为第3行并用不到(点集只要取前两个维度X'Y'的结果值)
190
- TODO 不过这里我有个点也没想明白,如果不用第3行,本质上不是又变回仿射变换了,如何达到透视变换效果?第三维的深度信息能完全舍弃?
191
- :rtype: np.ndarray
192
-
193
- >>> warp_mat = [[0, 1, 0], [1, 0, 0], [0, 0, 1]] # 对换x、y
194
- >>> warp_points([[1, 2], [11, 22]], warp_mat) # 处理两个点
195
- array([[ 2, 1],
196
- [22, 11]])
197
- >>> warp_points([[1, 2], [11, 22]], [[0, 1, 0], [1, 0, 0]]) # 输入2*3的变换矩阵也可以
198
- array([[ 2, 1],
199
- [22, 11]])
200
- >>> warp_points([1, 2, 11, 22], warp_mat) # 也可以用一维的结构来输入点集
201
- array([[ 2, 1],
202
- [22, 11]])
203
- >>> warp_points([1, 2, 11, 22, 111, 222], warp_mat) # 点的数量任意,返回的结构同输入的结构形式
204
- array([[ 2, 1],
205
- [ 22, 11],
206
- [222, 111]])
207
- >>> warp_points(np.array([1, 2, 11, 22, 111, 222]), warp_mat) # 也可以用np.ndarray等结构
208
- array([[ 2, 1],
209
- [ 22, 11],
210
- [222, 111]])
211
- >>> warp_points([1, 2, 11, 22], warp_mat) # 也可以用一维的结构来输入点集
212
- array([[ 2, 1],
213
- [22, 11]])
214
- """
215
- pts1 = np.array(pts).reshape(-1, 2).T
216
- pts1 = np.concatenate([pts1, [[1] * pts1.shape[1]]], axis=0)
217
- pts2 = np.dot(warp_mat[:2], pts1)
218
- pts2 = pts2.T
219
- return pts2
220
-
221
-
222
- def get_warp_mat(src, dst):
223
- """ 从前后点集计算仿射变换矩阵
224
-
225
- :param src: 原点集,支持多种格式输入
226
- :param dst: 变换后的点集
227
- :return np.ndarray: 3*3的变换矩阵
228
- """
229
-
230
- def cvt_data(pts):
231
- # opencv的透视变换,输入的点集有类型限制,必须使用float32
232
- return np.array(pts, dtype='float32').reshape((-1, 2))
233
-
234
- src, dst = cvt_data(src), cvt_data(dst)
235
- n = src.shape[0]
236
- if n == 3:
237
- # 只有3个点,则使用仿射变换
238
- warp_mat = cv2.getAffineTransform(src, dst)
239
- warp_mat = np.concatenate([warp_mat, [[0, 0, 1]]], axis=0)
240
- elif n == 4:
241
- # 有4个点,则使用透视变换
242
- warp_mat = cv2.getPerspectiveTransform(src, dst)
243
- else:
244
- raise ValueError('点集数量过多')
245
- return warp_mat
246
-
247
-
248
- def quad_warp_wh(pts, method='average'):
249
- """ 四边形转为矩形的宽、高
250
-
251
- :param pts: 四个点坐标
252
- TODO 暂时认为pts是按点集顺时针顺序输入的
253
- TODO 暂时认为pts[0]就是第一个坐标点
254
- :param method:
255
- 记四条边分别为w1, h1, w2, h2
256
- average: 平均宽、高
257
- max: 最大宽、高
258
- min: 最小宽、高
259
- :return: (w, h) 变换后的矩形宽、高
260
- """
261
- # 1 计算四边长
262
- from math import hypot
263
- # pts = ReshapeCoords.list_2d(pts)
264
- lens = [0] * 4
265
- for i in range(4):
266
- pt1, pt2 = pts[i], pts[(i + 1) % 4]
267
- lens[i] = hypot(pt1[0] - pt2[0], pt1[1] - pt2[1])
268
-
269
- # 2 目标宽、高
270
- if method is True:
271
- method = 'average'
272
- if method == 'average':
273
- w, h = (lens[0] + lens[2]) / 2, (lens[1] + lens[3]) / 2
274
- elif method == 'max':
275
- w, h = max(lens[0], lens[2]), max(lens[1], lens[3])
276
- elif method == 'min':
277
- w, h = min(lens[0], lens[2]), min(lens[1], lens[3])
278
- else:
279
- raise ValueError(f'不支持的方法 {method}')
280
- # 这个主要是用于图像变换的,而图像一般像素坐标要用整数,所以就取整运算了
281
- return round(w), round(h)
282
-
283
-
284
- def warp_quad_pts(pts, method='average'):
285
- """ 将不规则四边形转为矩形
286
-
287
- :param pts: 不规则四边形的四个点坐标
288
- :param method: 计算矩形宽、高的算法
289
- :return: 返回时,仍然用四个点的坐标表达,规则矩形的四个点坐标
290
-
291
- >>> warp_quad_pts([[89, 424], [931, 424], [399, 290], [621, 290]])
292
- [[0, 0], [532, 0], [532, 549], [0, 549]]
293
- """
294
- w, h = quad_warp_wh(pts, method)
295
- return rect2polygon([[0, 0], [w, h]])
296
-
297
-
298
- ____polygon = """
299
- """
300
-
301
-
302
- class ComputeIou:
303
- """ 两个多边形的交并比 Intersection Over Union """
304
-
305
- @classmethod
306
- def ltrb(cls, pts1, pts2):
307
- """ https://gist.github.com/meyerjo/dd3533edc97c81258898f60d8978eddc
308
- """
309
- # determine the (x, y)-coordinates of the intersection rectangle
310
- x_a = max(pts1[0], pts2[0])
311
- y_a = max(pts1[1], pts2[1])
312
- x_b = min(pts1[2], pts2[2])
313
- y_b = min(pts1[3], pts2[3])
314
-
315
- # compute the area of intersection rectangle
316
- inter_area = abs(max((x_b - x_a, 0)) * max((y_b - y_a), 0))
317
- if inter_area == 0:
318
- return 0
319
- # compute the area of both the prediction and ground-truth
320
- # rectangles
321
- box_a_area = abs((pts1[2] - pts1[0]) * (pts1[3] - pts1[1]))
322
- box_b_area = abs((pts2[2] - pts2[0]) * (pts2[3] - pts2[1]))
323
-
324
- # compute the intersection over union by taking the intersection
325
- # area and dividing it by the sum of prediction + ground-truth
326
- # areas - the interesection area
327
- iou = inter_area / float(box_a_area + box_b_area - inter_area)
328
-
329
- # return the intersection over union value
330
- return iou
331
-
332
- @classmethod
333
- def polygon(cls, pts1, pts2):
334
- inter_area = pts1.intersection(pts2).area
335
- if inter_area:
336
- union_area = pts1.area + pts2.area - inter_area
337
- return (inter_area / union_area) if union_area else 0
338
- else:
339
- return 0
340
-
341
- @classmethod
342
- def polygon2(cls, pts1, pts2):
343
- """ 会强制转为polygon对象再处理
344
-
345
- >>> ComputeIou.polygon2([[0, 0], [10, 10]], [[5, 5], [15, 15]])
346
- 0.14285714285714285
347
- """
348
- from pyxllib.algo.shapelylib import ShapelyPolygon
349
- polygon1, polygon2 = ShapelyPolygon.gen(pts1), ShapelyPolygon.gen(pts2)
350
- return cls.polygon(polygon1, polygon2)
351
-
352
- @classmethod
353
- def nms_basic(cls, boxes, func, iou=0.5, *, key=None, index=False):
354
- """ 假设boxes已经按权重从大到小排过序
355
-
356
- :param boxes: 支持输入一组box列表 [box1, box2, box3, ...]
357
- :param key: 将框映射为可计算对象
358
- :param index: 返回不是原始框,而是对应的下标 [i1, i2, i3, ...]
359
- """
360
- # 1 映射到items来操作
361
- if callable(key):
362
- items = list(enumerate([key(b) for b in boxes]))
363
- else:
364
- items = list(enumerate(boxes))
365
-
366
- # 2 正常nms功能
367
- idxs = []
368
- while items:
369
- # 1 加入权值大的框
370
- i, b = items[0]
371
- idxs.append(i)
372
- # 2 抑制其他框
373
- left_items = []
374
- for j in range(1, len(items)):
375
- if func(b, items[j][1]) < iou:
376
- left_items.append(items[j])
377
- items = left_items
378
-
379
- # 3 返回值
380
- if index:
381
- return idxs
382
- else:
383
- return [boxes[i] for i in idxs]
384
-
385
- @classmethod
386
- def nms_ltrb(cls, boxes, iou=0.5, *, key=None, index=False):
387
- return cls.nms_basic(boxes, cls.ltrb, iou, key=key, index=index)
388
-
389
- @classmethod
390
- def nms_xywh(cls, boxes, iou=0.5, *, key=None, index=False):
391
- if callable(key):
392
- func = lambda x: xywh2ltrb(key(x))
393
- else:
394
- func = xywh2ltrb
395
- return cls.nms_ltrb(boxes, iou, key=func, index=index)
396
-
397
- @classmethod
398
- def nms_polygon(cls, boxes, iou=0.5, *, key=None, index=False):
399
- # ShapelyPolygon.gen
400
- return cls.nms_basic(boxes, cls.polygon, iou, key=key, index=index)
401
-
402
-
403
- ____other = """
404
- """
405
-
406
-
407
- def divide_quadrangle(coords, r1=0.5, r2=None):
408
- """ 切分一个四边形为两个四边形
409
-
410
- :param coords: 4*2的坐标
411
- :param r1: 第一个切分比例,0.5相当于中点(即第一个四边形右边位置)
412
- :param r2: 第二个切分比例,即第二个四边形左边位置
413
- :return: 返回切割后所有的四边形
414
-
415
- 一般用在改标注结果中,把一个框拆成两个框
416
- TODO 把接口改成切分一个四边形为任意多个四边形?即把r1、r2等整合为一个list参数输入
417
- """
418
-
419
- # 1 计算分割点工具
420
- def segment_point(pt1, pt2, rate=0.5):
421
- """ 两点间的分割点
422
- :param rate: 默认0.5是二分点,rate为0时即pt1,rate为1时为pt2,取值可以小于0、大于-1
423
- :return:
424
- """
425
- x1, y1 = pt1
426
- x2, y2 = pt2
427
- x, y = x1 + rate * (x2 - x1), y1 + rate * (y2 - y1)
428
- return int(x), int(y)
429
-
430
- # 2 优化参数值
431
- # coords = ReshapeCoords.list_2d(coords)
432
- if not r2: r2 = 1 - r1
433
-
434
- # 3 计算切分后的四边形坐标
435
- pt1, pt2, pt3, pt4 = coords
436
- pt5, pt6 = segment_point(pt1, pt2, r1), segment_point(pt4, pt3, r1)
437
- pt7, pt8 = segment_point(pt1, pt2, r2), segment_point(pt4, pt3, r2)
438
- return [pt1, pt5, pt6, pt4], [pt7, pt2, pt3, pt8]
439
-
440
-
441
- def split_vector_interval(vec, maxsplit=None, minwidth=3):
442
- """
443
- :param vec: 一个一维向量,需要对这个向量进行切割
444
- 需要前置工作先处理好数值
445
- 使得背景在非正数,背景概率越大,负值绝对值越大
446
- 前景在正值,前景概率越大,数值越大
447
- 要得到能量最大(数值最大、前景内容)的几个区域
448
- 但是因为有噪声的原因,该算法要有一定的抗干扰能力
449
-
450
- 一般情况下
451
- 用 0 代表背景
452
- 用 <1 的正值表示这一列黑点所占比例(np.mean)
453
- np.sum 传入整数暂时也行,但考虑以后功能扩展性,用比例会更好
454
- 传入负数,表示特殊背景,该背景可以抵消掉的minwidth宽度数
455
- :param maxsplit: 最大切分数量,即最多得到几个子区间
456
- 没设置的时候,会对所有满足条件的情况进行切割
457
- :param minwidth: 每个切分位置最小具有的宽度
458
- :return: [(l, r), (l, r), ...] 每一段文本的左右区间
459
- """
460
- # 1 裁剪左边、右边
461
- n_vec = len(vec)
462
- left, right = 0, n_vec
463
- while left < right and vec[left] <= 0:
464
- left += 1
465
- while right > left and vec[right - 1] <= 0:
466
- right -= 1
467
- # 左右空白至少也要达到minwidth才去除
468
- # if left < minwidth: left = 0
469
- # if n_vec - right + 1 < minwidth: right = n_vec
470
-
471
- vec = vec[left:right]
472
- width = len(vec)
473
- if width == 0:
474
- return [] # 没有内容,返回空list
475
-
476
- # 2 找切分位置
477
- # 统计每一段连续的背景长度,并且对其数值求和,作为这段是背景的置信度
478
- bg_probs, bg_start, cnt = [], 0, 0
479
-
480
- def update_fg():
481
- """ 遇到前景内容,或者循环结束,更新一下 """
482
- nonlocal cnt
483
- prob = vec[bg_start:bg_start + cnt].sum()
484
- # print(cnt, prob)
485
- if cnt >= (minwidth + prob): # 负值可以减小minwidth限定
486
- itv = [bg_start, bg_start + cnt]
487
- bg_probs.append([itv, prob])
488
- cnt = 0
489
-
490
- for i in range(width):
491
- if vec[i] <= 0:
492
- if not cnt:
493
- bg_start = i
494
- cnt += 1
495
- else:
496
- update_fg()
497
- else:
498
- update_fg()
499
-
500
- # 3 取置信度最大的几个分割点
501
- if maxsplit:
502
- bg_probs = sorted(bg_probs, key=lambda x: x[1])[:(maxsplit - 1)]
503
- bg_probs = sorted(bg_probs, key=lambda x: x[0]) # 从左到右排序
504
-
505
- # 4 返回文本区间(反向计算)
506
- res = []
507
- intervals = Intervals([itv for itv, prob in bg_probs]).invert(width) + left
508
- # print(intervals)
509
- for interval in intervals:
510
- res.append([interval.start(), interval.end()])
511
- return res
512
-
513
-
514
- def bound_scale(bound, scale):
515
- """ 一个矩形,以中心为原点,缩放面积为原来scale的新矩形
516
-
517
- :param bound: [x1, y1, x2, y2]
518
- :param scale: 比例,例如0.5,就是缩放一半
519
- """
520
- x1, y1, x2, y2 = bound
521
- x0 = (x2 + x1) / 2
522
- y0 = (y2 + y1) / 2
523
- r = 1 - scale ** 0.5
524
-
525
- x1 += r * abs(x0 - x1)
526
- y1 += r * abs(y0 - y1)
527
- x2 -= r * abs(x0 - x2)
528
- y2 -= r * abs(y0 - y2)
529
- return x1, y1, x2, y2
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2020/11/15 10:16
6
+
7
+ """ 几何、数学运算
8
+
9
+ specialist级别
10
+ """
11
+
12
+ from pyxllib.prog.pupil import check_install_package
13
+
14
+ check_install_package('cv2', 'opencv-python')
15
+
16
+ import copy
17
+
18
+ import numpy as np
19
+ import cv2
20
+
21
+ from pyxllib.algo.intervals import Intervals
22
+
23
+ ____base = """
24
+
25
+ """
26
+
27
+
28
+ def xywh2ltrb(p):
29
+ return [p[0], p[1], p[0] + p[2], p[1] + p[3]]
30
+
31
+
32
+ def ltrb2xywh(p):
33
+ return [p[0], p[1], p[2] - p[0], p[3] - p[1]]
34
+
35
+
36
+ def ltrb2polygon(p):
37
+ """ ltrb坐标转多边形
38
+
39
+ :param list|tuple p: [left, top, right, bottom]
40
+ :rtype: list
41
+
42
+ >>> ltrb2polygon([100, 50, 200, 150])
43
+ [[100, 50], [200, 50], [200, 150], [100, 150]]
44
+ """
45
+ return [p[:2], [p[2], p[1]], p[2:], [p[0], p[3]]]
46
+
47
+
48
+ def rect2polygon(src_pts):
49
+ """ 矩形对角线两个点,转成四边形四个点的模式来表达
50
+ (输入左上、右下两个顶点坐标)
51
+
52
+ :param list|np.ndarray src_pts: size 2*2
53
+ :rtype: list
54
+
55
+ >>> rect2polygon([[0, 0], [10, 20]])
56
+ [[0, 0], [10, 0], [10, 20], [0, 20]]
57
+ >>> rect2polygon(np.array([[0, 0], [10, 20]]))
58
+ [[0, 0], [10, 0], [10, 20], [0, 20]]
59
+ >>> rect2polygon([[10, 0], [0, 20]])
60
+ [[0, 0], [10, 0], [10, 20], [0, 20]]
61
+ """
62
+ [[x1, y1], [x2, y2]] = src_pts
63
+ dst_pts = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
64
+ dst_pts = resort_quad_points(dst_pts)
65
+ return dst_pts
66
+
67
+
68
+ def reshape_coords(coords, m, dtype=None):
69
+ """ 重置坐标点的维度
70
+
71
+ :param list coords: 这个函数主要还是封装了对list情况的处理
72
+ 其实np.ndarray结构也行,但这种情况直接用np接口操作就行,不需要引用该函数
73
+ :rtype: list
74
+
75
+ # 转成 n*1 的矩阵
76
+
77
+ >>> reshape_coords([(1, 2), (3, 4)], 1)
78
+ [1, 2, 3, 4]
79
+ >>> reshape_coords(np.array([[1, 2], [3, 4]]), 1)
80
+ [1, 2, 3, 4]
81
+ >>> reshape_coords([1, 2, 3, 4], 1)
82
+ [1, 2, 3, 4]
83
+
84
+ >>> reshape_coords([[1.5, 2], [3.5, 4]], 1)
85
+ [1.5, 2.0, 3.5, 4.0]
86
+
87
+ # 这种情况,[3,4]、[5,6,7]都是一个整体
88
+ # VisibleDeprecationWarning
89
+ >>> reshape_coords([1, 2, [3, 4], [5, 6, 7]], 1)
90
+ [1, 2, [3, 4], [5, 6, 7]]
91
+
92
+ >>> reshape_coords([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], 1)
93
+ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
94
+
95
+ # 变成 n*2 的矩阵
96
+
97
+ >>> reshape_coords([1, 2, 3, 4], 2)
98
+ [[1, 2], [3, 4]]
99
+ >>> reshape_coords(np.array([1, 2, 3, 4]), 2)
100
+ [[1, 2], [3, 4]]
101
+ >>> reshape_coords([[1, 2], [3, 4]], 2)
102
+ [[1, 2], [3, 4]]
103
+ >>> reshape_coords([1.5, 2, 3.5, 4], 2)
104
+ [[1.5, 2.0], [3.5, 4.0]]
105
+ >>> reshape_coords([1.5, 2, 3.5, 4], 2, dtype=int) # 数据类型转换
106
+ [[1, 2], [3, 4]]
107
+ """
108
+ if m == 1:
109
+ return np.array(coords, dtype=dtype).reshape(-1).tolist()
110
+ else:
111
+ return np.array(coords, dtype=dtype).reshape((-1, m)).tolist()
112
+
113
+
114
+ def rect_bounds(coords):
115
+ """ 多边形的最大外接矩形
116
+
117
+ :param coords: 支持list、np等类型,支持1d、2d两种维度表达方式
118
+ :return: rect的两个点坐标,同时也是 [left, top, right, bottom]
119
+ """
120
+ pts = np.array(coords).reshape(-1).tolist() # tolist不能删,不然int类型就变了。比如int64不能json.dump
121
+ p = [min(pts[::2]), min(pts[1::2]), max(pts[::2]), max(pts[1::2])]
122
+ return [v for v in p]
123
+
124
+
125
+ def resort_quad_points(src_pts):
126
+ """ 重置四边形点集顺序,确保以左上角为起点,顺时针罗列点集
127
+
128
+ 算法:先确保pt1、pt2在上面,然后再确保pt1在pt2左边
129
+
130
+ :param list|tuple|np.ndarray src_pts: 点集
131
+ :rtype: list|np.ndarray
132
+
133
+ >>> pts = [[100, 50], [200, 0], [100, 0], [0, 50]]
134
+ >>> resort_quad_points(pts)
135
+ [[100, 0], [200, 0], [100, 50], [0, 50]]
136
+ >>> pts # 原来的点不会被修改
137
+ [[100, 50], [200, 0], [100, 0], [0, 50]]
138
+
139
+ >>> pts = np.array([[100, 50], [200, 0], [100, 0], [0, 50]])
140
+ >>> resort_quad_points(pts)
141
+ array([[100, 0],
142
+ [200, 0],
143
+ [100, 0],
144
+ [ 0, 50]])
145
+ >>> pts # 原来的点不会被修改
146
+ array([[100, 50],
147
+ [200, 0],
148
+ [100, 0],
149
+ [ 0, 50]])
150
+ """
151
+ pts = copy.copy(src_pts)
152
+ if isinstance(pts, np.ndarray):
153
+ pts = pts.tolist()
154
+ if pts[0][1] > pts[2][1]:
155
+ pts[0], pts[2] = pts[2], pts[0]
156
+ if pts[1][1] > pts[3][1]:
157
+ pts[1], pts[3] = pts[3], pts[1]
158
+ if pts[0][0] > pts[1][0]:
159
+ pts[0], pts[1] = pts[1], pts[0]
160
+ pts[2], pts[3] = pts[3], pts[2]
161
+ return pts
162
+
163
+
164
+ def ltrb_border(ltrb, border, size=None):
165
+ """ 给原来的ltrb定位扩展border像素
166
+
167
+ Args:
168
+ ltrb:
169
+ border: 可以一个数字,表示统一添加的像素值
170
+ 也可以四个数字,表示每个维度分别加的像素值
171
+ size:
172
+ 原图的 (width, height),防止越界
173
+ 可以不填,默认不考虑越界问题
174
+ Returns: 新的ltrb坐标
175
+ """
176
+ if isinstance(border, int):
177
+ border = [border] * 4
178
+
179
+ l = max(0, ltrb[0] - border[0])
180
+ t = max(0, ltrb[1] - border[1])
181
+ r = min(size[0], ltrb[2] + border[2])
182
+ b = min(size[1], ltrb[3] + border[3])
183
+
184
+ return [l, t, r, b]
185
+
186
+
187
+ ____warp_perspective = """
188
+ 仿射、透视变换相关功能
189
+
190
+ https://www.yuque.com/xlpr/pyxllib/warpperspective
191
+ """
192
+
193
+
194
+ def warp_points(pts, warp_mat):
195
+ """ 透视等点集坐标转换
196
+
197
+ :param list|tuple|np.ndarray pts: 支持1d、2d的维度
198
+ 其实这个坐标变换就是一个简单的矩阵乘法,只是pts的数据结构往往比较特殊,
199
+ 并不是一个n*3的矩阵结构,所以需要进行一些简单的格式转换
200
+ 例如 [x1, y1, x2, y2, x3, y3] --> [[x1, x2, x3], [y1, y2, y3], [1, 1, 1]]
201
+ :param list|tuple|np.ndarray warp_mat: 变换矩阵,一般是个3*3的矩阵,但是只输入2*3的矩阵也行,因为第3行并用不到(点集只要取前两个维度X'Y'的结果值)
202
+ TODO 不过这里我有个点也没想明白,如果不用第3行,本质上不是又变回仿射变换了,如何达到透视变换效果?第三维的深度信息能完全舍弃?
203
+ :rtype: np.ndarray
204
+
205
+ >>> warp_mat = [[0, 1, 0], [1, 0, 0], [0, 0, 1]] # 对换x、y
206
+ >>> warp_points([[1, 2], [11, 22]], warp_mat) # 处理两个点
207
+ array([[ 2, 1],
208
+ [22, 11]])
209
+ >>> warp_points([[1, 2], [11, 22]], [[0, 1, 0], [1, 0, 0]]) # 输入2*3的变换矩阵也可以
210
+ array([[ 2, 1],
211
+ [22, 11]])
212
+ >>> warp_points([1, 2, 11, 22], warp_mat) # 也可以用一维的结构来输入点集
213
+ array([[ 2, 1],
214
+ [22, 11]])
215
+ >>> warp_points([1, 2, 11, 22, 111, 222], warp_mat) # 点的数量任意,返回的结构同输入的结构形式
216
+ array([[ 2, 1],
217
+ [ 22, 11],
218
+ [222, 111]])
219
+ >>> warp_points(np.array([1, 2, 11, 22, 111, 222]), warp_mat) # 也可以用np.ndarray等结构
220
+ array([[ 2, 1],
221
+ [ 22, 11],
222
+ [222, 111]])
223
+ >>> warp_points([1, 2, 11, 22], warp_mat) # 也可以用一维的结构来输入点集
224
+ array([[ 2, 1],
225
+ [22, 11]])
226
+ """
227
+ pts1 = np.array(pts).reshape(-1, 2).T
228
+ pts1 = np.concatenate([pts1, [[1] * pts1.shape[1]]], axis=0)
229
+ pts2 = np.dot(warp_mat[:2], pts1)
230
+ pts2 = pts2.T
231
+ return pts2
232
+
233
+
234
+ def get_warp_mat(src, dst):
235
+ """ 从前后点集计算仿射变换矩阵
236
+
237
+ :param src: 原点集,支持多种格式输入
238
+ :param dst: 变换后的点集
239
+ :return np.ndarray: 3*3的变换矩阵
240
+ """
241
+
242
+ def cvt_data(pts):
243
+ # opencv的透视变换,输入的点集有类型限制,必须使用float32
244
+ return np.array(pts, dtype='float32').reshape((-1, 2))
245
+
246
+ src, dst = cvt_data(src), cvt_data(dst)
247
+ n = src.shape[0]
248
+ if n == 3:
249
+ # 只有3个点,则使用仿射变换
250
+ warp_mat = cv2.getAffineTransform(src, dst)
251
+ warp_mat = np.concatenate([warp_mat, [[0, 0, 1]]], axis=0)
252
+ elif n == 4:
253
+ # 有4个点,则使用透视变换
254
+ warp_mat = cv2.getPerspectiveTransform(src, dst)
255
+ else:
256
+ raise ValueError('点集数量过多')
257
+ return warp_mat
258
+
259
+
260
+ def quad_warp_wh(pts, method='average'):
261
+ """ 四边形转为矩形的宽、高
262
+
263
+ :param pts: 四个点坐标
264
+ TODO 暂时认为pts是按点集顺时针顺序输入的
265
+ TODO 暂时认为pts[0]就是第一个坐标点
266
+ :param method:
267
+ 记四条边分别为w1, h1, w2, h2
268
+ average: 平均宽、高
269
+ max: 最大宽、高
270
+ min: 最小宽、高
271
+ :return: (w, h) 变换后的矩形宽、高
272
+ """
273
+ # 1 计算四边长
274
+ from math import hypot
275
+ # pts = ReshapeCoords.list_2d(pts)
276
+ lens = [0] * 4
277
+ for i in range(4):
278
+ pt1, pt2 = pts[i], pts[(i + 1) % 4]
279
+ lens[i] = hypot(pt1[0] - pt2[0], pt1[1] - pt2[1])
280
+
281
+ # 2 目标宽、高
282
+ if method is True:
283
+ method = 'average'
284
+ if method == 'average':
285
+ w, h = (lens[0] + lens[2]) / 2, (lens[1] + lens[3]) / 2
286
+ elif method == 'max':
287
+ w, h = max(lens[0], lens[2]), max(lens[1], lens[3])
288
+ elif method == 'min':
289
+ w, h = min(lens[0], lens[2]), min(lens[1], lens[3])
290
+ else:
291
+ raise ValueError(f'不支持的方法 {method}')
292
+ # 这个主要是用于图像变换的,而图像一般像素坐标要用整数,所以就取整运算了
293
+ return round(w), round(h)
294
+
295
+
296
+ def warp_quad_pts(pts, method='average'):
297
+ """ 将不规则四边形转为矩形
298
+
299
+ :param pts: 不规则四边形的四个点坐标
300
+ :param method: 计算矩形宽、高的算法
301
+ :return: 返回时,仍然用四个点的坐标表达,规则矩形的四个点坐标
302
+
303
+ >>> warp_quad_pts([[89, 424], [931, 424], [399, 290], [621, 290]])
304
+ [[0, 0], [532, 0], [532, 549], [0, 549]]
305
+ """
306
+ w, h = quad_warp_wh(pts, method)
307
+ return rect2polygon([[0, 0], [w, h]])
308
+
309
+
310
+ ____polygon = """
311
+ """
312
+
313
+
314
+ class ComputeIou:
315
+ """ 两个多边形的交并比 Intersection Over Union """
316
+
317
+ @classmethod
318
+ def ltrb(cls, pts1, pts2):
319
+ """ https://gist.github.com/meyerjo/dd3533edc97c81258898f60d8978eddc
320
+ """
321
+ # determine the (x, y)-coordinates of the intersection rectangle
322
+ x_a = max(pts1[0], pts2[0])
323
+ y_a = max(pts1[1], pts2[1])
324
+ x_b = min(pts1[2], pts2[2])
325
+ y_b = min(pts1[3], pts2[3])
326
+
327
+ # compute the area of intersection rectangle
328
+ inter_area = abs(max((x_b - x_a, 0)) * max((y_b - y_a), 0))
329
+ if inter_area == 0:
330
+ return 0
331
+ # compute the area of both the prediction and ground-truth
332
+ # rectangles
333
+ box_a_area = abs((pts1[2] - pts1[0]) * (pts1[3] - pts1[1]))
334
+ box_b_area = abs((pts2[2] - pts2[0]) * (pts2[3] - pts2[1]))
335
+
336
+ # compute the intersection over union by taking the intersection
337
+ # area and dividing it by the sum of prediction + ground-truth
338
+ # areas - the interesection area
339
+ iou = inter_area / float(box_a_area + box_b_area - inter_area)
340
+
341
+ # return the intersection over union value
342
+ return iou
343
+
344
+ @classmethod
345
+ def polygon(cls, pts1, pts2):
346
+ inter_area = pts1.intersection(pts2).area
347
+ if inter_area:
348
+ union_area = pts1.area + pts2.area - inter_area
349
+ return (inter_area / union_area) if union_area else 0
350
+ else:
351
+ return 0
352
+
353
+ @classmethod
354
+ def polygon2(cls, pts1, pts2):
355
+ """ 会强制转为polygon对象再处理
356
+
357
+ >>> ComputeIou.polygon2([[0, 0], [10, 10]], [[5, 5], [15, 15]])
358
+ 0.14285714285714285
359
+ """
360
+ from pyxllib.algo.shapelylib import ShapelyPolygon
361
+ polygon1, polygon2 = ShapelyPolygon.gen(pts1), ShapelyPolygon.gen(pts2)
362
+ return cls.polygon(polygon1, polygon2)
363
+
364
+ @classmethod
365
+ def nms_basic(cls, boxes, func, iou=0.5, *, key=None, index=False):
366
+ """ 假设boxes已经按权重从大到小排过序
367
+
368
+ :param boxes: 支持输入一组box列表 [box1, box2, box3, ...]
369
+ :param key: 将框映射为可计算对象
370
+ :param index: 返回不是原始框,而是对应的下标 [i1, i2, i3, ...]
371
+ """
372
+ # 1 映射到items来操作
373
+ if callable(key):
374
+ items = list(enumerate([key(b) for b in boxes]))
375
+ else:
376
+ items = list(enumerate(boxes))
377
+
378
+ # 2 正常nms功能
379
+ idxs = []
380
+ while items:
381
+ # 1 加入权值大的框
382
+ i, b = items[0]
383
+ idxs.append(i)
384
+ # 2 抑制其他框
385
+ left_items = []
386
+ for j in range(1, len(items)):
387
+ if func(b, items[j][1]) < iou:
388
+ left_items.append(items[j])
389
+ items = left_items
390
+
391
+ # 3 返回值
392
+ if index:
393
+ return idxs
394
+ else:
395
+ return [boxes[i] for i in idxs]
396
+
397
+ @classmethod
398
+ def nms_ltrb(cls, boxes, iou=0.5, *, key=None, index=False):
399
+ return cls.nms_basic(boxes, cls.ltrb, iou, key=key, index=index)
400
+
401
+ @classmethod
402
+ def nms_xywh(cls, boxes, iou=0.5, *, key=None, index=False):
403
+ if callable(key):
404
+ func = lambda x: xywh2ltrb(key(x))
405
+ else:
406
+ func = xywh2ltrb
407
+ return cls.nms_ltrb(boxes, iou, key=func, index=index)
408
+
409
+ @classmethod
410
+ def nms_polygon(cls, boxes, iou=0.5, *, key=None, index=False):
411
+ # ShapelyPolygon.gen
412
+ return cls.nms_basic(boxes, cls.polygon, iou, key=key, index=index)
413
+
414
+
415
+ ____other = """
416
+ """
417
+
418
+
419
+ def divide_quadrangle(coords, r1=0.5, r2=None):
420
+ """ 切分一个四边形为两个四边形
421
+
422
+ :param coords: 4*2的坐标
423
+ :param r1: 第一个切分比例,0.5相当于中点(即第一个四边形右边位置)
424
+ :param r2: 第二个切分比例,即第二个四边形左边位置
425
+ :return: 返回切割后所有的四边形
426
+
427
+ 一般用在改标注结果中,把一个框拆成两个框
428
+ TODO 把接口改成切分一个四边形为任意多个四边形?即把r1、r2等整合为一个list参数输入
429
+ """
430
+
431
+ # 1 计算分割点工具
432
+ def segment_point(pt1, pt2, rate=0.5):
433
+ """ 两点间的分割点
434
+ :param rate: 默认0.5是二分点,rate为0时即pt1,rate为1时为pt2,取值可以小于0、大于-1
435
+ :return:
436
+ """
437
+ x1, y1 = pt1
438
+ x2, y2 = pt2
439
+ x, y = x1 + rate * (x2 - x1), y1 + rate * (y2 - y1)
440
+ return int(x), int(y)
441
+
442
+ # 2 优化参数值
443
+ # coords = ReshapeCoords.list_2d(coords)
444
+ if not r2: r2 = 1 - r1
445
+
446
+ # 3 计算切分后的四边形坐标
447
+ pt1, pt2, pt3, pt4 = coords
448
+ pt5, pt6 = segment_point(pt1, pt2, r1), segment_point(pt4, pt3, r1)
449
+ pt7, pt8 = segment_point(pt1, pt2, r2), segment_point(pt4, pt3, r2)
450
+ return [pt1, pt5, pt6, pt4], [pt7, pt2, pt3, pt8]
451
+
452
+
453
+ def split_vector_interval(vec, maxsplit=None, minwidth=3):
454
+ """
455
+ :param vec: 一个一维向量,需要对这个向量进行切割
456
+ 需要前置工作先处理好数值
457
+ 使得背景在非正数,背景概率越大,负值绝对值越大
458
+ 前景在正值,前景概率越大,数值越大
459
+ 要得到能量最大(数值最大、前景内容)的几个区域
460
+ 但是因为有噪声的原因,该算法要有一定的抗干扰能力
461
+
462
+ 一般情况下
463
+ 0 代表背景
464
+ <1 的正值表示这一列黑点所占比例(np.mean)
465
+ np.sum 传入整数暂时也行,但考虑以后功能扩展性,用比例会更好
466
+ 传入负数,表示特殊背景,该背景可以抵消掉的minwidth宽度数
467
+ :param maxsplit: 最大切分数量,即最多得到几个子区间
468
+ 没设置的时候,会对所有满足条件的情况进行切割
469
+ :param minwidth: 每个切分位置最小具有的宽度
470
+ :return: [(l, r), (l, r), ...] 每一段文本的左右区间
471
+ """
472
+ # 1 裁剪左边、右边
473
+ n_vec = len(vec)
474
+ left, right = 0, n_vec
475
+ while left < right and vec[left] <= 0:
476
+ left += 1
477
+ while right > left and vec[right - 1] <= 0:
478
+ right -= 1
479
+ # 左右空白至少也要达到minwidth才去除
480
+ # if left < minwidth: left = 0
481
+ # if n_vec - right + 1 < minwidth: right = n_vec
482
+
483
+ vec = vec[left:right]
484
+ width = len(vec)
485
+ if width == 0:
486
+ return [] # 没有内容,返回空list
487
+
488
+ # 2 找切分位置
489
+ # 统计每一段连续的背景长度,并且对其数值求和,作为这段是背景的置信度
490
+ bg_probs, bg_start, cnt = [], 0, 0
491
+
492
+ def update_fg():
493
+ """ 遇到前景内容,或者循环结束,更新一下 """
494
+ nonlocal cnt
495
+ prob = vec[bg_start:bg_start + cnt].sum()
496
+ # print(cnt, prob)
497
+ if cnt >= (minwidth + prob): # 负值可以减小minwidth限定
498
+ itv = [bg_start, bg_start + cnt]
499
+ bg_probs.append([itv, prob])
500
+ cnt = 0
501
+
502
+ for i in range(width):
503
+ if vec[i] <= 0:
504
+ if not cnt:
505
+ bg_start = i
506
+ cnt += 1
507
+ else:
508
+ update_fg()
509
+ else:
510
+ update_fg()
511
+
512
+ # 3 取置信度最大的几个分割点
513
+ if maxsplit:
514
+ bg_probs = sorted(bg_probs, key=lambda x: x[1])[:(maxsplit - 1)]
515
+ bg_probs = sorted(bg_probs, key=lambda x: x[0]) # 从左到右排序
516
+
517
+ # 4 返回文本区间(反向计算)
518
+ res = []
519
+ intervals = Intervals([itv for itv, prob in bg_probs]).invert(width) + left
520
+ # print(intervals)
521
+ for interval in intervals:
522
+ res.append([interval.start(), interval.end()])
523
+ return res
524
+
525
+
526
+ def bound_scale(bound, scale):
527
+ """ 一个矩形,以中心为原点,缩放面积为原来scale的新矩形
528
+
529
+ :param bound: [x1, y1, x2, y2]
530
+ :param scale: 比例,例如0.5,就是缩放一半
531
+ """
532
+ x1, y1, x2, y2 = bound
533
+ x0 = (x2 + x1) / 2
534
+ y0 = (y2 + y1) / 2
535
+ r = 1 - scale ** 0.5
536
+
537
+ x1 += r * abs(x0 - x1)
538
+ y1 += r * abs(y0 - y1)
539
+ x2 -= r * abs(x0 - x2)
540
+ y2 -= r * abs(y0 - y2)
541
+ return x1, y1, x2, y2