Skip to content

Commit 66d6283

Browse files
authored
Added support of matching instance level tags, and update liver seg (#345)
* Added support of matching instance level tags, and update liver seg Signed-off-by: M Q <[email protected]> * Fix Flake8 complaint Signed-off-by: M Q <[email protected]> * Still enable publish intermediate nii files. Signed-off-by: M Q <[email protected]> * Corrections per review comments Signed-off-by: M Q <[email protected]> Signed-off-by: M Q <[email protected]>
1 parent 8c37598 commit 66d6283

File tree

3 files changed

+38
-6
lines changed

3 files changed

+38
-6
lines changed

examples/apps/ai_livertumor_seg_app/app.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@
2323
from monai.deploy.operators.dicom_series_to_volume_operator import DICOMSeriesToVolumeOperator
2424
from monai.deploy.operators.publisher_operator import PublisherOperator
2525

26+
# This is a sample series selection rule in JSON, simply selecting CT series.
27+
# If the study has more than 1 CT series, then all of them will be selected.
28+
# Please see more detail in DICOMSeriesSelectorOperator.
29+
# For list of string values, e.g. "ImageType": ["PRIMARY", "ORIGINAL"], it is a match if all elements
30+
# are all in the multi-value attribute of the DICOM series.
31+
32+
Sample_Rules_Text = """
33+
{
34+
"selections": [
35+
{
36+
"name": "CT Series",
37+
"conditions": {
38+
"Modality": "(?i)CT",
39+
"ImageType": ["PRIMARY", "ORIGINAL"],
40+
"PhotometricInterpretation": "MONOCHROME2"
41+
}
42+
}
43+
]
44+
}
45+
"""
46+
2647

2748
@resource(cpu=1, gpu=1, memory="7Gi")
2849
# pip_packages can be a string that is a path(str) to requirements.txt file or a list of packages.
@@ -46,7 +67,7 @@ def compose(self):
4667
self._logger.debug(f"Begin {self.compose.__name__}")
4768
# Creates the custom operator(s) as well as SDK built-in operator(s).
4869
study_loader_op = DICOMDataLoaderOperator()
49-
series_selector_op = DICOMSeriesSelectorOperator()
70+
series_selector_op = DICOMSeriesSelectorOperator(rules=Sample_Rules_Text)
5071
series_to_vol_op = DICOMSeriesToVolumeOperator()
5172
# Model specific inference operator, supporting MONAI transforms.
5273
liver_tumor_seg_op = LiverTumorSegOperator()

examples/apps/ai_livertumor_seg_app/livertumor_seg_operator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
@md.input("image", Image, IOType.IN_MEMORY)
3535
@md.output("seg_image", Image, IOType.IN_MEMORY)
3636
@md.output("saved_images_folder", DataPath, IOType.DISK)
37-
@md.env(pip_packages=["monai>=0.8.1", "torch>=1.5", "numpy>=1.21", "nibabel"])
37+
@md.env(pip_packages=["monai==0.9.0", "torch>=1.5", "numpy>=1.21", "nibabel"])
3838
class LiverTumorSegOperator(Operator):
3939
"""Performs liver and tumor segmentation using a DL model with an image converted from a DICOM CT series.
4040

monai/deploy/operators/dicom_series_selector_operator.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,17 @@ def _select_series(self, attributes: dict, study: DICOMStudy, all_matched=False)
219219
continue
220220
# Try getting the attribute value from Study and current Series prop dict
221221
attr_value = series_attr.get(key, None)
222-
logging.info(f" Series attribute value: {attr_value}")
222+
logging.info(f" Series attribute {key} value: {attr_value}")
223+
224+
# If not found, try the best at the native instance level for string VR
225+
# This is mainly for attributes like ImageType
226+
if not attr_value:
227+
try:
228+
attr_value = [series.get_sop_instances()[0].get_native_sop_instance()[key].repval]
229+
series_attr.update({key: attr_value})
230+
except Exception:
231+
logging.info(f" Attribute {key} not at instance level either.")
232+
223233
if not attr_value:
224234
matched = False
225235
elif isinstance(attr_value, numbers.Number):
@@ -233,12 +243,13 @@ def _select_series(self, attributes: dict, study: DICOMStudy, all_matched=False)
233243
if re.search(value_to_match, attr_value, re.IGNORECASE):
234244
matched = True
235245
elif isinstance(attr_value, list):
236-
meta_data_set = {str(element).lower() for element in attr_value}
246+
# Assume multi value string attributes
247+
meta_data_list = str(attr_value).lower()
237248
if isinstance(value_to_match, list):
238249
value_set = {str(element).lower() for element in value_to_match}
239-
matched = all(val in meta_data_set for val in value_set)
250+
matched = all(val in meta_data_list for val in value_set)
240251
elif isinstance(value_to_match, (str, numbers.Number)):
241-
matched = str(value_to_match).lower() in meta_data_set
252+
matched = str(value_to_match).lower() in meta_data_list
242253
else:
243254
raise NotImplementedError(f"Not support for matching on this type: {type(value_to_match)}")
244255

0 commit comments

Comments
 (0)