jacksung-dev 0.0.4.15__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.
- jacksung/__init__.py +1 -0
- jacksung/ai/GeoAttX.py +356 -0
- jacksung/ai/GeoNet/__init__.py +0 -0
- jacksung/ai/GeoNet/m_block.py +393 -0
- jacksung/ai/GeoNet/m_blockV2.py +442 -0
- jacksung/ai/GeoNet/m_network.py +107 -0
- jacksung/ai/GeoNet/m_networkV2.py +91 -0
- jacksung/ai/__init__.py +0 -0
- jacksung/ai/latex_tool.py +199 -0
- jacksung/ai/metrics.py +181 -0
- jacksung/ai/utils/__init__.py +0 -0
- jacksung/ai/utils/cmorph.py +42 -0
- jacksung/ai/utils/data_parallelV2.py +90 -0
- jacksung/ai/utils/fy.py +333 -0
- jacksung/ai/utils/goes.py +161 -0
- jacksung/ai/utils/gsmap.py +24 -0
- jacksung/ai/utils/imerg.py +159 -0
- jacksung/ai/utils/metsat.py +164 -0
- jacksung/ai/utils/norm_util.py +109 -0
- jacksung/ai/utils/util.py +300 -0
- jacksung/libs/times.ttf +0 -0
- jacksung/utils/__init__.py +1 -0
- jacksung/utils/base_db.py +72 -0
- jacksung/utils/cache.py +71 -0
- jacksung/utils/data_convert.py +273 -0
- jacksung/utils/exception.py +27 -0
- jacksung/utils/fastnumpy.py +115 -0
- jacksung/utils/figure.py +251 -0
- jacksung/utils/hash.py +26 -0
- jacksung/utils/image.py +221 -0
- jacksung/utils/log.py +86 -0
- jacksung/utils/login.py +149 -0
- jacksung/utils/mean_std.py +66 -0
- jacksung/utils/multi_task.py +129 -0
- jacksung/utils/number.py +6 -0
- jacksung/utils/nvidia.py +140 -0
- jacksung/utils/time.py +87 -0
- jacksung/utils/web.py +63 -0
- jacksung_dev-0.0.4.15.dist-info/LICENSE +201 -0
- jacksung_dev-0.0.4.15.dist-info/METADATA +228 -0
- jacksung_dev-0.0.4.15.dist-info/RECORD +44 -0
- jacksung_dev-0.0.4.15.dist-info/WHEEL +5 -0
- jacksung_dev-0.0.4.15.dist-info/entry_points.txt +3 -0
- jacksung_dev-0.0.4.15.dist-info/top_level.txt +1 -0
jacksung/utils/figure.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import math
|
|
3
|
+
import cv2
|
|
4
|
+
import shutil
|
|
5
|
+
from jacksung.utils.multi_task import ThreadingLock
|
|
6
|
+
from PIL import ImageFont
|
|
7
|
+
from osgeo import gdal, osr
|
|
8
|
+
import numpy as np
|
|
9
|
+
import matplotlib
|
|
10
|
+
|
|
11
|
+
matplotlib.use('Agg')
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
from random import randint
|
|
14
|
+
import os
|
|
15
|
+
from jacksung.utils.data_convert import nc2np, np2tif
|
|
16
|
+
from jacksung.utils.image import crop_png, zoom_image, zoomAndDock, draw_text, concatenate_images, make_block
|
|
17
|
+
from jacksung.utils.cache import Cache
|
|
18
|
+
import rasterio
|
|
19
|
+
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
|
|
20
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
21
|
+
import cartopy.feature as cfeature
|
|
22
|
+
import cartopy.crs as ccrs
|
|
23
|
+
import cartopy.io.img_tiles as cimgt
|
|
24
|
+
from rasterio.transform import from_origin
|
|
25
|
+
import yaml
|
|
26
|
+
import argparse
|
|
27
|
+
import jacksung.utils.fastnumpy as fnp
|
|
28
|
+
from tqdm import tqdm
|
|
29
|
+
from datetime import datetime, timedelta
|
|
30
|
+
from matplotlib.ticker import MaxNLocator
|
|
31
|
+
import netCDF4 as nc
|
|
32
|
+
import math
|
|
33
|
+
from jacksung.utils.data_convert import np2tif, Coordinate
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_color_normalization(data, colors):
|
|
37
|
+
max_value = colors[-1][0]
|
|
38
|
+
min_value = colors[0][0]
|
|
39
|
+
data[data < min_value] = min_value
|
|
40
|
+
data[data > max_value] = max_value
|
|
41
|
+
data = (data - min_value) / (max_value - min_value)
|
|
42
|
+
new_colors = []
|
|
43
|
+
for idx, color in enumerate(colors):
|
|
44
|
+
if idx == 0:
|
|
45
|
+
value = 0
|
|
46
|
+
elif idx == len(colors) - 1:
|
|
47
|
+
value = 1
|
|
48
|
+
else:
|
|
49
|
+
value = (color[0] - min_value) / (max_value - min_value)
|
|
50
|
+
new_colors.append([value, color[1]])
|
|
51
|
+
return data, new_colors
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# 色带颜色定位
|
|
55
|
+
def _get_color_position(value, colors):
|
|
56
|
+
colors_min, colors_max = colors[0][0], colors[-1][0]
|
|
57
|
+
colors = [[(color[0] - colors_min) / (colors_max - colors_min), color[1]] for color in colors]
|
|
58
|
+
i = 0
|
|
59
|
+
while i < len(colors) - 1:
|
|
60
|
+
if value <= colors[i + 1][0]:
|
|
61
|
+
break
|
|
62
|
+
i += 1
|
|
63
|
+
color_str0, color_str1 = colors[i][1], colors[i + 1][1]
|
|
64
|
+
r1, g1, b1, r2, g2, b2 = int(color_str0[1:3], 16), int(color_str0[3:5], 16), int(color_str0[5:7], 16), \
|
|
65
|
+
int(color_str1[1:3], 16), int(color_str1[3:5], 16), int(color_str1[5:7], 16)
|
|
66
|
+
r = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (r2 - r1) + r1
|
|
67
|
+
g = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (g2 - g1) + g1
|
|
68
|
+
b = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (b2 - b1) + b1
|
|
69
|
+
return np.array((b, g, r))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def make_color_map(colors, h, w, unit='', l_margin=300, r_margin=200, font_size=150, round_digits=1):
|
|
73
|
+
colors_map = np.zeros((h, w, 3), dtype=np.uint8) + 255
|
|
74
|
+
w = w - l_margin - r_margin
|
|
75
|
+
for i in range(l_margin, w + l_margin):
|
|
76
|
+
i = i - l_margin
|
|
77
|
+
colors_map[:h - 150, i + l_margin] = _get_color_position(i / w, colors)
|
|
78
|
+
if i in [0, w // 2, w - 1]:
|
|
79
|
+
text_value = round((i / w) * (colors[-1][0] - colors[0][0]) + colors[0][0], round_digits)
|
|
80
|
+
if round_digits == 0:
|
|
81
|
+
text_value = int(text_value)
|
|
82
|
+
text = str(text_value)
|
|
83
|
+
if i == 0:
|
|
84
|
+
text += unit
|
|
85
|
+
colors_map = draw_text(colors_map, (i - 100 + l_margin, h - 150),
|
|
86
|
+
font=ImageFont.truetype(
|
|
87
|
+
rf'{os.path.abspath(os.path.dirname(__file__))}/../libs/times.ttf', font_size),
|
|
88
|
+
text=text)
|
|
89
|
+
return colors_map
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _make_fig(file_np,
|
|
93
|
+
# [经度起,经度止,经度步长],[纬度起,纬度止,纬度步长]
|
|
94
|
+
# np数据会自动填充整个图形,确保数据范围和area范围一致
|
|
95
|
+
area, file_title='', save_name='img1.png',
|
|
96
|
+
# 色带范围,请给出实际的数据范围
|
|
97
|
+
# color=((0, '#1E90FF'), (2, '#1874CD'), (5, '#3A5FCD'), (10, '#0000CD'), (30, '#9400D3')),
|
|
98
|
+
colors=None,
|
|
99
|
+
colors_only=None,
|
|
100
|
+
# 字体大小
|
|
101
|
+
font_size=15,
|
|
102
|
+
# 放大区域
|
|
103
|
+
zoom_rectangle=(310 * 5, 300 * 5, 50 * 5, 40 * 5),
|
|
104
|
+
# 放大区域停靠位置
|
|
105
|
+
zoom_docker=(300, 730),
|
|
106
|
+
# 图片清晰度
|
|
107
|
+
dpi=500,
|
|
108
|
+
xy_axis=None,
|
|
109
|
+
draw_x_label=True,
|
|
110
|
+
draw_y_label=True,
|
|
111
|
+
draw_lon_grid=True,
|
|
112
|
+
draw_lat_grid=True,
|
|
113
|
+
clip_left=None,
|
|
114
|
+
clip_right=None,
|
|
115
|
+
clip_top=None,
|
|
116
|
+
clip_bottom=None,
|
|
117
|
+
# 添加各种特征
|
|
118
|
+
# 自然海岸界,其他自带要素的参考cartopy
|
|
119
|
+
# '10m', '50m', or '110m'
|
|
120
|
+
features=(
|
|
121
|
+
cfeature.NaturalEarthFeature('physical', 'land', '50m', edgecolor='black', facecolor='none',
|
|
122
|
+
linewidth=0.4), cfeature.OCEAN, cfeature.LAND, cfeature.RIVERS),
|
|
123
|
+
border_type=None, make_fig_lock=None):
|
|
124
|
+
# corp = [92, 31, 542, 456]
|
|
125
|
+
if xy_axis is None:
|
|
126
|
+
xy_axis = area
|
|
127
|
+
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
|
|
128
|
+
# 设置经纬度范围,限定为中国
|
|
129
|
+
# 注意指定crs关键字,否则范围不一定完全准确
|
|
130
|
+
extents = [area[0][0], area[0][1], area[1][0], area[1][1]]
|
|
131
|
+
if area[0][1] > 180:
|
|
132
|
+
central_longitude = 180
|
|
133
|
+
else:
|
|
134
|
+
central_longitude = 0
|
|
135
|
+
proj = ccrs.PlateCarree(central_longitude=central_longitude)
|
|
136
|
+
data_crs = ccrs.PlateCarree()
|
|
137
|
+
if make_fig_lock is not None:
|
|
138
|
+
make_fig_lock.acquire()
|
|
139
|
+
fig = plt.figure(dpi=dpi)
|
|
140
|
+
ax = fig.add_subplot(111, projection=proj)
|
|
141
|
+
|
|
142
|
+
if colors is None:
|
|
143
|
+
np_min, np_max = np.nanpercentile(file_np, [5, 95])
|
|
144
|
+
if colors_only is None:
|
|
145
|
+
colors_only = ('#1E90FF', '#1874CD', '#3A5FCD', '#0000CD', '#9400D3')
|
|
146
|
+
break_value = (np_max - np_min) / (len(colors_only) - 1)
|
|
147
|
+
colors = [(np_min, colors_only[0])]
|
|
148
|
+
for i in range(1, len(colors_only)):
|
|
149
|
+
colors.append((np_min + i * break_value, colors_only[i]))
|
|
150
|
+
colors.append((np_max, colors_only[-1]))
|
|
151
|
+
# 用色带给数据上色,输入单通道,返回三通道图
|
|
152
|
+
data_np, new_colors = _get_color_normalization(file_np, colors)
|
|
153
|
+
cmap = LinearSegmentedColormap.from_list('custom_cmap', new_colors)
|
|
154
|
+
for feature in features:
|
|
155
|
+
ax.add_feature(feature)
|
|
156
|
+
ax.imshow(data_np, origin='upper', extent=extents, transform=data_crs, cmap=cmap, vmin=new_colors[0][0],
|
|
157
|
+
vmax=new_colors[-1][0])
|
|
158
|
+
# 添加网格线
|
|
159
|
+
if border_type is not None:
|
|
160
|
+
# ax.gridlines(line_style='--')
|
|
161
|
+
ax.gridlines(line_style=border_type)
|
|
162
|
+
# 设置大刻度和小刻度
|
|
163
|
+
tick_proj = ccrs.PlateCarree()
|
|
164
|
+
if draw_x_label:
|
|
165
|
+
ax.set_xticks(np.arange(xy_axis[0][0], xy_axis[0][1] + 1, xy_axis[0][2]), crs=tick_proj)
|
|
166
|
+
# ax.set_xticks(np.arange(-180, 180 + 30, 30), minor=True, crs=tick_proj)
|
|
167
|
+
if draw_y_label:
|
|
168
|
+
ax.set_yticks(np.arange(xy_axis[1][0], xy_axis[1][1] + 1, xy_axis[1][2]), crs=tick_proj)
|
|
169
|
+
# ax.set_yticks(np.arange(-90, 90 + 15, 15), minor=True, crs=tick_proj)
|
|
170
|
+
# 利用Formatter格式化刻度标签
|
|
171
|
+
if draw_lon_grid:
|
|
172
|
+
ax.xaxis.set_major_formatter(LongitudeFormatter())
|
|
173
|
+
if draw_lat_grid:
|
|
174
|
+
ax.yaxis.set_major_formatter(LatitudeFormatter())
|
|
175
|
+
ax.set_title(file_title, fontsize=font_size)
|
|
176
|
+
plt.xticks(fontsize=font_size)
|
|
177
|
+
plt.yticks(fontsize=font_size)
|
|
178
|
+
# plt.title(fontsize=font_size)
|
|
179
|
+
# 关键修复:set_extent的crs应该是数据坐标系
|
|
180
|
+
ax.set_extent(extents, crs=data_crs)
|
|
181
|
+
plt.savefig(save_name)
|
|
182
|
+
if zoom_rectangle is not None:
|
|
183
|
+
read_png = cv2.imread(save_name)
|
|
184
|
+
read_png = zoomAndDock(read_png, zoom_rectangle, zoom_docker, scale_factor=5, border=14)
|
|
185
|
+
cv2.imwrite(save_name, read_png)
|
|
186
|
+
np_data = cv2.imread(save_name) - 255
|
|
187
|
+
np_sum_h = np.nonzero(np_data.sum(axis=(1, 2)))[0]
|
|
188
|
+
np_sum_w = np.nonzero(np_data.sum(axis=(0, 2)))[0]
|
|
189
|
+
# print(np_sum_h, np_sum_w)
|
|
190
|
+
h, w = np_data.shape[:2]
|
|
191
|
+
clip_left = min(np_sum_w[0], clip_left) if clip_left is not None else np_sum_w[0]
|
|
192
|
+
clip_right = max(np_sum_w[-1], w - clip_right) if clip_right is not None else np_sum_w[-1]
|
|
193
|
+
clip_top = min(np_sum_h[0], clip_top) if clip_top is not None else np_sum_h[0]
|
|
194
|
+
clip_bottom = max(np_sum_h[-1], h - clip_bottom) if clip_bottom is not None else np_sum_h[-1]
|
|
195
|
+
crop_png(save_name, left=clip_left, top=clip_top, right=clip_right, bottom=clip_bottom)
|
|
196
|
+
plt.close()
|
|
197
|
+
if make_fig_lock is not None:
|
|
198
|
+
make_fig_lock.release()
|
|
199
|
+
return colors
|
|
200
|
+
# plt.show()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def make_fig(data,
|
|
204
|
+
area,
|
|
205
|
+
xy_axis=None,
|
|
206
|
+
draw_x_label=True,
|
|
207
|
+
draw_y_label=True,
|
|
208
|
+
draw_lon_grid=True,
|
|
209
|
+
draw_lat_grid=True,
|
|
210
|
+
clip_left=None,
|
|
211
|
+
clip_right=None,
|
|
212
|
+
clip_top=None,
|
|
213
|
+
clip_bottom=None,
|
|
214
|
+
file_title='',
|
|
215
|
+
save_name='figure_default.png',
|
|
216
|
+
colors=None,
|
|
217
|
+
colors_only=None,
|
|
218
|
+
font_size=15,
|
|
219
|
+
zoom_rectangle=None,
|
|
220
|
+
zoom_docker=(300, 730),
|
|
221
|
+
dpi=500,
|
|
222
|
+
features=(
|
|
223
|
+
cfeature.NaturalEarthFeature('physical', 'land', '50m', edgecolor='black', facecolor='none',
|
|
224
|
+
linewidth=0.4), cfeature.OCEAN, cfeature.LAND, cfeature.RIVERS),
|
|
225
|
+
border_type=None,
|
|
226
|
+
draw_colormap=True,
|
|
227
|
+
colormap_l_margin=300,
|
|
228
|
+
colormap_r_margin=200,
|
|
229
|
+
cm_font_size=150,
|
|
230
|
+
round_digits=1,
|
|
231
|
+
colormap_unit=''):
|
|
232
|
+
colors = _make_fig(data, font_size=font_size, zoom_rectangle=zoom_rectangle, zoom_docker=zoom_docker, dpi=dpi,
|
|
233
|
+
features=features, border_type=border_type, xy_axis=xy_axis, draw_x_label=draw_x_label,
|
|
234
|
+
draw_y_label=draw_y_label, draw_lon_grid=draw_lon_grid, draw_lat_grid=draw_lat_grid,
|
|
235
|
+
file_title=file_title, save_name=save_name, area=area, colors=colors, colors_only=colors_only,
|
|
236
|
+
clip_left=clip_left, clip_right=clip_right, clip_top=clip_top, clip_bottom=clip_bottom)
|
|
237
|
+
if draw_colormap:
|
|
238
|
+
img = cv2.imread(save_name)
|
|
239
|
+
h, w, c = img.shape
|
|
240
|
+
cm = make_color_map(colors, 180, w, unit=colormap_unit, l_margin=colormap_l_margin, r_margin=colormap_r_margin,
|
|
241
|
+
font_size=cm_font_size, round_digits=round_digits)
|
|
242
|
+
white_block = make_block(10, w)
|
|
243
|
+
merge_img = concatenate_images([img, white_block, cm], direction='v')
|
|
244
|
+
cv2.imwrite(save_name, merge_img)
|
|
245
|
+
return colors
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
if __name__ == '__main__':
|
|
249
|
+
data = np.load(r'C:\Users\ECNU\Desktop\delete_me\20230101_00_400_400.npy')[-1]
|
|
250
|
+
data[data < 0.1] = np.nan
|
|
251
|
+
make_fig(data, area=((40, 120, 20), (20, 60, 20)))
|
jacksung/utils/hash.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def calculate_file_hash(file_path, hash_algorithm="md5", chunk_size=4096):
|
|
5
|
+
hash_obj = hashlib.new(hash_algorithm)
|
|
6
|
+
with open(file_path, "rb") as file:
|
|
7
|
+
while True:
|
|
8
|
+
data = file.read(chunk_size)
|
|
9
|
+
if not data:
|
|
10
|
+
break
|
|
11
|
+
hash_obj.update(data)
|
|
12
|
+
return hash_obj.hexdigest()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def hash_files(file_paths, hash_algorithm="md5"):
|
|
16
|
+
hash_list = []
|
|
17
|
+
for filepath in file_paths:
|
|
18
|
+
hash_list.append(calculate_file_hash(filepath, hash_algorithm))
|
|
19
|
+
return hash_string(''.join(hash_list), hash_algorithm)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def hash_string(s, hash_algorithm="md5"):
|
|
23
|
+
hash_obj = hashlib.new(hash_algorithm)
|
|
24
|
+
# 对连接后的字符串进行哈希
|
|
25
|
+
hash_obj.update(s.encode())
|
|
26
|
+
return hash_obj.hexdigest()
|
jacksung/utils/image.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
from PIL import Image, ImageFont, ImageDraw
|
|
5
|
+
import numpy as np
|
|
6
|
+
from jacksung.utils.data_convert import Coordinate
|
|
7
|
+
import os
|
|
8
|
+
from importlib import resources
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_pixel_by_coord(img, coord, x, y):
|
|
12
|
+
left, top, x_res, y_res = coord.left, coord.top, coord.x_res, coord.y_res
|
|
13
|
+
if x < left or y > top:
|
|
14
|
+
raise Exception(f'x:{x} or y:{y} is lower than border {left},{top}!'
|
|
15
|
+
f'left:{left}, top:{top},x_res:{x_res}, y_res:{y_res}.')
|
|
16
|
+
s = img.shape
|
|
17
|
+
if x > left + s[-1] * x_res or y < top - s[-2] * y_res:
|
|
18
|
+
raise Exception(f'x:{x} or y:{y} is greater than border {left + s[-1] * x_res},{top - s[-2] * y_res}!'
|
|
19
|
+
f'left:{left}, top:{top},x_res:{x_res}, y_res:{y_res}.')
|
|
20
|
+
return img[..., int((top - y) // y_res), int((x - left) // x_res)]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def draw_text(img, xy, font=None, font_size=35, text='test text', color=(0, 0, 0)):
|
|
24
|
+
if xy[0] < 0 or xy[1] < 0:
|
|
25
|
+
xy_txt = (0 if xy[0] < 0 else xy[0], 0 if xy[1] < 0 else xy[1])
|
|
26
|
+
h, w, c = img.shape
|
|
27
|
+
white_block = make_block(h, w, color=(255, 255, 255))
|
|
28
|
+
txt_block = _draw_text(white_block, xy_txt, font, font_size, text, color)
|
|
29
|
+
non_white_mask = np.any(txt_block != [255, 255, 255], axis=-1)
|
|
30
|
+
row_indices, col_indices = np.where(non_white_mask)
|
|
31
|
+
# 3. 计算最大/最小索引
|
|
32
|
+
min_row, max_row = row_indices.min(), row_indices.max()
|
|
33
|
+
min_col, max_col = col_indices.min(), col_indices.max()
|
|
34
|
+
txt_length = max_col - min_col
|
|
35
|
+
txt_width = max_row - min_row
|
|
36
|
+
if xy[0] < 0:
|
|
37
|
+
xy = ((w - txt_length) // 2, xy[1])
|
|
38
|
+
if xy[1] < 0:
|
|
39
|
+
xy = (xy[0], (h - txt_width) // 2)
|
|
40
|
+
return _draw_text(img, xy, font, font_size, text, color)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _draw_text(img, xy, font=None, font_size=35, text='test text', color=(0, 0, 0)):
|
|
44
|
+
if font is None:
|
|
45
|
+
try:
|
|
46
|
+
with resources.path("jacksung.libs", "times.ttf") as font_path:
|
|
47
|
+
font = ImageFont.truetype(str(font_path), size=font_size) # 指定字体大小
|
|
48
|
+
except:
|
|
49
|
+
print('load times ttf failed, using the default font')
|
|
50
|
+
font = ImageFont.load_default()
|
|
51
|
+
im = Image.fromarray(img.astype(np.uint8))
|
|
52
|
+
draw = ImageDraw.Draw(im)
|
|
53
|
+
draw.text(xy, text, font=font, fill=color)
|
|
54
|
+
image = np.array(im)
|
|
55
|
+
return image
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _check_border(in_n, n):
|
|
59
|
+
if in_n < 0:
|
|
60
|
+
return 0
|
|
61
|
+
if in_n > n:
|
|
62
|
+
return n
|
|
63
|
+
return in_n
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def border(img, point1, point2, color=(0, 0, 255), border=5):
|
|
67
|
+
point1_h, point1_w = point1
|
|
68
|
+
point2_h, point2_w = point2
|
|
69
|
+
h, w, _ = img.shape
|
|
70
|
+
img[_check_border(point1_h - border // 2, h): _check_border(point1_h + border // 2, h),
|
|
71
|
+
_check_border(point1_w - border // 2, w): _check_border(point2_w + border // 2, w), :] = color
|
|
72
|
+
img[_check_border(point2_h - border // 2, h): _check_border(point2_h + border // 2, h),
|
|
73
|
+
_check_border(point1_w - border // 2, w): _check_border(point2_w + border // 2, w), :] = color
|
|
74
|
+
img[_check_border(point1_h - border // 2, h): _check_border(point2_h + border // 2, h),
|
|
75
|
+
_check_border(point1_w - border // 2, w): _check_border(point1_w + border // 2, w), :] = color
|
|
76
|
+
img[_check_border(point1_h - border // 2, h): _check_border(point2_h + border // 2, h),
|
|
77
|
+
_check_border(point2_w - border // 2, w): _check_border(point2_w + border // 2, w), :] = color
|
|
78
|
+
|
|
79
|
+
return img
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def make_block(h, w, color=(255, 255, 255), dtype=np.int32):
|
|
83
|
+
return np.array([[color for _ in range(w)] for _ in range(h)], dtype=dtype)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def get_color_position(value, colors):
|
|
87
|
+
colors_min, colors_max = colors[0][0], colors[-1][0]
|
|
88
|
+
colors = [[(color[0] - colors_min) / (colors_max - colors_min), color[1]] for color in colors]
|
|
89
|
+
i = 0
|
|
90
|
+
while i < len(colors) - 1:
|
|
91
|
+
if value <= colors[i + 1][0]:
|
|
92
|
+
break
|
|
93
|
+
i += 1
|
|
94
|
+
color_str0, color_str1 = colors[i][1], colors[i + 1][1]
|
|
95
|
+
r1, g1, b1, r2, g2, b2 = int(color_str0[1:3], 16), int(color_str0[3:5], 16), int(color_str0[5:7], 16), \
|
|
96
|
+
int(color_str1[1:3], 16), int(color_str1[3:5], 16), int(color_str1[5:7], 16)
|
|
97
|
+
r = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (r2 - r1) + r1
|
|
98
|
+
g = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (g2 - g1) + g1
|
|
99
|
+
b = (value - colors[i][0]) / (colors[i + 1][0] - colors[i][0]) * (b2 - b1) + b1
|
|
100
|
+
return np.array((b, g, r))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def make_color_map(colors, h, w, unit=''):
|
|
104
|
+
colors_map = np.zeros((h, w, 3), dtype=np.uint8) + 255
|
|
105
|
+
l_margin, r_margin = 300, 200
|
|
106
|
+
w = w - l_margin - r_margin
|
|
107
|
+
for i in range(l_margin, w + l_margin):
|
|
108
|
+
i = i - l_margin
|
|
109
|
+
colors_map[:100, i + l_margin] = get_color_position(i / w, colors)
|
|
110
|
+
if i in [0, w // 2, w - 1]:
|
|
111
|
+
text = str(round((i / w) * (colors[-1][0] - colors[0][0]) + colors[0][0]))
|
|
112
|
+
if i == 0:
|
|
113
|
+
text += unit
|
|
114
|
+
try:
|
|
115
|
+
with resources.path("jacksung.libs", "times.ttf") as font_path:
|
|
116
|
+
font = ImageFont.truetype(str(font_path), size=150) # 指定字体大小
|
|
117
|
+
except:
|
|
118
|
+
print('load times ttf failed, using the default font')
|
|
119
|
+
font = ImageFont.load_default()
|
|
120
|
+
colors_map = draw_text(colors_map, (i - 100 + l_margin, 100),
|
|
121
|
+
font=font, text=text)
|
|
122
|
+
return colors_map
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def crop_png(input_path, left=0, top=0, right=None, bottom=None, right_margin=0, bottom_margin=0):
|
|
126
|
+
# 打开 PNG 图像
|
|
127
|
+
image = Image.open(input_path)
|
|
128
|
+
width, height = image.size
|
|
129
|
+
if right is None:
|
|
130
|
+
right = width
|
|
131
|
+
if bottom is None:
|
|
132
|
+
bottom = height
|
|
133
|
+
right -= right_margin
|
|
134
|
+
bottom -= bottom_margin
|
|
135
|
+
# 对图像进行裁剪
|
|
136
|
+
cropped_image = image.crop((left, top, right, bottom))
|
|
137
|
+
# 保存裁剪后的图像
|
|
138
|
+
cropped_image.save(input_path)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def concatenate_images(imgs, direction="h"):
|
|
142
|
+
# 读取所有的图片
|
|
143
|
+
# 获取图片的宽度和高度
|
|
144
|
+
heights = [img.shape[0] for img in imgs]
|
|
145
|
+
widths = [img.shape[1] for img in imgs]
|
|
146
|
+
|
|
147
|
+
if direction == "h":
|
|
148
|
+
# 水平拼接,高度不变,宽度为所有图片宽度之和
|
|
149
|
+
max_height = max(heights)
|
|
150
|
+
total_width = sum(widths)
|
|
151
|
+
if imgs[0].ndim == 2:
|
|
152
|
+
new_img = np.zeros((max_height, total_width), dtype=np.uint8)
|
|
153
|
+
else:
|
|
154
|
+
new_img = np.zeros((max_height, total_width, 3), dtype=np.uint8)
|
|
155
|
+
x_offset = 0
|
|
156
|
+
for img in imgs:
|
|
157
|
+
new_img[:, x_offset:x_offset + img.shape[1]] = img
|
|
158
|
+
x_offset += img.shape[1]
|
|
159
|
+
elif direction == "v":
|
|
160
|
+
# 垂直拼接,宽度不变,高度为所有图片高度之和
|
|
161
|
+
max_width = max(widths)
|
|
162
|
+
total_height = sum(heights)
|
|
163
|
+
if imgs[0].ndim == 2:
|
|
164
|
+
new_img = np.zeros((total_height, max_width), dtype=np.uint8)
|
|
165
|
+
else:
|
|
166
|
+
new_img = np.zeros((total_height, max_width, 3), dtype=np.uint8)
|
|
167
|
+
y_offset = 0
|
|
168
|
+
for img in imgs:
|
|
169
|
+
new_img[y_offset:y_offset + img.shape[0], :] = img
|
|
170
|
+
y_offset += img.shape[0]
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError("Invalid direction. Please choose 'h' or 'v'.")
|
|
173
|
+
return new_img
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def create_gif(images_in, output_path, duration=500, idx=None):
|
|
177
|
+
images = []
|
|
178
|
+
if type(images_in) == str:
|
|
179
|
+
for file_name in sorted(os.listdir(images_in)):
|
|
180
|
+
# if file_name.endswith('.png'):
|
|
181
|
+
file_path = os.path.join(images_in, file_name)
|
|
182
|
+
images.append(Image.open(file_path))
|
|
183
|
+
else:
|
|
184
|
+
for img in images_in:
|
|
185
|
+
images.append(Image.fromarray(cv2.cvtColor(img.astype(np.uint8), cv2.COLOR_BGR2RGB)))
|
|
186
|
+
if idx is not None:
|
|
187
|
+
images = images[idx[0]:idx[1]]
|
|
188
|
+
images[0].save(output_path, save_all=True, append_images=images[1:], duration=duration, loop=0)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def zoom_image(image, scale_factor=2):
|
|
192
|
+
# 获取图像的尺寸
|
|
193
|
+
height, width = image.shape[:2]
|
|
194
|
+
# 计算缩放后的尺寸
|
|
195
|
+
new_dimensions = (int(width * scale_factor), int(height * scale_factor))
|
|
196
|
+
# 使用cv2.resize进行缩放
|
|
197
|
+
zoomed_image = cv2.resize(image, new_dimensions, interpolation=cv2.INTER_LINEAR)
|
|
198
|
+
return zoomed_image
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def zoomAndDock(img, zoom_rectangle, docker, scale_factor=2, border=10):
|
|
202
|
+
corp_png = img[zoom_rectangle[1]:zoom_rectangle[1] + zoom_rectangle[3],
|
|
203
|
+
zoom_rectangle[0]:zoom_rectangle[0] + zoom_rectangle[2], :]
|
|
204
|
+
corp_png = zoom_image(corp_png, scale_factor=scale_factor)
|
|
205
|
+
cv2.rectangle(img, (zoom_rectangle[0], zoom_rectangle[1]),
|
|
206
|
+
(zoom_rectangle[0] + zoom_rectangle[2], zoom_rectangle[1] + zoom_rectangle[3]), (0, 0, 255), border)
|
|
207
|
+
corp_png[:, 0:border, :] = [0, 0, 0]
|
|
208
|
+
corp_png[:, corp_png.shape[1] - border:corp_png.shape[1], :] = [0, 0, 0]
|
|
209
|
+
corp_png[0:border, :, :] = [0, 0, 0]
|
|
210
|
+
corp_png[corp_png.shape[0] - border:corp_png.shape[0], :, :] = [0, 0, 0]
|
|
211
|
+
img[docker[0]:docker[0] + corp_png.shape[0], docker[1]:docker[1] + corp_png.shape[1], :] = corp_png
|
|
212
|
+
return img
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == '__main__':
|
|
216
|
+
# path = r'D:\python_Project\FYpredict\metrics\make_figure\band_metrics.png'
|
|
217
|
+
# crop_png(path, right_margin=50)
|
|
218
|
+
white = make_block(200, 500, color=(random.randint(0, 255), 255, 255))
|
|
219
|
+
white = draw_text(white, (-1, 10), text='testtesttesttesttesttest text')
|
|
220
|
+
cv2.imshow('white', white)
|
|
221
|
+
cv2.waitKey()
|
jacksung/utils/log.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import requests
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
import _thread
|
|
5
|
+
import time
|
|
6
|
+
import threading
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
threadLock = threading.Lock()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def format_log(*args):
|
|
13
|
+
return '[' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '] ' + ' '.join([str(x) for x in args])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def oprint(*args, **kwargs):
|
|
17
|
+
log = format_log(*args)
|
|
18
|
+
print(log, end=kwargs.get('end', '\n'), flush=kwargs.get('flush', False))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def thread_send_log(url, content, name):
|
|
22
|
+
threadLock.acquire()
|
|
23
|
+
content = quote(content, 'utf-8')
|
|
24
|
+
name = quote(name, 'utf-8')
|
|
25
|
+
url = url + '&name=' + name + '&content=' + content
|
|
26
|
+
# print('sendLog:' + url)
|
|
27
|
+
try:
|
|
28
|
+
# print("----------------sendLog...----------------")
|
|
29
|
+
requests.get(url, timeout=5)
|
|
30
|
+
# print('\nsendLog finish', r.status_code, r.content)
|
|
31
|
+
# print('sendLog finish')
|
|
32
|
+
except Exception as e:
|
|
33
|
+
print('\nsendLog network error!')
|
|
34
|
+
finally:
|
|
35
|
+
# print("----------------sendLog...----------------")
|
|
36
|
+
threadLock.release()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class StdLog(object):
|
|
40
|
+
def __init__(self, filename='default.log', common_path='warning_log', stream=sys.stdout):
|
|
41
|
+
self.terminal = stream
|
|
42
|
+
self.log = open(filename, 'a')
|
|
43
|
+
self.common_log = None
|
|
44
|
+
self.common_path = common_path
|
|
45
|
+
|
|
46
|
+
def write(self, message):
|
|
47
|
+
message = str(message)
|
|
48
|
+
if message.count('[TemporaryTag]') == 0:
|
|
49
|
+
if message.count('[Common]') != 0 or message.count('[Warning]') != 0 \
|
|
50
|
+
or message.count('[Error]') != 0 or message.count('[OnlyFile]') != 0:
|
|
51
|
+
if self.common_log is None:
|
|
52
|
+
self.common_log = open(self.common_path, 'a')
|
|
53
|
+
self.common_log.write(message.replace('[Common]', '').replace('[OnlyFile]', ''))
|
|
54
|
+
self.common_log.flush()
|
|
55
|
+
else:
|
|
56
|
+
self.log.write(message)
|
|
57
|
+
self.log.flush()
|
|
58
|
+
else:
|
|
59
|
+
message = message.replace('[TemporaryTag]', '')
|
|
60
|
+
if message.count('[OnlyFile]') == 0:
|
|
61
|
+
self.terminal.write(message)
|
|
62
|
+
|
|
63
|
+
def flush(self):
|
|
64
|
+
self.terminal.flush()
|
|
65
|
+
self.log.flush()
|
|
66
|
+
if self.common_log is not None:
|
|
67
|
+
self.common_log.flush()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class LogClass:
|
|
71
|
+
def __init__(self, on=False, url=None):
|
|
72
|
+
self.on = on
|
|
73
|
+
self.url = url
|
|
74
|
+
|
|
75
|
+
def send_log(self, content, name):
|
|
76
|
+
if self.on:
|
|
77
|
+
try:
|
|
78
|
+
_thread.start_new_thread(thread_send_log, (self.url, content, name))
|
|
79
|
+
except Exception as e:
|
|
80
|
+
print("Cloud Log Error")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == '__main__':
|
|
84
|
+
# sys.stdout = StdLog(filename='log.txt', common_path='warning.txt')
|
|
85
|
+
oprint('this is a log', end=' ')
|
|
86
|
+
oprint('this is a log')
|