feat: add GPU-accelerated 3D z-stack renderer#1096
Open
keejkrej wants to merge 1 commit intoSchmollerLab:mainfrom
Open
feat: add GPU-accelerated 3D z-stack renderer#1096keejkrej wants to merge 1 commit intoSchmollerLab:mainfrom
keejkrej wants to merge 1 commit intoSchmollerLab:mainfrom
Conversation
Implements a standalone vispy-based 3D volume renderer with full Cell_ACDC integration: multi-channel overlay, segmentation mask volumes, anisotropic voxel scaling, plane/ISO/MIP depiction modes, smooth-ISO pre-filter, percentile auto-contrast, opacity/contrast controls, keyboard shortcuts, QSettings persistence, and screenshot export. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new GPU-accelerated 3D z-stack renderer (VisPy-based) and integrates it into the main GUI as a new z-projection option, alongside a large smoke-test suite intended to validate renderer behavior without requiring an active OpenGL window.
Changes:
- Introduces
cellacdc.renderer3dwith a Qt window, controls, overlay volumes, voxel anisotropy scaling, and screenshot export. - Integrates the renderer into
gui.pyvia a new z-projection dropdown entry and adapter-based wiring for feeding current volumes/overlays. - Adds
renderer3doptional dependency group (VisPy + PyOpenGL) and a newtests/test_renderer3d.pytest module.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| tests/test_renderer3d.py | Adds renderer3d smoke/API/tests, including vispy-compat checks and GUI integration expectations. |
| pyproject.toml | Adds renderer3d optional dependencies and includes them in all. |
| cellacdc/renderer3d.py | New VisPy/Qt 3D volume renderer window, controls, overlays, voxel scaling, settings persistence, screenshot export. |
| cellacdc/gui.py | Adds “3D z-render” mode, launches/updates/hides renderer window, and supplies current z-stack + overlays + voxel sizes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def test_rendering_modes_match_vispy(): | ||
| """Every mode registered in renderer3d must be accepted by vispy.Volume.""" | ||
| from cellacdc import renderer3d | ||
| import vispy |
|
|
||
| def test_interpolation_modes_valid_in_vispy(): | ||
| """Every INTERPOLATION_MODES id must be a valid vispy Volume interpolation.""" | ||
| from cellacdc import renderer3d |
Comment on lines
+357
to
+361
| from cellacdc import renderer3d | ||
| import vispy | ||
| from qtpy import API_NAME | ||
| vispy.use(API_NAME) | ||
| from vispy.scene.visuals import Volume |
Comment on lines
+396
to
+400
| def test_depiction_plane_configs_valid_in_vispy(): | ||
| """_PLANE_CONFIGS normals must be unit vectors; vispy supports 'plane' mode.""" | ||
| import vispy; from qtpy import API_NAME; vispy.use(API_NAME) | ||
| from vispy.scene.visuals import Volume | ||
| from cellacdc import renderer3d |
Comment on lines
+176
to
+185
| """VolumeRenderer3DWindow must expose all expected public methods.""" | ||
| from cellacdc.renderer3d import VolumeRenderer3DWindow | ||
| required = { | ||
| 'update_volume', 'set_rendering_mode', 'set_colormap', 'set_clim', | ||
| 'set_gamma', 'set_opacity', 'set_iso_threshold', 'set_attenuation', | ||
| 'set_interpolation', 'set_step_size', 'set_smooth_iso', | ||
| 'set_depiction', 'set_zplane_position', 'set_plane_thickness', | ||
| 'set_voxel_scale', 'reset_view', 'save_screenshot', | ||
| 'auto_contrast_percentile', | ||
| } |
Comment on lines
+591
to
+595
| s = QSettings(TEST_ORG, TEST_APP) | ||
| assert s.value('mode_idx', type=int) == 3 | ||
| assert s.value('colormap', type=str) == 'viridis' | ||
| assert s.value('interp_idx', type=int) == 1 | ||
| assert abs(s.value('clim_min', type=float) - 0.05) < 1e-6 |
Comment on lines
+93
to
+97
| COLORMAPS: list[str] = [ | ||
| 'grays', 'viridis', 'hot', 'coolwarm', 'blues', 'reds', | ||
| 'greens', 'plasma', 'inferno', 'magma', | ||
| ] | ||
|
|
| except ImportError: | ||
| pass # scipy not available — skip smoothing silently | ||
| self._gpu_data_is_smoothed = smoothed | ||
| current_cmap = 'grays' |
| s.setValue('clim_max', c._clim_max.value()) | ||
| s.setValue('gamma', c._gamma_spin.value()) | ||
| s.setValue('step_size', c._step_spin.value()) | ||
| s.setValue('smooth_iso', c._smooth_iso_cb.isChecked()) |
Comment on lines
25346
to
+25350
| ol_img = img.mean(axis=0) | ||
| elif zProjHow == 'median z-proj.': | ||
| ol_img = np.median(img, axis=0) | ||
| else: | ||
| ol_img = img[z].copy() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
vispy 3d rendering of z-stacks, supports overlay channel and segmentation channel
don't merge yet, haven't thoroughly tested
feel free to checkout and test