Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StructureSetToSegmentation types #126

Closed
jjjermiah opened this issue Oct 22, 2024 · 1 comment · Fixed by #128
Closed

StructureSetToSegmentation types #126

jjjermiah opened this issue Oct 22, 2024 · 1 comment · Fixed by #128

Comments

@jjjermiah
Copy link
Contributor

Between:

class StructureSetToSegmentation(BaseOp):
"""StructureSetToSegmentation operation class:
A callable class that accepts ROI names, a StrutureSet object, and a reference image, and
returns Segmentation mask.
To instantiate:
obj = StructureSet(roi_names)
To call:
mask = obj(structure_set, reference_image)
Parameters
----------
roi_names
List of Region of Interests
"""
def __init__(self,
roi_names: Dict[str, str],
continuous: bool = True):
"""Initialize the op.
Parameters
----------
roi_names
List of ROI names to export. Both full names and
case-insensitive regular expressions are allowed.
All labels within one sublist will be assigned
the same label.
continuous
flag passed to 'physical_points_to_idxs' in 'StructureSet.to_segmentation'.
Helps to resolve errors caused by ContinuousIndex > Index.
Notes
-----
If `self.roi_names` contains lists of strings, each matching
name within a sublist will be assigned the same label. This means
that `roi_names=['pat']` and `roi_names=[['pat']]` can lead
to different label assignments, depending on how many ROI names
match the pattern. E.g. if `self.roi_names = ['fooa', 'foob']`,
passing `roi_names=['foo(a|b)']` will result in a segmentation with
two labels, but passing `roi_names=[['foo(a|b)']]` will result in
one label for both `'fooa'` and `'foob'`.
If `roi_names` is kept empty ([]), the pipeline will process all ROIs/contours
found according to their original names.
In general, the exact ordering of the returned labels cannot be
guaranteed (unless all patterns in `roi_names` can only match
a single name or are lists of strings).
"""
self.roi_names = roi_names
self.continuous = continuous
def __call__(self,
structure_set: StructureSet,
reference_image: sitk.Image,
existing_roi_indices: Dict[str, int],
ignore_missing_regex: bool,
roi_select_first: bool = False,
roi_separate: bool = False) -> Segmentation:
"""Convert the structure set to a Segmentation object.
Parameters
----------
structure_set
The structure set to convert.
reference_image
Image used as reference geometry.
Returns
-------
Segmentation
The segmentation object.
"""
return structure_set.to_segmentation(reference_image,
roi_names=self.roi_names,
continuous=self.continuous,
existing_roi_indices=existing_roi_indices,
ignore_missing_regex=ignore_missing_regex,
roi_select_first=roi_select_first,
roi_separate=roi_separate)

and

def to_segmentation(self, reference_image: sitk.Image,
roi_names: Dict[str, str] = None,
continuous: bool = True,
existing_roi_indices: Dict[str, int] = None,
ignore_missing_regex: bool = False,
roi_select_first: bool = False,
roi_separate: bool = False) -> Segmentation:
"""Convert the structure set to a Segmentation object.
Parameters
----------
reference_image
Image used as reference geometry.
roi_names
List of ROI names to export. Both full names and
case-insensitive regular expressions are allowed.
All labels within one sublist will be assigned
the same label.
Returns
-------
Segmentation
The segmentation object.
Notes
-----
If `roi_names` contains lists of strings, each matching
name within a sublist will be assigned the same label. This means
that `roi_names=['pat']` and `roi_names=[['pat']]` can lead
to different label assignments, depending on how many ROI names
match the pattern. E.g. if `self.roi_names = ['fooa', 'foob']`,
passing `roi_names=['foo(a|b)']` will result in a segmentation with
two labels, but passing `roi_names=[['foo(a|b)']]` will result in
one label for both `'fooa'` and `'foob'`.
In general, the exact ordering of the returned labels cannot be
guaranteed (unless all patterns in `roi_names` can only match
a single name or are lists of strings).
"""
labels = {}
if roi_names is None or roi_names == {}:
roi_names = self.roi_names # all the contour names
labels = self._assign_labels(roi_names, roi_select_first, roi_separate) # only the ones that match the regex
elif isinstance(roi_names, dict):
for name, pattern in roi_names.items():
if isinstance(pattern, str):
matching_names = list(self._assign_labels([pattern], roi_select_first).keys())
if matching_names:
labels[name] = matching_names # {"GTV": ["GTV1", "GTV2"]} is the result of _assign_labels()
elif isinstance(pattern, list): # for inputs that have multiple patterns for the input, e.g. {"GTV": ["GTV.*", "HTVI.*"]}
labels[name] = []
for pattern_one in pattern:
matching_names = list(self._assign_labels([pattern_one], roi_select_first).keys())
if matching_names:
labels[name].extend(matching_names) # {"GTV": ["GTV1", "GTV2"]}
if isinstance(roi_names, str):
roi_names = [roi_names]
if isinstance(roi_names, list): # won't this always trigger after the previous?
labels = self._assign_labels(roi_names, roi_select_first)
print("labels:", labels)

can I assume that the type hint for roi_names is incorrect?

@jjjermiah
Copy link
Contributor Author

unsure about the examples but at a glance:?

    Examples
    --------
    Example 1: Using `None` (Default Behavior)
    If no `roi_names` is specified or set to None, all ROIs will be used.
    
    >>> roi_names = None
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {"GTV1", "GTV2", "CTV", "Lung_Left", "Heart"}  # Example output

    Example 2: Using a Single String Pattern
    Matches any ROI name that contains "GTV" (case-sensitive).
    
    >>> roi_names = "GTV"
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {"GTV1", "GTV2"}  # Example output when self.roi_names = ["GTV1", "GTV2", "CTV"]

    Example 3: Using a Regular Expression String Pattern
    Matches ROI names starting with "PTV" or "CTV".
    
    >>> roi_names = "^(PTV|CTV)"
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {"PTV1", "CTV1"}  # Example output when self.roi_names = ["PTV1", "CTV1", "GTV"]

    Example 4: Using a List of Patterns
    Matches ROI names that start with "Lung" or "Heart".
    
    >>> roi_names = ["Lung", "Heart"]
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {"Lung_Left", "Lung_Right", "Heart"}  # Example output

    Example 5: Using a Dictionary with Single Patterns
    Matches specific ROIs and groups them under a common label.
    
    >>> roi_names = {"Tumor": "GTV", "Lung": "Lung"}
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {"Tumor": ["GTV1", "GTV2"], "Lung": ["Lung_Left"]}  # Example output

    Example 6: Using a Dictionary with Lists of Patterns
    Combine ROIs under common labels using multiple patterns.
    
    >>> roi_names = {
    ...     "Tumor": ["GTV.*", "Neoplasm"],
    ...     "Organs": ["Lung.*", "Heart"]
    ... }
    >>> seg = StructureSetToSegmentation(reference_image, roi_names=roi_names)
    >>> print(seg.roi_names)
    {
        "Tumor": ["GTV1", "Neoplasm_1"],
        "Organs": ["Lung_Left", "Heart"]
    }  # Example output

@jjjermiah jjjermiah linked a pull request Oct 22, 2024 that will close this issue
jjjermiah added a commit that referenced this issue Oct 22, 2024
…128)

* chore: bump artifact action version

* refactor: update StructureSetToSegmentation docstring and remove unused dev group in pyproject.toml

* chore: fix download action in workflow to use correct artifact version

* chore: correct instantiation reference and update parameter description in StructureSetToSegmentation docstring

* chore: enhance StructureSetToSegmentation docstring to support None as a roi_names option for loading all ROIs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant