visiontext 0.33.4__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.
- visiontext/__init__.py +1 -0
- visiontext/__main__.py +33 -0
- visiontext/audiotools.py +83 -0
- visiontext/bboxes.py +250 -0
- visiontext/cacheutils.py +66 -0
- visiontext/cli/__init__.py +0 -0
- visiontext/cli/collect_env_details.py +92 -0
- visiontext/cli/compare_jpeg_decoder_performance.py +48 -0
- visiontext/cli/download_hf_dataset.py +59 -0
- visiontext/cli/export_sqlite_to_csv.py +57 -0
- visiontext/cli/gui_lib_example.py +113 -0
- visiontext/cli/gui_scroll_example.py +33 -0
- visiontext/cli/index_tar.py +55 -0
- visiontext/cli/profile_gpu.py +51 -0
- visiontext/cli/spek.py +36 -0
- visiontext/cli/start_debugger.py +11 -0
- visiontext/colormaps.py +196 -0
- visiontext/configutils.py +41 -0
- visiontext/denormalize.py +83 -0
- visiontext/distutils.py +173 -0
- visiontext/examples/__init__.py +0 -0
- visiontext/examples/cc0images/__init__.py +0 -0
- visiontext/examples/cc0images/lowres/__init__.py +0 -0
- visiontext/font.py +85 -0
- visiontext/gui/__init__.py +0 -0
- visiontext/gui/gui_base.py +177 -0
- visiontext/gui/gui_config.py +70 -0
- visiontext/gui/gui_modules.py +66 -0
- visiontext/gui/message_box.py +66 -0
- visiontext/htmltools.py +238 -0
- visiontext/image_downloader.py +445 -0
- visiontext/images.py +596 -0
- visiontext/imports.py +128 -0
- visiontext/iotools/__init__.py +0 -0
- visiontext/iotools/feature_compression.py +169 -0
- visiontext/iotools/pth.py +20 -0
- visiontext/iotools/single_tar_lookup.py +228 -0
- visiontext/iotools/tar_lookup.py +371 -0
- visiontext/mathutils.py +154 -0
- visiontext/nlp/__init__.py +3 -0
- visiontext/nlp/lemmatizer.py +183 -0
- visiontext/nlp/nltktools.py +131 -0
- visiontext/nlp/regextools.py +15 -0
- visiontext/nlp/spacytools.py +43 -0
- visiontext/pandatools.py +154 -0
- visiontext/plots.py +12 -0
- visiontext/profiling/__init__.py +8 -0
- visiontext/profiling/code_profiler.py +65 -0
- visiontext/profiling/hardware_profiler.py +285 -0
- visiontext/sqlalchemist/__init__.py +31 -0
- visiontext/sqlalchemist/db_opener.py +61 -0
- visiontext/sqlalchemist/sqlalchemist.py +400 -0
- visiontext/terminal.py +51 -0
- visiontext/testdata/__init__.py +0 -0
- visiontext/torchutils.py +186 -0
- visiontext/visualize_logits.py +82 -0
- visiontext/visualize_ratios.py +141 -0
- visiontext/webdataset_pipeline.py +208 -0
- visiontext/webdataset_pipeline_separate_metadata.py +346 -0
- visiontext-0.33.4.dist-info/METADATA +132 -0
- visiontext-0.33.4.dist-info/RECORD +65 -0
- visiontext-0.33.4.dist-info/WHEEL +5 -0
- visiontext-0.33.4.dist-info/licenses/LICENSE +201 -0
- visiontext-0.33.4.dist-info/top_level.txt +1 -0
- visiontext-0.33.4.dist-info/zip-safe +1 -0
visiontext/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.33.4"
|
visiontext/__main__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import importlib.util
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
lib_module_name = __file__.replace("\\", "/").split("/")[-2]
|
|
9
|
+
if len(sys.argv) <= 1:
|
|
10
|
+
print(f"Usage: {lib_module_name} <module> <*args>\n")
|
|
11
|
+
from packg.packaging import get_modules_for_autocomplete
|
|
12
|
+
from packg.strings import format_pseudo_table
|
|
13
|
+
|
|
14
|
+
output_modules = get_modules_for_autocomplete(lib_module_name, run_dir=None)
|
|
15
|
+
terminal_width = shutil.get_terminal_size().columns
|
|
16
|
+
print(format_pseudo_table(output_modules, max_width=terminal_width))
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
target_module = sys.argv[1]
|
|
19
|
+
args = sys.argv[2:]
|
|
20
|
+
# load the spec to modify sys.argv before importing, otherwise it's too late
|
|
21
|
+
module_to_load = f"{lib_module_name}.{target_module}"
|
|
22
|
+
spec = importlib.util.find_spec(module_to_load)
|
|
23
|
+
if spec is None:
|
|
24
|
+
raise ValueError(f"Module {module_to_load} not found")
|
|
25
|
+
origin = spec.origin
|
|
26
|
+
sys.argv = [origin] + args
|
|
27
|
+
# import the module and run the main function
|
|
28
|
+
module = importlib.import_module(module_to_load)
|
|
29
|
+
module.main()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|
visiontext/audiotools.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from tempfile import NamedTemporaryFile
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from pydub import AudioSegment
|
|
7
|
+
from scipy.io import wavfile
|
|
8
|
+
from scipy.signal import spectrogram
|
|
9
|
+
|
|
10
|
+
from packg import Const
|
|
11
|
+
from packg.typext import PathType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AudioConvertC(Const):
|
|
15
|
+
NONE = "none"
|
|
16
|
+
MONO = "mono"
|
|
17
|
+
STEREO = "stereo"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def load_audio_file(
|
|
21
|
+
input_file: PathType, verbose: bool = False, convert=AudioConvertC.STEREO
|
|
22
|
+
) -> Tuple[int, np.ndarray]:
|
|
23
|
+
"""
|
|
24
|
+
Load audio file and return frequency and data.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
input_file:
|
|
28
|
+
verbose: print debug info
|
|
29
|
+
convert: convert to either stereo (shape (N, 2)) or mono (shape (N,)) or leave as is.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
tuple of frequency int and data numpy array of shape (N, 2) (stereo) or (N,) (mono)
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
file_format = input_file.name.split(".")[-1]
|
|
36
|
+
if verbose:
|
|
37
|
+
logger.info(f"Reading file {input_file} detected format {file_format}")
|
|
38
|
+
audio = AudioSegment.from_file(input_file, format=file_format)
|
|
39
|
+
|
|
40
|
+
# convert to wav
|
|
41
|
+
with NamedTemporaryFile(suffix=".wav") as tmpfile:
|
|
42
|
+
audio.export(tmpfile, format="wav")
|
|
43
|
+
frequency, data = wavfile.read(tmpfile)
|
|
44
|
+
|
|
45
|
+
# convert to stereo or mono as needed
|
|
46
|
+
if convert == AudioConvertC.STEREO and len(data.shape) == 1:
|
|
47
|
+
data = np.stack([data, data], axis=-1) # mono to stereo
|
|
48
|
+
elif convert == AudioConvertC.MONO and len(data.shape) == 2:
|
|
49
|
+
data = data.mean(-1) # stereo to mono
|
|
50
|
+
|
|
51
|
+
if verbose:
|
|
52
|
+
logger.info(f"Got frequency {frequency} shape {data.shape} after conversion '{convert}'")
|
|
53
|
+
|
|
54
|
+
return frequency, data
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def check_audio_quality(frequency, data, eps=1e-6) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
logic: a good quality music file will have audio at 20khz. a bad one will not.
|
|
60
|
+
"""
|
|
61
|
+
# dmin, dmax = data.min(), data.max()
|
|
62
|
+
# dnorm = (data - dmin) / (dmax - dmin)
|
|
63
|
+
# print_ndarray_lovely(data) # raw mono audio
|
|
64
|
+
# example data shape 11,796,960 freq 48000 => 246 seconds
|
|
65
|
+
|
|
66
|
+
freqs, time, spec = spectrogram(data, fs=frequency, nperseg=256, noverlap=128)
|
|
67
|
+
|
|
68
|
+
# print_ndarray_lovely(freqs) # frequency bins
|
|
69
|
+
# select all bins within 19 and 21 khz
|
|
70
|
+
freqs_to_use = np.less_equal(19000, freqs) & np.less_equal(freqs, 21000)
|
|
71
|
+
# example: 129 equally spaced values from 0 ... 24000
|
|
72
|
+
|
|
73
|
+
# print_ndarray_lovely(time) # time bins
|
|
74
|
+
# print_ndarray_lovely(spec) # spec shape (n_freq, n_time)
|
|
75
|
+
smin, smax = spec.min(), spec.max()
|
|
76
|
+
snorm = (spec - smin) / (smax - smin)
|
|
77
|
+
|
|
78
|
+
srel = snorm[freqs_to_use]
|
|
79
|
+
return srel.max() > eps
|
|
80
|
+
# now we want to normalize with something between mean (1-norm) and max (inf-norm)
|
|
81
|
+
# srel_norm_freq = np.linalg.norm(srel, ord=np.inf, axis=0)
|
|
82
|
+
# srel_norm_all = np.linalg.norm(srel_norm_freq, ord=np.inf, axis=0)
|
|
83
|
+
# return srel_norm_all
|
visiontext/bboxes.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
from typing import Dict, List, Optional, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from matplotlib import colors as mpl_colors
|
|
4
|
+
from PIL import Image, ImageDraw, ImageFont
|
|
5
|
+
|
|
6
|
+
from packg.maths import clip_rectangle_coords
|
|
7
|
+
from visiontext.colormaps import (
|
|
8
|
+
create_colorgetter_from_colormap,
|
|
9
|
+
create_colormap_for_dark_background,
|
|
10
|
+
)
|
|
11
|
+
from visiontext.font import get_dejavusans_font_path
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_bbox_images(
|
|
15
|
+
bx: int,
|
|
16
|
+
by: int,
|
|
17
|
+
bw: int,
|
|
18
|
+
bh: int,
|
|
19
|
+
w: int,
|
|
20
|
+
h: int,
|
|
21
|
+
box_color=(0, 0, 255, 255),
|
|
22
|
+
bbox_width: int = 5,
|
|
23
|
+
mask_opacity: int = 64,
|
|
24
|
+
) -> Dict[str, Image.Image]:
|
|
25
|
+
"""
|
|
26
|
+
Create box overlay images, originally used in the OVAD visualizer e.g.:
|
|
27
|
+
https://lmb.informatik.uni-freiburg.de/resources/datasets/ovad/?imgnum=14#top
|
|
28
|
+
|
|
29
|
+
This function creates transparent PNGs that can be overlayed on top of an image to show or
|
|
30
|
+
highlight a bounding box.
|
|
31
|
+
"""
|
|
32
|
+
# create one image with transparent background and one with half transparent
|
|
33
|
+
img = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
|
34
|
+
draw = ImageDraw.Draw(img)
|
|
35
|
+
img2 = Image.new("RGBA", (w, h), (0, 0, 0, mask_opacity))
|
|
36
|
+
draw2 = ImageDraw.Draw(img2)
|
|
37
|
+
|
|
38
|
+
# boxes should be approximately the same width after scaling the image
|
|
39
|
+
# so we have to scale the box thickness now
|
|
40
|
+
target_w = 800
|
|
41
|
+
rel_w = w / target_w # if small image, create smaller boxes
|
|
42
|
+
rel_bbox_width = round(bbox_width * rel_w)
|
|
43
|
+
|
|
44
|
+
# the inside of the thick drawn box will be the true bbox
|
|
45
|
+
# this means that the box can clip outside the image and become almost invisible
|
|
46
|
+
# to avoid that, we move the edges of the box outwards and then clip them
|
|
47
|
+
x1 = bx - rel_bbox_width
|
|
48
|
+
y1 = by - rel_bbox_width
|
|
49
|
+
x2 = bx + bw + rel_bbox_width
|
|
50
|
+
y2 = by + bh + rel_bbox_width
|
|
51
|
+
(x1, y1, x2, y2) = clip_rectangle_coords((x1, y1, x2, y2), w, h)
|
|
52
|
+
|
|
53
|
+
for bbox_step in range(rel_bbox_width):
|
|
54
|
+
rectangle_coords = (
|
|
55
|
+
x1 + bbox_step,
|
|
56
|
+
y1 + bbox_step,
|
|
57
|
+
x2 - bbox_step,
|
|
58
|
+
y2 - bbox_step,
|
|
59
|
+
)
|
|
60
|
+
# a 2nd clip should now not be necessary anymore and we assert instead
|
|
61
|
+
assert (
|
|
62
|
+
rectangle_coords[0] >= 0
|
|
63
|
+
and rectangle_coords[1] >= 0
|
|
64
|
+
and rectangle_coords[2] <= w
|
|
65
|
+
and rectangle_coords[3] <= h
|
|
66
|
+
), f"{rectangle_coords} outside {w}x{h}"
|
|
67
|
+
|
|
68
|
+
draw.rectangle(rectangle_coords, fill=(0, 0, 0, 0), outline=box_color)
|
|
69
|
+
draw2.rectangle(rectangle_coords, fill=(0, 0, 0, 0), outline=box_color)
|
|
70
|
+
|
|
71
|
+
return {"box": img, "box-mask": img2}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def convert_bbox_abs_to_rel(bx, by, bw, bh, image_w, image_h):
|
|
75
|
+
"""
|
|
76
|
+
Convert bounding box coordinates to relative coordinates.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
bx: x coordinate of the top-left corner of the bounding box
|
|
80
|
+
by: y coordinate of the top-left corner of the bounding box
|
|
81
|
+
bw: width of the bounding box
|
|
82
|
+
bh: height of the bounding box
|
|
83
|
+
image_w: width of the image
|
|
84
|
+
image_h: height of the image
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Tuple of relative coordinates (x, y, w, h)
|
|
88
|
+
|
|
89
|
+
"""
|
|
90
|
+
x = bx / image_w
|
|
91
|
+
y = by / image_h
|
|
92
|
+
w = bw / image_w
|
|
93
|
+
h = bh / image_h
|
|
94
|
+
return x, y, w, h
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def convert_bbox_rel_to_abs(x, y, w, h, image_w, image_h):
|
|
98
|
+
"""
|
|
99
|
+
Convert bounding box coordinates to absolute coordinates.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
x: x coordinate of the top-left corner of the bounding box
|
|
103
|
+
y: y coordinate of the top-left corner of the bounding box
|
|
104
|
+
w: width of the bounding box
|
|
105
|
+
h: height of the bounding box
|
|
106
|
+
image_w: width of the image
|
|
107
|
+
image_h: height of the image
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Tuple of absolute coordinates (bx, by, bw, bh)
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
bx = x * image_w
|
|
114
|
+
by = y * image_h
|
|
115
|
+
bw = w * image_w
|
|
116
|
+
bh = h * image_h
|
|
117
|
+
return bx, by, bw, bh
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_bbox_bounds(
|
|
121
|
+
box_x: float,
|
|
122
|
+
box_y: float,
|
|
123
|
+
box_w: float,
|
|
124
|
+
box_h: float,
|
|
125
|
+
image_w: int,
|
|
126
|
+
image_h: int,
|
|
127
|
+
min_w=32,
|
|
128
|
+
min_h=32,
|
|
129
|
+
create_squares=False,
|
|
130
|
+
):
|
|
131
|
+
"""
|
|
132
|
+
Get the bounding box boundaries for cropping the image.
|
|
133
|
+
Increase the box size if it is too small.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
box_x: absolute box coordinates for the given image
|
|
137
|
+
box_y:
|
|
138
|
+
box_w:
|
|
139
|
+
box_h:
|
|
140
|
+
image_w: image size
|
|
141
|
+
image_h:
|
|
142
|
+
min_w: minimum box size
|
|
143
|
+
min_h:
|
|
144
|
+
create_squares: if True, try to create a square bounding box instead of a rectangle
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Cropping parameters tuple (x1, y1, x2, y2)
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
# Calculate the center coordinates of the bounding box
|
|
151
|
+
center_x = box_x + (box_w / 2)
|
|
152
|
+
center_y = box_y + (box_h / 2)
|
|
153
|
+
|
|
154
|
+
# Calculate the new width and height of the bounding box
|
|
155
|
+
new_w = max(box_w, min_w)
|
|
156
|
+
new_h = max(box_h, min_h)
|
|
157
|
+
|
|
158
|
+
if create_squares:
|
|
159
|
+
new_w = max(new_w, new_h)
|
|
160
|
+
new_h = max(new_w, new_h)
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
# Calculate the new x and y coordinates of the top-left corner of the bounding box
|
|
164
|
+
new_x = center_x - (new_w / 2)
|
|
165
|
+
new_y = center_y - (new_h / 2)
|
|
166
|
+
|
|
167
|
+
# Adjust the new x coordinate if the box is too close to the left border
|
|
168
|
+
if new_x < 0:
|
|
169
|
+
new_x = 0
|
|
170
|
+
new_x2 = min(new_x + new_w, image_w)
|
|
171
|
+
else:
|
|
172
|
+
new_x2 = min(new_x + new_w, image_w)
|
|
173
|
+
if new_x2 > image_w:
|
|
174
|
+
new_x2 = image_w
|
|
175
|
+
new_x = max(new_x2 - new_w, 0)
|
|
176
|
+
|
|
177
|
+
# Adjust the new y coordinate if the box is too close to the top border
|
|
178
|
+
if new_y < 0:
|
|
179
|
+
new_y = 0
|
|
180
|
+
new_y2 = min(new_y + new_h, image_h)
|
|
181
|
+
else:
|
|
182
|
+
new_y2 = min(new_y + new_h, image_h)
|
|
183
|
+
if new_y2 > image_h:
|
|
184
|
+
new_y2 = image_h
|
|
185
|
+
new_y = max(new_y2 - new_h, 0)
|
|
186
|
+
|
|
187
|
+
# Again make sure all bounds are respected
|
|
188
|
+
new_x = max(new_x, 0)
|
|
189
|
+
new_y = max(new_y, 0)
|
|
190
|
+
new_x2 = min(new_x2, image_w)
|
|
191
|
+
new_y2 = min(new_y2, image_h)
|
|
192
|
+
|
|
193
|
+
# Return the cropping boundaries as integers
|
|
194
|
+
return int(new_x), int(new_y), int(new_x2), int(new_y2)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def draw_bounding_boxes_pil(
|
|
198
|
+
image: Image.Image,
|
|
199
|
+
boxes: List[Tuple[float, float, float, float]], # x, y, w, h
|
|
200
|
+
labels: Tuple[List[str], str, None] = "numbers",
|
|
201
|
+
colors: Optional[List[Tuple[int, int, int]]] = None,
|
|
202
|
+
width: int = 3,
|
|
203
|
+
font_size: int = 14,
|
|
204
|
+
cmap: Optional[Union[str, mpl_colors.Colormap]] = None,
|
|
205
|
+
):
|
|
206
|
+
draw = ImageDraw.Draw(image)
|
|
207
|
+
font = ImageFont.truetype(get_dejavusans_font_path(), font_size)
|
|
208
|
+
|
|
209
|
+
if colors is None:
|
|
210
|
+
if cmap is None:
|
|
211
|
+
cmap = create_colormap_for_dark_background(add_brightness=0.5)
|
|
212
|
+
# automatically generate a bunch of colors from the colormap
|
|
213
|
+
getter = create_colorgetter_from_colormap(cmap)
|
|
214
|
+
colors = [tuple(getter(i / len(boxes))) for i in range(len(boxes))]
|
|
215
|
+
|
|
216
|
+
if isinstance(labels, str):
|
|
217
|
+
if labels == "numbers":
|
|
218
|
+
labels = [str(i) for i in range(len(boxes))]
|
|
219
|
+
else:
|
|
220
|
+
raise ValueError(f"Unknown label type: {labels}")
|
|
221
|
+
|
|
222
|
+
for i, (box, color) in enumerate(zip(boxes, colors)):
|
|
223
|
+
x, y, w, h = box
|
|
224
|
+
x1, y1, x2, y2 = get_bbox_bounds(x, y, w, h, image.width, image.height, min_w=0, min_h=0)
|
|
225
|
+
draw.rectangle([(x1, y1), (x2, y2)], outline=color, width=width)
|
|
226
|
+
|
|
227
|
+
text_args = dict(font=font, stroke_width=2)
|
|
228
|
+
if labels is not None:
|
|
229
|
+
label = labels[i]
|
|
230
|
+
|
|
231
|
+
# # removed in pillow 10...
|
|
232
|
+
# text_width, text_height = draw.textsize(label, **text_args)
|
|
233
|
+
|
|
234
|
+
# new version with textbbox
|
|
235
|
+
tx0, ty0, tx1, ty1 = draw.textbbox((0, 0), label, **text_args)
|
|
236
|
+
text_width = tx1 - tx0
|
|
237
|
+
text_height = ty1 - ty0
|
|
238
|
+
|
|
239
|
+
text_x = min(x1 + width + 2, image.width - text_width)
|
|
240
|
+
text_y = min(y1 + width + 2, image.height - text_height)
|
|
241
|
+
draw.text(
|
|
242
|
+
(text_x, text_y),
|
|
243
|
+
label,
|
|
244
|
+
fill=color,
|
|
245
|
+
anchor="lt",
|
|
246
|
+
stroke_fill="black",
|
|
247
|
+
**text_args,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
return image
|
visiontext/cacheutils.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import lmdb
|
|
6
|
+
import msgpack
|
|
7
|
+
from platformdirs import user_cache_path
|
|
8
|
+
|
|
9
|
+
from packg.strings import hash_object
|
|
10
|
+
from packg.typext import PathType
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CachePlugin:
|
|
14
|
+
"""
|
|
15
|
+
Helper to create a cache for a class.
|
|
16
|
+
|
|
17
|
+
cache_name and cache_kwargs define a unique filename.
|
|
18
|
+
cache_kwargs must be jsonable
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
cache_name: str,
|
|
24
|
+
cache_kwargs: Optional[dict] = None,
|
|
25
|
+
cache_dir: Optional[PathType] = None,
|
|
26
|
+
map_size=10 * 1024**3, # 10 GB max size of database, increase if necessary
|
|
27
|
+
verbose: bool = False,
|
|
28
|
+
):
|
|
29
|
+
if cache_dir is None:
|
|
30
|
+
cache_dir = user_cache_path("python_visiontext")
|
|
31
|
+
if cache_kwargs is None:
|
|
32
|
+
kwargs_str = "none"
|
|
33
|
+
else:
|
|
34
|
+
kwargs_str = hash_object(cache_kwargs)
|
|
35
|
+
|
|
36
|
+
lmdb_file = Path(cache_dir) / "cache_plugin" / f"{cache_name}~{kwargs_str}.lmdb"
|
|
37
|
+
os.makedirs(lmdb_file.parent, exist_ok=True)
|
|
38
|
+
self.db: lmdb.Environment = lmdb.open(lmdb_file.as_posix(), map_size=map_size)
|
|
39
|
+
self.lmdb_file = lmdb_file
|
|
40
|
+
if verbose:
|
|
41
|
+
print(f"Using cache file: {self.lmdb_file}")
|
|
42
|
+
|
|
43
|
+
def get_values(self, list_of_keys):
|
|
44
|
+
outputs = []
|
|
45
|
+
with self.db.begin(write=False) as txn:
|
|
46
|
+
for key in list_of_keys:
|
|
47
|
+
bkey = msgpack.packb(key)
|
|
48
|
+
bvalue = txn.get(bkey)
|
|
49
|
+
if bvalue is None:
|
|
50
|
+
outputs.append(None)
|
|
51
|
+
else:
|
|
52
|
+
value = msgpack.unpackb(bvalue)
|
|
53
|
+
outputs.append(value)
|
|
54
|
+
return outputs
|
|
55
|
+
|
|
56
|
+
def put_values(self, list_of_keys, list_of_values):
|
|
57
|
+
assert len(list_of_keys) == len(
|
|
58
|
+
list_of_values
|
|
59
|
+
), f"Got {len(list_of_keys)} keys and {len(list_of_values)} values."
|
|
60
|
+
if len(list_of_keys) == 0:
|
|
61
|
+
return
|
|
62
|
+
with self.db.begin(write=True) as txn:
|
|
63
|
+
for key, value in zip(list_of_keys, list_of_values):
|
|
64
|
+
bkey = msgpack.packb(key)
|
|
65
|
+
bvalue = msgpack.packb(value)
|
|
66
|
+
txn.put(bkey, bvalue)
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# source: github lightning-ai pytorch-lightning
|
|
2
|
+
#
|
|
3
|
+
# Copyright The Lightning AI team.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
"""Diagnose your system and show basic information.
|
|
17
|
+
|
|
18
|
+
This server mainly to get detail info for better bug reporting.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import os
|
|
23
|
+
import platform
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
import pkg_resources
|
|
27
|
+
import torch
|
|
28
|
+
|
|
29
|
+
sys.path += [os.path.abspath(".."), os.path.abspath("")]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
LEVEL_OFFSET = "\t"
|
|
33
|
+
KEY_PADDING = 20
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def info_system() -> dict:
|
|
37
|
+
return {
|
|
38
|
+
"OS": platform.system(),
|
|
39
|
+
"architecture": platform.architecture(),
|
|
40
|
+
"version": platform.version(),
|
|
41
|
+
"release": platform.release(),
|
|
42
|
+
"processor": platform.processor(),
|
|
43
|
+
"python": platform.python_version(),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def info_cuda() -> dict:
|
|
48
|
+
return {
|
|
49
|
+
"GPU": [torch.cuda.get_device_name(i) for i in range(torch.cuda.device_count())] or None,
|
|
50
|
+
"available": torch.cuda.is_available(),
|
|
51
|
+
"version": torch.version.cuda,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def info_packages() -> dict:
|
|
56
|
+
"""Get name and version of all installed packages."""
|
|
57
|
+
packages = {}
|
|
58
|
+
for dist in pkg_resources.working_set:
|
|
59
|
+
package = dist.as_requirement()
|
|
60
|
+
packages[package.key] = package.specs[0][1]
|
|
61
|
+
return packages
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def nice_print(details: dict, level: int = 0) -> list:
|
|
65
|
+
lines = []
|
|
66
|
+
for k in sorted(details):
|
|
67
|
+
key = f"* {k}:" if level == 0 else f"- {k}:"
|
|
68
|
+
if isinstance(details[k], dict):
|
|
69
|
+
lines += [level * LEVEL_OFFSET + key]
|
|
70
|
+
lines += nice_print(details[k], level + 1)
|
|
71
|
+
elif isinstance(details[k], (set, list, tuple)):
|
|
72
|
+
lines += [level * LEVEL_OFFSET + key]
|
|
73
|
+
lines += [(level + 1) * LEVEL_OFFSET + "- " + v for v in details[k]]
|
|
74
|
+
else:
|
|
75
|
+
template = "{:%is} {}" % KEY_PADDING
|
|
76
|
+
key_val = template.format(key, details[k])
|
|
77
|
+
lines += [(level * LEVEL_OFFSET) + key_val]
|
|
78
|
+
return lines
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def main() -> None:
|
|
82
|
+
details = {"System": info_system(), "CUDA": info_cuda(), "Packages": info_packages()}
|
|
83
|
+
details["Lightning"] = {
|
|
84
|
+
k: v for k, v in details["Packages"].items() if "torch" in k or "lightning" in k
|
|
85
|
+
}
|
|
86
|
+
lines = nice_print(details)
|
|
87
|
+
text = os.linesep.join(lines)
|
|
88
|
+
print(f"<details>\n <summary>Current environment</summary>\n\n{text}\n\n</details>")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
main()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from timeit import default_timer
|
|
3
|
+
|
|
4
|
+
from visiontext.examples import cc0images
|
|
5
|
+
from visiontext.images import JPEGDecoderConst, decode_jpeg
|
|
6
|
+
|
|
7
|
+
image_shapes_highres = [(5184, 3456, 3), (2038, 3057, 3), (2560, 2048, 3)]
|
|
8
|
+
image_shapes_lowres = [(500, 333, 3), (333, 500, 3), (500, 400, 3)]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
src_dir_base = Path(cc0images.__file__).parent
|
|
13
|
+
|
|
14
|
+
for src_dir, image_shapes, n_trials in [
|
|
15
|
+
(src_dir_base, image_shapes_highres, 2),
|
|
16
|
+
(src_dir_base / "lowres", image_shapes_lowres, 100),
|
|
17
|
+
]:
|
|
18
|
+
print(f"Test images in {src_dir}")
|
|
19
|
+
images_f = list(sorted(src_dir.glob("*.jpg")))
|
|
20
|
+
|
|
21
|
+
if len(images_f) != len(image_shapes):
|
|
22
|
+
raise ValueError(
|
|
23
|
+
f"Expected {len(image_shapes)} images in {src_dir}, got {len(images_f)}"
|
|
24
|
+
)
|
|
25
|
+
images_b = [Path(image_f).read_bytes() for image_f in images_f]
|
|
26
|
+
|
|
27
|
+
for decoder in JPEGDecoderConst.values():
|
|
28
|
+
if decoder == JPEGDecoderConst.PILLOW_IMAGE:
|
|
29
|
+
continue
|
|
30
|
+
t0 = default_timer()
|
|
31
|
+
for n in range(n_trials):
|
|
32
|
+
for i, image_b in enumerate(images_b):
|
|
33
|
+
arr = decode_jpeg(image_b, method=decoder)
|
|
34
|
+
if n == 0:
|
|
35
|
+
# only test shape for first trial to not disturb the performance test
|
|
36
|
+
ex_s = image_shapes[i]
|
|
37
|
+
act_s = arr.shape
|
|
38
|
+
if ex_s != act_s:
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"Expected shape {ex_s} for {images_f[i]}, got {act_s}"
|
|
41
|
+
)
|
|
42
|
+
tdelta = default_timer() - t0
|
|
43
|
+
tper_ms = (tdelta / n_trials / len(images_f)) * 1000
|
|
44
|
+
print(f" Decoder {decoder} took {tper_ms:.2f} ms per image on average")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
download a dataset from huggingface.
|
|
3
|
+
|
|
4
|
+
potentially useful args:
|
|
5
|
+
-o name=DatasetConfigName
|
|
6
|
+
-o use_auth_token=True
|
|
7
|
+
-o split="train"
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from attrs import define
|
|
13
|
+
from loguru import logger
|
|
14
|
+
|
|
15
|
+
from packg.log import SHORTEST_FORMAT, configure_logger, get_logger_level_from_args
|
|
16
|
+
from packg.paths import get_packg_data_dir
|
|
17
|
+
from typedparser import TypedParser, VerboseQuietArgs, add_argument
|
|
18
|
+
from visiontext.configutils import load_dotlist
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@define
|
|
22
|
+
class Args(VerboseQuietArgs):
|
|
23
|
+
base_dir: Path | None = add_argument(
|
|
24
|
+
shortcut="-d", type=str, help="Base dir", default=get_packg_data_dir()
|
|
25
|
+
)
|
|
26
|
+
dataset_name: str = add_argument(positional=True, type=str, help="Dataset name")
|
|
27
|
+
options: list[str] | None = add_argument(shortcut="-o", action="append", help="dataset kwargs")
|
|
28
|
+
subdir: str | None = add_argument(
|
|
29
|
+
shortcut="-s", type=str, help="Subdir (none=auto)", default=None
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main():
|
|
34
|
+
parser = TypedParser.create_parser(Args, description=__doc__)
|
|
35
|
+
args: Args = parser.parse_args()
|
|
36
|
+
configure_logger(level=get_logger_level_from_args(args), format=SHORTEST_FORMAT)
|
|
37
|
+
logger.info(f"{args}")
|
|
38
|
+
|
|
39
|
+
dict_dotlist = load_dotlist(args.options)
|
|
40
|
+
subdir = args.subdir
|
|
41
|
+
if subdir is None:
|
|
42
|
+
subdir = args.dataset_name.replace("/", "__")
|
|
43
|
+
|
|
44
|
+
logger.info(f"Downloading dataset {args.dataset_name} to {args.base_dir / subdir}")
|
|
45
|
+
try:
|
|
46
|
+
from datasets import load_dataset
|
|
47
|
+
except ImportError as e:
|
|
48
|
+
logger.error(f"datasets is not installed. Please install it with `pip install datasets`.")
|
|
49
|
+
raise e
|
|
50
|
+
dataset = load_dataset(
|
|
51
|
+
args.dataset_name,
|
|
52
|
+
streaming=False, # this downloads to disk
|
|
53
|
+
cache_dir=(args.base_dir / subdir).as_posix(),
|
|
54
|
+
**dict_dotlist,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
main()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sqlite3
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from attrs import define
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from packg.log import SHORTEST_FORMAT, configure_logger, get_logger_level_from_args
|
|
10
|
+
from typedparser import TypedParser, VerboseQuietArgs, add_argument
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@define
|
|
14
|
+
class Args(VerboseQuietArgs):
|
|
15
|
+
sqlite_file: Path = add_argument(positional=True, help="sqlite file")
|
|
16
|
+
run_export: bool = add_argument(action="store_true", help="Run the export", shortcut="-e")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
parser = TypedParser.create_parser(Args, description=__doc__)
|
|
21
|
+
args: Args = parser.parse_args()
|
|
22
|
+
configure_logger(level=get_logger_level_from_args(args), format=SHORTEST_FORMAT)
|
|
23
|
+
logger.info(f"{args}")
|
|
24
|
+
|
|
25
|
+
conn = sqlite3.connect(args.sqlite_file)
|
|
26
|
+
|
|
27
|
+
tables = pd.read_sql_query("SELECT name FROM sqlite_master WHERE type='table';", conn)
|
|
28
|
+
print("Tables in the database:")
|
|
29
|
+
print(tables)
|
|
30
|
+
|
|
31
|
+
# For each table, display the first few rows
|
|
32
|
+
for table_name in tables["name"]:
|
|
33
|
+
print(f"\nTable: {table_name}")
|
|
34
|
+
df = pd.read_sql_query(f"SELECT * FROM {table_name} LIMIT 5;", conn)
|
|
35
|
+
print(df)
|
|
36
|
+
cursor = conn.execute(f"SELECT COUNT(*) FROM {table_name}")
|
|
37
|
+
count = cursor.fetchone()[0]
|
|
38
|
+
print(f"Count: {count}")
|
|
39
|
+
|
|
40
|
+
if args.run_export:
|
|
41
|
+
export_dir = Path(f"{args.sqlite_file}_export")
|
|
42
|
+
if export_dir.is_dir():
|
|
43
|
+
logger.warning(f"Export directory already exists: {export_dir}")
|
|
44
|
+
os.makedirs(export_dir, exist_ok=True)
|
|
45
|
+
for table_name in tables["name"]:
|
|
46
|
+
print(f"\nTable: {table_name}")
|
|
47
|
+
df = pd.read_sql_query(f"SELECT * FROM {table_name}", conn)
|
|
48
|
+
print(df.shape)
|
|
49
|
+
csv_file = export_dir / f"{table_name}.csv"
|
|
50
|
+
df.to_csv(csv_file, sep=",", index=False)
|
|
51
|
+
print(f"Exported to: {csv_file}")
|
|
52
|
+
|
|
53
|
+
conn.close()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
if __name__ == "__main__":
|
|
57
|
+
main()
|