Source code for deid.dicom.filter

__author__ = "Vanessa Sochat"
__copyright__ = "Copyright 2016-2022, Vanessa Sochat"
__license__ = "MIT"

import re

from pydicom.dataset import Dataset

from deid.logger import bot

# These filters are based off the the CTP Dicom Filter
# http://mircwiki.rsna.org/index.php?title=The_CTP_DICOM_Filter
# We don't apply them to the tag, as in their examples:

# !ImageType.contains("SECONDARY")

# we apply them to the dataset, with the tag as an argument:
# dicom.contains("ImageType","SECONDARY")


[docs]def apply_filter(dicom, field, filter_name, value): """essentially a switch statement to apply a filter to a dicom file. Parameters ========== dicom: the pydicom.dataset Dataset (pydicom.read_file) field: the name of the field to apply the filter to filer_name: the name of the filter to apply (e.g., contains) value: the value to set, if filter_name is valid """ filter_name = filter_name.lower().strip() if filter_name == "contains": return dicom.contains(field, value) if filter_name == "notcontains": return dicom.notContains(field, value) elif filter_name == "equals": return dicom.equals(field, value) elif filter_name == "missing": return dicom.missing(field) elif filter_name == "present": return not dicom.missing(field) elif filter_name == "empty": return dicom.empty(field) elif filter_name == "notequals": return dicom.notEquals(field, value) bot.warning("%s is not a valid filter name, returning False" % filter_name) return False
################################################################################ # Equals ################################################################################
[docs]def equalsBase(self, field, term, ignore_case=True, not_equals=False): """base of equals, with variable for ignore case (default True)""" is_equal = False contenders = self.get(field) if not isinstance(contenders, list): contenders = [contenders] # In this loop we can only switch to True for contender in contenders: if contender is not None: try: # both converted to string (handles tags) contender = str(contender) term = str(term) if ignore_case: contender = contender.lower().strip() term = term.lower().strip() if contender == term: is_equal = True except AttributeError: pass # we are dealing with number or sequence # If we want to know not_equals, reverse if not_equals is True: is_equal = not is_equal return is_equal
[docs]def equals(self, field, term): """returns true if the value of the identifier exactly equals the string argument; otherwise, it returns false.""" return self.equalsBase(field, term)
[docs]def notEquals(self, field, term): return self.equalsBase(field=field, term=term, not_equals=True)
Dataset.equalsBase = equalsBase Dataset.equals = equals Dataset.notEquals = notEquals ################################################################################ # Empty and Null # # missing: means the field is not present (None) # empty: means the field is present and empty ################################################################################
[docs]def missing(self, field): """Determine if the dicom is missing a field. Missing returns True if the dicom is missing the field entirely This means that the entire field is None """ content = self.get(field) if content is None: return True return False
[docs]def empty(self, field): """Determine if the value is empty. Empty returns True if the value is found to be "". If the field is not present for the dicom, then we return False (missing != empty) """ if field not in self: return False content = self.get(field) # Case 1: No content (empty list or none) if not content: return True if hasattr(content, "_list"): return len(content) == 0 # This is the case of a data element elif not isinstance(content, str): content = content.value if content == "": return True return False
Dataset.empty = empty Dataset.missing = missing ################################################################################ # Matches and Contains # # contains: searches across entire field # matches: looks for exact match ################################################################################
[docs]def compareBase(self, field, expression, func, ignore_case=True): """ Search a field for an expression. compareBase takes either re.search (for contains) or re.match (for matches) and returns True if the given regular expression is contained or matched """ is_match = False contenders = self.get(field) if not isinstance(contenders, list): contenders = [contenders] for contender in contenders: if contender is not None: try: contender = str(contender) expression = str(expression) if ignore_case: contender = contender.lower().strip() expression = expression.lower().strip() if func(expression, contender): is_match = True except AttributeError: pass # we are dealing with number or sequence return is_match
[docs]def matches(self, field, expression): """ Determine if a field value matches an expression. matches returns true if the value of the identifier matches the regular expression specified in the string argument; otherwise, it returns false. """ return self.compareBase(field=field, expression=expression, func=re.match)
[docs]def contains(self, field, expression): """ Determine if a field value contains an expression. contains returns true if the value of the identifier contains the the string argument anywhere within it; otherwise, it returns false. """ return self.compareBase(field=field, expression=expression, func=re.search)
[docs]def notContains(self, field, expression): """ Determine if a field value does not contain an expression. notContains returns true if the value of the identifier does not contain the the string argument anywhere within it; """ return not self.compareBase(field=field, expression=expression, func=re.search)
Dataset.compareBase = compareBase Dataset.matches = matches Dataset.contains = contains Dataset.notContains = notContains ################################################################################ # Starts and Endswith ################################################################################
[docs]def startsWith(self, field, term): """ Determine if a field value starts with an expression. startsWith returns true if the value of the identifier starts with the string argument; otherwise, it returns false. """ expression = "^%s" % term return self.compareBase(field=field, expression=expression, func=re.match)
[docs]def endsWith(self, field, term): """ Determine if a field value ends with an expression. endsWith returns true if the value of the identifier ends with the string argument; otherwise, it returns false. """ expression = "%s$" % term return self.compareBase(field=field, expression=expression, func=re.match)
Dataset.startsWith = startsWith Dataset.endsWith = endsWith