numpyimage 3.3.0__tar.gz → 3.4.1__tar.gz
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.
- {numpyimage-3.3.0/numpyimage.egg-info → numpyimage-3.4.1}/PKG-INFO +1 -1
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/graphics.py +31 -18
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/imageio.py +10 -3
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/operations.py +3 -3
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/utils.py +22 -7
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/vidio.py +23 -12
- {numpyimage-3.3.0 → numpyimage-3.4.1/numpyimage.egg-info}/PKG-INFO +1 -1
- {numpyimage-3.3.0 → numpyimage-3.4.1}/pyproject.toml +1 -1
- {numpyimage-3.3.0 → numpyimage-3.4.1}/LICENSE +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/README.md +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/__init__.py +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/align.py +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/npimage/nrrd_utils.py +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/numpyimage.egg-info/SOURCES.txt +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/numpyimage.egg-info/dependency_links.txt +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/numpyimage.egg-info/requires.txt +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/numpyimage.egg-info/top_level.txt +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/setup.cfg +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/tests/test_heic.py +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/tests/test_pbm.py +0 -0
- {numpyimage-3.3.0 → numpyimage-3.4.1}/tests/test_video_writers.py +0 -0
|
@@ -204,29 +204,42 @@ def drawcircle(image, center, perpendicular, radius, value,
|
|
|
204
204
|
- out_of_bounds: Literal['ignore', 'wrap', 'raise'] = 'ignore'
|
|
205
205
|
- add_missing_dims: Literal[None, 'start', 'end'] = None
|
|
206
206
|
"""
|
|
207
|
-
ndims = 3 # TODO code up logic for determining this from the input
|
|
208
207
|
return_image = False
|
|
209
208
|
if image is None and center is None:
|
|
209
|
+
ndims = len(perpendicular) if perpendicular is not None else 2
|
|
210
210
|
image = np.zeros((2*iceil(radius) + 1,)*ndims, dtype=np.uint8)
|
|
211
|
-
center = (radius,
|
|
211
|
+
center = (radius,) * ndims
|
|
212
212
|
imset_kwargs['convention'] = 'center'
|
|
213
213
|
return_image = True
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
214
|
+
if image is None:
|
|
215
|
+
raise ValueError("Specifying center without image doesn't make sense.")
|
|
216
|
+
ndims = image.ndim
|
|
217
|
+
|
|
218
|
+
if ndims == 1:
|
|
219
|
+
raise ValueError("Can't draw a circle on 1D array.")
|
|
220
|
+
elif ndims == 2:
|
|
221
|
+
if perpendicular is not None:
|
|
222
|
+
print('[npimage.drawcircle] WARNING: perpendicular ignored since ndims is 2.')
|
|
223
|
+
v0 = np.array((1, 0))
|
|
224
|
+
v1 = np.array((0, 1))
|
|
225
|
+
elif perpendicular is None:
|
|
226
|
+
raise ValueError("Must specify perpendicular for ndims > 2")
|
|
227
|
+
else:
|
|
228
|
+
norm = lambda v: np.sqrt(sum(v**2))
|
|
229
|
+
perpendicular = np.array(perpendicular)
|
|
230
|
+
if eq(norm(perpendicular), 0):
|
|
231
|
+
print('WARNING: Perpendicular had 0 length. No circle was drawn.')
|
|
232
|
+
perpendicular = perpendicular / norm(perpendicular)
|
|
233
|
+
|
|
234
|
+
ax = np.array((1,) + (0,) * (ndims-1))
|
|
235
|
+
v0 = np.cross(ax, perpendicular)
|
|
236
|
+
if eq(norm(v0), 0):
|
|
237
|
+
v0 = np.cross(np.flip(ax), perpendicular)
|
|
238
|
+
v1 = np.cross(v0, perpendicular)
|
|
239
|
+
v0 = v0 / norm(v0)
|
|
240
|
+
v1 = v1 / norm(v1)
|
|
241
|
+
|
|
242
|
+
num_pts = iceil(math.pi * radius / spacing) + 1
|
|
230
243
|
angles = np.linspace(0, math.pi, num_pts)
|
|
231
244
|
half1 = (np.outer(np.cos(angles), v0)
|
|
232
245
|
+ np.outer(np.sin(angles), v1)) * radius + center
|
|
@@ -367,7 +367,8 @@ def show(data,
|
|
|
367
367
|
dim_order='yx',
|
|
368
368
|
data_type: Literal['image', 'segmentation'] = 'image',
|
|
369
369
|
mode: Literal['PIL', 'mpl'] = 'PIL',
|
|
370
|
-
convert_to_8bit=True,
|
|
370
|
+
convert_to_8bit: bool = True,
|
|
371
|
+
convert_kwargs: dict = {'maximize_contrast': True},
|
|
371
372
|
channel_axis='guess',
|
|
372
373
|
**kwargs) -> None:
|
|
373
374
|
"""
|
|
@@ -410,8 +411,14 @@ def show(data,
|
|
|
410
411
|
if utils.isint(channel_axis) and channel_axis != -1:
|
|
411
412
|
data = np.moveaxis(data, utils.find_channel_axis(data), -1)
|
|
412
413
|
|
|
413
|
-
if
|
|
414
|
-
data =
|
|
414
|
+
if data.dtype == np.bool:
|
|
415
|
+
data = data.astype(np.uint8) * 255
|
|
416
|
+
elif convert_to_8bit and data.dtype != np.uint8:
|
|
417
|
+
if any(key in convert_kwargs for key in
|
|
418
|
+
['bottom_value', 'bottom_percentile', 'top_value', 'top_percentile']):
|
|
419
|
+
if 'maximize_contrast' not in convert_kwargs:
|
|
420
|
+
convert_kwargs['maximize_contrast'] = True
|
|
421
|
+
data = operations.to_8bit(data, **convert_kwargs)
|
|
415
422
|
|
|
416
423
|
colorbar = False
|
|
417
424
|
if 'colorbar' in kwargs:
|
|
@@ -16,7 +16,7 @@ from typing import Iterable, Literal, Union, Optional, Tuple, List
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
from tqdm import tqdm
|
|
18
18
|
|
|
19
|
-
from .utils import iround, eq,
|
|
19
|
+
from .utils import iround, eq, find_channel_axis
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def squeeze_dtype(image: np.ndarray, minimum_bits=1):
|
|
@@ -266,7 +266,7 @@ def downsample(image: np.ndarray,
|
|
|
266
266
|
array([[3.5, 5.5]])
|
|
267
267
|
"""
|
|
268
268
|
channel_axis = find_channel_axis(image)
|
|
269
|
-
if
|
|
269
|
+
if np.issubdtype(type(factor), np.integer):
|
|
270
270
|
if channel_axis is not None:
|
|
271
271
|
# If RGB/RGBA image, don't downsample the colors axis
|
|
272
272
|
factor = (factor,) * (len(image.shape) - 1)
|
|
@@ -617,7 +617,7 @@ def overlay_two_images(im1: np.ndarray,
|
|
|
617
617
|
"""
|
|
618
618
|
Overlay two images, with the second image offset by the given amount.
|
|
619
619
|
"""
|
|
620
|
-
if
|
|
620
|
+
if any(np.issubdtype(type(im2_offset), t) for t in [np.integer, np.floating]):
|
|
621
621
|
im2_offset = [im2_offset] * im2.ndim
|
|
622
622
|
if expand_bounds:
|
|
623
623
|
offsets = ([max(0, -o) for o in im2_offset],
|
|
@@ -49,13 +49,28 @@ def eq(a, b, tolerance=1e-8):
|
|
|
49
49
|
return abs(a - b) < tolerance
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
#TODO duck type this to accept a more general class of objects that extend ints
|
|
53
52
|
def isint(n):
|
|
53
|
+
"""
|
|
54
|
+
Check whether the given variable is an integer (either a built-in
|
|
55
|
+
int or a numpy integer type). If the argument is iterable, return
|
|
56
|
+
a list of booleans indicating whether each element is an integer
|
|
57
|
+
(again either a built-in int or a numpy integer type).
|
|
58
|
+
|
|
59
|
+
Notes
|
|
60
|
+
-----
|
|
61
|
+
You might think that isinstance(n, int) is what you want, but that
|
|
62
|
+
has the undesirable behavior of returning True for bools, and
|
|
63
|
+
returning False for numpy integer types.
|
|
64
|
+
To check whether n is a built-in int or np.integer _but not an iterable
|
|
65
|
+
of them_, use `if npimage.isint(n) is True`, or just
|
|
66
|
+
call `if np.issubdtype(type(n), np.integer)` yourself.
|
|
67
|
+
This function does NOT recognize integer types from other packages
|
|
68
|
+
including TensorFlow, PyTorch, or JAX.
|
|
69
|
+
"""
|
|
54
70
|
try:
|
|
55
|
-
return [
|
|
56
|
-
for a in n]
|
|
71
|
+
return [np.issubdtype(type(a), np.integer) for a in n]
|
|
57
72
|
except TypeError:
|
|
58
|
-
return
|
|
73
|
+
return np.issubdtype(type(n), np.integer)
|
|
59
74
|
|
|
60
75
|
|
|
61
76
|
def find_channel_axis(data,
|
|
@@ -133,15 +148,15 @@ def find_channel_axis(data,
|
|
|
133
148
|
>>> npimage.find_channel_axis(np.empty((30, 4)), minimum_image_size=0)
|
|
134
149
|
1
|
|
135
150
|
"""
|
|
136
|
-
if
|
|
151
|
+
if np.issubdtype(type(possible_channel_axes), np.integer):
|
|
137
152
|
possible_channel_axes = [possible_channel_axes]
|
|
138
153
|
if possible_channel_axes is None:
|
|
139
154
|
possible_channel_axes = range(data.ndim)
|
|
140
155
|
|
|
141
|
-
if
|
|
156
|
+
if np.issubdtype(type(possible_channel_lengths), np.integer):
|
|
142
157
|
possible_channel_lengths = [possible_channel_lengths]
|
|
143
158
|
|
|
144
|
-
if
|
|
159
|
+
if np.issubdtype(type(minimum_image_size), np.integer):
|
|
145
160
|
minimum_image_size = (minimum_image_size,)
|
|
146
161
|
|
|
147
162
|
for axis in possible_channel_axes:
|
|
@@ -280,7 +280,7 @@ class VideoStreamer:
|
|
|
280
280
|
self.frames_pts = index['frames_pts']
|
|
281
281
|
else:
|
|
282
282
|
self.pts0 = index['pts0']
|
|
283
|
-
if
|
|
283
|
+
if np.issubdtype(type(index['framerate']), np.integer):
|
|
284
284
|
self._framerate = index['framerate']
|
|
285
285
|
else:
|
|
286
286
|
self._framerate = Fraction(index['framerate']['numerator'],
|
|
@@ -343,15 +343,29 @@ class VideoStreamer:
|
|
|
343
343
|
return (pts - self.pts0) // self.pts_delta
|
|
344
344
|
|
|
345
345
|
def __getitem__(self, key) -> np.ndarray:
|
|
346
|
-
if
|
|
347
|
-
frame_idx = key[0]
|
|
348
|
-
frame = self._get_frame(frame_idx)
|
|
349
|
-
if len(key) == 1:
|
|
350
|
-
return frame
|
|
351
|
-
else:
|
|
352
|
-
return frame[key[1:]]
|
|
353
|
-
else:
|
|
346
|
+
if np.issubdtype(type(key), np.integer):
|
|
354
347
|
return self._get_frame(key)
|
|
348
|
+
if isinstance(key, slice): # Support slicing
|
|
349
|
+
key = (key,) # Logic is handled in the tuple case below
|
|
350
|
+
if not isinstance(key, tuple):
|
|
351
|
+
raise TypeError('Key must be an int, slice, or a tuple of ints/slices')
|
|
352
|
+
|
|
353
|
+
frame_idx = key[0]
|
|
354
|
+
if np.issubdtype(type(frame_idx), np.integer):
|
|
355
|
+
frames = self._get_frame(frame_idx)
|
|
356
|
+
key = key[1:]
|
|
357
|
+
elif isinstance(frame_idx, slice): # Support slicing
|
|
358
|
+
start, stop, step = frame_idx.indices(self.n_frames)
|
|
359
|
+
frames = np.array([self._get_frame(i) for i in range(start, stop, step)])
|
|
360
|
+
key = (slice(None),) + key[1:]
|
|
361
|
+
elif isinstance(frame_idx, (list, tuple, np.ndarray)): # Support sequences of ints
|
|
362
|
+
if not all(utils.isint(frame_idx)):
|
|
363
|
+
raise TypeError('Sequences of frame indices must contain only integers')
|
|
364
|
+
frames = np.array([self._get_frame(i) for i in frame_idx])
|
|
365
|
+
key = (slice(None),) + key[1:]
|
|
366
|
+
else:
|
|
367
|
+
raise TypeError("Key's first element must be an int, slice, or sequence of ints")
|
|
368
|
+
return frames[key]
|
|
355
369
|
|
|
356
370
|
def _get_frame(self, frame_number) -> np.ndarray:
|
|
357
371
|
"""
|
|
@@ -390,9 +404,6 @@ class VideoStreamer:
|
|
|
390
404
|
f'{target_pts}). Last seen frame was {self._current_frame_number}'
|
|
391
405
|
f' (PTS {self.frame_number_to_pts(self._current_frame_number)}')
|
|
392
406
|
|
|
393
|
-
if isinstance(frame_number, slice): # Support slicing
|
|
394
|
-
start, stop, step = frame_number.indices(self.n_frames)
|
|
395
|
-
return np.array([self._get_frame(i) for i in range(start, stop, step)])
|
|
396
407
|
with self._lock:
|
|
397
408
|
frame_number = self._normalize_frame_number(frame_number)
|
|
398
409
|
if (self._current_frame_number is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|