pyxllib 0.0.43__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 (186) hide show
  1. pyxllib/__init__.py +9 -2
  2. pyxllib/algo/__init__.py +8 -0
  3. pyxllib/algo/disjoint.py +54 -0
  4. pyxllib/algo/geo.py +541 -0
  5. pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
  6. pyxllib/algo/matcher.py +389 -0
  7. pyxllib/algo/newbie.py +166 -0
  8. pyxllib/algo/pupil.py +629 -0
  9. pyxllib/algo/shapelylib.py +67 -0
  10. pyxllib/algo/specialist.py +241 -0
  11. pyxllib/algo/stat.py +494 -0
  12. pyxllib/algo/treelib.py +149 -0
  13. pyxllib/algo/unitlib.py +66 -0
  14. pyxllib/autogui/__init__.py +5 -0
  15. pyxllib/autogui/activewin.py +246 -0
  16. pyxllib/autogui/all.py +9 -0
  17. pyxllib/autogui/autogui.py +852 -0
  18. pyxllib/autogui/uiautolib.py +362 -0
  19. pyxllib/autogui/virtualkey.py +102 -0
  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 +1 -11
  24. pyxllib/cv/expert.py +267 -0
  25. pyxllib/cv/{imlib.py → imfile.py} +18 -83
  26. pyxllib/cv/imhash.py +39 -0
  27. pyxllib/cv/pupil.py +9 -0
  28. pyxllib/cv/rgbfmt.py +1525 -0
  29. pyxllib/cv/slidercaptcha.py +137 -0
  30. pyxllib/cv/trackbartools.py +163 -49
  31. pyxllib/cv/xlcvlib.py +1040 -0
  32. pyxllib/cv/xlpillib.py +423 -0
  33. pyxllib/data/__init__.py +0 -0
  34. pyxllib/data/echarts.py +240 -0
  35. pyxllib/data/jsonlib.py +89 -0
  36. pyxllib/{util/oss2_.py → data/oss.py} +11 -9
  37. pyxllib/data/pglib.py +1127 -0
  38. pyxllib/data/sqlite.py +568 -0
  39. pyxllib/{util → data}/sqllib.py +13 -31
  40. pyxllib/ext/JLineViewer.py +505 -0
  41. pyxllib/ext/__init__.py +6 -0
  42. pyxllib/{util → ext}/demolib.py +119 -35
  43. pyxllib/ext/drissionlib.py +277 -0
  44. pyxllib/ext/kq5034lib.py +12 -0
  45. pyxllib/{util/main.py → ext/old.py} +122 -284
  46. pyxllib/ext/qt.py +449 -0
  47. pyxllib/ext/robustprocfile.py +497 -0
  48. pyxllib/ext/seleniumlib.py +76 -0
  49. pyxllib/{util/tklib.py → ext/tk.py} +10 -11
  50. pyxllib/ext/unixlib.py +827 -0
  51. pyxllib/ext/utools.py +351 -0
  52. pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
  53. pyxllib/ext/win32lib.py +40 -0
  54. pyxllib/ext/wjxlib.py +88 -0
  55. pyxllib/ext/wpsapi.py +124 -0
  56. pyxllib/ext/xlwork.py +9 -0
  57. pyxllib/ext/yuquelib.py +1105 -0
  58. pyxllib/file/__init__.py +17 -0
  59. pyxllib/file/docxlib.py +761 -0
  60. pyxllib/{util → file}/gitlib.py +40 -27
  61. pyxllib/file/libreoffice.py +165 -0
  62. pyxllib/file/movielib.py +148 -0
  63. pyxllib/file/newbie.py +10 -0
  64. pyxllib/file/onenotelib.py +1469 -0
  65. pyxllib/file/packlib/__init__.py +330 -0
  66. pyxllib/{util → file/packlib}/zipfile.py +598 -195
  67. pyxllib/file/pdflib.py +426 -0
  68. pyxllib/file/pupil.py +185 -0
  69. pyxllib/file/specialist/__init__.py +685 -0
  70. pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
  71. pyxllib/file/specialist/download.py +193 -0
  72. pyxllib/file/specialist/filelib.py +2829 -0
  73. pyxllib/file/xlsxlib.py +3131 -0
  74. pyxllib/file/xlsyncfile.py +341 -0
  75. pyxllib/prog/__init__.py +5 -0
  76. pyxllib/prog/cachetools.py +64 -0
  77. pyxllib/prog/deprecatedlib.py +233 -0
  78. pyxllib/prog/filelock.py +42 -0
  79. pyxllib/prog/ipyexec.py +253 -0
  80. pyxllib/prog/multiprogs.py +940 -0
  81. pyxllib/prog/newbie.py +451 -0
  82. pyxllib/prog/pupil.py +1197 -0
  83. pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
  84. pyxllib/prog/specialist/__init__.py +391 -0
  85. pyxllib/prog/specialist/bc.py +203 -0
  86. pyxllib/prog/specialist/browser.py +497 -0
  87. pyxllib/prog/specialist/common.py +347 -0
  88. pyxllib/prog/specialist/datetime.py +199 -0
  89. pyxllib/prog/specialist/tictoc.py +240 -0
  90. pyxllib/prog/specialist/xllog.py +180 -0
  91. pyxllib/prog/xlosenv.py +108 -0
  92. pyxllib/stdlib/__init__.py +17 -0
  93. pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
  94. pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
  95. pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
  96. pyxllib/text/__init__.py +8 -0
  97. pyxllib/text/ahocorasick.py +39 -0
  98. pyxllib/text/airscript.js +744 -0
  99. pyxllib/text/charclasslib.py +121 -0
  100. pyxllib/text/jiebalib.py +267 -0
  101. pyxllib/text/jinjalib.py +32 -0
  102. pyxllib/text/jsa_ai_prompt.md +271 -0
  103. pyxllib/text/jscode.py +922 -0
  104. pyxllib/text/latex/__init__.py +158 -0
  105. pyxllib/text/levenshtein.py +303 -0
  106. pyxllib/text/nestenv.py +1215 -0
  107. pyxllib/text/newbie.py +300 -0
  108. pyxllib/text/pupil/__init__.py +8 -0
  109. pyxllib/text/pupil/common.py +1121 -0
  110. pyxllib/text/pupil/xlalign.py +326 -0
  111. pyxllib/text/pycode.py +47 -0
  112. pyxllib/text/specialist/__init__.py +8 -0
  113. pyxllib/text/specialist/common.py +112 -0
  114. pyxllib/text/specialist/ptag.py +186 -0
  115. pyxllib/text/spellchecker.py +172 -0
  116. pyxllib/text/templates/echart_base.html +11 -0
  117. pyxllib/text/templates/highlight_code.html +17 -0
  118. pyxllib/text/templates/latex_editor.html +103 -0
  119. pyxllib/text/vbacode.py +17 -0
  120. pyxllib/text/xmllib.py +747 -0
  121. pyxllib/xl.py +39 -0
  122. pyxllib/xlcv.py +17 -0
  123. pyxllib-0.3.197.dist-info/METADATA +48 -0
  124. pyxllib-0.3.197.dist-info/RECORD +126 -0
  125. {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
  126. pyxllib/basic/_1_strlib.py +0 -945
  127. pyxllib/basic/_2_timelib.py +0 -488
  128. pyxllib/basic/_3_pathlib.py +0 -916
  129. pyxllib/basic/_4_loglib.py +0 -419
  130. pyxllib/basic/__init__.py +0 -54
  131. pyxllib/basic/arrow_.py +0 -250
  132. pyxllib/basic/chardet_.py +0 -66
  133. pyxllib/basic/dirlib.py +0 -529
  134. pyxllib/basic/dprint.py +0 -202
  135. pyxllib/basic/extension.py +0 -12
  136. pyxllib/basic/judge.py +0 -31
  137. pyxllib/basic/log.py +0 -204
  138. pyxllib/basic/pathlib_.py +0 -705
  139. pyxllib/basic/pytictoc.py +0 -102
  140. pyxllib/basic/qiniu_.py +0 -61
  141. pyxllib/basic/strlib.py +0 -761
  142. pyxllib/basic/timer.py +0 -132
  143. pyxllib/cv/cv.py +0 -834
  144. pyxllib/cv/cvlib/_1_geo.py +0 -543
  145. pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
  146. pyxllib/cv/cvlib/_2_imgproc.py +0 -594
  147. pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
  148. pyxllib/cv/cvlib/_4_cvimg.py +0 -211
  149. pyxllib/cv/cvlib/__init__.py +0 -10
  150. pyxllib/cv/debugtools.py +0 -82
  151. pyxllib/cv/fitz_.py +0 -300
  152. pyxllib/cv/installer.py +0 -42
  153. pyxllib/debug/_0_installer.py +0 -38
  154. pyxllib/debug/_1_typelib.py +0 -277
  155. pyxllib/debug/_2_chrome.py +0 -198
  156. pyxllib/debug/_3_showdir.py +0 -161
  157. pyxllib/debug/_4_bcompare.py +0 -140
  158. pyxllib/debug/__init__.py +0 -49
  159. pyxllib/debug/bcompare.py +0 -132
  160. pyxllib/debug/chrome.py +0 -198
  161. pyxllib/debug/installer.py +0 -38
  162. pyxllib/debug/showdir.py +0 -158
  163. pyxllib/debug/typelib.py +0 -278
  164. pyxllib/image/__init__.py +0 -12
  165. pyxllib/torch/__init__.py +0 -20
  166. pyxllib/torch/modellib.py +0 -37
  167. pyxllib/torch/trainlib.py +0 -344
  168. pyxllib/util/__init__.py +0 -20
  169. pyxllib/util/aip_.py +0 -141
  170. pyxllib/util/casiadb.py +0 -59
  171. pyxllib/util/excellib.py +0 -495
  172. pyxllib/util/filelib.py +0 -612
  173. pyxllib/util/jsondata.py +0 -27
  174. pyxllib/util/jsondata2.py +0 -92
  175. pyxllib/util/labelmelib.py +0 -139
  176. pyxllib/util/onepy/__init__.py +0 -29
  177. pyxllib/util/onepy/onepy.py +0 -574
  178. pyxllib/util/onepy/onmanager.py +0 -170
  179. pyxllib/util/pyautogui_.py +0 -219
  180. pyxllib/util/textlib.py +0 -1305
  181. pyxllib/util/unorder.py +0 -22
  182. pyxllib/util/xmllib.py +0 -639
  183. pyxllib-0.0.43.dist-info/METADATA +0 -39
  184. pyxllib-0.0.43.dist-info/RECORD +0 -80
  185. pyxllib-0.0.43.dist-info/top_level.txt +0 -1
  186. {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/__init__.py CHANGED
@@ -2,13 +2,20 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  # @Author : 陈坤泽
4
4
  # @Email : 877362867@qq.com
5
- # @Data : 2020/05/30 10:00
5
+ # @Date : 2020/05/30 10:00
6
6
 
7
7
 
8
8
  """
9
9
  厦门理工模式识别团队通用python代码工具库
10
10
 
11
11
  注意为了避免循环嵌套引用,代码逻辑清晰,请尽量不要在此文件写代码
12
+
13
+ 文档:https://www.yuque.com/xlpr/pyxllib/home/edit
12
14
  """
13
15
 
14
- VERSION = '0.0.43'
16
+ import sys
17
+
18
+ if sys.version_info.major == 3 and sys.version_info.minor >= 8:
19
+ import importlib.metadata
20
+
21
+ version = importlib.metadata.version('pyxllib')
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/06/03 14:21
6
+
7
+ """ 算法功能库 """
8
+
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # @Author : 陈坤泽
4
+ # @Email : 877362867@qq.com
5
+ # @Date : 2021/06/03 14:26
6
+
7
+ """
8
+ 并查集相关功能
9
+ """
10
+
11
+ from tqdm import tqdm
12
+
13
+ from pyxllib.prog.pupil import check_install_package
14
+
15
+ check_install_package('disjoint_set', 'disjoint-set==0.6.3')
16
+
17
+ from itertools import combinations
18
+
19
+ from disjoint_set import DisjointSet
20
+
21
+
22
+ def disjoint_set(items, join_checker, print_mode=False):
23
+ """ 按照一定的相连规则分组
24
+
25
+ :param items: 项目清单
26
+ :param join_checker: 检查任意两个对象是否相连,进行分组
27
+ :return:
28
+
29
+ 算法:因为会转成下标,按照下标进行分组合并,所以支持items里有重复值,或者unhashable对象
30
+
31
+ >>> disjoint_set([-1, -2, 2, 0, 0, 1], lambda x, y: x*y>0)
32
+ [[-1, -2], [2, 1], [0], [0]]
33
+
34
+ 注意:因为会两两进行运算,所以数据量大的时候计算会特别慢。
35
+ """
36
+
37
+ # 1 添加元素
38
+ ds = DisjointSet()
39
+ items = tuple(items)
40
+ n = len(items)
41
+ for i in range(n):
42
+ ds.find(i)
43
+
44
+ # 2 连接、分组
45
+ for i, j in tqdm(combinations(range(n), 2), disable=not print_mode):
46
+ if join_checker(items[i], items[j]):
47
+ ds.union(i, j)
48
+
49
+ # 3 返回分组信息
50
+ res = []
51
+ for group in ds.itersets():
52
+ group_elements = [items[g] for g in group]
53
+ res.append(group_elements)
54
+ return res
pyxllib/algo/geo.py ADDED
@@ -0,0 +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 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