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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.3.0
3
+ Version: 3.4.1
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -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, radius, radius)
211
+ center = (radius,) * ndims
212
212
  imset_kwargs['convention'] = 'center'
213
213
  return_image = True
214
-
215
- norm = lambda v: np.sqrt(sum(v**2))
216
- perpendicular = np.array(perpendicular)
217
- if eq(norm(perpendicular), 0):
218
- print('WARNING: Perpendicular had 0 length. No circle was drawn.')
219
- perpendicular = perpendicular / norm(perpendicular)
220
-
221
- ax = np.array((1,) + (0,) * (ndims-1))
222
- v0 = np.cross(ax, perpendicular)
223
- if eq(norm(v0), 0):
224
- v0 = np.cross(np.flip(ax), perpendicular)
225
- v1 = np.cross(v0, perpendicular)
226
- v0 = v0 / norm(v0)
227
- v1 = v1 / norm(v1)
228
-
229
- num_pts = iceil(math.pi*radius/spacing) + 1
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 convert_to_8bit and data.dtype != np.uint8:
414
- data = operations.to_8bit(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, isint, find_channel_axis
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 isinstance(factor, int):
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 isint(im2_offset):
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 [isinstance(a, int) or np.issubdtype(type(a), np.integer)
56
- for a in n]
71
+ return [np.issubdtype(type(a), np.integer) for a in n]
57
72
  except TypeError:
58
- return isinstance(n, int) or np.issubdtype(type(n), np.integer)
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 isinstance(possible_channel_axes, int):
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 isinstance(possible_channel_lengths, int):
156
+ if np.issubdtype(type(possible_channel_lengths), np.integer):
142
157
  possible_channel_lengths = [possible_channel_lengths]
143
158
 
144
- if isinstance(minimum_image_size, int):
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 isinstance(index['framerate'], int):
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 isinstance(key, tuple):
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: numpyimage
3
- Version: 3.3.0
3
+ Version: 3.4.1
4
4
  Summary: Load, save, & manipulate image files as numpy arrays
5
5
  Author-email: Jasper Phelps <jasper.s.phelps@gmail.com>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
4
4
 
5
5
  [project]
6
6
  name = 'numpyimage'
7
- version = '3.3.0'
7
+ version = '3.4.1'
8
8
  description = 'Load, save, & manipulate image files as numpy arrays'
9
9
  readme.file = 'README.md'
10
10
  readme.content-type = 'text/markdown'
File without changes
File without changes
File without changes
File without changes
File without changes