impy-array 2.4.7__py3-none-any.whl → 2.4.9__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.
impy/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "2.4.7"
1
+ __version__ = "2.4.9"
2
2
  __author__ = "Hanjin Liu"
3
3
  __email__ = "liuhanjin-sc@g.ecc.u-tokyo.ac.jp"
4
4
 
impy/_const.py CHANGED
@@ -8,13 +8,13 @@ MAX_GB_LIMIT = memory.total / 2 * 1e-9
8
8
 
9
9
  class GlobalConstant(MutableMapping[str, Any]):
10
10
  _const: dict[str, Any]
11
-
11
+
12
12
  def __init__(self, **kwargs):
13
13
  object.__setattr__(self, "_const", dict(**kwargs))
14
-
14
+
15
15
  def __len__(self) -> int:
16
16
  return len(self._const)
17
-
17
+
18
18
  def __iter__(self):
19
19
  raise StopIteration
20
20
 
@@ -47,15 +47,15 @@ class GlobalConstant(MutableMapping[str, Any]):
47
47
  dask.config.set(scheduler=v)
48
48
  else:
49
49
  raise RuntimeError("Cannot set new keys.")
50
-
50
+
51
51
  self._const[k] = v
52
-
52
+
53
53
  __getattr__ = __getitem__
54
54
  __setattr__ = __setitem__
55
55
 
56
56
  def __delitem__(self, v):
57
57
  raise RuntimeError("Cannot delete any items.")
58
-
58
+
59
59
  def __repr__(self):
60
60
  return (
61
61
  f"""
@@ -65,12 +65,12 @@ class GlobalConstant(MutableMapping[str, Any]):
65
65
  SCHEDULER : {self['SCHEDULER']}
66
66
  """
67
67
  )
68
-
68
+
69
69
  def asdict(self) -> dict[str, Any]:
70
70
  return self._const.copy()
71
71
 
72
72
  Const = GlobalConstant(
73
- MAX_GB = MAX_GB_LIMIT/2,
73
+ MAX_GB = MAX_GB_LIMIT * 0.8,
74
74
  ID_AXIS = "N",
75
75
  RESOURCE = "numpy",
76
76
  SCHEDULER = "threads",
@@ -80,12 +80,12 @@ class SetConst:
80
80
  n_ongoing = 0
81
81
  _locked_keys: set[str] = set()
82
82
  _old_dict: dict[str, Any] = dict()
83
-
83
+
84
84
  def __init__(self, dict_: dict[str, Any] | None =None, **kwargs):
85
85
  dict_ = dict_ or {}
86
86
  dict_.update(kwargs)
87
87
  self._kwargs = dict_
88
-
88
+
89
89
  def __enter__(self):
90
90
  self.__class__.n_ongoing += 1
91
91
  if self.__class__.n_ongoing == 1:
@@ -52,6 +52,8 @@ binary_closing = get_func("binary_closing")
52
52
  closing = get_func("grey_closing")
53
53
  gaussian_filter = get_func("gaussian_filter")
54
54
  median_filter = get_func("median_filter")
55
+ min_filter = get_func("minimum_filter")
56
+ max_filter = get_func("maximum_filter")
55
57
  spline_filter = get_func("spline_filter")
56
58
  convolve = get_func("convolve")
57
59
  white_tophat = get_func("white_tophat")
@@ -69,6 +71,8 @@ if TYPE_CHECKING:
69
71
  closing = scipy_ndi.grey_closing
70
72
  gaussian_filter = scipy_ndi.gaussian_filter
71
73
  median_filter = scipy_ndi.median_filter
74
+ min_filter = scipy_ndi.minimum_filter
75
+ max_filter = scipy_ndi.maximum_filter
72
76
  spline_filter = scipy_ndi.spline_filter
73
77
  convolve = scipy_ndi.convolve
74
78
  white_tophat = scipy_ndi.white_tophat
impy/arrays/imgarray.py CHANGED
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
  import warnings
3
+ from functools import partial
4
+ from itertools import product
5
+ from typing import TYPE_CHECKING, Literal, Sequence, Iterable, Callable, overload, Any
6
+
3
7
  import numpy as np
4
8
  from numpy.typing import DTypeLike
5
- from functools import partial
6
9
  from scipy import ndimage as ndi
7
- from typing import TYPE_CHECKING, Literal, Sequence, Iterable, Callable, overload, Any
8
10
 
9
11
  from impy.arrays.labeledarray import LabeledArray
10
12
  from impy.arrays.label import Label
@@ -706,8 +708,7 @@ class ImgArray(LabeledArray):
706
708
  @dims_to_spatial_axes
707
709
  @check_input_and_output
708
710
  def structure_tensor_eig(self, sigma: nDFloat = 1, *, dims: Dims = None)-> tuple[ImgArray, ImgArray]:
709
- """
710
- Calculate structure tensor's eigenvalues and eigenvectors.
711
+ """Calculate structure tensor's eigenvalues and eigenvectors.
711
712
 
712
713
  Parameters
713
714
  ----------
@@ -747,8 +748,7 @@ class ImgArray(LabeledArray):
747
748
  dims: Dims = None,
748
749
  update: bool = False
749
750
  ) -> ImgArray:
750
- """
751
- Sobel filter. This filter is useful for edge detection.
751
+ """Edge detection such as Sobel filter.
752
752
 
753
753
  Parameters
754
754
  ----------
@@ -783,8 +783,7 @@ class ImgArray(LabeledArray):
783
783
  dims: Dims = None,
784
784
  update: bool = False,
785
785
  ) -> ImgArray:
786
- """
787
- Butterworth low-pass filter.
786
+ """Butterworth low-pass filter.
788
787
 
789
788
  Parameters
790
789
  ----------
@@ -829,9 +828,10 @@ class ImgArray(LabeledArray):
829
828
  dims: Dims = None,
830
829
  update: bool = False
831
830
  ) -> ImgArray:
832
- """
833
- Butterworth low-pass filter in real space. Butterworth kernel is created first using inverse
834
- Fourier transform of weight function.
831
+ """Butterworth low-pass filter in real space.
832
+
833
+ Butterworth kernel is created first using inverse Fourier transform of weight
834
+ function.
835
835
 
836
836
  Parameters
837
837
  ----------
@@ -874,8 +874,7 @@ class ImgArray(LabeledArray):
874
874
  dims: Dims = None,
875
875
  update: bool = False
876
876
  ) -> ImgArray:
877
- """
878
- Butterworth high-pass filter.
877
+ """Butterworth high-pass filter.
879
878
 
880
879
  Parameters
881
880
  ----------
@@ -916,8 +915,7 @@ class ImgArray(LabeledArray):
916
915
  dims: Dims = None,
917
916
  update: bool = False
918
917
  ):
919
- """
920
- Butterworth band-pass filter.
918
+ """Butterworth band-pass filter.
921
919
 
922
920
  Parameters
923
921
  ----------
@@ -961,8 +959,7 @@ class ImgArray(LabeledArray):
961
959
  dims: Dims = None,
962
960
  update: bool = False,
963
961
  ) -> ImgArray:
964
- """
965
- General linear convolution by running kernel filtering.
962
+ """General linear convolution by running kernel filtering.
966
963
 
967
964
  Parameters
968
965
  ----------
@@ -996,8 +993,7 @@ class ImgArray(LabeledArray):
996
993
  dims: Dims = None,
997
994
  update: bool = False,
998
995
  ) -> ImgArray:
999
- """
1000
- Morphological erosion.
996
+ """Morphological erosion.
1001
997
 
1002
998
  If input is binary image, the running function will automatically switched to
1003
999
  ``binary_erosion`` to speed up calculation.
@@ -1037,8 +1033,9 @@ class ImgArray(LabeledArray):
1037
1033
  dims: Dims = None,
1038
1034
  update: bool = False,
1039
1035
  ) -> ImgArray:
1040
- """
1041
- Morphological dilation. If input is binary image, the running function will automatically switched to
1036
+ """Morphological dilation.
1037
+
1038
+ If input is binary image, the running function will automatically switched to
1042
1039
  ``binary_dilation`` to speed up calculation.
1043
1040
 
1044
1041
  Parameters
@@ -1076,8 +1073,9 @@ class ImgArray(LabeledArray):
1076
1073
  dims: Dims = None,
1077
1074
  update: bool = False,
1078
1075
  ) -> ImgArray:
1079
- """
1080
- Morphological opening. If input is binary image, the running function will automatically switched to
1076
+ """Morphological opening.
1077
+
1078
+ If input is binary image, the running function will automatically switched to
1081
1079
  ``binary_opening`` to speed up calculation.
1082
1080
 
1083
1081
  Parameters
@@ -1115,8 +1113,7 @@ class ImgArray(LabeledArray):
1115
1113
  dims: Dims = None,
1116
1114
  update: bool = False,
1117
1115
  ) -> ImgArray:
1118
- """
1119
- Morphological closing.
1116
+ """Morphological closing.
1120
1117
 
1121
1118
  If input is binary image, the running function will automatically switched to
1122
1119
  ``binary_closing`` to speed up calculation.
@@ -1156,8 +1153,7 @@ class ImgArray(LabeledArray):
1156
1153
  dims: Dims = None,
1157
1154
  update: bool = False,
1158
1155
  ) -> ImgArray:
1159
- """
1160
- Tophat morphological image processing. This is useful for background subtraction.
1156
+ """Tophat morphological image processing. This is useful for background subtraction.
1161
1157
 
1162
1158
  Parameters
1163
1159
  ----------
@@ -1188,8 +1184,7 @@ class ImgArray(LabeledArray):
1188
1184
  *,
1189
1185
  dims: Dims = None
1190
1186
  ) -> ImgArray:
1191
- """
1192
- Smoothen binary mask image at its edges. This is useful to make a "soft mask".
1187
+ """Smoothen binary mask image at its edges. This is useful to make a "soft mask".
1193
1188
 
1194
1189
  This method applies erosion/dilation to a binary image and then smooth its edges by Gaussian.
1195
1190
  The total value is always larger after Gaussian smoothing.
@@ -1241,8 +1236,7 @@ class ImgArray(LabeledArray):
1241
1236
  cval: float = 0.0,
1242
1237
  dims: Dims = None,
1243
1238
  update: bool = False) -> ImgArray:
1244
- """
1245
- Mean filter. Kernel is filled with same values.
1239
+ """Mean filter. Kernel is filled with same values.
1246
1240
 
1247
1241
  Parameters
1248
1242
  ----------
@@ -1262,6 +1256,65 @@ class ImgArray(LabeledArray):
1262
1256
  kwargs=dict(mode=mode, cval=cval),
1263
1257
  )
1264
1258
 
1259
+
1260
+ @_docs.write_docs
1261
+ @dims_to_spatial_axes
1262
+ @check_input_and_output
1263
+ def min_filter(
1264
+ self,
1265
+ radius: float = 1,
1266
+ *,
1267
+ mode: PaddingMode = "reflect",
1268
+ cval: float = 0.0,
1269
+ dims: Dims = None,
1270
+ ) -> ImgArray:
1271
+ """Minimum filter.
1272
+
1273
+ Parameters
1274
+ ----------
1275
+ {radius}{mode}{cval}{dims}
1276
+
1277
+ Returns
1278
+ -------
1279
+ ImgArray
1280
+ Filtered image
1281
+ """
1282
+ disk = _structures.ball_like(radius, len(dims))
1283
+ return self.as_float()._apply_dask(
1284
+ _filters.min_filter,
1285
+ c_axes=complement_axes(dims, self.axes),
1286
+ kwargs=dict(footprint=disk, mode=mode, cval=cval),
1287
+ )
1288
+
1289
+ @_docs.write_docs
1290
+ @dims_to_spatial_axes
1291
+ @check_input_and_output
1292
+ def max_filter(
1293
+ self,
1294
+ radius: float = 1,
1295
+ *,
1296
+ mode: PaddingMode = "reflect",
1297
+ cval: float = 0.0,
1298
+ dims: Dims = None,
1299
+ ) -> ImgArray:
1300
+ """Maximum filter.
1301
+
1302
+ Parameters
1303
+ ----------
1304
+ {radius}{mode}{cval}{dims}
1305
+
1306
+ Returns
1307
+ -------
1308
+ ImgArray
1309
+ Filtered image
1310
+ """
1311
+ disk = _structures.ball_like(radius, len(dims))
1312
+ return self.as_float()._apply_dask(
1313
+ _filters.max_filter,
1314
+ c_axes=complement_axes(dims, self.axes),
1315
+ kwargs=dict(footprint=disk, mode=mode, cval=cval),
1316
+ )
1317
+
1265
1318
  @_docs.write_docs
1266
1319
  @dims_to_spatial_axes
1267
1320
  @check_input_and_output
@@ -3987,8 +4040,7 @@ class ImgArray(LabeledArray):
3987
4040
  upsample_factor: int = 10,
3988
4041
  max_shift: nDFloat | None = None,
3989
4042
  ) -> MarkerFrame:
3990
- """
3991
- Calculate yx-directional drift using the method equivalent to
4043
+ """Calculate yx-directional drift using the method equivalent to
3992
4044
  ``skimage.registration.phase_cross_correlation``.
3993
4045
 
3994
4046
  Parameters
@@ -4063,9 +4115,10 @@ class ImgArray(LabeledArray):
4063
4115
  dims: Dims = 2,
4064
4116
  update: bool = False,
4065
4117
  ) -> ImgArray:
4066
- """
4067
- Drift correction using iterative Affine translation. If translation vectors ``shift``
4068
- is not given, then it will be determined using ``track_drift`` method of ImgArray.
4118
+ """Drift correction using iterative Affine translation.
4119
+
4120
+ If translation vectors ``shift`` is not given, then it will be determined using
4121
+ ``track_drift`` method of ImgArray.
4069
4122
 
4070
4123
  Parameters
4071
4124
  ----------
@@ -4092,7 +4145,7 @@ class ImgArray(LabeledArray):
4092
4145
  Examples
4093
4146
  --------
4094
4147
  Drift correction of multichannel image using the first channel as the reference.
4095
- >>> img.drift_correction(ref=img["c=0"])
4148
+ >>> img.drift_correction(ref=img["c=0"])
4096
4149
  """
4097
4150
 
4098
4151
  if along is None:
@@ -4107,7 +4160,6 @@ class ImgArray(LabeledArray):
4107
4160
  elif not isinstance(ref, ImgArray):
4108
4161
  ref = self[ref]
4109
4162
  if ref.axes != [along] + dims:
4110
- from itertools import product
4111
4163
  _c_axes = complement_axes([along] + dims, str(ref.axes))
4112
4164
  fmt = slicer.get_formatter(_c_axes)
4113
4165
  out = np.empty_like(self)
@@ -3,7 +3,7 @@ import numpy as np
3
3
  from pathlib import Path
4
4
  import itertools
5
5
  from functools import partial
6
- from typing import TYPE_CHECKING, Any, Sequence, MutableMapping, Protocol
6
+ from typing import TYPE_CHECKING, Any, Literal, Sequence, MutableMapping, Protocol
7
7
  from warnings import warn
8
8
  from scipy import ndimage as ndi
9
9
 
@@ -23,7 +23,7 @@ from impy.array_api import xp
23
23
 
24
24
  if TYPE_CHECKING:
25
25
  from typing_extensions import Self
26
- from numpy.typing import ArrayLike, DTypeLike
26
+ from numpy.typing import ArrayLike, DTypeLike, NDArray
27
27
  from impy.frame import PathFrame
28
28
  from impy.roi import RoiList
29
29
 
@@ -401,10 +401,18 @@ class LabeledArray(MetaArray):
401
401
  out._set_info(self)
402
402
  return out
403
403
 
404
- def as_float(self) -> Self:
405
- if self.dtype == np.float32:
404
+ def as_float(self, *, depth: int = 32) -> Self:
405
+ if depth == 16:
406
+ dtype = np.float16
407
+ elif depth == 32:
408
+ dtype = np.float32
409
+ elif depth == 64:
410
+ dtype = np.float64
411
+ else:
412
+ raise ValueError(f"depth must be 16, 32, or 64, but got {depth}")
413
+ if self.dtype == dtype:
406
414
  return self
407
- out = self.value.astype(np.float32).view(self.__class__)
415
+ out = self.value.astype(dtype).view(self.__class__)
408
416
  out._set_info(self)
409
417
  return out
410
418
 
@@ -417,8 +425,12 @@ class LabeledArray(MetaArray):
417
425
  return self.as_uint16()
418
426
  elif dtype == "uint8":
419
427
  return self.as_uint8()
428
+ elif dtype == "float16":
429
+ return self.as_float(depth=16)
420
430
  elif dtype == "float32":
421
- return self.as_float()
431
+ return self.as_float(depth=32)
432
+ elif dtype == "float64":
433
+ return self.as_float(depth=64)
422
434
  elif dtype == "bool":
423
435
  return self.astype("bool")
424
436
  elif dtype == "float64":
@@ -576,6 +588,7 @@ class LabeledArray(MetaArray):
576
588
  *,
577
589
  order: int = 3,
578
590
  prefilter: bool | None = None,
591
+ how: Literal["pack", "equal"] = "pack",
579
592
  ) -> PropArray:
580
593
  """
581
594
  Measure line profile (kymograph) iteratively for every slice of image. This
@@ -590,7 +603,12 @@ class LabeledArray(MetaArray):
590
603
  `a = [[y0, x0], [y1, x1], ..., [yn, xn]]`
591
604
  b : array-like, optional
592
605
  Destination coordinate. If specified, `a` must be the source coordinate.
593
- {order}
606
+ {order}{prefilter}
607
+ how : "pack" or "equal", default "pack"
608
+ How to sample points on the line. "pack" means that the points are sampled
609
+ at 1-px interval from the start point. "equal" means that the points are
610
+ sampled equally at the interval larger but neareset to 1-px so that the
611
+ points covers the entire line.
594
612
 
595
613
  Returns
596
614
  -------
@@ -616,7 +634,12 @@ class LabeledArray(MetaArray):
616
634
  a = np.asarray(a, dtype=np.float32)
617
635
  _, ndim = a.shape
618
636
  seg = SegmentedLine(a)
619
- coords = seg.sample_points().T
637
+ if how == "pack":
638
+ coords = np.stack(seg.arange(), axis=0)
639
+ elif how == "equal":
640
+ coords = np.stack(seg.linspace(int(seg.length())), axis=0)
641
+ else:
642
+ raise ValueError(f"No option matches how={how}")
620
643
 
621
644
  if ndim == self.ndim:
622
645
  dims = self.axes
@@ -632,7 +655,11 @@ class LabeledArray(MetaArray):
632
655
  axes=c_axes+[new_axis], propname="reslice")
633
656
 
634
657
  out.set_scale(self)
635
- out.set_scale({new_axis: self.scale[dims[-1]] * seg.interv})
658
+ axis = self.axes[dims[-1]]
659
+ if how == "equal":
660
+ out.set_scale({new_axis: axis.scale * seg.interv}, unit=axis.unit)
661
+ else:
662
+ out.set_scale({new_axis: axis.scale}, unit=axis.unit)
636
663
  return out
637
664
 
638
665
  @_docs.write_docs
@@ -1576,31 +1603,61 @@ class SegmentedLine:
1576
1603
  dist = np.sqrt(np.sum(vec**2, axis=1))
1577
1604
  dist_sum = np.sum(dist)
1578
1605
  npoints = int(dist_sum)
1579
- interv = dist_sum / npoints
1606
+ self.interv = dist_sum / npoints
1580
1607
 
1581
- self.length = dist_sum
1582
1608
  self.vec = vec
1583
1609
  self.dist = dist
1584
1610
  self.nodes = nodes
1585
- self.interv = interv
1586
1611
 
1587
1612
  def sample_points(self) -> np.ndarray:
1588
1613
  res = 0
1589
1614
  out = [self.nodes[0:1]]
1590
1615
  npoints = 1
1616
+ interv = self.length() / int(self.length())
1591
1617
  for v, d, p in zip(self.vec, self.dist, self.nodes[:-1]):
1592
1618
  res0 = res
1593
- d_int, res = divmod(d + res, self.interv)
1594
- idx = self.interv*(np.arange(d_int) + 1) - res0
1619
+ d_int, res = divmod(d + res, interv)
1620
+ idx = interv * (np.arange(d_int) + 1) - res0
1595
1621
  xs = idx[:, np.newaxis] * v[np.newaxis]/d + p
1596
1622
  out.append(xs)
1597
1623
  npoints += xs.shape[0]
1598
1624
 
1599
- if npoints <= int(self.length):
1625
+ if npoints <= int(self.length()):
1600
1626
  out.append(self.nodes[-1:])
1601
1627
 
1602
1628
  return np.concatenate(out, axis=0)
1603
1629
 
1630
+ def length(self) -> np.float64:
1631
+ return np.sum(self.lengths())
1632
+
1633
+ def lengths(self) -> NDArray[np.float64]:
1634
+ return np.sqrt(np.sum(np.diff(self.nodes, axis=0) ** 2, axis=1))
1635
+
1636
+ def linspace(self, num: int) -> tuple[NDArray[np.float64], ...]:
1637
+ """Return a tuple of x and y coordinates of np.linspace along the line."""
1638
+ tnots = np.cumsum(
1639
+ np.concatenate([[0], self.lengths()], dtype=np.float64)
1640
+ )
1641
+ teval = np.linspace(0, tnots[-1], num)
1642
+ return tuple(
1643
+ np.interp(teval, tnots, self.nodes[:, i])
1644
+ for i in range(self.nodes.shape[1])
1645
+ )
1646
+
1647
+ def arange(
1648
+ self, step: float = 1.0
1649
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
1650
+ tnots = np.cumsum(
1651
+ np.concatenate([[0], self.lengths()], dtype=np.float64)
1652
+ )
1653
+ length = tnots[-1]
1654
+ num, rem = divmod(length, step)
1655
+ teval = np.linspace(0, length - rem, int(num + 1))
1656
+ return tuple(
1657
+ np.interp(teval, tnots, self.nodes[:, i])
1658
+ for i in range(self.nodes.shape[1])
1659
+ )
1660
+
1604
1661
  def _count_list_depth(x) -> int:
1605
1662
  n = 0
1606
1663
  out = x
impy/arrays/lazy.py CHANGED
@@ -890,6 +890,47 @@ class LazyImgArray(AxesMixin):
890
890
  kwargs=dict(footprint=disk, mode=mode, cval=cval),
891
891
  )
892
892
 
893
+ @_docs.copy_docs(ImgArray.min_filter)
894
+ @dims_to_spatial_axes
895
+ @same_dtype
896
+ @check_input_and_output_lazy
897
+ def min_filter(
898
+ self,
899
+ radius: float = 1,
900
+ *,
901
+ mode: PaddingMode = "reflect",
902
+ cval: float = 0,
903
+ dims: Dims = None,
904
+ update: bool = False
905
+ ) -> LazyImgArray:
906
+ disk = _structures.ball_like(radius, len(dims))
907
+ return self._apply_map_overlap(
908
+ xp.ndi.minimum_filter,
909
+ depth=_ceilint(radius),
910
+ c_axes=complement_axes(dims, self.axes),
911
+ kwargs=dict(footprint=disk, mode=mode, cval=cval),
912
+ )
913
+
914
+ @_docs.copy_docs(ImgArray.max_filter)
915
+ @dims_to_spatial_axes
916
+ @same_dtype
917
+ @check_input_and_output_lazy
918
+ def max_filter(
919
+ self,
920
+ radius: float = 1,
921
+ *,
922
+ mode: PaddingMode = "reflect",
923
+ cval: float = 0,
924
+ dims: Dims = None,
925
+ update: bool = False
926
+ ) -> LazyImgArray:
927
+ disk = _structures.ball_like(radius, len(dims))
928
+ return self._apply_map_overlap(
929
+ xp.ndi.maximum_filter,
930
+ depth=_ceilint(radius),
931
+ c_axes=complement_axes(dims, self.axes),
932
+ kwargs=dict(footprint=disk, mode=mode, cval=cval),
933
+ )
893
934
 
894
935
  @_docs.copy_docs(ImgArray.std_filter)
895
936
  @dims_to_spatial_axes
@@ -1612,10 +1653,18 @@ class LazyImgArray(AxesMixin):
1612
1653
  out._set_info(self)
1613
1654
  return out
1614
1655
 
1615
- def as_float(self) -> LazyImgArray:
1616
- if self.dtype == np.float32:
1656
+ def as_float(self, *, depth: int = 32) -> LazyImgArray:
1657
+ if depth == 16:
1658
+ dtype = np.float16
1659
+ elif depth == 32:
1660
+ dtype = np.float32
1661
+ elif depth == 64:
1662
+ dtype = np.float64
1663
+ else:
1664
+ raise ValueError(f"depth must be 16, 32, or 64, but got {depth}")
1665
+ if self.dtype == dtype:
1617
1666
  return self
1618
- out = self.value.astype(np.float32)
1667
+ out = self.value.astype(dtype)
1619
1668
  out = self.__class__(out)
1620
1669
  out._set_info(self)
1621
1670
  return out
@@ -1628,12 +1677,12 @@ class LazyImgArray(AxesMixin):
1628
1677
  return self.as_uint16()
1629
1678
  elif dtype == "uint8":
1630
1679
  return self.as_uint8()
1680
+ elif dtype == "float16":
1681
+ return self.as_float(depth=16)
1631
1682
  elif dtype == "float32":
1632
- return self.as_float()
1683
+ return self.as_float(depth=32)
1633
1684
  elif dtype == "float64":
1634
- warn("Data type float64 is not valid for images. It was converted to float32 instead",
1635
- UserWarning)
1636
- return self.as_float()
1685
+ return self.as_float(depth=64)
1637
1686
  elif dtype == "complex64":
1638
1687
  out = self.value.astype(np.complex64)
1639
1688
  out = self.__class__(out)
impy/core.py CHANGED
@@ -99,8 +99,7 @@ def array(
99
99
  axes: str = None,
100
100
  like: MetaArray | None = None,
101
101
  ) -> ImgArray:
102
- """
103
- make an ImgArray object, like ``np.array(x)``
102
+ """Make an ImgArray object, like ``np.array(x)``
104
103
 
105
104
  Parameters
106
105
  ----------
@@ -116,12 +115,7 @@ def array(
116
115
 
117
116
  """
118
117
  if isinstance(arr, np.ndarray) and dtype is None:
119
- if arr.dtype in (np.uint8, np.uint16, np.float32):
120
- dtype = arr.dtype
121
- elif arr.dtype.kind == "f":
122
- dtype = np.float32
123
- else:
124
- dtype = arr.dtype
118
+ dtype = arr.dtype
125
119
 
126
120
  axes, name = _normalize_params(axes, name, like)
127
121
 
@@ -150,8 +144,7 @@ def asarray(
150
144
  axes: str | None = None,
151
145
  like: MetaArray | None = None,
152
146
  ) -> ImgArray:
153
- """
154
- make an ImgArray object, like ``np.asarray(x)``
147
+ """Make an ImgArray object, like ``np.asarray(x)``
155
148
 
156
149
  Parameters
157
150
  ----------
@@ -296,9 +289,8 @@ def _inject_numpy_function(func: Callable[_P, Any | None]) -> Callable[_P, ImgAr
296
289
  return asarray(npfunc(*args, **kwargs), name=name, axes=axes, like=like)
297
290
 
298
291
  _func.__doc__ = (
299
- f"""
300
- impy version of numpy.{func.__name__}. This function has additional parameters ``axes``
301
- and ``name``. Original docstring follows.
292
+ f"""impy version of numpy.{func.__name__}. This function has additional
293
+ parameters ``axes`` and ``name``. Original docstring follows.
302
294
 
303
295
  Additional Parameters
304
296
  ---------------------
@@ -396,7 +388,7 @@ def gaussian_kernel(
396
388
  """
397
389
  if np.isscalar(sigma):
398
390
  sigma = (sigma,)*len(shape)
399
- g = gauss.GaussianParticle([(np.array(shape)-1)/2, sigma, peak, 0])
391
+ g = gauss.GaussianParticle([(np.array(shape) - 1) / 2, sigma, peak, 0])
400
392
  ker = g.generate(shape)
401
393
  ker = array(ker, name=name or "Gaussian-Kernel", axes=axes)
402
394
  if axes is None and ker.ndim == 3:
@@ -551,12 +543,12 @@ def imread(
551
543
  return _imread_stack(path, dtype=dtype, key=key, squeeze=squeeze)
552
544
  elif "*" in path:
553
545
  return _imread_glob(path, dtype=dtype, key=key, squeeze=squeeze)
554
- elif not os.path.exists(path):
546
+ elif not Path(path).exists():
555
547
  raise FileNotFoundError(f"No such file or directory: {path}")
556
548
 
557
549
  # read tif metadata
558
550
  if not is_memmap:
559
- size = os.path.getsize(path) / 2**30
551
+ size = Path(path).stat().st_size / 2**30
560
552
  if size > Const["MAX_GB"]:
561
553
  raise MemoryError(f"Too large {size:.2f} GB")
562
554
 
impy/io/_mrc.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from pathlib import Path
3
4
  from typing import TYPE_CHECKING
4
5
  import warnings
5
6
  import os
@@ -59,7 +60,13 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
59
60
 
60
61
  if img.dtype == "bool":
61
62
  img = img.astype(np.int8)
62
-
63
+ elif img.dtype == "float64":
64
+ warnings.warn(
65
+ "MRC format does not support float64. Converting to float32.",
66
+ UserWarning,
67
+ stacklevel=2,
68
+ )
69
+ img = img.astype(np.float32)
63
70
  if lazy:
64
71
  from dask import array as da
65
72
 
@@ -75,9 +82,10 @@ def _(path: str, img: ImpyArray, lazy: bool = False):
75
82
  # get voxel_size
76
83
  if img.axes not in ("zyx", "yx"):
77
84
  raise ImageAxesError(
78
- f"Can only save zyx- or yx- image as a mrc file, but image has {img.axes} axes."
79
- )
80
- if os.path.exists(path):
85
+ f"Can only save zyx- or yx- image as a mrc file, but image has {img.axes} "
86
+ "axes."
87
+ )
88
+ if Path(path).exists():
81
89
  with mrcfile.open(path, mode="r+") as mrc:
82
90
  mrc.set_data(img.value)
83
91
  mrc.voxel_size = voxel_size
@@ -98,13 +106,12 @@ _MRC_MODE = {
98
106
  }
99
107
 
100
108
  def _parse_mrcfile(mrc: MrcObject) -> ImageMetadata:
101
- ndim = len(mrc.voxel_size.item())
102
- if ndim == 3:
103
- axes = "zyx"
104
- elif ndim == 2:
109
+ if mrc.is_single_image():
105
110
  axes = "yx"
111
+ elif mrc.is_volume_stack():
112
+ axes = "tzyx"
106
113
  else:
107
- raise RuntimeError(f"ndim = {ndim} not supported")
114
+ axes = "zyx"
108
115
 
109
116
  scale = dict.fromkeys(axes, 1.0)
110
117
  for a in axes:
impy/io/_tiff.py CHANGED
@@ -14,7 +14,7 @@ from impy.utils.axesop import complement_axes
14
14
  if TYPE_CHECKING:
15
15
  from tifffile import TiffFile
16
16
 
17
- @IO.mark_reader(".tif", ".tiff")
17
+ @IO.mark_reader(".tif", ".tiff", ".lsm")
18
18
  def _(path: str, memmap: bool = False) -> ImageData:
19
19
  """The tif file reader."""
20
20
  from tifffile import TiffFile
@@ -28,7 +28,7 @@ def _(path: str, memmap: bool = False) -> ImageData:
28
28
 
29
29
  return ImageData.from_metadata(image, meta)
30
30
 
31
- @IO.mark_header_reader(".tif", ".tiff")
31
+ @IO.mark_header_reader(".tif", ".tiff", ".lsm")
32
32
  def _(path: str) -> ImageMetadata:
33
33
  """The tif header reader."""
34
34
  from tifffile import TiffFile
@@ -39,7 +39,7 @@ def _(path: str) -> ImageMetadata:
39
39
  return meta
40
40
 
41
41
 
42
- @IO.mark_writer(".tif", ".tiff")
42
+ @IO.mark_writer(".tif", ".tiff", ".lsm")
43
43
  def _(path: str, img: ImpyArray, lazy: bool = False):
44
44
  """The TIFF writer."""
45
45
  from tifffile import imwrite, memmap
@@ -103,7 +103,7 @@ def _parse_tifffile(tif: TiffFile) -> ImageMetadata:
103
103
  tags = {v.name: v.value for v in pagetag.values()}
104
104
 
105
105
  labels = ijmeta.get("Labels")
106
- scale = dict()
106
+ scale: dict[str, float] = {}
107
107
  dz = ijmeta.get("spacing", 1.0)
108
108
  scale_unit: dict[str, float] = {}
109
109
  try:
@@ -133,9 +133,9 @@ def _parse_tifffile(tif: TiffFile) -> ImageMetadata:
133
133
  _unit = ijmeta.pop("unit", None)
134
134
  for k in scale.keys():
135
135
  scale_unit[k] = _unit
136
- if tif.is_ome:
136
+ if ome_meta := tif.ome_metadata:
137
137
  try:
138
- xml = xml2dict(tif.ome_metadata)
138
+ xml = xml2dict(ome_meta)
139
139
  meta_data: dict[str, Any] = xml["OME"]["Image"]
140
140
  if isinstance(meta_data, list):
141
141
  meta_data = meta_data[0]
@@ -156,6 +156,14 @@ def _parse_tifffile(tif: TiffFile) -> ImageMetadata:
156
156
 
157
157
  except Exception:
158
158
  pass
159
+ if lsm_meta := tif.lsm_metadata:
160
+ scale_unit = "μm"
161
+ if voxel_size_x := lsm_meta.get("VoxelSizeX"):
162
+ scale["x"] = voxel_size_x * 1e6
163
+ if voxel_size_y := lsm_meta.get("VoxelSizeY"):
164
+ scale["y"] = voxel_size_y * 1e6
165
+ if voxel_size_z := lsm_meta.get("VoxelSizeZ"):
166
+ scale["z"] = voxel_size_z * 1e6
159
167
 
160
168
  return ImageMetadata(
161
169
  axes=axes,
@@ -188,7 +196,7 @@ def _get_ijmeta_from_img(img: ImpyArray, update_lut=True):
188
196
  info = {}
189
197
  metadata["Info"] = str(info)
190
198
  scale_unit = img.scale_unit
191
- if scale_unit[0] in ("μ", "\xb5", "\xc2"):
199
+ if scale_unit and scale_unit[0] in ("μ", "\xb5", "\xc2"):
192
200
  scale_unit = "\\u00B5" + scale_unit[1:]
193
201
  metadata["unit"] = scale_unit
194
202
 
impy/lazy/core.py CHANGED
@@ -108,8 +108,7 @@ def array(
108
108
  axes: str = None,
109
109
  like: MetaArray | None = None,
110
110
  ) -> LazyImgArray:
111
- """
112
- make an LazyImgArray object, like ``np.array(x)``
111
+ """Make an LazyImgArray object, like ``np.array(x)``
113
112
 
114
113
  Parameters
115
114
  ----------
@@ -144,12 +143,7 @@ def array(
144
143
  arr = da.asarray(arr)
145
144
 
146
145
  if isinstance(arr, np.ndarray) and dtype is None:
147
- if arr.dtype in (np.uint8, np.uint16, np.float32):
148
- dtype = arr.dtype
149
- elif arr.dtype.kind == "f":
150
- dtype = np.float32
151
- else:
152
- dtype = arr.dtype
146
+ dtype = arr.dtype
153
147
 
154
148
  axes, name = _normalize_params(axes, name, like)
155
149
 
@@ -172,8 +166,7 @@ def asarray(
172
166
  like: MetaArray | None = None,
173
167
  chunks="auto",
174
168
  ) -> LazyImgArray:
175
- """
176
- make an LazyImgArray object, like ``np.asarray(x)``
169
+ """Make an LazyImgArray object, like ``np.asarray(x)``
177
170
 
178
171
  Parameters
179
172
  ----------
impy/random.py CHANGED
@@ -127,7 +127,7 @@ class ImageGenerator:
127
127
  def standard_normal(
128
128
  self,
129
129
  size: int | tuple[int, ...] | None = None,
130
- dtype = None,
130
+ dtype=np.float32,
131
131
  *,
132
132
  axes: AxesLike | None = None,
133
133
  name: str | None = None,
@@ -142,7 +142,7 @@ class ImageGenerator:
142
142
  def standard_exponential(
143
143
  self,
144
144
  size: int | tuple[int, ...] | None = None,
145
- dtype = None,
145
+ dtype=np.float32,
146
146
  method: Literal["zig", "inv"] = None,
147
147
  *,
148
148
  axes: AxesLike | None = None,
@@ -159,7 +159,7 @@ class ImageGenerator:
159
159
  def random(
160
160
  self,
161
161
  size: int | tuple[int, ...] | None = None,
162
- dtype = None,
162
+ dtype=np.float32,
163
163
  *,
164
164
  axes: AxesLike | None = None,
165
165
  name: str | None = None,
@@ -187,7 +187,7 @@ class ImageGenerator:
187
187
  arr = self._rng.normal(loc=loc, scale=scale, size=size)
188
188
  if np.isscalar(arr):
189
189
  return arr
190
- return asarray(xp.asnumpy(arr), axes=axes, name=name)
190
+ return asarray(xp.asnumpy(arr), axes=axes, name=name).astype(np.float32)
191
191
 
192
192
  def poisson(
193
193
  self,
impy/utils/gauss.py CHANGED
@@ -19,7 +19,10 @@ def diagonal_gaussian(r, *params):
19
19
  ndim = len(params[:-2])//2
20
20
  mu = params[:ndim]
21
21
  sg = params[ndim:-2]
22
- z_value = np.array([(x0 - mu0)/sg0 for x0, mu0, sg0 in zip(r, mu, sg)])
22
+ z_value = np.array(
23
+ [(x0 - mu0) / sg0 for x0, mu0, sg0 in zip(r, mu, sg)],
24
+ dtype=np.float32
25
+ )
23
26
  return a * np.exp(-np.sum(z_value**2, axis=0) / 2) + b
24
27
 
25
28
  class Gaussian:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: impy-array
3
- Version: 2.4.7
3
+ Version: 2.4.9
4
4
  Summary: Speed up coding/extending image analysis in Python.
5
5
  Project-URL: Download, https://github.com/hanjinliu/impy
6
6
  Author-email: Hanjin Liu <liuhanjin-sc@g.ecc.u-tokyo.ac.jp>
@@ -131,6 +131,7 @@ img_prj.imsave("image_max.tif") # Save in the same place. Don't spend tim
131
131
  `impy` automatically chooses the proper reader/writer according to the extension.
132
132
 
133
133
  - Tiff file (".tif", ".tiff")
134
+ - LSM file (".lsm")
134
135
  - MRC file (".mrc", ".rec", ".st", ".map", ".map.gz")
135
136
  - Zarr file (".zarr")
136
137
  - ND2 file (".nd2")
@@ -1,22 +1,22 @@
1
- impy/__init__.py,sha256=ElW_q_sd5NE1DWtX7zciQLcgl47n3a55WpQwq2ChVLk,1638
1
+ impy/__init__.py,sha256=hoRBdnPuuB2oQ4-ECrHuGN3vObmgt0R40T_uxML6VwQ,1638
2
2
  impy/__main__.py,sha256=BAQkQ4X8EFo42pbNx__EqpZ5KL4MTfTUWsVbJIQF0O0,4974
3
- impy/_const.py,sha256=XFkgkyqs_clQJDqr2vdh5rCyfM83iMFChOLJjwNWHtM,3736
3
+ impy/_const.py,sha256=uNHxxY5ltZuOsJWpwlBWFmUM0QfMgIk5WODqlHuBomo,3700
4
4
  impy/_types.py,sha256=gO4l0hx9qxe6eI0PyC4ukW3k4IYxCEZ9gT-rQ3jj3zU,808
5
5
  impy/array_api.py,sha256=9suP_Wck9maSiEa5qK6aZrCfaky6xhCMfIRcc9WuIs8,5585
6
6
  impy/binder.py,sha256=CTMb11g6uYDVVQV5Wtef865IO0G2V3vGXc8SiGYRD5k,9651
7
7
  impy/collections.py,sha256=2e55bLjZN7W4LFUCkyxekcDfLwZodMaU4JG5NQ3RS54,10946
8
- impy/core.py,sha256=UcMY7vQpp9l1CW_hukexqx92RZcEBWR48Ce_bVrQv0U,25809
8
+ impy/core.py,sha256=itTwflodeyUJf5A4mK-XyrrQ1TGiFz0QU4w8Eel5iFg,25615
9
9
  impy/correlation.py,sha256=Yw1T_0eL9FmsMcnCysc49oRJQMn8ho5z9bAzNg5i59Y,28232
10
- impy/random.py,sha256=RJZAgXp3gkOcCsKbAbuMI1pDdA4_atY3mausFxPNWpk,6973
10
+ impy/random.py,sha256=MQl0ViN6A7xDtM8BS4gbv1atgPKsrmp0TXzV1OtsDlU,7004
11
11
  impy/roi.py,sha256=p9ttjBl5SGHHXj0xg5UjPlg_V9cY3MnNZxFfYIjTYsg,14164
12
12
  impy/arrays/__init__.py,sha256=IHpTBIJysomS64rOZsv960YBmM1vc4M-asTSs1OJBWg,454
13
13
  impy/arrays/_imshow.py,sha256=6mJ_PDqmE4UZO9e9SUgyY_YrzQdR2QKlTG7KF_aC_tI,6949
14
14
  impy/arrays/axesmixin.py,sha256=9PK6kMZAfNVipTL65aF5-yR3qt2fy4ATl1OACXLYrAE,10596
15
15
  impy/arrays/big.py,sha256=tHNijyPgyIfTltLP3hQwZdHOpSX3IwG5DHPamajZ8Kg,2745
16
- impy/arrays/imgarray.py,sha256=MxPVVdxnsJDqNVawSykNkFr2y6Cb1ESVYeCzAQXzrN4,159129
16
+ impy/arrays/imgarray.py,sha256=jJQTAWxua0-MmQNIzfL1ducm6VxQeZQEeuSTSMYFNkY,160457
17
17
  impy/arrays/label.py,sha256=l0SZXcZxU8qwUgj0QZPZ32XsNaoBtenlrn6hNR03SPI,6406
18
- impy/arrays/labeledarray.py,sha256=ZbwgP1Fk-lzFckQ12aQO7QnjNMxpmn_y6E3j3DMLCl0,57081
19
- impy/arrays/lazy.py,sha256=hYTZeaW6HEq0CgPcpfqLcLWSpBrJli6ZTN_ddH_56ro,59902
18
+ impy/arrays/labeledarray.py,sha256=vbWUONnMBOXwLZBHlO2fjl9zqF1nljVdq1y0EFc2ias,59456
19
+ impy/arrays/lazy.py,sha256=XrslWD3Eg0QSD3pKkbvuGDyHJpL-gGsNxbJwTIByuYU,61437
20
20
  impy/arrays/phasearray.py,sha256=IojWaNs5sEujn6Lhgqe00H0xF-4CZEFWt5cVW6U_DWQ,13981
21
21
  impy/arrays/specials.py,sha256=4sNg_zeJUce_rwnasPoyL2eZ0-gx_4Z-KwitmLB4a5k,7896
22
22
  impy/arrays/tiled.py,sha256=T44sS6v1Ovln1z1g61Eg8irgZR1Jmpb6BCY3nS6TmDE,9400
@@ -24,7 +24,7 @@ impy/arrays/_utils/__init__.py,sha256=M5HogfyUBvV8kp-X1ZthS8vDLUP7-O2f9Gvsc1ZXdT
24
24
  impy/arrays/_utils/_corr.py,sha256=ThGQV3KSv8ir8BTWDoV83ysalPB2p--LmSCv-J9qq2w,10047
25
25
  impy/arrays/_utils/_deconv.py,sha256=5UM7CoRpWphEYuuiq89bmFCKU-n1G_ArMCkDq7tamdQ,2754
26
26
  impy/arrays/_utils/_docs.py,sha256=AT6tRxXcX433FCfPXhE9wsaeduy_Um44KOqrYHYTFZE,4968
27
- impy/arrays/_utils/_filters.py,sha256=aigQnaURbUnmOXGDxkWksISWQbKAUdwtS8kzbaMzgoQ,7226
27
+ impy/arrays/_utils/_filters.py,sha256=pVkL4tuH8u51sc4fMS9U1edne4GdWYB9_l31ZtXkrtQ,7394
28
28
  impy/arrays/_utils/_glcm.py,sha256=AoUP-p1MPtAzP0Eyt_DEE_wZYwezNICzdkXMBY-OEBo,4150
29
29
  impy/arrays/_utils/_linalg.py,sha256=06jnv-P6nwUjYVTHX2f9i-KDSYZ0bxwNfWWP8A8e_Vw,3233
30
30
  impy/arrays/_utils/_misc.py,sha256=vyyw-G4iu7RiNRAJWyvWWGRalWlcMy5Kg_0VvLBBPq8,2953
@@ -44,28 +44,28 @@ impy/axes/_slicer.py,sha256=Ts4m9V3PBlEb3xeHd6j1XdMEb1SwYGbBF5rnC9WWYZE,2657
44
44
  impy/frame/__init__.py,sha256=Z4XfXzFSDq49tMrVoeYx1ThnHyw43iLVSVcte6b6-as,65
45
45
  impy/frame/frames.py,sha256=7NxDeuaG8CxzMnzczarmLz6ANBpl1tZIhn8KwdvBYg8,12450
46
46
  impy/io/__init__.py,sha256=wp1EOl0O6anbkxpsh_lW2V898l-UPIKiOEkLw1q5r-I,303
47
- impy/io/_mrc.py,sha256=DTWVpRiG2aUd-nlheyAOl5uILfrC0nYnX8bdVRyeUZ8,3392
47
+ impy/io/_mrc.py,sha256=JiPIKar62GhMlZ8SRVbSPcLBbamdQ_2WfuG1_abI7Xg,3611
48
48
  impy/io/_nd2.py,sha256=Squ8-wbCquJ8WfROEUZka26jrCkNNE24OTg_ottY7IY,1380
49
49
  impy/io/_npy.py,sha256=G6fnjA2RkV7ohFV-R1-TA6o9FjdfcTBwLiccfYj-iVo,2222
50
50
  impy/io/_registry.py,sha256=xAD6M46uJhEm9aFO_B0ZFsvRAMNnfXPRd4fPz_awaJI,10251
51
- impy/io/_tiff.py,sha256=p2u1c0qx0wwcWQQhPXHy6fWpLkPHNeZYn3aMvCcyg5Y,6096
51
+ impy/io/_tiff.py,sha256=kfRB6H-fKBk-lrv_y6tY2fNbG_BEcPm2Ja41-0EtZ_8,6528
52
52
  impy/io/_utils.py,sha256=HHyuVqds52pod4Y-CyRG4FVBmonQVBm7OAFj4QqhRCw,3604
53
53
  impy/io/_zarr.py,sha256=Nfr5VJ3DzKN5d7nYoq_ZG9AG2GIDWHYJ33LpG9SwGcs,1336
54
54
  impy/lazy/__init__.py,sha256=hRjpE2zCy5qDAr_27imwRM9J0C7q-a_b5wfC3ux8ZLE,59
55
- impy/lazy/core.py,sha256=_B-ELfmy8dMoMNFiAyb7vtmravJEjtAHF-AxtwPCnzs,9828
55
+ impy/lazy/core.py,sha256=8pU0_T6IAN1rUNc7EbdORz2vqof0bFuUHQ7aJGztW7s,9637
56
56
  impy/lazy/random.py,sha256=2rwHRsHPOvi8tI9kTtGuZ5SzN0MA7yn-30bZSvHeLVc,7713
57
57
  impy/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
58
  impy/utils/axesop.py,sha256=c86XdNlUBFLrwHhAT2j0eELxopNe2QEVaNeOQhXr2do,3076
59
59
  impy/utils/deco.py,sha256=lItN0UdadZsjI9eW3fI402k7DNR9jcnD6ZgjdkYsDdg,5946
60
- impy/utils/gauss.py,sha256=_jvmaUYSRB8KvX9Qp36rhNaNw9lllf68g_43wshpAMo,4713
60
+ impy/utils/gauss.py,sha256=a5C-Ig3aX8SyjqLuQ8VwdjQHdbkNC0LAiKSikTBXZtU,4758
61
61
  impy/utils/misc.py,sha256=zV_NeqEaiTW4Rki5fqWfspQjczRaoTJ5MxTOUx6qstY,622
62
62
  impy/utils/slicer.py,sha256=otDrizWdzoUjFUMlsha0zNFSS4TkgAmLPOAotaTVaUA,4316
63
63
  impy/utils/utilcls.py,sha256=3Td9G2ZkU4C0BiW2LH0sq7RBjdco_BtyxNr-s-seLhs,437
64
64
  impy/viewer/__init__.py,sha256=MH-jhpFxP8JVJwUzbKMLRhhWSEYavVHuik9uOS3dDNU,292
65
65
  impy/viewer/utils.py,sha256=_RWV9LePbmYeC8qRBUBf3qG70BaLOndlzRLcvUYgTtE,12248
66
66
  impy/viewer/viewer.py,sha256=Sfq-obSLoHkBkIW0NDWCxEEaX7-YsC4ifAJ-nxpvJws,17632
67
- impy_array-2.4.7.dist-info/METADATA,sha256=pQoD1_kxoBzLFncPKdriWz4-mLvI2DUt_jo3u7Pl0CA,7547
68
- impy_array-2.4.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
- impy_array-2.4.7.dist-info/entry_points.txt,sha256=reyrgy4odXvi1CcPOIIyRYBmsNGG2tO7ZkFOdG5CBkU,44
70
- impy_array-2.4.7.dist-info/licenses/LICENSE,sha256=rpxKafH9r42sxLLYYB5VbZBHOdtYeLuYJjVU1LBwrxk,1546
71
- impy_array-2.4.7.dist-info/RECORD,,
67
+ impy_array-2.4.9.dist-info/METADATA,sha256=2bE_GlsO3-oxky5vfOAddrBg5-uDzoIYYjihUqzzEiQ,7567
68
+ impy_array-2.4.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
69
+ impy_array-2.4.9.dist-info/entry_points.txt,sha256=reyrgy4odXvi1CcPOIIyRYBmsNGG2tO7ZkFOdG5CBkU,44
70
+ impy_array-2.4.9.dist-info/licenses/LICENSE,sha256=rpxKafH9r42sxLLYYB5VbZBHOdtYeLuYJjVU1LBwrxk,1546
71
+ impy_array-2.4.9.dist-info/RECORD,,