Skip to content

Commit 45d88c8

Browse files
committed
exclusion matching for set type
Signed-off-by: bluna301 <[email protected]>
1 parent 7aa5901 commit 45d88c8

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

monai/deploy/operators/dicom_series_selector_operator.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class DICOMSeriesSelectorOperator(Operator):
4848
3. each condition uses the implicit equal operator; in addition, the following are supported:
4949
- regex, relational, and range matching for float and int types
5050
- regex matching for str type
51-
- union matching for set type
51+
- inclusion and exclusion matching for set type
5252
- image orientation check for the ImageOrientationPatient tag
5353
4. DICOM attribute keywords are used, and only for those defined as DICOMStudy and DICOMSeries properties
5454
@@ -88,6 +88,14 @@ class DICOMSeriesSelectorOperator(Operator):
8888
"ImageOrientationPatient": "Axial",
8989
"SliceThickness": [2, ">"]
9090
}
91+
},
92+
{
93+
"name": "CT Series 5",
94+
"conditions": {
95+
"StudyDescription": "(.*?)",
96+
"Modality": "(?i)CT",
97+
"ImageType": ["PRIMARY", "!SECONDARY"]
98+
}
9199
}
92100
]
93101
}
@@ -163,7 +171,7 @@ def filter(
163171
Supported matching logic:
164172
Float + Int: exact matching, relational matching, range matching, and regex matching
165173
String: matches case insensitive, if fails then tries RegEx search
166-
String array (set): matches as subset, case insensitive
174+
String array (set): inclusive and exclusive (via !) matching as subsets, case insensitive
167175
ImageOrientationPatient tag: image orientation (Axial, Coronal, Sagittal) matching
168176
169177
Args:
@@ -326,9 +334,19 @@ def _select_series(
326334
meta_data_list = str(attr_value).lower()
327335
if isinstance(value_to_match, list):
328336
value_set = {str(element).lower() for element in value_to_match}
329-
matched = all(val in meta_data_list for val in value_set)
337+
# split inclusion and exclusion matches using ! indicator
338+
include_terms = {v for v in value_set if not v.startswith("!")}
339+
exclude_terms = {v[1:] for v in value_set if v.startswith("!")}
340+
matched = all(term in meta_data_list for term in include_terms) and all(
341+
term not in meta_data_list for term in exclude_terms
342+
)
330343
elif isinstance(value_to_match, (str, numbers.Number)):
331-
matched = str(value_to_match).lower() in meta_data_list
344+
v = str(value_to_match).lower()
345+
# ! indicates exclusion match
346+
if v.startswith("!"):
347+
matched = v[1:] not in meta_data_list
348+
else:
349+
matched = v in meta_data_list
332350
else:
333351
raise NotImplementedError(
334352
f"No support for matching condition {value_to_match} (type: {type(value_to_match)})"
@@ -594,6 +612,14 @@ def test():
594612
"ImageOrientationPatient": "Axial",
595613
"SliceThickness": [2, ">"]
596614
}
615+
},
616+
{
617+
"name": "CT Series 5",
618+
"conditions": {
619+
"StudyDescription": "(.*?)",
620+
"Modality": "(?i)CT",
621+
"ImageType": ["PRIMARY", "!SECONDARY"]
622+
}
597623
}
598624
]
599625
}

0 commit comments

Comments
 (0)