Skip to content

Conversation

@Czaki
Copy link
Collaborator

@Czaki Czaki commented Oct 1, 2025

Summary by Sourcery

Add two CLI example scripts for image processing: one to dilate segmentation masks and another to generate max projections of TIFF image stacks.

New Features:

  • Add example script to dilate segmentation masks with configurable radius and filename suffix.
  • Add example script to generate max projections of TIFF image collections with configurable filename suffix.

Summary by CodeRabbit

  • New Features

    • Added an example CLI to dilate segmentation/ROI masks: set dilation radius, optional only-selected mode, suffixed outputs, batch globs, and progress messages.
    • Added an example CLI to create maximum-intensity Z projections: optional suffix, optional mask handling, batch globs, and progress messages.
    • Added an example CLI to extract and save components from project files with batch processing and progress messages.
  • Documentation

    • Clarified mask parameter description in image reader docs.

✏️ Tip: You can customize this high-level summary in your review settings.

@Czaki Czaki added this to the 0.16.4 milestone Oct 1, 2025
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 1, 2025

Reviewer's Guide

Introduce two example CLI scripts: one for dilating segmentation masks by leveraging PartSegCore’s ROI operations and Euclidean sprawl computation, and another for generating max-intensity projections of TIFF stacks using PartSegImage utilities.

Sequence diagram for dilate_segmentation_mask.py CLI workflow

sequenceDiagram
    actor User
    participant Script
    participant "PartSegCore"
    participant "PartSegCore_compiled_backend"
    User->>Script: Run dilate_segmentation_mask.py with files, radius, suffix
    Script->>"PartSegCore": Load segmentation mask file
    Script->>"PartSegCore": Convert mask to binary
    Script->>"PartSegCore": Dilate binary mask
    Script->>"PartSegCore": Calculate distances and neighbors
    Script->>"PartSegCore_compiled_backend": Compute euclidean sprawl
    Script->>"PartSegCore": Fit array to image
    Script->>"PartSegCore": Save new mask file
Loading

Sequence diagram for max_projection.py CLI workflow

sequenceDiagram
    actor User
    participant Script
    participant "PartSegImage"
    User->>Script: Run max_projection.py with image files and suffix
    Script->>"PartSegImage": Read TIFF image
    Script->>"PartSegImage": Compute max projection along Z axis
    Script->>"PartSegImage": Create new image object
    Script->>"PartSegImage": Save new image file
Loading

File-Level Changes

Change Details Files
Add CLI script to dilate segmentation masks
  • Parse arguments for input files, dilation radius, and suffix
  • Load and binarize ROI image, apply morphological dilation
  • Compute Euclidean sprawl distances and expand segments
  • Save the dilated ROI mask with updated filename and parameters
examples/dilate_segmentation_mask.py
Add CLI script for max-intensity projection of TIFF files
  • Parse arguments for input files and output suffix
  • Read TIFF image stacks and determine Z-axis index
  • Compute max projection along the Z axis
  • Wrap projection in new Image instance and save with updated filename
examples/max_projection.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds three new example scripts: examples/dilate_segmentation_mask.py (dilates ROI segmentation masks with optional only-selected filtering and writes updated ROI files), examples/max_projection.py (computes Z-axis max-intensity projections of TIFF stacks and writes outputs with a suffix), and examples/extract_components_from_project.py (extracts and saves components from project ROI files). Also refines a docstring in package/PartSegImage/image_reader.py.

Changes

Cohort / File(s) Summary
Segmentation mask dilation example
examples/dilate_segmentation_mask.py
New script adding convert_mask(file_path: Path, radius: float, suffix: str, only_selected: bool) and main(). Validates radius (>0), loads ROI, optionally filters to selected components (remaps values), binarizes, computes dilation and neighborhood/euclidean_sprawl, refits result to image metadata, and writes a new ROI file with the provided suffix. Includes CLI and if __name__ == "__main__" guard.
Max intensity projection example
examples/max_projection.py
New script adding max_projection(file_path: Path, suffix: str = "_max", with_mask: bool = False) and main(). Reads TIFF via TiffImageReader, requires Z axis, computes max projection over Z, optionally projects mask, removes Z dimension while preserving spacing/axes, constructs new Image, and saves output(s) with suffix(es). Includes CLI and if __name__ == "__main__" guard.
Component extraction example
examples/extract_components_from_project.py
New script adding cut_components(project_file: Path) and main(). Loads ROI image via LoadROIImage.load and saves components via SaveComponents.save to a sibling path with _components suffix using SaveComponentsOptions (frame=0, mask_data=True). CLI expands globs and iterates files; main() now calls cut_components with the correct single Path argument.
Docstring clarification
package/PartSegImage/image_reader.py
Small docstring change: clarifies the mask_path parameter description in read_image and BaseImageReaderBuffer.read_image to indicate "path or opened file contains mask". No signature or behavior changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as dilate_segmentation_mask.py
  participant FS as FileSystem
  participant ROIops as ROI/Image Ops
  participant Saver as SaveROI

  User->>CLI: Run with file patterns, --radius, --suffix, [--only-selected]
  CLI->>CLI: Parse args, expand globs
  loop For each file
    CLI->>FS: Load ROI file
    CLI->>ROIops: Optionally filter to selected components (remap values)
    CLI->>ROIops: Binarize, compute dilation & neighborhood distances
    ROIops->>ROIops: Apply euclidean_sprawl and refit to image metadata
    ROIops->>Saver: Prepare updated ROIInfo/metadata and new filename
    Saver->>FS: Write output ROI file with suffix
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant CLI as max_projection.py
  participant FS as FileSystem
  participant TIFF as TiffImageReader
  participant Proc as Projection

  User->>CLI: Run with file patterns, --suffix, [--with-mask]
  CLI->>CLI: Parse args, expand globs
  loop For each file
    CLI->>TIFF: Read TIFF stack
    TIFF-->>CLI: Return image with Z axis
    CLI->>Proc: Compute max along Z (and optional mask projection)
    Proc-->>CLI: Return reduced-dimension Image(s) with updated axes/spacings
    CLI->>FS: Save output(s) with suffix(es)
  end
Loading
sequenceDiagram
  autonumber
  actor User
  participant CLI as extract_components_from_project.py
  participant FS as FileSystem
  participant Loader as LoadROIImage
  participant Saver as SaveComponents

  User->>CLI: Run with project file globs
  CLI->>CLI: Parse args, expand globs
  loop For each file
    CLI->>Loader: Load ROI image
    Loader-->>CLI: Return ROI image data
    CLI->>Saver: Call SaveComponents.save(...) to export components
    Saver->>FS: Write `_components` output file
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas needing extra attention:
    • examples/dilate_segmentation_mask.py: verify ROI value remapping, euclidean_sprawl usage, and metadata/refit logic for spatial alignment.
    • examples/max_projection.py: confirm axis detection/handling, spacing update, and mask naming/skip logic when --with-mask is used.
    • examples/extract_components_from_project.py: verify SaveComponents usage and output path/metadata correctness.

Poem

I hop through masks and stacks tonight,
stretching edges until they’re just right.
I leap through Z to find the best frame,
save new files with a jubilant name.
🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main changes: it adds two new CLI example scripts for segmentation dilation and max projection, which directly matches the changeset content across dilate_segmentation_mask.py and max_projection.py.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add_usefull_scripts

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a03eee8 and 8182ac5.

📒 Files selected for processing (1)
  • examples/extract_components_from_project.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/extract_components_from_project.py
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: Base py3.11 / macos-15-intel py 3.11 latest PyQt5
  • GitHub Check: Base py3.10 / ubuntu-22.04 py 3.10 latest PySide2
  • GitHub Check: Base py3.10 / ubuntu-24.04 py 3.10 latest PySide6
  • GitHub Check: Base py3.12 / ubuntu-24.04 py 3.12 latest PyQt5
  • GitHub Check: Base py3.11 / windows-latest py 3.11 latest PyQt5
  • GitHub Check: Base py3.12 / ubuntu-22.04 py 3.12 latest PyQt6
  • GitHub Check: Base py3.10 / ubuntu-24.04 py 3.10 latest PyQt5
  • GitHub Check: Base py3.10 / ubuntu-22.04 py 3.10 latest PyQt5 _pydantic_1
  • GitHub Check: Base py3.12 / macos-15 py 3.12 latest PyQt6
  • GitHub Check: Base py3.11 / ubuntu-24.04 py 3.11 latest PyQt5
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Sourcery review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `examples/max_projection.py:15` </location>
<code_context>
+
+def max_projection(file_path: Path, suffix: str = "_max"):
+    image = TiffImageReader.read_image(str(file_path))
+    max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
+    image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
+    ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
</code_context>

<issue_to_address>
**issue:** Potential issue if 'Z' axis is missing from axis_order.

Add a check to ensure 'Z' is present in axis_order before calling index(), or handle cases where 'Z' is missing to prevent ValueError.
</issue_to_address>

### Comment 2
<location> `examples/max_projection.py:16` </location>
<code_context>
+def max_projection(file_path: Path, suffix: str = "_max"):
+    image = TiffImageReader.read_image(str(file_path))
+    max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
+    image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
+    ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
+
</code_context>

<issue_to_address>
**issue:** Assumes spacing[1:] matches axes after removing 'Z'.

Explicitly map spacing to axes to ensure correct correspondence, especially if axis order varies.
</issue_to_address>

### Comment 3
<location> `examples/dilate_segmentation_mask.py:19` </location>
<code_context>
+
+def convert_mask(file_path: Path, radius: float, suffix: str):
+    print(f"Converting {file_path} to {suffix} with radius {radius}")
+    if radius <= 0:
+        return
+    project = LoadROIImage.load([str(file_path)])
</code_context>

<issue_to_address>
**suggestion:** Early return for non-positive radius may silently skip files.

Consider adding a warning or error log when skipping files due to non-positive radius to inform the user.

Suggested implementation:

```python
import logging

def convert_mask(file_path: Path, radius: float, suffix: str):
    print(f"Converting {file_path} to {suffix} with radius {radius}")
    if radius <= 0:
        logging.warning(f"Skipping {file_path}: radius must be positive, got {radius}")
        return
    project = LoadROIImage.load([str(file_path)])

```

If your codebase does not already use the `logging` module, you may want to configure it at the top of your script, e.g.:
```python
import logging
logging.basicConfig(level=logging.INFO)
```
Alternatively, if you prefer not to use logging, replace `logging.warning(...)` with `print(f"WARNING: Skipping {file_path}: radius must be positive, got {radius}")`.
</issue_to_address>

### Comment 4
<location> `examples/dilate_segmentation_mask.py:66-67` </location>
<code_context>
+
+    args = parser.parse_args()
+
+    files = list(chain.from_iterable(glob(x) for x in args.project_files))
+    print(f"{files=} {args.project_files=}")
+
</code_context>

<issue_to_address>
**suggestion:** No check for empty file list after glob expansion.

Add a warning or error if no files are found to improve user feedback.

```suggestion
    files = list(chain.from_iterable(glob(x) for x in args.project_files))
    print(f"{files=} {args.project_files=}")
    if not files:
        print("Warning: No files found matching the provided patterns.", flush=True)
        exit(1)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

def max_projection(file_path: Path, suffix: str = "_max"):
image = TiffImageReader.read_image(str(file_path))
max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Assumes spacing[1:] matches axes after removing 'Z'.

Explicitly map spacing to axes to ensure correct correspondence, especially if axis order varies.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
examples/max_projection.py (1)

20-28: Add error handling for individual file processing.

The script will terminate on the first error without processing remaining files. Consider wrapping the processing in a try-except block to handle errors gracefully.

 def main():
     parser = ArgumentParser()
     parser.add_argument("image_files", nargs="+", type=str)
     parser.add_argument("--suffix", type=str, default="_max")
     args = parser.parse_args()
     files = list(chain.from_iterable(glob(f) for f in args.image_files))
+    if not files:
+        print("Warning: No files matched the provided patterns")
+        return
     for file_path in files:
         print(f"Processing {file_path}")
-        max_projection(Path(file_path), args.suffix)
+        try:
+            max_projection(Path(file_path), args.suffix)
+            print(f"  ✓ Successfully saved max projection")
+        except Exception as e:
+            print(f"  ✗ Failed: {e}")
examples/dilate_segmentation_mask.py (2)

18-20: Move print statement after validation.

The progress message is printed before checking if radius <= 0, leading to misleading output when no processing occurs.

-    print(f"Converting {file_path} to {suffix} with radius {radius}")
     if radius <= 0:
+        print(f"Skipping {file_path}: radius must be positive")
         return
+    print(f"Converting {file_path} to {suffix} with radius {radius}")

69-70: Consider adding error handling for robustness.

Like in max_projection.py, wrapping individual file processing in try-except would prevent the script from terminating on the first error.

     for file_path in files:
-        convert_mask(Path(file_path).absolute(), args.dilate, args.suffix)
+        try:
+            convert_mask(Path(file_path).absolute(), args.dilate, args.suffix)
+        except Exception as e:
+            print(f"Failed to process {file_path}: {e}")
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8bbe49 and f112fc5.

📒 Files selected for processing (2)
  • examples/dilate_segmentation_mask.py (1 hunks)
  • examples/max_projection.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
examples/dilate_segmentation_mask.py (5)
package/PartSegCore/image_operations.py (2)
  • dilate (96-105)
  • to_binary_image (138-140)
package/PartSegCore/mask/io_functions.py (4)
  • LoadROIImage (410-452)
  • MaskProjectTuple (62-111)
  • SaveROI (529-554)
  • SaveROIOptions (114-125)
package/PartSegCore/roi_info.py (1)
  • ROIInfo (33-124)
package/PartSegCore/segmentation/watershed.py (2)
  • calculate_distances_array (281-297)
  • get_neigh (268-269)
package/PartSegImage/image.py (2)
  • spacing (758-760)
  • fit_array_to_image (555-565)
examples/max_projection.py (3)
package/PartSegImage/image.py (2)
  • Image (183-971)
  • spacing (758-760)
package/PartSegImage/image_writer.py (1)
  • ImageWriter (25-112)
package/PartSegImage/image_reader.py (1)
  • TiffImageReader (460-653)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: Base py3.11 / macos-13 py 3.11 latest PyQt5
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Sourcery review
🔇 Additional comments (6)
examples/max_projection.py (2)

1-10: LGTM! Clean imports.

All necessary dependencies are imported. The module docstring clearly describes the script's purpose.


31-32: LGTM! Standard entry point.

examples/dilate_segmentation_mask.py (4)

1-14: LGTM! Comprehensive imports.

All necessary dependencies are imported for ROI mask dilation operations.


29-55: LGTM! Correct euclidean sprawl and save logic.

The ROI dilation workflow is correctly implemented:

  • Euclidean sprawl extends components within the dilated area
  • Result is fitted to the image shape
  • Metadata (spacing, frame_thickness) is properly preserved
  • New ROIInfo is correctly constructed

73-74: LGTM! Standard entry point.


23-28: Clarify ROI dimensionality and matching dilation radius

  • roi_ = project.roi_info.roi.squeeze(): collapsing singleton dimensions can unpredictably change your mask shape (e.g. (1,Z,Y,X)(Z,Y,X) or (Z,1,Y,X)(Z,Y,X)). Ensure the squeezed shape aligns with whether you intend per‐slice or full‐volume operations.
  • Passing [radius, radius] with layer=True invokes 2D dilation on each slice. For 3D sprawl, supply a radius list of length 3 (e.g. [radius]*3) and call dilate(..., layer=False).
  • To avoid hard-coding, derive the radius list from roi_.ndim (or len(project.image.spacing)) so it automatically matches your data’s dimensionality.

Comment on lines 13 to 19
def max_projection(file_path: Path, suffix: str = "_max"):
image = TiffImageReader.read_image(str(file_path))
max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add Z-axis validation and fix spacing calculation.

The function has critical assumptions that may fail at runtime:

  1. Line 15: image.axis_order.index("Z") will raise ValueError if the image doesn't contain a Z axis.
  2. Line 16: image.spacing[1:] assumes Z is the first dimension in spacing, but axis_order.index("Z") could return any position. The spacing slice should match the actual Z position in the axis order.

Consider this safer approach:

 def max_projection(file_path: Path, suffix: str = "_max"):
     image = TiffImageReader.read_image(str(file_path))
+    if "Z" not in image.axis_order:
+        raise ValueError(f"Image {file_path} does not contain a Z axis")
+    z_index = image.axis_order.index("Z")
-    max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
-    image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
+    max_proj = image.get_data().max(axis=z_index)
+    new_spacing = tuple(s for i, s in enumerate(image.spacing) if i != z_index)
+    image2 = Image(max_proj, new_spacing, axes_order=image.axis_order.replace("Z", ""))
     ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def max_projection(file_path: Path, suffix: str = "_max"):
image = TiffImageReader.read_image(str(file_path))
max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
def max_projection(file_path: Path, suffix: str = "_max"):
image = TiffImageReader.read_image(str(file_path))
if "Z" not in image.axis_order:
raise ValueError(f"Image {file_path} does not contain a Z axis")
z_index = image.axis_order.index("Z")
max_proj = image.get_data().max(axis=z_index)
new_spacing = tuple(s for i, s in enumerate(image.spacing) if i != z_index)
image2 = Image(max_proj, new_spacing, axes_order=image.axis_order.replace("Z", ""))
ImageWriter.save(image2, str(file_path.with_name(file_path.stem + suffix + file_path.suffix)))
🤖 Prompt for AI Agents
In examples/max_projection.py around lines 13 to 17, the code assumes a Z axis
and slices spacing incorrectly; first check that "Z" is present in
image.axis_order and raise or return a clear error if not, compute z_idx =
image.axis_order.index("Z"), use that z_idx when taking the max (axis=z_idx) and
build the new spacing by removing the element at z_idx (e.g., new_spacing =
tuple(s for i,s in enumerate(image.spacing) if i != z_idx)), set new_axes =
image.axis_order.replace("Z", "") and construct the Image with
spacing=new_spacing and axes_order=new_axes before saving.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
examples/max_projection.py (1)

17-18: Fix spacing calculation to match Z-axis position.

Line 18 assumes Z is the first dimension when slicing image.spacing[1:], but Z can appear at any position in axis_order. When Z is not first, the wrong spacing element is removed.

Apply this diff to correctly remove the spacing dimension corresponding to Z:

-    max_proj = image.get_data().max(axis=image.axis_order.index("Z"))
-    image2 = Image(max_proj, image.spacing[1:], axes_order=image.axis_order.replace("Z", ""))
+    z_index = image.axis_order.index("Z")
+    max_proj = image.get_data().max(axis=z_index)
+    new_spacing = tuple(s for i, s in enumerate(image.spacing) if i != z_index)
+    image2 = Image(max_proj, spacing=new_spacing, axes_order=image.axis_order.replace("Z", ""))
🧹 Nitpick comments (1)
examples/max_projection.py (1)

27-30: Consider validating the file list is non-empty.

If glob patterns match no files, the script silently succeeds with no feedback. Adding a check improves user experience.

Apply this diff:

     files = list(chain.from_iterable(glob(f) for f in args.image_files))
+    if not files:
+        print("Warning: No files found matching the provided patterns")
+        return
     for file_path in files:
         print(f"Processing {file_path}")
         max_projection(Path(file_path), args.suffix)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f112fc5 and 3310031.

📒 Files selected for processing (2)
  • examples/dilate_segmentation_mask.py (1 hunks)
  • examples/max_projection.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
examples/dilate_segmentation_mask.py (5)
package/PartSegCore/image_operations.py (2)
  • dilate (96-105)
  • to_binary_image (138-140)
package/PartSegCore/mask/io_functions.py (4)
  • LoadROIImage (410-452)
  • MaskProjectTuple (62-111)
  • SaveROI (529-554)
  • SaveROIOptions (114-125)
package/PartSegCore/roi_info.py (1)
  • ROIInfo (33-124)
package/PartSegCore/segmentation/watershed.py (2)
  • calculate_distances_array (281-297)
  • get_neigh (268-269)
package/PartSegImage/image.py (2)
  • spacing (758-760)
  • fit_array_to_image (555-565)
examples/max_projection.py (3)
package/PartSegImage/image.py (2)
  • Image (183-971)
  • spacing (758-760)
package/PartSegImage/image_writer.py (1)
  • ImageWriter (25-112)
package/PartSegImage/image_reader.py (1)
  • TiffImageReader (460-653)
🪛 Ruff (0.13.2)
examples/dilate_segmentation_mask.py

20-20: Avoid specifying long messages outside the exception class

(TRY003)

examples/max_projection.py

16-16: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: Base py3.11 / macos-13 py 3.11 latest PyQt5
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: Sourcery review
🔇 Additional comments (8)
examples/max_projection.py (2)

5-10: LGTM!

Imports are clean and all are utilized in the script.


33-34: LGTM!

Standard entry point pattern is correctly implemented.

examples/dilate_segmentation_mask.py (6)

3-15: LGTM!

All imports are necessary and correctly used throughout the script.


18-21: LGTM!

Radius validation now correctly raises an error for non-positive values, and the --dilate argument type has been fixed to float in the main function. The exception message is clear and appropriate for a CLI tool.

Note: Static analysis flags the exception message as potentially too long (TRY003), but for an example script this is acceptable and improves usability.


23-39: LGTM!

The dilation logic correctly:

  • Loads the ROI project
  • Converts to binary and dilates the mask
  • Applies euclidean sprawl to preserve component boundaries
  • Fits the result back to the image dimensions

The use of squeeze() and layer=True with 2D radius [radius, radius] is appropriate for layer-wise processing.


40-57: LGTM!

The save logic correctly:

  • Constructs the output filename with the specified suffix
  • Creates a new MaskProjectTuple preserving original image and metadata
  • Updates only the ROI with the dilated result
  • Uses appropriate SaveROIOptions to maintain data integrity

60-75: LGTM!

The main function correctly addresses all previous review concerns:

  • --dilate argument is now type=float matching the function signature
  • Empty file list validation provides user feedback
  • Debug print statement has been removed
  • Return codes indicate success (0) or failure (-1)

78-79: LGTM!

Entry point correctly propagates the return code via sys.exit(main()), enabling proper error detection in shell scripts.

@codecov
Copy link

codecov bot commented Oct 15, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.16%. Comparing base (04e95f9) to head (8182ac5).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop    #1307   +/-   ##
========================================
  Coverage    93.16%   93.16%           
========================================
  Files          210      210           
  Lines        33232    33232           
========================================
  Hits         30959    30959           
  Misses        2273     2273           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
examples/dilate_segmentation_mask.py (1)

76-76: Type mismatch: use float instead of int for --dilate argument.

The --dilate argument is declared as type=int, but convert_mask expects radius: float. While Python's duck typing allows integers to work as floats, this creates confusion and prevents users from specifying fractional radius values.

Note: This issue was flagged in previous review comments and supposedly addressed in commits 0d7b527 to 3310031, but the current code still shows type=int.

Apply this diff to fix the type:

-    parser.add_argument("--dilate", type=int, default=1)
+    parser.add_argument("--dilate", type=float, default=1.0)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3310031 and c5a8e15.

📒 Files selected for processing (1)
  • examples/dilate_segmentation_mask.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
examples/dilate_segmentation_mask.py (5)
package/PartSegCore/image_operations.py (2)
  • dilate (96-105)
  • to_binary_image (138-140)
package/PartSegCore/mask/io_functions.py (4)
  • LoadROIImage (410-452)
  • MaskProjectTuple (62-111)
  • SaveROI (529-554)
  • SaveROIOptions (114-125)
package/PartSegCore/roi_info.py (1)
  • ROIInfo (33-124)
package/PartSegCore/segmentation/watershed.py (2)
  • calculate_distances_array (281-297)
  • get_neigh (268-269)
package/PartSegImage/image.py (3)
  • dtype (461-463)
  • spacing (773-775)
  • fit_array_to_image (570-580)
🪛 Ruff (0.14.0)
examples/dilate_segmentation_mask.py

20-20: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: Prepare coverage
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: Sourcery review
🔇 Additional comments (6)
examples/dilate_segmentation_mask.py (6)

1-16: LGTM! Clean import structure.

The imports are well-organized and include all necessary modules for ROI dilation operations.


18-21: LGTM! Proper parameter validation.

The function signature and radius validation are appropriate for this CLI example script.


23-30: LGTM! Correct ROI loading and filtering.

The ROI loading and optional filtering logic correctly handles the selected components case.


52-70: LGTM! Proper file saving logic.

The file path construction and SaveROI usage are correct. The new MaskProjectTuple properly includes the dilated ROI and preserves relevant metadata.


80-89: LGTM! Proper CLI argument handling.

The file glob expansion, validation, and processing loop are correctly implemented. Good that the no-files-found check was added based on previous feedback.


92-93: LGTM! Standard main guard.

The main guard and exit handling are properly implemented.

selected_components = list(range(1, len(unique_values)))

bin_roi = to_binary_image(roi_)
sprawl_area = dilate(bin_roi, [radius, radius], True)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify 2D-only dilation is intentional.

The dilation uses [radius, radius] which appears to dilate only in X and Y dimensions, not Z. For 3D volumetric images, this might not produce the expected result. If Z-axis dilation is needed, consider using radius directly or [radius, radius, radius].

Run the following script to check if this project typically uses 3D images:


🏁 Script executed:

#!/bin/bash
# Check for 3D image handling in the codebase and verify dilation usage patterns
rg -nP -C3 'dilate\(' --type=py | head -50

Length of output: 3798


🏁 Script executed:

#!/bin/bash
# Inspect dilate implementation
sed -n '90,150p' package/PartSegCore/image_operations.py

# Inspect example usage and components_num context
sed -n '1,200p' examples/dilate_segmentation_mask.py

Length of output: 4620


Ensure full 3D dilation and correct component count

  • Replace 2D‐only dilation with full 3D, e.g.:
  • sprawl_area = dilate(bin_roi, [radius, radius], True)
  • sprawl_area = dilate(bin_roi, [radius, radius, radius], layer=False)
  • Compute component count from the filtered ROI, not the original, e.g.:
  • components_num = np.max(project.roi_info.roi)
  • components_num = np.max(roi_)
    
    
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sprawl_area = dilate(bin_roi, [radius, radius], True)
# … earlier in the function …
sprawl_area = dilate(bin_roi, [radius, radius, radius], layer=False)
components_num = np.max(roi_)
# … later in the function …

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
examples/dilate_segmentation_mask.py (2)

18-21: LGTM! Input validation is correct.

The radius validation properly raises ValueError for non-positive values. The error message is clear and appropriate for an example script.

Optional refinement: The static analysis tool suggests defining a custom exception class to avoid long messages in raise statements (TRY003). For a simple example script, the current approach is fine, but for production code, consider:

class InvalidRadiusError(ValueError):
    """Raised when radius is not positive."""
    pass

def convert_mask(file_path: Path, radius: float, suffix: str, only_selected: bool):
    if radius <= 0:
        raise InvalidRadiusError(f"Radius must be positive, got {radius}")

73-89: Suggest type consistency for --dilate argument.

The argument parser and file handling logic are correct. The empty file list check (lines 83-85) properly validates input.

Optional refinement: Line 76 declares --dilate as type=int while convert_mask expects radius: float. Although Python's duck typing allows this to work, it creates a type inconsistency. For clarity, consider:

-    parser.add_argument("--dilate", type=int, default=1)
+    parser.add_argument("--dilate", type=float, default=1.0)

This matches the function signature and allows fractional radius values (e.g., --dilate 1.5).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c5a8e15 and 5964f72.

📒 Files selected for processing (1)
  • examples/dilate_segmentation_mask.py (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-15T11:26:16.124Z
Learnt from: Czaki
PR: 4DNucleome/PartSeg#1307
File: examples/dilate_segmentation_mask.py:0-0
Timestamp: 2025-10-15T11:26:16.124Z
Learning: In the PartSeg project, the minimum supported numpy version is 1.22.2, with a restriction to numpy<2 for Python versions below 3.10. Features from newer numpy versions (e.g., numpy 2.3) should be avoided to maintain backward compatibility.

Applied to files:

  • examples/dilate_segmentation_mask.py
🧬 Code graph analysis (1)
examples/dilate_segmentation_mask.py (5)
package/PartSegCore/image_operations.py (2)
  • dilate (96-105)
  • to_binary_image (138-140)
package/PartSegCore/mask/io_functions.py (4)
  • LoadROIImage (410-452)
  • MaskProjectTuple (62-111)
  • SaveROI (529-554)
  • SaveROIOptions (114-125)
package/PartSegCore/roi_info.py (1)
  • ROIInfo (33-124)
package/PartSegCore/segmentation/watershed.py (2)
  • calculate_distances_array (281-297)
  • get_neigh (268-269)
package/PartSegImage/image.py (3)
  • dtype (461-463)
  • spacing (773-775)
  • fit_array_to_image (570-580)
🪛 Ruff (0.14.0)
examples/dilate_segmentation_mask.py

20-20: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Sourcery review
🔇 Additional comments (5)
examples/dilate_segmentation_mask.py (5)

1-16: LGTM! Imports are well-organized.

All imports are relevant and necessary for ROI dilation functionality.


23-38: LGTM! Filtering and remapping logic is correct.

The conditional filtering for only_selected mode properly:

  • Masks non-selected components to zero
  • Remaps remaining components to consecutive IDs (0, 1, 2, ...)
  • Updates the selected_components list accordingly

This ensures the downstream euclidean_sprawl function receives properly formatted input.


39-51: Verify that 2D-only dilation is intentional.

Line 40 uses dilate(bin_roi, [radius, radius], True) which dilates only in the XY plane (not Z) because:

  • The radius is 2D: [radius, radius]
  • The layer=True parameter processes each 2D slice independently

For 3D volumetric images typical in microscopy, this means the ROI expands horizontally but not vertically (in Z). If full 3D dilation is needed, use [radius, radius, radius] with layer=False:

-    sprawl_area = dilate(bin_roi, [radius, radius], True)
+    sprawl_area = dilate(bin_roi, [radius, radius, radius], layer=False)

If 2D-only dilation is intentional (e.g., for Z-stacks where vertical dilation is undesired), consider adding a comment to document this design choice.

Good catch on Line 41: components_num now correctly uses np.max(roi_) from the filtered/remapped ROI rather than the original, avoiding the mismatch flagged in past reviews. Based on learnings.


52-70: LGTM! Saving logic is correct.

The script properly:

  • Constructs the output file path by inserting the suffix before the file extension
  • Creates a MaskProjectTuple with the updated ROI and preserved metadata
  • Passes the selected_components list (updated when filtering or original otherwise)
  • Uses appropriate SaveROIOptions with relative paths and mask data preservation

92-93: LGTM! Standard script entry point.

The if __name__ == "__main__" guard with sys.exit(main()) is the correct idiom for executable Python scripts, ensuring the return code is properly propagated.

@Czaki Czaki force-pushed the add_usefull_scripts branch from 5964f72 to 50be8f4 Compare October 20, 2025 22:11
@Czaki Czaki removed this from the 0.16.4 milestone Oct 21, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
examples/dilate_segmentation_mask.py (2)

39-50: Confirm whether 2D-only dilation is intentional before sprawl

sprawl_area = dilate(bin_roi, [radius, radius], True) suggests dilation is applied only in X/Y with layer=True (per-plane), even for 3D volumes. Given that calculate_distances_array(project.image.spacing, get_neigh(True)) and euclidean_sprawl operate in full 3D when spacing is 3D, this may under-dilate along Z.

If the goal is isotropic 3D expansion, consider switching to a 3D radius and disabling per-layer mode, e.g.:

-    sprawl_area = dilate(bin_roi, [radius, radius], True)
+    sprawl_area = dilate(bin_roi, [radius, radius, radius], layer=False)

If instead you intentionally want per-slice 2D dilation, a short comment explaining that choice would make this behavior clearer to readers.


73-89: Align --dilate CLI type with radius: float parameter

convert_mask declares radius: float, but Line 76 defines --dilate as type=int. This works at runtime, but it’s inconsistent with the function signature and previous review feedback, and prevents passing non-integer radii if you ever support them.

You can align the types (and slightly improve UX) by treating it as float:

 def main():
     parser = ArgumentParser()
     parser.add_argument("project_files", nargs="+", type=str)
-    parser.add_argument("--dilate", type=int, default=1)
+    parser.add_argument("--dilate", type=float, default=1.0)
     parser.add_argument("--suffix", type=str, default="_dilated")
     parser.add_argument("--only-selected", action="store_true")
@@
     for file_path in files:
         convert_mask(Path(file_path).absolute(), args.dilate, args.suffix, args.only_selected)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5964f72 and a03eee8.

📒 Files selected for processing (4)
  • examples/dilate_segmentation_mask.py (1 hunks)
  • examples/extract_components_from_project.py (1 hunks)
  • examples/max_projection.py (1 hunks)
  • package/PartSegImage/image_reader.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • package/PartSegImage/image_reader.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-15T11:26:16.151Z
Learnt from: Czaki
Repo: 4DNucleome/PartSeg PR: 1307
File: examples/dilate_segmentation_mask.py:0-0
Timestamp: 2025-10-15T11:26:16.151Z
Learning: In the PartSeg project, the minimum supported numpy version is 1.22.2, with a restriction to numpy<2 for Python versions below 3.10. Features from newer numpy versions (e.g., numpy 2.3) should be avoided to maintain backward compatibility.

Applied to files:

  • examples/dilate_segmentation_mask.py
🧬 Code graph analysis (3)
examples/extract_components_from_project.py (1)
package/PartSegCore/mask/io_functions.py (3)
  • LoadROIImage (410-452)
  • SaveComponents (617-651)
  • SaveComponentsOptions (557-563)
examples/max_projection.py (3)
package/PartSegImage/image.py (2)
  • Image (198-1034)
  • spacing (779-781)
package/PartSegImage/image_writer.py (1)
  • ImageWriter (25-115)
package/PartSegImage/image_reader.py (3)
  • TiffImageReader (461-683)
  • read_image (168-189)
  • read_image (268-289)
examples/dilate_segmentation_mask.py (5)
package/PartSegCore/image_operations.py (2)
  • dilate (96-105)
  • to_binary_image (138-140)
package/PartSegCore/mask/io_functions.py (4)
  • LoadROIImage (410-452)
  • MaskProjectTuple (62-111)
  • SaveROI (529-554)
  • SaveROIOptions (114-125)
package/PartSegCore/roi_info.py (1)
  • ROIInfo (33-124)
package/PartSegCore/segmentation/watershed.py (2)
  • calculate_distances_array (281-297)
  • get_neigh (268-269)
package/PartSegImage/image.py (2)
  • spacing (779-781)
  • fit_array_to_image (576-586)
🪛 GitHub Check: Codacy Static Code Analysis
examples/extract_components_from_project.py

[warning] 28-28: examples/extract_components_from_project.py#L28
Too many positional arguments for function call

🪛 Ruff (0.14.5)
examples/max_projection.py

20-20: Avoid specifying long messages outside the exception class

(TRY003)

examples/dilate_segmentation_mask.py

20-20: Avoid specifying long messages outside the exception class

(TRY003)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Base py3.11 / macos-15-intel py 3.11 latest PyQt5
  • GitHub Check: Base py3.11 / windows-latest py 3.11 latest PyQt5
  • GitHub Check: Base py3.12 / ubuntu-22.04 py 3.12 latest PyQt6
  • GitHub Check: Base py3.10 / ubuntu-22.04 py 3.10 latest PySide2
  • GitHub Check: Base py3.12 / macos-15 py 3.12 latest PyQt6
  • GitHub Check: Test PartSeg conda
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos_arm)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test windows)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller macos)
  • GitHub Check: 4DNucleome.PartSeg (Builds sdist)
  • GitHub Check: 4DNucleome.PartSeg (Tests_other test macos)
  • GitHub Check: 4DNucleome.PartSeg (Tests_linux test_linux)
  • GitHub Check: 4DNucleome.PartSeg (Builds pyinstaller_linux)
  • GitHub Check: 4DNucleome.PartSeg (manifest_check manifest_check)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check help)
  • GitHub Check: 4DNucleome.PartSeg (formatting_check check_formating)
  • GitHub Check: 4DNucleome.PartSeg (Documentation_check Notebook_check)
  • GitHub Check: 4DNucleome.PartSeg (GetTestData linux)
  • GitHub Check: Sourcery review
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (3)
examples/max_projection.py (1)

13-45: Z max-projection with optional mask looks consistent with core APIs

The max_projection implementation correctly:

  • Validates presence of a "Z" axis before indexing.
  • Uses image.axis_order.index("Z") for the image data and image.array_axis_order.index("Z") for the mask, which matches how Image arranges data vs. mask.
  • Builds a projected Image with axes_order having "Z" removed and spacing=image.spacing[1:], consistent with the (Z, Y, X) spacing convention in the rest of the codebase.

The CLI wrapper is straightforward and skips _mask files appropriately when --with-mask is used.

examples/dilate_segmentation_mask.py (2)

18-21: Radius validation is fine; Ruff TRY003 can likely be ignored here

The radius <= 0 guard with ValueError("Radius must be positive") is an appropriate early validation for a CLI script. The Ruff TRY003 warning about “long messages outside the exception class” is stylistic; for this small example script, the explicit message is clear and reasonable, so I would keep it as-is unless you want to tune Ruff settings for examples.


25-38: Component remapping and selected_components update look coherent

The only_selected branch:

  • Masks roi_ down to selected_components.
  • Uses np.unique and a lookup mapping to remap labels to a dense 0..N range.
  • Resets selected_components to range(1, len(unique_values)) so it matches the new label IDs (skipping background 0).

This keeps components_num = np.max(roi_) in sync with the actual labels and avoids the earlier mismatch when using a filtered ROI.

@sonarqubecloud
Copy link

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.

2 participants