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.
- pyxllib/__init__.py +9 -2
- pyxllib/algo/__init__.py +8 -0
- pyxllib/algo/disjoint.py +54 -0
- pyxllib/algo/geo.py +541 -0
- pyxllib/{util/mathlib.py → algo/intervals.py} +172 -36
- pyxllib/algo/matcher.py +389 -0
- pyxllib/algo/newbie.py +166 -0
- pyxllib/algo/pupil.py +629 -0
- pyxllib/algo/shapelylib.py +67 -0
- pyxllib/algo/specialist.py +241 -0
- pyxllib/algo/stat.py +494 -0
- pyxllib/algo/treelib.py +149 -0
- pyxllib/algo/unitlib.py +66 -0
- pyxllib/autogui/__init__.py +5 -0
- pyxllib/autogui/activewin.py +246 -0
- pyxllib/autogui/all.py +9 -0
- pyxllib/autogui/autogui.py +852 -0
- pyxllib/autogui/uiautolib.py +362 -0
- pyxllib/autogui/virtualkey.py +102 -0
- pyxllib/autogui/wechat.py +827 -0
- pyxllib/autogui/wechat_msg.py +421 -0
- pyxllib/autogui/wxautolib.py +84 -0
- pyxllib/cv/__init__.py +1 -11
- pyxllib/cv/expert.py +267 -0
- pyxllib/cv/{imlib.py → imfile.py} +18 -83
- pyxllib/cv/imhash.py +39 -0
- pyxllib/cv/pupil.py +9 -0
- pyxllib/cv/rgbfmt.py +1525 -0
- pyxllib/cv/slidercaptcha.py +137 -0
- pyxllib/cv/trackbartools.py +163 -49
- pyxllib/cv/xlcvlib.py +1040 -0
- pyxllib/cv/xlpillib.py +423 -0
- pyxllib/data/__init__.py +0 -0
- pyxllib/data/echarts.py +240 -0
- pyxllib/data/jsonlib.py +89 -0
- pyxllib/{util/oss2_.py → data/oss.py} +11 -9
- pyxllib/data/pglib.py +1127 -0
- pyxllib/data/sqlite.py +568 -0
- pyxllib/{util → data}/sqllib.py +13 -31
- pyxllib/ext/JLineViewer.py +505 -0
- pyxllib/ext/__init__.py +6 -0
- pyxllib/{util → ext}/demolib.py +119 -35
- pyxllib/ext/drissionlib.py +277 -0
- pyxllib/ext/kq5034lib.py +12 -0
- pyxllib/{util/main.py → ext/old.py} +122 -284
- pyxllib/ext/qt.py +449 -0
- pyxllib/ext/robustprocfile.py +497 -0
- pyxllib/ext/seleniumlib.py +76 -0
- pyxllib/{util/tklib.py → ext/tk.py} +10 -11
- pyxllib/ext/unixlib.py +827 -0
- pyxllib/ext/utools.py +351 -0
- pyxllib/{util/webhooklib.py → ext/webhook.py} +45 -17
- pyxllib/ext/win32lib.py +40 -0
- pyxllib/ext/wjxlib.py +88 -0
- pyxllib/ext/wpsapi.py +124 -0
- pyxllib/ext/xlwork.py +9 -0
- pyxllib/ext/yuquelib.py +1105 -0
- pyxllib/file/__init__.py +17 -0
- pyxllib/file/docxlib.py +761 -0
- pyxllib/{util → file}/gitlib.py +40 -27
- pyxllib/file/libreoffice.py +165 -0
- pyxllib/file/movielib.py +148 -0
- pyxllib/file/newbie.py +10 -0
- pyxllib/file/onenotelib.py +1469 -0
- pyxllib/file/packlib/__init__.py +330 -0
- pyxllib/{util → file/packlib}/zipfile.py +598 -195
- pyxllib/file/pdflib.py +426 -0
- pyxllib/file/pupil.py +185 -0
- pyxllib/file/specialist/__init__.py +685 -0
- pyxllib/{basic/_5_dirlib.py → file/specialist/dirlib.py} +364 -93
- pyxllib/file/specialist/download.py +193 -0
- pyxllib/file/specialist/filelib.py +2829 -0
- pyxllib/file/xlsxlib.py +3131 -0
- pyxllib/file/xlsyncfile.py +341 -0
- pyxllib/prog/__init__.py +5 -0
- pyxllib/prog/cachetools.py +64 -0
- pyxllib/prog/deprecatedlib.py +233 -0
- pyxllib/prog/filelock.py +42 -0
- pyxllib/prog/ipyexec.py +253 -0
- pyxllib/prog/multiprogs.py +940 -0
- pyxllib/prog/newbie.py +451 -0
- pyxllib/prog/pupil.py +1197 -0
- pyxllib/{sitepackages.py → prog/sitepackages.py} +5 -3
- pyxllib/prog/specialist/__init__.py +391 -0
- pyxllib/prog/specialist/bc.py +203 -0
- pyxllib/prog/specialist/browser.py +497 -0
- pyxllib/prog/specialist/common.py +347 -0
- pyxllib/prog/specialist/datetime.py +199 -0
- pyxllib/prog/specialist/tictoc.py +240 -0
- pyxllib/prog/specialist/xllog.py +180 -0
- pyxllib/prog/xlosenv.py +108 -0
- pyxllib/stdlib/__init__.py +17 -0
- pyxllib/{util → stdlib}/tablepyxl/__init__.py +1 -3
- pyxllib/{util → stdlib}/tablepyxl/style.py +1 -1
- pyxllib/{util → stdlib}/tablepyxl/tablepyxl.py +2 -4
- pyxllib/text/__init__.py +8 -0
- pyxllib/text/ahocorasick.py +39 -0
- pyxllib/text/airscript.js +744 -0
- pyxllib/text/charclasslib.py +121 -0
- pyxllib/text/jiebalib.py +267 -0
- pyxllib/text/jinjalib.py +32 -0
- pyxllib/text/jsa_ai_prompt.md +271 -0
- pyxllib/text/jscode.py +922 -0
- pyxllib/text/latex/__init__.py +158 -0
- pyxllib/text/levenshtein.py +303 -0
- pyxllib/text/nestenv.py +1215 -0
- pyxllib/text/newbie.py +300 -0
- pyxllib/text/pupil/__init__.py +8 -0
- pyxllib/text/pupil/common.py +1121 -0
- pyxllib/text/pupil/xlalign.py +326 -0
- pyxllib/text/pycode.py +47 -0
- pyxllib/text/specialist/__init__.py +8 -0
- pyxllib/text/specialist/common.py +112 -0
- pyxllib/text/specialist/ptag.py +186 -0
- pyxllib/text/spellchecker.py +172 -0
- pyxllib/text/templates/echart_base.html +11 -0
- pyxllib/text/templates/highlight_code.html +17 -0
- pyxllib/text/templates/latex_editor.html +103 -0
- pyxllib/text/vbacode.py +17 -0
- pyxllib/text/xmllib.py +747 -0
- pyxllib/xl.py +39 -0
- pyxllib/xlcv.py +17 -0
- pyxllib-0.3.197.dist-info/METADATA +48 -0
- pyxllib-0.3.197.dist-info/RECORD +126 -0
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info}/WHEEL +4 -5
- pyxllib/basic/_1_strlib.py +0 -945
- pyxllib/basic/_2_timelib.py +0 -488
- pyxllib/basic/_3_pathlib.py +0 -916
- pyxllib/basic/_4_loglib.py +0 -419
- pyxllib/basic/__init__.py +0 -54
- pyxllib/basic/arrow_.py +0 -250
- pyxllib/basic/chardet_.py +0 -66
- pyxllib/basic/dirlib.py +0 -529
- pyxllib/basic/dprint.py +0 -202
- pyxllib/basic/extension.py +0 -12
- pyxllib/basic/judge.py +0 -31
- pyxllib/basic/log.py +0 -204
- pyxllib/basic/pathlib_.py +0 -705
- pyxllib/basic/pytictoc.py +0 -102
- pyxllib/basic/qiniu_.py +0 -61
- pyxllib/basic/strlib.py +0 -761
- pyxllib/basic/timer.py +0 -132
- pyxllib/cv/cv.py +0 -834
- pyxllib/cv/cvlib/_1_geo.py +0 -543
- pyxllib/cv/cvlib/_2_cvprcs.py +0 -309
- pyxllib/cv/cvlib/_2_imgproc.py +0 -594
- pyxllib/cv/cvlib/_3_pilprcs.py +0 -80
- pyxllib/cv/cvlib/_4_cvimg.py +0 -211
- pyxllib/cv/cvlib/__init__.py +0 -10
- pyxllib/cv/debugtools.py +0 -82
- pyxllib/cv/fitz_.py +0 -300
- pyxllib/cv/installer.py +0 -42
- pyxllib/debug/_0_installer.py +0 -38
- pyxllib/debug/_1_typelib.py +0 -277
- pyxllib/debug/_2_chrome.py +0 -198
- pyxllib/debug/_3_showdir.py +0 -161
- pyxllib/debug/_4_bcompare.py +0 -140
- pyxllib/debug/__init__.py +0 -49
- pyxllib/debug/bcompare.py +0 -132
- pyxllib/debug/chrome.py +0 -198
- pyxllib/debug/installer.py +0 -38
- pyxllib/debug/showdir.py +0 -158
- pyxllib/debug/typelib.py +0 -278
- pyxllib/image/__init__.py +0 -12
- pyxllib/torch/__init__.py +0 -20
- pyxllib/torch/modellib.py +0 -37
- pyxllib/torch/trainlib.py +0 -344
- pyxllib/util/__init__.py +0 -20
- pyxllib/util/aip_.py +0 -141
- pyxllib/util/casiadb.py +0 -59
- pyxllib/util/excellib.py +0 -495
- pyxllib/util/filelib.py +0 -612
- pyxllib/util/jsondata.py +0 -27
- pyxllib/util/jsondata2.py +0 -92
- pyxllib/util/labelmelib.py +0 -139
- pyxllib/util/onepy/__init__.py +0 -29
- pyxllib/util/onepy/onepy.py +0 -574
- pyxllib/util/onepy/onmanager.py +0 -170
- pyxllib/util/pyautogui_.py +0 -219
- pyxllib/util/textlib.py +0 -1305
- pyxllib/util/unorder.py +0 -22
- pyxllib/util/xmllib.py +0 -639
- pyxllib-0.0.43.dist-info/METADATA +0 -39
- pyxllib-0.0.43.dist-info/RECORD +0 -80
- pyxllib-0.0.43.dist-info/top_level.txt +0 -1
- {pyxllib-0.0.43.dist-info → pyxllib-0.3.197.dist-info/licenses}/LICENSE +0 -0
pyxllib/cv/cvlib/_2_imgproc.py
DELETED
@@ -1,594 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/11/15 10:09
|
6
|
-
|
7
|
-
from pyxllib.basic import is_file, Path
|
8
|
-
from pyxllib.cv.cvlib._1_geo import *
|
9
|
-
|
10
|
-
import cv2
|
11
|
-
import PIL.Image
|
12
|
-
from PIL import Image
|
13
|
-
|
14
|
-
try:
|
15
|
-
import accimage
|
16
|
-
except ImportError:
|
17
|
-
accimage = None
|
18
|
-
from collections.abc import Sequence, Iterable
|
19
|
-
from abc import ABC
|
20
|
-
|
21
|
-
____functional = """
|
22
|
-
torchvision.transforms搬过来的功能
|
23
|
-
|
24
|
-
这个库太大了,底层又依赖tensor,
|
25
|
-
"""
|
26
|
-
|
27
|
-
|
28
|
-
def is_pil_image(img):
|
29
|
-
if accimage is not None:
|
30
|
-
return isinstance(img, (Image.Image, accimage.Image))
|
31
|
-
else:
|
32
|
-
return isinstance(img, Image.Image)
|
33
|
-
|
34
|
-
|
35
|
-
def is_numpy(img):
|
36
|
-
return isinstance(img, np.ndarray)
|
37
|
-
|
38
|
-
|
39
|
-
def is_numpy_image(img):
|
40
|
-
return is_numpy(img) and img.ndim in {2, 3}
|
41
|
-
|
42
|
-
|
43
|
-
____opencv = """
|
44
|
-
对opencv相关功能的一些优化、 封装
|
45
|
-
"""
|
46
|
-
|
47
|
-
|
48
|
-
class CvPlot:
|
49
|
-
@classmethod
|
50
|
-
def get_plot_color(cls, src):
|
51
|
-
""" 获得比较适合的作画颜色
|
52
|
-
|
53
|
-
TODO 可以根据背景色智能推导画线用的颜色,目前是固定红色
|
54
|
-
"""
|
55
|
-
if src.ndim == 3:
|
56
|
-
return 0, 0, 255
|
57
|
-
elif src.ndim == 2:
|
58
|
-
return 255 # 灰度图,默认先填白色
|
59
|
-
|
60
|
-
@classmethod
|
61
|
-
def get_plot_args(cls, src, color=None):
|
62
|
-
# 1 作图颜色
|
63
|
-
if not color:
|
64
|
-
color = cls.get_plot_color(src)
|
65
|
-
|
66
|
-
# 2 画布
|
67
|
-
if len(color) >= 3 and src.ndim <= 2:
|
68
|
-
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
|
69
|
-
else:
|
70
|
-
dst = np.array(src)
|
71
|
-
|
72
|
-
return dst, color
|
73
|
-
|
74
|
-
@classmethod
|
75
|
-
def lines(cls, src, lines, color=None, thickness=1, line_type=cv2.LINE_AA, shift=None):
|
76
|
-
""" 在src图像上画系列线段
|
77
|
-
"""
|
78
|
-
# 1 判断 lines 参数内容
|
79
|
-
lines = np_array(lines).reshape(-1, 4)
|
80
|
-
if not lines.size:
|
81
|
-
return src
|
82
|
-
|
83
|
-
# 2 参数
|
84
|
-
dst, color = cls.get_plot_args(src, color)
|
85
|
-
|
86
|
-
# 3 画线
|
87
|
-
if lines.any():
|
88
|
-
for line in lines:
|
89
|
-
x1, y1, x2, y2 = line
|
90
|
-
cv2.line(dst, (x1, y1), (x2, y2), color, thickness, line_type)
|
91
|
-
return dst
|
92
|
-
|
93
|
-
@classmethod
|
94
|
-
def circles(cls, src, circles, color=None, thickness=1, center=False):
|
95
|
-
""" 在图片上画圆形
|
96
|
-
|
97
|
-
:param src: 要作画的图
|
98
|
-
:param circles: 要画的圆形参数 (x, y, 半径 r)
|
99
|
-
:param color: 画笔颜色
|
100
|
-
:param center: 是否画出圆心
|
101
|
-
"""
|
102
|
-
# 1 圆 参数
|
103
|
-
circles = np_array(circles, dtype=int).reshape(-1, 3)
|
104
|
-
if not circles.size:
|
105
|
-
return src
|
106
|
-
|
107
|
-
# 2 参数
|
108
|
-
dst, color = cls.get_plot_args(src, color)
|
109
|
-
|
110
|
-
# 3 作画
|
111
|
-
for x in circles:
|
112
|
-
cv2.circle(dst, (x[0], x[1]), x[2], color, thickness)
|
113
|
-
if center:
|
114
|
-
cv2.circle(dst, (x[0], x[1]), 2, color, thickness)
|
115
|
-
|
116
|
-
return dst
|
117
|
-
|
118
|
-
|
119
|
-
____process = """
|
120
|
-
|
121
|
-
opencv-python文档: https://opencv-python-tutroals.readthedocs.io/en/latest/
|
122
|
-
pillow文档: https://pillow.readthedocs.io/en/stable/reference/
|
123
|
-
|
124
|
-
|
125
|
-
TODO 201115周日20:18
|
126
|
-
1、很多功能是新写的,可能有bug,多用多优化~~
|
127
|
-
2、画图功能
|
128
|
-
① CvPlot有待整合进CvImg
|
129
|
-
② PilImg增加对应的画图功能
|
130
|
-
③ 整合、统一接口形式、名称
|
131
|
-
3、旧的仿射变换等功能
|
132
|
-
① 需要整合进CvImg
|
133
|
-
② PilImg需要对应的实现
|
134
|
-
③ 整合,统一接口
|
135
|
-
"""
|
136
|
-
|
137
|
-
|
138
|
-
class ImgProcesser(ABC):
|
139
|
-
""" 无论是np图片,还是pil图片,共有的一些功能、处理逻辑
|
140
|
-
|
141
|
-
写有 NotImplementedError 的是np和pil机制有区别的底层功能,必须要实现的接口
|
142
|
-
"""
|
143
|
-
__slots__ = ('img',)
|
144
|
-
|
145
|
-
@classmethod
|
146
|
-
def read(cls, file, flag=1, **kwargs):
|
147
|
-
"""
|
148
|
-
:param file: 支持非文件路径参数,会做类型转换
|
149
|
-
因为这个接口的灵活性,要判断file参数类型等,速度会慢一点点
|
150
|
-
如果需要效率,可以显式使用imread、Image.open等明确操作类型
|
151
|
-
:param flag:
|
152
|
-
-1,按照图像原样读取,保留Alpha通道(第4通道)
|
153
|
-
0,将图像转成单通道灰度图像后读取
|
154
|
-
1,将图像转换成3通道BGR彩色图像
|
155
|
-
"""
|
156
|
-
raise NotImplementedError
|
157
|
-
|
158
|
-
def cvt_channel(self, flags):
|
159
|
-
""" 确保图片目前是flags指示的通道情况 """
|
160
|
-
raise NotImplementedError
|
161
|
-
|
162
|
-
def write(self, path, if_exists='replace', **kwargs):
|
163
|
-
raise NotImplementedError
|
164
|
-
|
165
|
-
@property
|
166
|
-
def size(self):
|
167
|
-
""" 图片尺寸,统一返回(height, width),不含通道 """
|
168
|
-
raise NotImplementedError
|
169
|
-
|
170
|
-
@property
|
171
|
-
def n_channels(self):
|
172
|
-
""" 通道数 """
|
173
|
-
raise NotImplementedError
|
174
|
-
|
175
|
-
def resize(self, size, interpolation=None, **kwargs):
|
176
|
-
"""
|
177
|
-
:param size: (h, w)
|
178
|
-
"""
|
179
|
-
raise NotImplementedError
|
180
|
-
|
181
|
-
def reduce_by_area(self, area):
|
182
|
-
""" 根据面积上限缩小图片
|
183
|
-
|
184
|
-
即图片面积超过area时,按照等比例缩小到面积为area的图片
|
185
|
-
"""
|
186
|
-
h, w = self.size
|
187
|
-
s = h * w
|
188
|
-
if s > area:
|
189
|
-
r = (area / s) ** 0.5
|
190
|
-
size = int(r * h), int(r * w)
|
191
|
-
img = self.resize(size)
|
192
|
-
else:
|
193
|
-
img = self
|
194
|
-
return img
|
195
|
-
|
196
|
-
|
197
|
-
class CvImg(ImgProcesser):
|
198
|
-
def __init__(self, img):
|
199
|
-
""" 注意源生的初始化不做任何类型判断,直接赋值 """
|
200
|
-
self.img = img
|
201
|
-
|
202
|
-
_show_win_num = 0
|
203
|
-
|
204
|
-
@classmethod
|
205
|
-
def read(cls, file, flags=1, **kwargs):
|
206
|
-
if is_numpy_image(file):
|
207
|
-
img = file
|
208
|
-
elif Path(file).is_file():
|
209
|
-
# https://www.yuque.com/xlpr/pyxllib/imread
|
210
|
-
img = cv2.imdecode(np.fromfile(str(file), dtype=np.uint8), flags)
|
211
|
-
elif is_pil_image(file):
|
212
|
-
img = cls.pil2np(file)
|
213
|
-
else:
|
214
|
-
raise TypeError(f'类型错误:{type(file)}')
|
215
|
-
return CvImg(img).cvt_channel(flags)
|
216
|
-
|
217
|
-
@classmethod
|
218
|
-
def pil2np(cls, img):
|
219
|
-
""" pil图片转np图片 """
|
220
|
-
x = img
|
221
|
-
y = np_array(x)
|
222
|
-
y = PIL.Image.fromarray(cv2.cvtColor(y, cv2.COLOR_BGR2RGB)) if y.size else None
|
223
|
-
return y
|
224
|
-
|
225
|
-
def cvt_channel(self, flags):
|
226
|
-
img = self.img
|
227
|
-
n_c = self.n_channels
|
228
|
-
if flags == 0 and n_c > 1:
|
229
|
-
if n_c == 3:
|
230
|
-
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
231
|
-
elif n_c == 4:
|
232
|
-
img = cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY)
|
233
|
-
elif flags == 1 and n_c != 3:
|
234
|
-
if n_c == 1:
|
235
|
-
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
236
|
-
elif n_c == 4:
|
237
|
-
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
|
238
|
-
return CvImg(img)
|
239
|
-
|
240
|
-
def write(self, path, if_exists='replace', **kwargs):
|
241
|
-
img = self.img
|
242
|
-
if not isinstance(path, Path):
|
243
|
-
path = Path(path)
|
244
|
-
data = cv2.imencode(ext=path.suffix, img=img)[1]
|
245
|
-
return path.write(data.tobytes(), if_exists=if_exists)
|
246
|
-
|
247
|
-
@property
|
248
|
-
def size(self):
|
249
|
-
return self.img.shape[:2]
|
250
|
-
|
251
|
-
@property
|
252
|
-
def n_channels(self):
|
253
|
-
""" 通道数 """
|
254
|
-
img = self.img
|
255
|
-
if img.ndim == 3:
|
256
|
-
return img.shape[2]
|
257
|
-
else:
|
258
|
-
return 1
|
259
|
-
|
260
|
-
def resize(self, size, interpolation=cv2.INTER_CUBIC, **kwargs):
|
261
|
-
"""
|
262
|
-
>>> im = CvImg(np.zeros([100, 200], dtype='uint8'))
|
263
|
-
>>> im.size
|
264
|
-
(100, 200)
|
265
|
-
>>> im2 = im.reduce_by_area(50*50)
|
266
|
-
>>> im2.size
|
267
|
-
(35, 70)
|
268
|
-
"""
|
269
|
-
img = cv2.resize(self.img, size[::-1], interpolation, **kwargs)
|
270
|
-
return CvImg(img)
|
271
|
-
|
272
|
-
def show(self, winname=None, flags=1):
|
273
|
-
""" 展示窗口
|
274
|
-
|
275
|
-
:param winname: 未输入时,则按test1、test2依次生成窗口
|
276
|
-
:param flags:
|
277
|
-
cv2.WINDOW_NORMAL,0,输入2等偶数值好像也等价于输入0
|
278
|
-
cv2.WINDOW_AUTOSIZE,1,输入3等奇数值好像等价于1
|
279
|
-
cv2.WINDOW_OPENGL,4096
|
280
|
-
:return:
|
281
|
-
"""
|
282
|
-
if winname is None:
|
283
|
-
n = CvImg._show_win_num + 1
|
284
|
-
winname = f'test{n}'
|
285
|
-
cv2.namedWindow(winname, flags)
|
286
|
-
cv2.imshow(winname, self.img)
|
287
|
-
|
288
|
-
|
289
|
-
class PilImg(ImgProcesser):
|
290
|
-
def __init__(self, img):
|
291
|
-
""" 注意源生的初始化不做任何类型判断,直接赋值 """
|
292
|
-
self.img = img
|
293
|
-
|
294
|
-
@classmethod
|
295
|
-
def read(cls, file, flags=1, **kwargs):
|
296
|
-
if is_pil_image(file):
|
297
|
-
img = file
|
298
|
-
elif is_numpy_image(file):
|
299
|
-
img = cls.np2pil(file)
|
300
|
-
elif Path(file).is_file():
|
301
|
-
img = Image.open(file, **kwargs)
|
302
|
-
else:
|
303
|
-
raise TypeError(f'类型错误:{type(file)}')
|
304
|
-
return PilImg(img).cvt_channel(flags)
|
305
|
-
|
306
|
-
@classmethod
|
307
|
-
def np2pil(cls, pic, mode=None):
|
308
|
-
"""Convert a tensor or an ndarray to PIL Image. (删除了tensor的转换功能)
|
309
|
-
|
310
|
-
See :class:`~torchvision.transforms.ToPILImage` for more details.
|
311
|
-
|
312
|
-
Args:
|
313
|
-
pic (Tensor or numpy.ndarray): Image to be converted to PIL Image.
|
314
|
-
mode (`PIL.Image mode`_): color space and pixel depth of input data (optional).
|
315
|
-
|
316
|
-
.. _PIL.Image mode: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#concept-modes
|
317
|
-
|
318
|
-
Returns:
|
319
|
-
PIL Image: Image converted to PIL Image.
|
320
|
-
"""
|
321
|
-
if pic.ndim not in {2, 3}:
|
322
|
-
raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim))
|
323
|
-
if pic.ndim == 2:
|
324
|
-
# if 2D image, add channel dimension (HWC)
|
325
|
-
pic = np.expand_dims(pic, 2)
|
326
|
-
|
327
|
-
npimg = pic
|
328
|
-
|
329
|
-
if npimg.shape[2] == 1:
|
330
|
-
expected_mode = None
|
331
|
-
npimg = npimg[:, :, 0]
|
332
|
-
if npimg.dtype == np.uint8:
|
333
|
-
expected_mode = 'L'
|
334
|
-
elif npimg.dtype == np.int16:
|
335
|
-
expected_mode = 'I;16'
|
336
|
-
elif npimg.dtype == np.int32:
|
337
|
-
expected_mode = 'I'
|
338
|
-
elif npimg.dtype == np.float32:
|
339
|
-
expected_mode = 'F'
|
340
|
-
if mode is not None and mode != expected_mode:
|
341
|
-
raise ValueError("Incorrect mode ({}) supplied for input type {}. Should be {}"
|
342
|
-
.format(mode, np.dtype, expected_mode))
|
343
|
-
mode = expected_mode
|
344
|
-
|
345
|
-
elif npimg.shape[2] == 2:
|
346
|
-
permitted_2_channel_modes = ['LA']
|
347
|
-
if mode is not None and mode not in permitted_2_channel_modes:
|
348
|
-
raise ValueError("Only modes {} are supported for 2D inputs".format(permitted_2_channel_modes))
|
349
|
-
|
350
|
-
if mode is None and npimg.dtype == np.uint8:
|
351
|
-
mode = 'LA'
|
352
|
-
|
353
|
-
elif npimg.shape[2] == 4:
|
354
|
-
permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX']
|
355
|
-
if mode is not None and mode not in permitted_4_channel_modes:
|
356
|
-
raise ValueError("Only modes {} are supported for 4D inputs".format(permitted_4_channel_modes))
|
357
|
-
|
358
|
-
if mode is None and npimg.dtype == np.uint8:
|
359
|
-
mode = 'RGBA'
|
360
|
-
else:
|
361
|
-
permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV']
|
362
|
-
if mode is not None and mode not in permitted_3_channel_modes:
|
363
|
-
raise ValueError("Only modes {} are supported for 3D inputs".format(permitted_3_channel_modes))
|
364
|
-
if mode is None and npimg.dtype == np.uint8:
|
365
|
-
mode = 'RGB'
|
366
|
-
|
367
|
-
if mode is None:
|
368
|
-
raise TypeError('Input type {} is not supported'.format(npimg.dtype))
|
369
|
-
|
370
|
-
return Image.fromarray(npimg, mode=mode)
|
371
|
-
|
372
|
-
def cvt_channel(self, flags):
|
373
|
-
img = self.img
|
374
|
-
n_c = self.n_channels
|
375
|
-
if flags == 0 and n_c > 1:
|
376
|
-
img = img.convert('L')
|
377
|
-
elif flags == 1 and n_c != 3:
|
378
|
-
img = img.convert('RGB')
|
379
|
-
return PilImg(img)
|
380
|
-
|
381
|
-
def write(self, path, if_exists='replace', **kwargs):
|
382
|
-
p = Path(path)
|
383
|
-
if p.preprocess(if_exists):
|
384
|
-
p.ensure_dir('file')
|
385
|
-
self.img.save(str(p), **kwargs)
|
386
|
-
|
387
|
-
def resize(self, size, interpolation=Image.BILINEAR, **kwargs):
|
388
|
-
"""
|
389
|
-
>>> im = PilImg.read(np.zeros([100, 200], dtype='uint8'), 0)
|
390
|
-
>>> im.size
|
391
|
-
(100, 200)
|
392
|
-
>>> im2 = im.reduce_by_area(50*50)
|
393
|
-
>>> im2.size
|
394
|
-
(35, 70)
|
395
|
-
"""
|
396
|
-
# 注意pil图像尺寸接口都是[w,h],跟标准的[h,w]相反
|
397
|
-
return PilImg(self.img.resize(size[::-1], interpolation))
|
398
|
-
|
399
|
-
@property
|
400
|
-
def size(self):
|
401
|
-
w, h = self.img.size
|
402
|
-
return h, w
|
403
|
-
|
404
|
-
@property
|
405
|
-
def n_channels(self):
|
406
|
-
""" 通道数 """
|
407
|
-
return len(self.img.getbands())
|
408
|
-
|
409
|
-
def show(self, title=None, command=None):
|
410
|
-
return self.img.show(title, command)
|
411
|
-
|
412
|
-
def random_direction(self):
|
413
|
-
""" 假设原图片是未旋转的状态0
|
414
|
-
|
415
|
-
顺时针转90度是label=1,顺时针转180度是label2 ...
|
416
|
-
"""
|
417
|
-
img = self.img
|
418
|
-
label = np.random.randint(4)
|
419
|
-
if label == 1:
|
420
|
-
# PIL的旋转角度,是指逆时针角度;但是我这里编号是顺时针
|
421
|
-
img = img.transpose(PIL.Image.ROTATE_270)
|
422
|
-
elif label == 2:
|
423
|
-
img = img.transpose(PIL.Image.ROTATE_180)
|
424
|
-
elif label == 3:
|
425
|
-
img = img.transpose(PIL.Image.ROTATE_90)
|
426
|
-
return img, label
|
427
|
-
|
428
|
-
|
429
|
-
____get_sub_image = """
|
430
|
-
|
431
|
-
TODO 这里很多功能,都要抽时间整理进CvImg、PilImg
|
432
|
-
|
433
|
-
"""
|
434
|
-
|
435
|
-
|
436
|
-
def get_warp_mat(src, dst):
|
437
|
-
"""
|
438
|
-
:param src: 原点集,支持多种格式输入
|
439
|
-
:param dst: 变换后的点集
|
440
|
-
:return: np.ndarray,3*3的变换矩阵
|
441
|
-
"""
|
442
|
-
|
443
|
-
def cvt_data(pts):
|
444
|
-
# opencv的透视变换,输入的点集有类型限制,必须使用float32
|
445
|
-
return np.array(pts, dtype='float32').reshape((-1, 2))
|
446
|
-
|
447
|
-
src, dst = cvt_data(src), cvt_data(dst)
|
448
|
-
n = src.shape[0]
|
449
|
-
if n == 3:
|
450
|
-
# 只有3个点,则使用仿射变换
|
451
|
-
warp_mat = cv2.getAffineTransform(src, dst)
|
452
|
-
warp_mat = np.concatenate([warp_mat, [[0, 0, 1]]], axis=0)
|
453
|
-
elif n == 4:
|
454
|
-
# 有4个点,则使用透视变换
|
455
|
-
warp_mat = cv2.getPerspectiveTransform(src, dst)
|
456
|
-
else:
|
457
|
-
raise ValueError('点集数量过多')
|
458
|
-
return warp_mat
|
459
|
-
|
460
|
-
|
461
|
-
def warp_image(img, warp_mat, dsize=None, *, view_rate=False, max_zoom=1, reserve_struct=False):
|
462
|
-
""" 对图像进行透视变换
|
463
|
-
|
464
|
-
:param img: np.ndarray的图像数据
|
465
|
-
TODO 支持PIL.Image格式?
|
466
|
-
:param warp_mat: 变换矩阵
|
467
|
-
:param dsize: 目标图片尺寸
|
468
|
-
没有任何输入时,同原图
|
469
|
-
如果有指定,则会决定最终的图片大小
|
470
|
-
如果使用了view_rate、max_zoom,会改变变换矩阵所展示的内容
|
471
|
-
:param view_rate: 视野比例,默认不开启,当输入非0正数时,几个数值功能效果如下
|
472
|
-
1,关注原图四个角点位置在变换后的位置,确保新的4个点依然在目标图中
|
473
|
-
为了达到该效果,会增加【平移】变换,以及自动控制dsize
|
474
|
-
2,将原图依中心面积放到至2倍,记录新的4个角点变换后的位置,确保变换后的4个点依然在目标图中
|
475
|
-
0.5,同理,只是只关注原图局部的一半位置
|
476
|
-
:param max_zoom: 默认1倍,当设置时(只在开启view_rate时有用),会增加【缩小】变换,限制view_rate扩展的上限
|
477
|
-
:param reserve_struct: 是否保留原来img的数据类型返回,默认True
|
478
|
-
关掉该功能可以提高性能,此时返回结果统一为 np 矩阵
|
479
|
-
:return: 见 reserve_struct
|
480
|
-
"""
|
481
|
-
from math import sqrt
|
482
|
-
|
483
|
-
# 0 参数整理
|
484
|
-
img = CvImg.read(img).img
|
485
|
-
|
486
|
-
# 1 得到3*3的变换矩阵
|
487
|
-
warp_mat = np_array(warp_mat)
|
488
|
-
if warp_mat.shape[0] == 2:
|
489
|
-
warp_mat = np.concatenate([warp_mat, [[0, 0, 1]]], axis=0)
|
490
|
-
|
491
|
-
# 2 view_rate,视野比例改变导致的变换矩阵规则变化
|
492
|
-
if view_rate:
|
493
|
-
# 2.1 视野变化后的四个角点
|
494
|
-
h, w = img.shape[:2]
|
495
|
-
y, x = h / 2, w / 2 # 图片中心点坐标
|
496
|
-
h1, w1 = view_rate * h / 2, view_rate * w / 2
|
497
|
-
l, t, r, b = [-w1 + x, -h1 + y, w1 + x, h1 + y]
|
498
|
-
pts1 = np.array([[l, t], [r, t], [r, b], [l, b]])
|
499
|
-
# 2.2 变换后角点位置产生的外接矩形
|
500
|
-
left, top, right, bottom = rect_bounds1d(warp_points(pts1, warp_mat))
|
501
|
-
# 2.3 增加平移变换确保左上角在原点
|
502
|
-
warp_mat = np.dot([[1, 0, -left], [0, 1, -top], [0, 0, 1]], warp_mat)
|
503
|
-
# 2.4 控制面积变化率
|
504
|
-
h2, w2 = (bottom - top, right - left)
|
505
|
-
if max_zoom:
|
506
|
-
rate = w2 * h2 / w / h # 目标面积比原面积
|
507
|
-
if rate > max_zoom:
|
508
|
-
r = 1 / sqrt(rate / max_zoom)
|
509
|
-
warp_mat = np.dot([[r, 0, 0], [0, r, 0], [0, 0, 1]], warp_mat)
|
510
|
-
h2, w2 = round(h2 * r), round(w2 * r)
|
511
|
-
if not dsize:
|
512
|
-
dsize = (w2, h2)
|
513
|
-
|
514
|
-
# 3 标准操作,不做额外处理,按照原图默认的图片尺寸展示
|
515
|
-
if dsize is None:
|
516
|
-
dsize = (img.shape[1], img.shape[0])
|
517
|
-
dst = cv2.warpPerspective(img, warp_mat, dsize)
|
518
|
-
|
519
|
-
# 4 返回值
|
520
|
-
return dst
|
521
|
-
|
522
|
-
|
523
|
-
def get_sub_image(src_image, pts, warp_quad=False):
|
524
|
-
""" 从src_image取一个子图
|
525
|
-
|
526
|
-
:param src_image: 原图
|
527
|
-
可以是图片路径、np.ndarray、PIL.Image对象
|
528
|
-
TODO 目前只支持np.ndarray、pil图片输入,返回统一是np.ndarray
|
529
|
-
:param pts: 子图位置信息
|
530
|
-
只有两个点,认为是矩形的两个对角点
|
531
|
-
只有四个点,认为是任意四边形
|
532
|
-
同理,其他点数量,默认为
|
533
|
-
:param warp_quad: 变形的四边形
|
534
|
-
默认是截图pts的外接四边形区域,使用该参数
|
535
|
-
且当pts为四个点时,是否强行扭转为矩形
|
536
|
-
一般写 'average',也可以写'max'、'min',详见 quad_warp_wh()
|
537
|
-
:return: 子图
|
538
|
-
文件、np.ndarray --> np.ndarray
|
539
|
-
PIL.Image --> PIL.Image
|
540
|
-
"""
|
541
|
-
src_img = CvImg.read(src_image)
|
542
|
-
pts = coords2d(pts)
|
543
|
-
if not warp_quad or len(pts) != 4:
|
544
|
-
x1, y1, x2, y2 = rect_bounds1d(pts)
|
545
|
-
dst = src_img[y1:y2, x1:x2] # 这里越界不会报错,只是越界的那个维度shape为0
|
546
|
-
else:
|
547
|
-
w, h = quad_warp_wh(pts, method=warp_quad)
|
548
|
-
warp_mat = get_warp_mat(pts, rect2polygon([0, 0, w, h]))
|
549
|
-
dst = warp_image(src_img, warp_mat, (w, h))
|
550
|
-
return dst
|
551
|
-
|
552
|
-
|
553
|
-
____other = """
|
554
|
-
"""
|
555
|
-
|
556
|
-
|
557
|
-
def get_background_color(src_img, edge_size=5, binary_img=None):
|
558
|
-
""" 智能判断图片背景色
|
559
|
-
|
560
|
-
对全图二值化后,考虑最外一层宽度未edge_size的环中,0、1分布最多的作为背景色
|
561
|
-
然后取全部背景色的平均值返回
|
562
|
-
|
563
|
-
:param src_img: 支持黑白图、彩图
|
564
|
-
:param edge_size: 边缘宽度,宽度越高一般越准确,但也越耗性能
|
565
|
-
:param binary_img: 运算中需要用二值图,如果外部已经计算了,可以直接传入进来,避免重复运算
|
566
|
-
:return: color
|
567
|
-
|
568
|
-
TODO 可以写个获得前景色,道理类似,只是最后再图片中心去取平均值
|
569
|
-
"""
|
570
|
-
from itertools import chain
|
571
|
-
|
572
|
-
# 1 获得二值图,区分前背景
|
573
|
-
if binary_img is None:
|
574
|
-
gray_img = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY) if src_img.ndim == 3 else src_img
|
575
|
-
_, binary_img = cv2.threshold(gray_img, np.mean(gray_img), 255, cv2.THRESH_BINARY)
|
576
|
-
|
577
|
-
# 2 分别存储点集
|
578
|
-
n, m = src_img.shape[:2]
|
579
|
-
colors0, colors1 = [], []
|
580
|
-
for i in range(n):
|
581
|
-
if i < edge_size or i >= n - edge_size:
|
582
|
-
js = range(m)
|
583
|
-
else:
|
584
|
-
js = chain(range(edge_size), range(m - edge_size, m))
|
585
|
-
for j in js:
|
586
|
-
if binary_img[i, j]:
|
587
|
-
colors1.append(src_img[i, j])
|
588
|
-
else:
|
589
|
-
colors0.append(src_img[i, j])
|
590
|
-
|
591
|
-
# 3 计算平均像素
|
592
|
-
# 以数量多的作为背景像素
|
593
|
-
colors = colors0 if len(colors0) > len(colors1) else colors1
|
594
|
-
return np.mean(np.array(colors), axis=0, dtype='int').tolist()
|
pyxllib/cv/cvlib/_3_pilprcs.py
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Data : 2020/11/17 15:13
|
6
|
-
|
7
|
-
from pyxllib.cv.cvlib._2_cvprcs import *
|
8
|
-
|
9
|
-
|
10
|
-
class PilPrcs(CvPrcs):
|
11
|
-
@classmethod
|
12
|
-
def read(cls, file, flags=1, **kwargs):
|
13
|
-
if is_pil_image(file):
|
14
|
-
img = file
|
15
|
-
elif is_numpy_image(file):
|
16
|
-
img = cv2pil(file)
|
17
|
-
elif Path(file).is_file():
|
18
|
-
img = Image.open(file, **kwargs)
|
19
|
-
else:
|
20
|
-
raise TypeError(f'类型错误:{type(file)}')
|
21
|
-
return cls.cvt_channel(img, flags)
|
22
|
-
|
23
|
-
@classmethod
|
24
|
-
def cvt_channel(cls, img, flags):
|
25
|
-
n_c = cls.n_channels(img)
|
26
|
-
if flags == 0 and n_c > 1:
|
27
|
-
img = img.convert('L')
|
28
|
-
elif flags == 1 and n_c != 3:
|
29
|
-
img = img.convert('RGB')
|
30
|
-
return img
|
31
|
-
|
32
|
-
@classmethod
|
33
|
-
def write(cls, img, path, if_exists='replace', **kwargs):
|
34
|
-
p = Path(path)
|
35
|
-
if p.preprocess(if_exists):
|
36
|
-
p.ensure_dir('file')
|
37
|
-
img.save(str(p), **kwargs)
|
38
|
-
|
39
|
-
@classmethod
|
40
|
-
def resize(cls, img, size, interpolation=Image.BILINEAR, **kwargs):
|
41
|
-
"""
|
42
|
-
>>> im = PilImg.read(np.zeros([100, 200], dtype='uint8'), 0)
|
43
|
-
>>> im.size
|
44
|
-
(100, 200)
|
45
|
-
>>> im2 = im.reduce_by_area(50*50)
|
46
|
-
>>> im2.size
|
47
|
-
(35, 70)
|
48
|
-
"""
|
49
|
-
# 注意pil图像尺寸接口都是[w,h],跟标准的[h,w]相反
|
50
|
-
return cls.resize(img, size[::-1], interpolation)
|
51
|
-
|
52
|
-
@classmethod
|
53
|
-
def size(cls, img):
|
54
|
-
w, h = img.size
|
55
|
-
return h, w
|
56
|
-
|
57
|
-
@classmethod
|
58
|
-
def n_channels(cls, img):
|
59
|
-
""" 通道数 """
|
60
|
-
return len(img.getbands())
|
61
|
-
|
62
|
-
@classmethod
|
63
|
-
def show(cls, img, title=None, command=None):
|
64
|
-
return cls.show(img, title, command)
|
65
|
-
|
66
|
-
@classmethod
|
67
|
-
def random_direction(cls, img):
|
68
|
-
""" 假设原图片是未旋转的状态0
|
69
|
-
|
70
|
-
顺时针转90度是label=1,顺时针转180度是label2 ...
|
71
|
-
"""
|
72
|
-
label = np.random.randint(4)
|
73
|
-
if label == 1:
|
74
|
-
# PIL的旋转角度,是指逆时针角度;但是我这里编号是顺时针
|
75
|
-
img = img.transpose(PIL.Image.ROTATE_270)
|
76
|
-
elif label == 2:
|
77
|
-
img = img.transpose(PIL.Image.ROTATE_180)
|
78
|
-
elif label == 3:
|
79
|
-
img = img.transpose(PIL.Image.ROTATE_90)
|
80
|
-
return img, label
|