arrayview 0.47.3__py3-none-any.whl → 0.47.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.
arrayview/_io.py CHANGED
@@ -667,7 +667,7 @@ class _FileSeries:
667
667
  """Lazy 4D/5D view over a directory of same-shape array files.
668
668
 
669
669
  Like ``_NiftiSeries`` but works with any supported file format
670
- (.npy, .npz, .zarr, .pt/.pth, .h5/.hdf5, .tif/.tiff, .mat).
670
+ (.npy, .npz, .zarr, .pt/.pth, .h5/.hdf5, .tif/.tiff, .mat, .gipl).
671
671
  Uses ``load_data`` for each volume — no nibabel reorientation.
672
672
  """
673
673
 
@@ -940,6 +940,8 @@ def _get_ext(filepath):
940
940
  return ".nii.gz"
941
941
  if lower.endswith(".zarr.zip"):
942
942
  return ".zarr.zip"
943
+ if lower.endswith(".gipl.gz"):
944
+ return ".gipl.gz"
943
945
  return os.path.splitext(lower)[1]
944
946
 
945
947
 
@@ -950,7 +952,7 @@ def _is_supported_collection_path(filepath):
950
952
  def _strip_array_ext(filepath):
951
953
  base = os.path.basename(filepath)
952
954
  lower = base.lower()
953
- for suffix in (".nii.gz", ".zarr.zip"):
955
+ for suffix in (".nii.gz", ".zarr.zip", ".gipl.gz"):
954
956
  if lower.endswith(suffix):
955
957
  return base[: -len(suffix)]
956
958
  return os.path.splitext(base)[0]
@@ -1738,6 +1740,8 @@ def load_data(filepath, key=None, *, progress=None):
1738
1740
  import tifffile
1739
1741
 
1740
1742
  return tifffile.imread(filepath)
1743
+ elif filepath.lower().endswith((".gipl", ".gipl.gz")):
1744
+ return _load_gipl(filepath)
1741
1745
  elif filepath.endswith(".mat"):
1742
1746
  try:
1743
1747
  import scipy.io
@@ -1783,10 +1787,68 @@ def load_data(filepath, key=None, *, progress=None):
1783
1787
  return data
1784
1788
  raise ValueError(
1785
1789
  "Unsupported format. Supported: .npy, .npz, .nii/.nii.gz, .zarr, "
1786
- ".pt/.pth, .h5/.hdf5, .tif/.tiff, .mat"
1790
+ ".pt/.pth, .h5/.hdf5, .tif/.tiff, .mat, .gipl/.gipl.gz"
1787
1791
  )
1788
1792
 
1789
1793
 
1794
+ # GIPL (Guys Image Processing Lab) image types -> numpy dtypes. Data is
1795
+ # big-endian; complex types store interleaved real/imaginary components.
1796
+ _GIPL_DTYPES = {
1797
+ 1: ">u1", # binary, one value per byte
1798
+ 7: ">i1",
1799
+ 8: ">u1",
1800
+ 15: ">i2",
1801
+ 16: ">u2",
1802
+ 31: ">u4",
1803
+ 32: ">i4",
1804
+ 64: ">f4",
1805
+ 65: ">f8",
1806
+ 144: ">i2", # complex short
1807
+ 160: ">i4", # complex int
1808
+ 192: ">f4", # complex float
1809
+ 193: ">f8", # complex double
1810
+ }
1811
+ _GIPL_COMPLEX = frozenset([144, 160, 192, 193])
1812
+ _GIPL_MAGIC = (4026526128, 719555000)
1813
+ _GIPL_HEADER_SIZE = 256
1814
+
1815
+
1816
+ def _load_gipl(filepath):
1817
+ """Read a .gipl/.gipl.gz file into an array indexed (x, y, z, t).
1818
+
1819
+ Trailing singleton dimensions are dropped, matching how a NIfTI volume
1820
+ of the same data is shown.
1821
+ """
1822
+ import gzip
1823
+
1824
+ opener = gzip.open if filepath.lower().endswith(".gz") else open
1825
+ with opener(filepath, "rb") as f:
1826
+ raw = f.read()
1827
+ if len(raw) < _GIPL_HEADER_SIZE:
1828
+ raise ValueError(f"{filepath!r} is too short to be a GIPL file.")
1829
+ header = raw[:_GIPL_HEADER_SIZE]
1830
+ magic = int(np.frombuffer(header, ">u4", count=1, offset=252)[0])
1831
+ if magic not in _GIPL_MAGIC:
1832
+ raise ValueError(f"{filepath!r} is not a GIPL file (bad magic number).")
1833
+ dims = [int(d) for d in np.frombuffer(header, ">u2", count=4, offset=0)]
1834
+ image_type = int(np.frombuffer(header, ">u2", count=1, offset=8)[0])
1835
+ if image_type not in _GIPL_DTYPES:
1836
+ raise ValueError(f"{filepath!r} has unsupported GIPL image type {image_type}.")
1837
+ dims = [max(d, 1) for d in dims]
1838
+ is_complex = image_type in _GIPL_COMPLEX
1839
+ count = int(np.prod(dims)) * (2 if is_complex else 1)
1840
+ data = np.frombuffer(raw, _GIPL_DTYPES[image_type], count=count, offset=_GIPL_HEADER_SIZE)
1841
+ data = data.astype(data.dtype.newbyteorder("="))
1842
+ if is_complex:
1843
+ data = data.astype(np.float64 if data.itemsize == 8 else np.float32)
1844
+ data = data[0::2] + 1j * data[1::2]
1845
+ # x varies fastest on disk.
1846
+ arr = data.reshape(dims[::-1]).transpose(3, 2, 1, 0)
1847
+ while arr.ndim > 2 and arr.shape[-1] == 1:
1848
+ arr = arr[..., 0]
1849
+ return np.ascontiguousarray(arr)
1850
+
1851
+
1790
1852
  def _tensor_to_numpy(obj, source="object"):
1791
1853
  """Convert a tensor-like object (PyTorch, etc.) to a numpy array."""
1792
1854
  if isinstance(obj, np.ndarray):
@@ -1823,13 +1885,15 @@ _SUPPORTED_EXTS = frozenset(
1823
1885
  ".tif",
1824
1886
  ".tiff",
1825
1887
  ".mat",
1888
+ ".gipl",
1889
+ ".gipl.gz",
1826
1890
  ".dcm",
1827
1891
  ]
1828
1892
  )
1829
1893
 
1830
1894
  # Formats that load the entire array into RAM (no mmap/lazy access).
1831
1895
  # RAM guard only applies to these.
1832
- FULL_LOAD_EXTS = frozenset([".pt", ".pth", ".tif", ".tiff", ".mat"])
1896
+ FULL_LOAD_EXTS = frozenset([".pt", ".pth", ".tif", ".tiff", ".mat", ".gipl", ".gipl.gz"])
1833
1897
 
1834
1898
 
1835
1899
  def _peek_file_shape(fpath: str, ext: str):
arrayview/_launcher.py CHANGED
@@ -6211,17 +6211,18 @@ def arrayview():
6211
6211
  pattern for _name, pattern in _normalize_dir_overlay_specs(args.overlay or [])
6212
6212
  )
6213
6213
  collection_inputs.extend(args.overlay_dir or [])
6214
- for pattern in collection_inputs:
6215
- pattern = lexical_abspath(pattern)
6216
- probe_prefix = scan_root_before_magic(pattern)
6217
- mount = direct_network_mount(probe_prefix)
6218
- nested_mount = network_mount_below(probe_prefix)
6219
- if mount is not None or nested_mount is not None:
6220
- _print_failure(
6221
- "network directory collections are not accessed directly. "
6222
- "Copy the collection to local storage before using --match."
6223
- )
6224
- sys.exit(1)
6214
+ if os.environ.get("ARRAYVIEW_SKIP_SOURCE_STAGING", "1") == "0":
6215
+ for pattern in collection_inputs:
6216
+ pattern = lexical_abspath(pattern)
6217
+ probe_prefix = scan_root_before_magic(pattern)
6218
+ mount = direct_network_mount(probe_prefix)
6219
+ nested_mount = network_mount_below(probe_prefix)
6220
+ if mount is not None or nested_mount is not None:
6221
+ _print_failure(
6222
+ "network directory collections are not accessed directly. "
6223
+ "Copy the collection to local storage before using --match."
6224
+ )
6225
+ sys.exit(1)
6225
6226
  if len(dir_patterns) == 1 and os.path.isdir(dir_patterns[0]):
6226
6227
  dir_patterns = [os.path.join(dir_patterns[0], "**", "*")]
6227
6228
  dir_overlay_specs = [
@@ -167,17 +167,17 @@ def register_loading_routes(app, *, notify_shells, setup_rgb) -> None:
167
167
  f"{nested_sibling_mount.mountpoint!r}; copy the series locally first."
168
168
  )
169
169
  }
170
- for pattern in dir_patterns or []:
171
- pattern_path = lexical_abspath(str(pattern))
172
- scan_root = scan_root_before_magic(pattern_path)
173
- unsafe_mount = direct_network_mount(scan_root) or network_mount_below(scan_root)
174
- if unsafe_mount is not None:
175
- return {
176
- "error": (
177
- f"Directory scan would enter network mount "
178
- f"{unsafe_mount.mountpoint!r}; copy the collection locally first."
179
- )
180
- }
170
+ for pattern in dir_patterns or []:
171
+ pattern_path = lexical_abspath(str(pattern))
172
+ scan_root = scan_root_before_magic(pattern_path)
173
+ unsafe_mount = direct_network_mount(scan_root) or network_mount_below(scan_root)
174
+ if unsafe_mount is not None:
175
+ return {
176
+ "error": (
177
+ f"Directory scan would enter network mount "
178
+ f"{unsafe_mount.mountpoint!r}; copy the collection locally first."
179
+ )
180
+ }
181
181
  if requested_sid in CANCELLED_PENDING_SESSIONS:
182
182
  CANCELLED_PENDING_SESSIONS.discard(requested_sid)
183
183
  raise HTTPException(status_code=409, detail="load request was cancelled")
@@ -53,6 +53,8 @@ _KNOWN_LOCAL_STAGE_FILESYSTEMS = frozenset(
53
53
  }
54
54
  )
55
55
  _SELF_CONTAINED_FILE_EXTENSIONS = (
56
+ ".gipl",
57
+ ".gipl.gz",
56
58
  ".h5",
57
59
  ".hdf5",
58
60
  ".mat",
@@ -58,7 +58,7 @@ _VSCODE_EXT_FRESH_INSTALL = False # True if we just installed it this session
58
58
  _VSCODE_EXT_RELOAD_REQUIRED = False # installed files are newer than the live host
59
59
  _VSCODE_EXT_INSTALL_FAILED = False # automatic install could not complete safely
60
60
  _VSCODE_EXT_NO_LIVE_WINDOW = False # no live host claims this terminal's window id
61
- _VSCODE_EXT_VERSION = "0.15.63" # current bundled extension version
61
+ _VSCODE_EXT_VERSION = "0.15.64" # current bundled extension version
62
62
  _VSCODE_CONFIGURED_PORTS: set[int] = set()
63
63
  # Version-skew notices already printed, so a long-lived process (Jupyter, a
64
64
  # script opening several arrays) states the mismatch once instead of per call.
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: arrayview
3
- Version: 0.47.3
3
+ Version: 0.47.4
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -12,10 +12,10 @@ arrayview/_extra_ports.py,sha256=MZcx04NI9Eg4coydb-yPIHp7uJbE6UscyYGnw8fxid0,111
12
12
  arrayview/_icon.png,sha256=a8RTAbvweLhh3hzD5eGg76Peq-KSDcS6YrCphRb_HEs,2748
13
13
  arrayview/_imaging.py,sha256=MQX1uy4m1lofOeoLDhCp6cHO5p_V5fXm4qlSu9Ykn-c,534
14
14
  arrayview/_instance_registry.py,sha256=h7vyvCl0LTt9R8sDB1Vxe4a-KzfkLVuwrSw_ANpPTD8,8833
15
- arrayview/_io.py,sha256=0QAKjU9WkBdXAfIAGv7HCeWpAwbn9TMCYlip36I2U4I,75238
15
+ arrayview/_io.py,sha256=u1wrxfEA3kRT9n6tV-7ptcQxr1mOylZaREYq3Ex6nvM,77652
16
16
  arrayview/_launch_plan.py,sha256=KGgIPGsflXArECB2txnIAewjuPvF4fZTbO63alC8x28,23570
17
17
  arrayview/_launch_trace.py,sha256=4yjJdkFyEvSrPN14QVFJ3u6on2q0TH2ySSdgUp9SjKI,5668
18
- arrayview/_launcher.py,sha256=W_CjLFa3_D7IwTYmBX0-7h6XrX4EKvwsJqgbgIu8y7g,243108
18
+ arrayview/_launcher.py,sha256=Eu1XFLhuXiO45StFWZoSPPjFkBLqcEeqGB8Utx7kotQ,243224
19
19
  arrayview/_lifecycle.py,sha256=bqG0oKr-e-5S1ssCTlGmF-yBE47ilrpO1S_Owfjs9Z8,5365
20
20
  arrayview/_overlays.py,sha256=CksypG9rWfCz_3RRGIeyaKeXse17AFxvkEjmYyXnIIE,5399
21
21
  arrayview/_platform.py,sha256=qSWjRcx9rLIdLvKrRArSueFnbyn5GPKfTEMOc5mCIUs,29970
@@ -23,7 +23,7 @@ arrayview/_render.py,sha256=sSNPWreJumIyFKvZtAS5v2gxbNHRVyAdc7JGj34caPw,34121
23
23
  arrayview/_routes_analysis.py,sha256=Xz9Ysr04u07pzSRB4Fzmcl6XwVxyaG6xCWxGwYZi_80,33085
24
24
  arrayview/_routes_drop.py,sha256=FC4e7STdCo3hYIA0C4Yy9Ka2UKFx7nLDU5dkujmc1Vo,12477
25
25
  arrayview/_routes_export.py,sha256=Ha8mMO2E9TlRL5x25-ADV8_8xUiC-jRz9N1oWwgKOsg,3898
26
- arrayview/_routes_loading.py,sha256=rC0cw4QRaTycvzvWemencUQCkCGyaT7GzUd9gH9Rc5Y,42360
26
+ arrayview/_routes_loading.py,sha256=r88PRn1ogSRe1Ec0odhCN3jd5FwIDH0y7acPdUoY9v0,42404
27
27
  arrayview/_routes_persistence.py,sha256=T03csHb7ppGT-8yMDf227XkqhnhjgSLYj0RW2wmg59g,17176
28
28
  arrayview/_routes_preferences.py,sha256=xoMHbPKliYspgjaclhx2r8HbP9mFo9Td-nTF76eWVMg,4680
29
29
  arrayview/_routes_preload.py,sha256=KOsankNguxdIWmFW9iZcY9p2SvUN1mOKY7KkW1qMGcc,1729
@@ -38,7 +38,7 @@ arrayview/_server.py,sha256=A8O8J6N4WTDcujFHjTWjlJYWIzkJe8y9XmKpj9XozE0,22918
38
38
  arrayview/_session.py,sha256=-Jems6lGOVU3uLs4Uq1ggIfY-qTrAXNzpUk6nMbuTBA,35616
39
39
  arrayview/_session_spec.py,sha256=mKDfMRsPYQdEmN9hLXmDU_9N7etiDyBXr8mz-fB8QBo,6825
40
40
  arrayview/_shell.html,sha256=vNa2Kiwx_eoB5H329jolYafLhwJDCH9DsJafBqvVMWU,13194
41
- arrayview/_source_safety.py,sha256=EetyfZFZrVYWV3bdFe4sZovrcdc8Fkr9vqLxc6oUBxI,27635
41
+ arrayview/_source_safety.py,sha256=cI7JAL7DWYuxuB_LnYExvJZaICX2R2qJEJWD1M0z_gg,27664
42
42
  arrayview/_synthetic_mri.py,sha256=CNL-2WC835i6WCqkvmWBCO6WQ5w7HE5trNb8OyEfe64,7365
43
43
  arrayview/_torch.py,sha256=13sqIMCUu_-b9SQ9KfDCExDz2UMStL87EDVjnbJxAEo,7536
44
44
  arrayview/_tutorial.py,sha256=2zZHJGJDa4n30GE1-9ADjKFAifGBH9vvHXP4uDt7BaA,19547
@@ -46,14 +46,14 @@ arrayview/_vectorfield.py,sha256=eDqMCMEGjO8Jdt-6Auh01cz3WD9dbF7xYUWD9xHAf1E,811
46
46
  arrayview/_viewer.html,sha256=aiHPSbCCtj90EnHV0nJ7kh8YkxAaUmzdDYJGW5KC0Zg,2372368
47
47
  arrayview/_vscode.py,sha256=EKnGN2zrjlybc4FQOmYA8BvYdtkcJbLHDEdgtvO7ul4,837
48
48
  arrayview/_vscode_browser.py,sha256=VCH9JpAAjFuhO0wQe8tIGjAL5837AXg_bpmBFtWOQqc,20573
49
- arrayview/_vscode_extension.py,sha256=aXkuw5lyqahOjK5OJfKYySFPX5yt1jSEUgqvoHiF3ZE,53156
49
+ arrayview/_vscode_extension.py,sha256=tF62xp_zVDMexlPtAZ7wKvKCKakdygmf3IGIcICoKeE,53156
50
50
  arrayview/_vscode_signal.py,sha256=N3PCHG6-3tCEc_tHLjg2dhSEQ2EnF_psE2IJRBpAuac,54938
51
51
  arrayview/_watch.py,sha256=PxxS_5hcj_S6xQf9Ep4p1HLRkrI5iDMh1aRa4olV7ew,1873
52
- arrayview/arrayview-opener.vsix,sha256=dga4sUvFEK8N-UjKcmhYW0SDedci83Q2gx4-XB8BoTY,61308
52
+ arrayview/arrayview-opener.vsix,sha256=BpRLXxm4j9gc34xusHzE80g01m6CvIK_fzf3ApgS5xE,61442
53
53
  arrayview/gsap.min.js,sha256=VGihm4idI0E1uqrWezWsiScB9F_jMosn7qs6pPSkeac,72304
54
54
  arrayview/todo.md,sha256=KhnRzpn6_jezR0nDDB61-zhox-rbOmEGW-A7Z0_7FnU,622
55
- arrayview-0.47.3.dist-info/METADATA,sha256=lxECYFGKs3cRc80zXrP19y9TYQPbBHW-bi4qFf5hWas,2288
56
- arrayview-0.47.3.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
57
- arrayview-0.47.3.dist-info/entry_points.txt,sha256=-0s08axutXkTbINiKs0Q7iv-33aWuTaamUg2q2-NyQ4,60
58
- arrayview-0.47.3.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
59
- arrayview-0.47.3.dist-info/RECORD,,
55
+ arrayview-0.47.4.dist-info/METADATA,sha256=V3wlriyNB8GhY_UZwcin_BktoAU6pkum8vDVg6FYVCY,2288
56
+ arrayview-0.47.4.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
57
+ arrayview-0.47.4.dist-info/entry_points.txt,sha256=-0s08axutXkTbINiKs0Q7iv-33aWuTaamUg2q2-NyQ4,60
58
+ arrayview-0.47.4.dist-info/licenses/LICENSE,sha256=34OivjM9faB2QaPjZpMRTIiSx31V3U9cEyQ6F2k-bH0,1062
59
+ arrayview-0.47.4.dist-info/RECORD,,