Skip to content

29 ceus bug report gui cannot load saved nifti voi#64

Open
OmidChaghaneh wants to merge 70 commits into
mainfrom
29-ceus-bug-report-gui-cannot-load-saved-nifti-voi
Open

29 ceus bug report gui cannot load saved nifti voi#64
OmidChaghaneh wants to merge 70 commits into
mainfrom
29-ceus-bug-report-gui-cannot-load-saved-nifti-voi

Conversation

@OmidChaghaneh
Copy link
Copy Markdown
Contributor

No description provided.

akannan05 and others added 30 commits December 18, 2025 14:18
- Add 'Load DICOM File' button in ROI selection menu
- Implement file dialog for DICOM selection
- Add transparency slider with 0-100% control
- 0% shows pure B-mode, 100% shows pure DICOM
- Fix brightness adjustment to preserve overlay
- Remove debug print statements
…sidebar-label-to-QUS-analysis

17 change rf analysis sidebar label to qus analysis
…ing signal and implementing preview transition
Copilot AI review requested due to automatic review settings April 2, 2026 10:26
@OmidChaghaneh OmidChaghaneh linked an issue Apr 2, 2026 that may be closed by this pull request
6 tasks
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the QuantUS GUI to improve segmentation loading/review workflows (including NIfTI/VOI handling) and to introduce DICOM loading support, alongside regenerated PyQt6 UI updates and CEUS analysis plumbing.

Changes:

  • Add DICOM loading support (DicomLoader + model/controller wiring) and extra diagnostic logging for NIfTI image/seg loads.
  • Extend CEUS segmentation flow to include post-draw preview/confirmation and add a CEUS analysis-loading pipeline.
  • Regenerate/normalize multiple Qt .ui files (PyQt6 enum scoping, geometry, labels) and update submodules/documentation.

Reviewed changes

Copilot reviewed 72 out of 83 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
src/qus/seg_loading/ui/seg_type_selection.ui PyQt6 .ui enum scoping + geometry/layout adjustments
src/qus/seg_loading/ui/seg_file_selection.ui PyQt6 .ui enum scoping + geometry/layout adjustments
src/qus/seg_loading/ui/roi_preview.ui PyQt6 .ui enum scoping + geometry/layout adjustments
src/qus/seg_loading/ui/frame_selection.ui PyQt6 .ui enum scoping + geometry/layout adjustments
src/qus/seg_loading/seg_loading_view_coordinator.py Pass controller reference down to ROI drawing widget
src/qus/seg_loading/seg_loading_controller.py Attach controller to view; add DICOM passthrough methods
src/qus/image_loading/ui/scan_type_ui.py New generated PyQt6 UI python for scan type selection
src/qus/image_loading/ui/scan_type.ui PyQt6 .ui enum scoping + label updates
src/qus/image_loading/ui/file_selection.ui PyQt6 .ui enum scoping updates
src/qus/image_loading/dicom_loader.py New DICOM loading + normalization/cropping helper
src/qus/image_loading/init.py Export DicomLoader from package
src/qus/export_loading/ui/export_loading_ui.py New generated PyQt6 export loading UI python
src/qus/export_loading/ui/export_loading.ui PyQt6 .ui enum scoping + geometry/layout tweaks
src/qus/config_loading/ui/custom_params.ui PyQt6 .ui enum scoping updates
src/qus/config_loading/ui/config_type_selection.ui PyQt6 .ui enum scoping updates
src/qus/config_loading/ui/config_preview.ui PyQt6 .ui enum scoping + scroll policy updates
src/qus/config_loading/ui/config_file_selection.ui PyQt6 .ui enum scoping updates
src/qus/application_model.py Add DICOM state/methods; extra NIfTI debug prints
src/qus/analysis_loading/ui/analysis_params.ui PyQt6 .ui enum scoping + sizing tweaks
src/qus/analysis_loading/ui/analysis_function_selection_ui.py New generated PyQt6 analysis function selection UI python
src/qus/analysis_loading/ui/analysis_function_selection.ui PyQt6 .ui enum scoping updates
src/ceus/seg_loading/views/spline.py Avoid spline failures from duplicate points; handle 4D point stripping
src/ceus/seg_loading/views/draw_roi_widget.py Add enhancement controls + confirm/review flow; emit completed segmentation
src/ceus/seg_loading/ui/seg_type_selection_ui.py New generated PyQt6 segmentation type selection UI python
src/ceus/seg_loading/seg_loading_view_coordinator.py Add segmentation preview step and wire completion/confirmation
src/ceus/seg_loading/seg_loading_controller.py Connect model signals; persist confirmed/manual segmentation into model
src/ceus/seg_loading/init.py Export new widgets (preview + ROI widget)
src/ceus/image_preprocessing/transforms.py Removed legacy preprocessing transforms module
src/ceus/image_preprocessing/options.py Removed legacy preprocessing options module
src/ceus/image_preprocessing/functions.py Removed legacy preprocessing functions module
src/ceus/image_preprocessing/decorators.py Removed legacy preprocessing decorators module
src/ceus/image_preprocessing/README.md Removed legacy preprocessing documentation
src/ceus/image_loading/views/file_selection_widget.py Fix folder-selection detection/validation logic
src/ceus/image_loading/ui/scan_type_ui.py New generated PyQt6 UI python for CEUS scan type selection
src/ceus/application_model.py Add CEUS preprocessing API + analysis worker/types + manual segmentation setter
src/ceus/application_controller.py Add CEUS analysis-loading navigation/controller lifecycle
src/ceus/analysis_loading/views/analysis_params_widget.py New CEUS analysis params widget scaffold
src/ceus/analysis_loading/views/analysis_function_selection_widget.py Port imports/types to CEUS engine objects
src/ceus/analysis_loading/views/analysis_execution_widget.py Port types to CurvesAnalysis; update label access
src/ceus/analysis_loading/analysis_loading_view_coordinator.py Port coordinator to CEUS widgets/types; harden error routing
src/ceus/analysis_loading/analysis_loading_controller.py Select available CEUS analysis type; fix completion action name
engines/qus Update QUS submodule pointer
engines/ceus Update CEUS submodule pointer
README.md Update clone URL; add pyradiomics install note and Windows build-tools note
Images/functions.py Removed legacy duplicate code under Images/
Images/application_controller.py Removed legacy duplicate controller under Images/
Images/README.md Removed legacy duplicate README under Images/
.gitmodules Pin submodules to branch = main
Comments suppressed due to low confidence (1)

src/qus/seg_loading/seg_loading_view_coordinator.py:1

  • Avoid setting a private attribute (_parent_controller) on another widget from the outside; this is brittle and bypasses the widget’s public API. Prefer passing the controller (or a narrower interface/callback) via the RoiDrawingWidget constructor, or add a public setter method (e.g., set_controller(...)) on RoiDrawingWidget.
"""

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +197 to +199
# Pass controller reference for DICOM loading via model
if hasattr(self, 'controller') and self.controller:
self._roi_drawing_widget._parent_controller = self.controller
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid setting a private attribute (_parent_controller) on another widget from the outside; this is brittle and bypasses the widget’s public API. Prefer passing the controller (or a narrower interface/callback) via the RoiDrawingWidget constructor, or add a public setter method (e.g., set_controller(...)) on RoiDrawingWidget.

Copilot uses AI. Check for mistakes.
"""Get the processed DICOM image data."""
return self._dicom_image

def load_dicom_file(self, dicom_file_path: str) -> bool:
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importing via from src.qus... is fragile and commonly fails when the package is installed/imported outside a 'src' layout context. Use a package-relative import (e.g., from .image_loading.dicom_loader import DicomLoader or from qus.image_loading.dicom_loader import DicomLoader, depending on your package structure) to ensure it works in all execution environments.

Copilot uses AI. Check for mistakes.
Returns:
bool: True if loaded successfully, False otherwise
"""
from src.qus.image_loading.dicom_loader import DicomLoader
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importing via from src.qus... is fragile and commonly fails when the package is installed/imported outside a 'src' layout context. Use a package-relative import (e.g., from .image_loading.dicom_loader import DicomLoader or from qus.image_loading.dicom_loader import DicomLoader, depending on your package structure) to ensure it works in all execution environments.

Suggested change
from src.qus.image_loading.dicom_loader import DicomLoader
from .image_loading.dicom_loader import DicomLoader

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +29
if not PYDICOM_AVAILABLE:
print("pydicom is not installed. Cannot load DICOM files.")
return None
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using print() for missing-dependency and processing diagnostics will spam stdout in GUI usage and makes it difficult to manage verbosity. Prefer the application's logging mechanism (or propagate errors upward so the model/controller can emit user-facing errors) and gate debug output behind a log level.

Copilot uses AI. Check for mistakes.
# Crop the image
cropped_image = image[crop_top:height - crop_bottom, :]

print(f"DICOM cropped: {image.shape} -> {cropped_image.shape} (removed {crop_top} from top, {crop_bottom} from bottom)")
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using print() for missing-dependency and processing diagnostics will spam stdout in GUI usage and makes it difficult to manage verbosity. Prefer the application's logging mechanism (or propagate errors upward so the model/controller can emit user-facing errors) and gate debug output behind a log level.

Copilot uses AI. Check for mistakes.
Comment on lines +982 to +998
# We need to create a 3D mask where this 2D ROI is on one slice or repeated.
# However, for consistency with DrawVOIWidget, we probably want a 3D volume.
# If DrawROIWidget is only for a single frame, z_len should match what is expected.
x_len, y_len, z_len = self._image_data.pixel_data.shape[:3]
seg_mask = np.zeros((x_len, y_len, z_len), dtype=np.uint8)

# Translate 2D mask [y, x] to [x, y, z] slice
# Assuming the ROI was drawn on a specific slice?
# Actually DrawROIWidget seems to be for 2D images or a specific frame.
# If the image is 4D [x, y, z, t], maybe it was drawn on the central slice?
# Let's assume it was for 2D or we put it on the middle slice of 3D.
mid_z = z_len // 2

# Handle shape mismatch if any
if mask_2d.shape[1] == x_len and mask_2d.shape[0] == y_len:
seg_mask[:, :, mid_z] = mask_2d.T

Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes pixel_data.shape[:3] corresponds to (x, y, z), but elsewhere the widget operates on _all_frames shaped like [t, y, x] (per your own comments). If pixel_data is time-first (or otherwise not (x,y,z,...)), the created seg_mask dimensions will be wrong (e.g., x_len could become t). Use the actual spatial dimensions of the frame you drew on (height/width) and construct a mask with a well-defined axis order that matches what CEUS downstream expects.

Suggested change
# We need to create a 3D mask where this 2D ROI is on one slice or repeated.
# However, for consistency with DrawVOIWidget, we probably want a 3D volume.
# If DrawROIWidget is only for a single frame, z_len should match what is expected.
x_len, y_len, z_len = self._image_data.pixel_data.shape[:3]
seg_mask = np.zeros((x_len, y_len, z_len), dtype=np.uint8)
# Translate 2D mask [y, x] to [x, y, z] slice
# Assuming the ROI was drawn on a specific slice?
# Actually DrawROIWidget seems to be for 2D images or a specific frame.
# If the image is 4D [x, y, z, t], maybe it was drawn on the central slice?
# Let's assume it was for 2D or we put it on the middle slice of 3D.
mid_z = z_len // 2
# Handle shape mismatch if any
if mask_2d.shape[1] == x_len and mask_2d.shape[0] == y_len:
seg_mask[:, :, mid_z] = mask_2d.T
# Construct a 3D mask using the spatial dimensions of the frame on which
# the ROI was drawn. The ROI is 2D [y, x]; we add a singleton z dimension.
mask_h, mask_w = mask_2d.shape # [y, x]
x_len, y_len, z_len = mask_w, mask_h, 1
seg_mask = np.zeros((x_len, y_len, z_len), dtype=np.uint8)
# Translate 2D mask [y, x] to [x, y, z] slice
# Place the ROI on the only slice (z = 0) of this 3D volume.
seg_mask[:, :, 0] = mask_2d.T

Copilot uses AI. Check for mistakes.
Comment on lines +147 to +148
# Position it next to the save button in the layout
self._ui.chooseImageButtonsLayout_4.addWidget(self.confirm_review_button)
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes chooseImageButtonsLayout_4 always exists in the loaded UI. If the .ui changes or the name differs across variants, this will raise an AttributeError at runtime. Consider guarding with hasattr(self._ui, 'chooseImageButtonsLayout_4') and failing gracefully (or place the button in a layout that is known to exist).

Suggested change
# Position it next to the save button in the layout
self._ui.chooseImageButtonsLayout_4.addWidget(self.confirm_review_button)
# Position it next to the save button in the layout, if that layout exists
layout = getattr(self._ui, "chooseImageButtonsLayout_4", None)
if layout is not None:
layout.addWidget(self.confirm_review_button)
else:
# Fallback: add to main_layout if the specific layout is not available
self._ui.main_layout.addWidget(self.confirm_review_button)

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +82
def _create_parameter_inputs(self) -> None:
"""Create input fields for each required parameter."""
# This implementation is simplified compared to QUS for now
# Ideally would dynamically create inputs based on CEUS requirements
pass
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This widget emits params_configured but never creates inputs for required parameters, so the analysis will always run with an empty params dict even when required params exist. Either implement dynamic input creation before enabling 'Run', or block execution and show an error until required parameters are supplied.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +87
def __init__(self, analysis_type: str, image_data: UltrasoundImage,
config_data: Any, seg_data: CeusSeg,
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config_data is accepted and stored on the worker but never used when constructing the analysis object. If the engine's analysis types require config, this will be incorrect; if they don't, remove config_data from the worker signature to avoid confusion and dead parameters.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +113
self.image_data,
self.seg_data,
self.selected_functions,
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config_data is accepted and stored on the worker but never used when constructing the analysis object. If the engine's analysis types require config, this will be incorrect; if they don't, remove config_data from the worker signature to avoid confusion and dead parameters.

Suggested change
self.image_data,
self.seg_data,
self.selected_functions,
self.image_data,
self.seg_data,
self.config_data,
self.selected_functions,

Copilot uses AI. Check for mistakes.
@OmidChaghaneh OmidChaghaneh self-assigned this Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CEUS Bug Report: GUI cannot load saved NIFTI VOI

5 participants