impy-array 2.5.0__tar.gz → 2.5.2__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.
Files changed (72) hide show
  1. {impy_array-2.5.0 → impy_array-2.5.2}/PKG-INFO +1 -1
  2. {impy_array-2.5.0 → impy_array-2.5.2}/impy/__init__.py +1 -1
  3. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/imgarray.py +3 -7
  4. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/labeledarray.py +0 -10
  5. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/lazy.py +5 -17
  6. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/specials.py +32 -33
  7. {impy_array-2.5.0 → impy_array-2.5.2}/impy/core.py +29 -7
  8. {impy_array-2.5.0 → impy_array-2.5.2}/impy/frame/frames.py +49 -60
  9. {impy_array-2.5.0 → impy_array-2.5.2}/impy/lazy/core.py +1 -1
  10. {impy_array-2.5.0 → impy_array-2.5.2}/impy/viewer/utils.py +61 -61
  11. {impy_array-2.5.0 → impy_array-2.5.2}/.gitignore +0 -0
  12. {impy_array-2.5.0 → impy_array-2.5.2}/LICENSE +0 -0
  13. {impy_array-2.5.0 → impy_array-2.5.2}/README.md +0 -0
  14. {impy_array-2.5.0 → impy_array-2.5.2}/impy/__main__.py +0 -0
  15. {impy_array-2.5.0 → impy_array-2.5.2}/impy/_const.py +0 -0
  16. {impy_array-2.5.0 → impy_array-2.5.2}/impy/_types.py +0 -0
  17. {impy_array-2.5.0 → impy_array-2.5.2}/impy/array_api.py +0 -0
  18. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/__init__.py +0 -0
  19. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_imshow.py +0 -0
  20. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/__init__.py +0 -0
  21. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_corr.py +0 -0
  22. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_deconv.py +0 -0
  23. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_docs.py +0 -0
  24. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_filters.py +0 -0
  25. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_glcm.py +0 -0
  26. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_linalg.py +0 -0
  27. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_misc.py +0 -0
  28. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_plot.py +0 -0
  29. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_process_numba.py +0 -0
  30. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_skimage.py +0 -0
  31. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_structures.py +0 -0
  32. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/_utils/_transform.py +0 -0
  33. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/axesmixin.py +0 -0
  34. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/bases/__init__.py +0 -0
  35. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/bases/metaarray.py +0 -0
  36. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/bases/overload.py +0 -0
  37. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/big.py +0 -0
  38. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/label.py +0 -0
  39. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/phasearray.py +0 -0
  40. {impy_array-2.5.0 → impy_array-2.5.2}/impy/arrays/tiled.py +0 -0
  41. {impy_array-2.5.0 → impy_array-2.5.2}/impy/axes/__init__.py +0 -0
  42. {impy_array-2.5.0 → impy_array-2.5.2}/impy/axes/_axes.py +0 -0
  43. {impy_array-2.5.0 → impy_array-2.5.2}/impy/axes/_axes_tuple.py +0 -0
  44. {impy_array-2.5.0 → impy_array-2.5.2}/impy/axes/_axis.py +0 -0
  45. {impy_array-2.5.0 → impy_array-2.5.2}/impy/axes/_slicer.py +0 -0
  46. {impy_array-2.5.0 → impy_array-2.5.2}/impy/binder.py +0 -0
  47. {impy_array-2.5.0 → impy_array-2.5.2}/impy/collections.py +0 -0
  48. {impy_array-2.5.0 → impy_array-2.5.2}/impy/correlation.py +0 -0
  49. {impy_array-2.5.0 → impy_array-2.5.2}/impy/frame/__init__.py +0 -0
  50. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/__init__.py +0 -0
  51. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_czi.py +0 -0
  52. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_mrc.py +0 -0
  53. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_nd2.py +0 -0
  54. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_npy.py +0 -0
  55. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_registry.py +0 -0
  56. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_tiff.py +0 -0
  57. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_utils.py +0 -0
  58. {impy_array-2.5.0 → impy_array-2.5.2}/impy/io/_zarr.py +0 -0
  59. {impy_array-2.5.0 → impy_array-2.5.2}/impy/lazy/__init__.py +0 -0
  60. {impy_array-2.5.0 → impy_array-2.5.2}/impy/lazy/random.py +0 -0
  61. {impy_array-2.5.0 → impy_array-2.5.2}/impy/random.py +0 -0
  62. {impy_array-2.5.0 → impy_array-2.5.2}/impy/roi.py +0 -0
  63. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/__init__.py +0 -0
  64. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/axesop.py +0 -0
  65. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/deco.py +0 -0
  66. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/gauss.py +0 -0
  67. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/misc.py +0 -0
  68. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/slicer.py +0 -0
  69. {impy_array-2.5.0 → impy_array-2.5.2}/impy/utils/utilcls.py +0 -0
  70. {impy_array-2.5.0 → impy_array-2.5.2}/impy/viewer/__init__.py +0 -0
  71. {impy_array-2.5.0 → impy_array-2.5.2}/impy/viewer/viewer.py +0 -0
  72. {impy_array-2.5.0 → impy_array-2.5.2}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: impy-array
3
- Version: 2.5.0
3
+ Version: 2.5.2
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>
@@ -1,4 +1,4 @@
1
- __version__ = "2.5.0"
1
+ __version__ = "2.5.2"
2
2
  __author__ = "Hanjin Liu"
3
3
  __email__ = "liuhanjin-sc@g.ecc.u-tokyo.ac.jp"
4
4
 
@@ -2675,7 +2675,7 @@ class ImgArray(LabeledArray):
2675
2675
  out = pd.concat(out, axis=0)
2676
2676
 
2677
2677
  out = MarkerFrame(out.reindex(columns=list(coords._axes)),
2678
- columns=str(coords._axes), dtype=np.float32).as_standard_type()
2678
+ columns=str(coords._axes), dtype=np.float32)
2679
2679
  out.set_scale(coords.scale)
2680
2680
 
2681
2681
  return out
@@ -3801,7 +3801,7 @@ class ImgArray(LabeledArray):
3801
3801
 
3802
3802
  def regionprops(
3803
3803
  self,
3804
- properties: Iterable[str] | str = ("mean_intensity",),
3804
+ properties: Iterable[str] | str = ("intensity_mean",),
3805
3805
  *,
3806
3806
  extra_properties: Iterable[Callable] | None = None,
3807
3807
  ) -> DataDict[str, PropArray]:
@@ -4111,8 +4111,6 @@ class ImgArray(LabeledArray):
4111
4111
 
4112
4112
  if along is None:
4113
4113
  along = find_first_appeared("tpzcia", include=self.axes)
4114
- elif len(along) != 1:
4115
- raise ValueError("`along` must be single character.")
4116
4114
  if not isinstance(upsample_factor, int):
4117
4115
  raise TypeError(f"upsample-factor must be integer but got {type(upsample_factor)}")
4118
4116
  if max_shift is not None:
@@ -4122,7 +4120,7 @@ class ImgArray(LabeledArray):
4122
4120
  c_axes = complement_axes(along, self.axes)
4123
4121
  last_img = None
4124
4122
  img_fft = self.fft(shift=False, dims=c_axes)
4125
- for i, (_, img) in enumerate(img_fft.iter(along)):
4123
+ for i, (_, img) in enumerate(img_fft.iter([along])):
4126
4124
  img = xp.asarray(img)
4127
4125
  if last_img is not None:
4128
4126
  result[i] = xp.asnumpy(
@@ -4198,8 +4196,6 @@ class ImgArray(LabeledArray):
4198
4196
 
4199
4197
  if along is None:
4200
4198
  along = find_first_appeared("tpzcia", include=self.axes, exclude=dims)
4201
- elif len(along) != 1:
4202
- raise ValueError("`along` must be single character.")
4203
4199
 
4204
4200
  if shift is None:
4205
4201
  # determine 'ref'
@@ -302,16 +302,6 @@ class LabeledArray(MetaArray):
302
302
  ext = ".tif"
303
303
  save_path = save_path.parent / (save_path.name + ext)
304
304
 
305
- if not Path(save_path).is_absolute():
306
- if self.source is None:
307
- raise ValueError(
308
- "Image directory path is unknown. Set by \n"
309
- " >>> img.source = \"...\"\n"
310
- "or specify absolute path like\n"
311
- " >>> img.imsave(\"/path/to/XXX.tif\")"
312
- )
313
- save_path = self.source.parent / save_path
314
-
315
305
  if not overwrite and save_path.exists():
316
306
  raise FileExistsError(f"File {save_path!r} already exists.")
317
307
  if self.metadata is None:
@@ -352,16 +352,6 @@ class LazyImgArray(AxesMixin):
352
352
  ext = ".tif"
353
353
  save_path = save_path.parent / (save_path.name + ext)
354
354
 
355
- if not Path(save_path).is_absolute():
356
- if self.source is None:
357
- raise ValueError(
358
- "Image directory path is unknown. Set by \n"
359
- " >>> img.source = \"...\"\n"
360
- "or specify absolute path like\n"
361
- " >>> img.imsave(\"/path/to/XXX.tif\")"
362
- )
363
- save_path = self.source.parent / save_path
364
-
365
355
  if not overwrite and save_path.exists():
366
356
  raise FileExistsError(f"File {save_path!r} already exists.")
367
357
  if dtype is None:
@@ -776,12 +766,13 @@ class LazyImgArray(AxesMixin):
776
766
  sigma: nDFloat = 1.0,
777
767
  *,
778
768
  dims: Dims = None,
769
+ fourier: bool = False,
779
770
  update: bool = False
780
771
  ) -> LazyImgArray:
781
772
  c_axes = complement_axes(dims, self.axes)
782
773
  depth = _ceilint(sigma*4)
783
774
  return self._apply_map_overlap(
784
- xp.ndi.gaussian_filter,
775
+ _filters.gaussian_filter_fourier if fourier else _filters.gaussian_filter,
785
776
  c_axes=c_axes,
786
777
  depth=depth,
787
778
  kwargs=dict(sigma=sigma),
@@ -795,13 +786,14 @@ class LazyImgArray(AxesMixin):
795
786
  low_sigma: nDFloat = 1.0,
796
787
  high_sigma: nDFloat = 2.0,
797
788
  *,
789
+ fourier: bool = False,
798
790
  dims: Dims = None,
799
791
  update: bool = False
800
792
  ) -> LazyImgArray:
801
793
  c_axes = complement_axes(dims, self.axes)
802
794
  depth = _ceilint(high_sigma*4)
803
795
  return self._apply_map_overlap(
804
- _filters.dog_filter,
796
+ _filters.dog_filter_fourier if fourier else _filters.dog_filter,
805
797
  c_axes=c_axes,
806
798
  depth=depth,
807
799
  kwargs=dict(low_sigma=low_sigma, high_sigma=high_sigma),
@@ -1317,8 +1309,6 @@ class LazyImgArray(AxesMixin):
1317
1309
  def track_drift(self, along: str = None, upsample_factor: int = 10) -> DaskArray:
1318
1310
  if along is None:
1319
1311
  along = find_first_appeared("tpzc<i", include=self.axes)
1320
- elif len(along) != 1:
1321
- raise ValueError("`along` must be single character.")
1322
1312
 
1323
1313
  dims = complement_axes(along, self.axes)
1324
1314
  chunks = switch_slice(dims, self.axes, ifin=self.shape, ifnot=1)
@@ -1370,8 +1360,6 @@ class LazyImgArray(AxesMixin):
1370
1360
  ) -> LazyImgArray:
1371
1361
  if along is None:
1372
1362
  along = find_first_appeared("tpzcia", include=self.axes, exclude=dims)
1373
- elif len(along) != 1:
1374
- raise ValueError("`along` must be single character.")
1375
1363
 
1376
1364
  from impy.frame import MarkerFrame
1377
1365
 
@@ -1387,7 +1375,7 @@ class LazyImgArray(AxesMixin):
1387
1375
  dims = _dims
1388
1376
  elif not isinstance(ref, self.__class__):
1389
1377
  ref = self[ref]
1390
- if ref.axes != along + dims:
1378
+ if ref.axes != [along] + dims:
1391
1379
  raise ValueError(f"Arguments `along`({along}) + `dims`({dims}) do not match "
1392
1380
  f"axes of `ref`({ref.axes})")
1393
1381
 
@@ -20,23 +20,23 @@ SCALAR_PROP = (
20
20
  class PropArray(MetaArray):
21
21
  additional_props = ["_source", "_metadata", "_name", "propname"]
22
22
  propname: str
23
- def __new__(cls, obj, *, name=None, axes=None, source=None,
23
+ def __new__(cls, obj, *, name=None, axes=None, source=None,
24
24
  metadata=None, propname=None, dtype=None):
25
25
  if propname is None:
26
26
  propname = "User_Defined"
27
-
27
+
28
28
  if dtype is None and propname in SCALAR_PROP:
29
29
  dtype = np.float32
30
30
  elif dtype is None:
31
31
  dtype = object
32
32
  else:
33
33
  dtype = dtype
34
-
34
+
35
35
  self = super().__new__(cls, obj, name, axes, source, metadata, dtype=dtype)
36
36
  self.propname = propname
37
-
37
+
38
38
  return self
39
-
39
+
40
40
  def _repr_dict_(self):
41
41
  return {
42
42
  "name": self.name,
@@ -44,7 +44,7 @@ class PropArray(MetaArray):
44
44
  "dtype": self.dtype,
45
45
  "source": self.source,
46
46
  "property name": self.propname}
47
-
47
+
48
48
  def plot(self, along=None, cmap="jet", cmap_range=(0, 1)):
49
49
  """
50
50
  Plot all the results with x-axis defined by `along`.
@@ -57,7 +57,7 @@ class PropArray(MetaArray):
57
57
  Colormap of each graph.
58
58
  cmap_range : tuple, default is (0, 1)
59
59
  Range of float for colormap iteration.
60
- """
60
+ """
61
61
  import matplotlib.pyplot as plt
62
62
  if self.dtype == object:
63
63
  raise TypeError(f"Cannot call plot_profile for {self.propname} "
@@ -67,21 +67,21 @@ class PropArray(MetaArray):
67
67
  along = find_first_appeared("tzp<yxc", include=self.axes)
68
68
  except ValueError:
69
69
  along = self.axes[-1]
70
-
70
+
71
71
  iteraxes = self.axes.drop(along)
72
72
  cmap = plt.get_cmap(cmap)
73
73
  positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False)
74
74
  x = np.arange(self.sizeof(along))*self.scale[along]
75
75
  for i, (sl, y) in enumerate(self.iter(iteraxes)):
76
76
  plt.plot(x, y, color=cmap(positions[i]))
77
-
77
+
78
78
  plt.title(f"{self.propname}")
79
79
  plt.xlabel(along)
80
80
  plt.show()
81
-
81
+
82
82
  return self
83
-
84
- def hist(self, along: str = "N", bins: int = None, cmap: str = "jet",
83
+
84
+ def hist(self, along: str = "N", bins: int = None, cmap: str = "jet",
85
85
  cmap_range: tuple[float, float] = (0., 1.)) -> PropArray:
86
86
  """
87
87
  Plot histogram.
@@ -96,31 +96,31 @@ class PropArray(MetaArray):
96
96
  Colormap of each graph.
97
97
  cmap_range : tuple, default is (0, 1)
98
98
  Range of float for colormap iteration.
99
-
99
+
100
100
  Returns
101
101
  -------
102
102
  self
103
- """
103
+ """
104
104
  import matplotlib.pyplot as plt
105
105
 
106
106
  if self.dtype == object:
107
107
  raise TypeError(f"Cannot call plot_profile for {self.propname} "
108
108
  "because dtype == object.")
109
-
109
+
110
110
  if along is None:
111
111
  along = find_first_appeared("pxyzt<c", include=self.axes)
112
-
112
+
113
113
  iteraxes = self.axes.drop(along)
114
114
  cmap = plt.get_cmap(cmap)
115
115
  positions = np.linspace(*cmap_range, self.size//self.sizeof(along), endpoint=False)
116
116
  for i, (sl, y) in enumerate(self.iter(iteraxes)):
117
117
  plt.hist(y, color=cmap(positions[i]), bins=bins, alpha=0.5)
118
-
118
+
119
119
  plt.title(f"{self.propname}")
120
120
  plt.show()
121
-
121
+
122
122
  return self
123
-
123
+
124
124
  def curve_fit(self, f: Callable, p0 = None, dims = "t", return_fit: bool = True) -> DataDict[str, PropArray]:
125
125
  """
126
126
  Run scipy.optimize.curve_fit for each dimesion.
@@ -130,12 +130,12 @@ class PropArray(MetaArray):
130
130
  f : callable
131
131
  Model function.
132
132
  p0 : array or callable, optional
133
- Initial parameter. Callable object that estimate initial paramter can also
133
+ Initial parameter. Callable object that estimate initial paramter can also
134
134
  be passed here.
135
135
  dims : str, by default "t"
136
136
  Along which axes fitting algorithms are conducted.
137
137
  return_fit : bool, by default True
138
- If fitting trajectories are returned. If the input array is large, this will
138
+ If fitting trajectories are returned. If the input array is large, this will
139
139
  save much memory.
140
140
 
141
141
  Returns
@@ -144,23 +144,23 @@ class PropArray(MetaArray):
144
144
  params : Fitting parameters
145
145
  covs : Covariances
146
146
  fit : fitting trajectories (if return_fit==True)
147
- """
147
+ """
148
148
  from scipy import optimize as opt
149
149
 
150
150
  c_axes = complement_axes(dims, all_axes=self.axes)
151
-
151
+
152
152
  if len(dims)!=1:
153
153
  raise NotImplementedError("Only 1-dimensional fitting is implemented.")
154
-
154
+
155
155
  n_params = len(signature(f).parameters)-1
156
-
156
+
157
157
  params = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32)
158
158
  errs = np.empty(self.sizesof(c_axes) + (n_params,), dtype=np.float32)
159
159
  if return_fit:
160
160
  fit = np.empty(self.sizesof(c_axes) + (self.sizeof(dims),), dtype=self.dtype)
161
-
161
+
162
162
  xdata = np.arange(self.sizeof(dims))*self.scale[dims]
163
-
163
+
164
164
  for sl, data in self.iter(c_axes, exclude=dims):
165
165
  p0_ = p0 if not callable(p0) else p0(data)
166
166
  result = opt.curve_fit(f, xdata, data, p0_)
@@ -168,22 +168,22 @@ class PropArray(MetaArray):
168
168
  errs[sl] = np.sqrt(np.diag(cov))
169
169
  if return_fit:
170
170
  fit[sl] = f(xdata, *(result[0]))
171
-
171
+
172
172
  # set infos
173
173
  params = params.view(self.__class__)
174
174
  errs = errs.view(self.__class__)
175
175
  params._set_info(self, new_axes=self.axes.drop(dims).extend("m"))
176
176
  errs._set_info(self, new_axes=self.axes.drop(dims).extend("m"))
177
-
177
+
178
178
  if return_fit:
179
179
  fit = fit.view(self.__class__)
180
180
  fit._set_info(self, new_axes=self.axes.drop(dims).extend(dims))
181
-
181
+
182
182
  if return_fit:
183
183
  return DataDict(params=params, errs=errs, fit=fit)
184
184
  else:
185
185
  return DataDict(params=params, errs=errs)
186
-
186
+
187
187
  def as_frame(self, colname: str = "f") -> AxesFrame:
188
188
  """
189
189
  N-dimensional data to DataFrame. The intensity data is stored in the `colname` column.
@@ -197,7 +197,7 @@ class PropArray(MetaArray):
197
197
  -------
198
198
  AxesFrame
199
199
  DataFrame with PropArray data.
200
- """
200
+ """
201
201
  from ..frame import AxesFrame
202
202
  if colname in self.axes:
203
203
  raise ImageAxesError(f"Axis {colname} already exists.")
@@ -210,4 +210,3 @@ class PropArray(MetaArray):
210
210
  df = AxesFrame(data_dict, columns=new_axes)
211
211
  df.set_scale(self)
212
212
  return df
213
-
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ from contextlib import suppress
2
3
  import os
3
4
  from pathlib import Path
4
5
  import re
@@ -489,12 +490,12 @@ def broadcast_arrays(*arrays: MetaArray) -> list[MetaArray]:
489
490
  out = [a.broadcast_to(shape, axes) for a in arrays]
490
491
  return out
491
492
 
492
- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
493
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
493
494
  # Imread functions
494
- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
495
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
495
496
 
496
497
  def imread(
497
- path: str,
498
+ path: str | Path,
498
499
  dtype: DTypeLike = None,
499
500
  key: AxesTargetedSlicer | None = None,
500
501
  *,
@@ -502,6 +503,7 @@ def imread(
502
503
  squeeze: bool = False,
503
504
  ) -> ImgArray:
504
505
  """Load image(s) from a path.
506
+
505
507
  You can read list of images from directories with wildcards or ``"$"`` in ``path``.
506
508
 
507
509
  Parameters
@@ -535,12 +537,30 @@ def imread(
535
537
  Wall time: 3.01 ms
536
538
 
537
539
  """
538
- path = str(Path(path).expanduser())
540
+ if str(path).startswith("~"):
541
+ path = str(Path(path).expanduser())
542
+ else:
543
+ path = str(path)
544
+
545
+ # if a directory of a ome.tiff is given, read the first image (which will eventually
546
+ # read all the positions) and use the directory name as source/name.
547
+ if (_fp := Path(path)).is_dir() and _fp.suffix != ".zarr":
548
+ _first_tif = next(_fp.glob("*.ome.tif"), None)
549
+ if _first_tif is None:
550
+ raise ValueError(
551
+ "If a directory is given to imread, only directory of ome.tif is "
552
+ "supported. Please use wildcard like `path/to/dir/*.tif` to read "
553
+ "images in a directory."
554
+ )
555
+ out = imread(_first_tif, dtype=dtype, key=key, name=_fp.name, squeeze=squeeze)
556
+ out.source = _fp
557
+ return out
558
+
539
559
  is_memmap = (key is not None)
540
560
 
541
561
  if "$" in path:
542
562
  return _imread_stack(path, dtype=dtype, key=key, squeeze=squeeze)
543
- elif "*" in path:
563
+ elif "*" in path or "?" in path or "[" in path:
544
564
  return _imread_glob(path, dtype=dtype, key=key, squeeze=squeeze)
545
565
  elif not Path(path).exists():
546
566
  raise FileNotFoundError(f"No such file or directory: {path}")
@@ -575,7 +595,8 @@ def imread(
575
595
  # In case the image is in yxc-order. This sometimes happens.
576
596
  self: ImgArray = np.moveaxis(self, -1, -3)
577
597
  if labels is not None:
578
- self.set_axis_label(c=labels)
598
+ with suppress(ValueError):
599
+ self.set_axis_label(c=labels)
579
600
 
580
601
  if dtype is None:
581
602
  dtype = self.dtype
@@ -619,8 +640,9 @@ def _imread_glob(path: str, squeeze: bool = False, **kwargs) -> ImgArray:
619
640
  out: ImgArray = np.stack(imgs, axis="p")
620
641
  if squeeze:
621
642
  out = np.squeeze(out)
643
+ sep = "*" if "*" in path else "?"
622
644
  try:
623
- base = os.path.split(path.split("*")[0])[0]
645
+ base = os.path.split(path.split(sep)[0])[0]
624
646
  out.source = base
625
647
  except Exception:
626
648
  pass