arrayview 0.41.0__tar.gz → 0.41.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.
- {arrayview-0.41.0 → arrayview-0.41.1}/PKG-INFO +1 -1
- {arrayview-0.41.0 → arrayview-0.41.1}/pyproject.toml +2 -1
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_analysis.py +1 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_io.py +39 -30
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_launcher.py +310 -71
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_lifecycle.py +16 -4
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_platform.py +11 -2
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_loading.py +100 -9
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_query.py +5 -1
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_state.py +55 -41
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_vectorfield.py +41 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_session.py +16 -2
- arrayview-0.41.1/src/arrayview/_source_safety.py +775 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_viewer.html +112 -39
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_vscode_extension.py +155 -7
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_vscode_signal.py +9 -3
- arrayview-0.41.1/src/arrayview/_watch.py +57 -0
- arrayview-0.41.1/src/arrayview/arrayview-opener.vsix +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_api.py +47 -10
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_browser.py +6 -5
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_launch_blackbox.py +275 -3
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_lifecycle_contract.py +158 -1
- arrayview-0.41.1/tests/test_nifti_meta.py +174 -0
- arrayview-0.41.1/tests/test_source_safety.py +716 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_vscode_ack_protocol.py +33 -0
- arrayview-0.41.0/src/arrayview/arrayview-opener.vsix +0 -0
- arrayview-0.41.0/tests/test_nifti_meta.py +0 -59
- {arrayview-0.41.0 → arrayview-0.41.1}/.gitignore +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/LICENSE +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/README.md +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/ARCHITECTURE.md +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/__init__.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/__main__.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_app.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_config.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_diagnostics.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_dicom.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_diff.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_icon.png +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_imaging.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_instance_registry.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_launch_plan.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_launch_trace.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_overlays.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_render.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_analysis.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_drop.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_export.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_persistence.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_preferences.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_preload.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_rendering.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_segmentation.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_routes_websocket.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_segmentation.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_server.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_session_spec.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_shell.html +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_synthetic_mri.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_torch.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_tutorial.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_vectorfield.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_vscode.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/_vscode_browser.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/gsap.min.js +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/src/arrayview/todo.md +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/capture_patient_loading.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/capture_v_animation.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/conftest.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/lifecycle_matrix.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/make_vectorfield_test_arrays.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_1d_arrays.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_backend_shared.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_cli.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_colorbar_hover_highlight.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_config.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_dicom.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_display_opener_results.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_doctor_commands.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_drop_import.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_instance_registry.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_invocation_contract_matrix.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_io.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_large_arrays.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_launch_diagnostics.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_launch_execution_ownership.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_launch_plan.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_launch_trace.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_loading_server.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_mode_consistency.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_mode_matrix.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_mode_roundtrip.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_nifti_series.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_preferences_ui.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_public_invocation_regressions.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_render_failure_reporting.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_render_progress.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_rgb_pixel_art.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_safe_server_stop.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_server_identity_status.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_session_spec.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_spawn_ownership.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_torch.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_tutorial.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_tutorial_browser.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_value_formatting.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_view_handle_lifecycle.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_view_launch_execution.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/test_window_level_preview.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/ui_audit.py +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/v_anim_frames/.gitkeep +0 -0
- {arrayview-0.41.0 → arrayview-0.41.1}/tests/visual_smoke.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "arrayview"
|
|
7
|
-
version = "0.41.
|
|
7
|
+
version = "0.41.1"
|
|
8
8
|
description = "Fast multi-dimensional array viewer"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -63,6 +63,7 @@ test = [
|
|
|
63
63
|
"httpx>=0.27",
|
|
64
64
|
"pytest-playwright>=0.5",
|
|
65
65
|
"pillow>=12.1.1",
|
|
66
|
+
"nbclassic>=1.3,<2",
|
|
66
67
|
]
|
|
67
68
|
docs = [
|
|
68
69
|
"mkdocs>=1.6",
|
|
@@ -51,6 +51,7 @@ def _build_metadata(session) -> dict:
|
|
|
51
51
|
"voxel_sizes": list(sm["voxel_sizes"]),
|
|
52
52
|
"axis_labels": list(sm["axis_labels"]),
|
|
53
53
|
"is_oblique": bool(sm["is_oblique"]),
|
|
54
|
+
"is_canonical": bool(sm.get("is_canonical", True)),
|
|
54
55
|
}
|
|
55
56
|
meta["ras_resample_active"] = bool(
|
|
56
57
|
getattr(session, "ras_resample_active", False)
|
|
@@ -139,8 +139,13 @@ def _select_npz_array(npz, filepath):
|
|
|
139
139
|
return npz[keys[0]]
|
|
140
140
|
|
|
141
141
|
|
|
142
|
-
def _nifti_header_with_meta(filepath):
|
|
143
|
-
"""Return a NIfTI image and
|
|
142
|
+
def _nifti_header_with_meta(filepath, *, native=False):
|
|
143
|
+
"""Return a NIfTI image and spatial metadata without reading voxels.
|
|
144
|
+
|
|
145
|
+
Directory collections use canonical metadata because their lazy series
|
|
146
|
+
objects expose canonical axes. A single volume uses native metadata so an
|
|
147
|
+
uncompressed NIfTI can keep its proxy and storage-friendly axis order.
|
|
148
|
+
"""
|
|
144
149
|
nib = _nib()
|
|
145
150
|
img = nib.load(filepath)
|
|
146
151
|
original_affine = np.asarray(img.affine, dtype=np.float64)
|
|
@@ -151,30 +156,40 @@ def _nifti_header_with_meta(filepath):
|
|
|
151
156
|
transform, img.shape[:3]
|
|
152
157
|
)
|
|
153
158
|
|
|
154
|
-
|
|
159
|
+
data_affine = original_affine if native else affine_canonical
|
|
160
|
+
rot = data_affine[:3, :3]
|
|
155
161
|
voxel_sizes = tuple(float(np.linalg.norm(rot[:, i])) for i in range(3))
|
|
162
|
+
canonical_rot = affine_canonical[:3, :3]
|
|
163
|
+
canonical_voxel_sizes = tuple(
|
|
164
|
+
float(np.linalg.norm(canonical_rot[:, i])) for i in range(3)
|
|
165
|
+
)
|
|
156
166
|
norm_rot = np.zeros((3, 3))
|
|
157
167
|
for i in range(3):
|
|
158
|
-
if
|
|
159
|
-
norm_rot[:, i] =
|
|
160
|
-
|
|
161
|
-
neg_labels = ("L", "P", "I")
|
|
162
|
-
axis_labels = tuple(
|
|
163
|
-
pos_labels[i] if norm_rot[i, i] >= 0 else neg_labels[i] for i in range(3)
|
|
164
|
-
)
|
|
168
|
+
if canonical_voxel_sizes[i] > 0:
|
|
169
|
+
norm_rot[:, i] = canonical_rot[:, i] / canonical_voxel_sizes[i]
|
|
170
|
+
axis_labels = tuple(str(v) for v in nib.aff2axcodes(data_affine))
|
|
165
171
|
off_diag_max = max(
|
|
166
172
|
(abs(norm_rot[i, j]) for i in range(3) for j in range(3) if i != j),
|
|
167
173
|
default=0.0,
|
|
168
174
|
)
|
|
169
175
|
canonical_shape = tuple(int(img.shape[int(axis)]) for axis in transform[:, 0])
|
|
170
|
-
|
|
176
|
+
meta = {
|
|
171
177
|
"affine": original_affine,
|
|
172
178
|
"affine_canonical": affine_canonical,
|
|
179
|
+
"data_affine": data_affine,
|
|
173
180
|
"voxel_sizes": voxel_sizes,
|
|
174
181
|
"axis_labels": axis_labels,
|
|
175
182
|
"is_oblique": bool(off_diag_max > 1e-3),
|
|
176
|
-
"
|
|
183
|
+
"is_canonical": bool(
|
|
184
|
+
np.array_equal(transform, np.asarray([[0, 1], [1, 1], [2, 1]]))
|
|
185
|
+
),
|
|
186
|
+
"to_canonical": transform,
|
|
177
187
|
}
|
|
188
|
+
if native:
|
|
189
|
+
meta["spatial_shape"] = tuple(int(v) for v in img.shape[:3])
|
|
190
|
+
else:
|
|
191
|
+
meta["canonical_shape"] = canonical_shape
|
|
192
|
+
return img, meta
|
|
178
193
|
|
|
179
194
|
|
|
180
195
|
def _nifti_display_array(proxy):
|
|
@@ -373,25 +388,19 @@ def _get_progressive_nifti_pool(*, overlay_prefetch=False):
|
|
|
373
388
|
|
|
374
389
|
|
|
375
390
|
def _load_nifti_with_meta(filepath):
|
|
376
|
-
"""Load a NIfTI file
|
|
377
|
-
|
|
378
|
-
meta is a dict with keys:
|
|
379
|
-
affine : original 4x4 affine (RAS+ mm)
|
|
380
|
-
affine_canonical : 4x4 affine after as_closest_canonical
|
|
381
|
-
voxel_sizes : tuple (sx, sy, sz) in mm, post-reorient
|
|
382
|
-
axis_labels : tuple of 3 strs from {"R","L","A","P","S","I"}
|
|
383
|
-
— positive direction of each canonical axis
|
|
384
|
-
is_oblique : bool — True if rotation part has off-diagonal magnitude > 1e-3
|
|
385
|
-
after normalizing voxel sizes
|
|
386
|
-
"""
|
|
387
|
-
img, meta = _nifti_header_with_meta(filepath)
|
|
388
|
-
canon = _nib().as_closest_canonical(img)
|
|
389
|
-
|
|
390
|
-
# NOTE: reorient requires materializing axis permutes/flips. .nii.gz is
|
|
391
|
-
# already eager (gzip not seekable), so this is free; .nii loses mmap as a
|
|
392
|
-
# necessary cost to apply the reorient.
|
|
393
|
-
arr = np.asarray(canon.dataobj)
|
|
391
|
+
"""Load a NIfTI file in native voxel order and return (array, meta).
|
|
394
392
|
|
|
393
|
+
Uncompressed .nii stays proxy-backed. This preserves cheap native-plane
|
|
394
|
+
reads even when the affine describes permuted or flipped anatomical axes.
|
|
395
|
+
Compressed .nii.gz still has to be decoded eagerly because gzip is not
|
|
396
|
+
random-access, but it is no longer silently reordered.
|
|
397
|
+
"""
|
|
398
|
+
img, meta = _nifti_header_with_meta(filepath, native=True)
|
|
399
|
+
arr = (
|
|
400
|
+
img.dataobj
|
|
401
|
+
if filepath.endswith(".nii")
|
|
402
|
+
else _nifti_display_array(img.dataobj)
|
|
403
|
+
)
|
|
395
404
|
return arr, meta
|
|
396
405
|
|
|
397
406
|
|