def generalfix_AddPresentationLUTShape(ds: Dataset, log: list) -> bool: fixed = False photo_in_tg = tag_for_keyword('PhotometricInterpretation') if photo_in_tg not in ds: return fixed photo_in_v = ds[photo_in_tg].value pres_lut_shape_tg = tag_for_keyword('PresentationLUTShape') if pres_lut_shape_tg in ds: pres_lut_shape_a = ds[pres_lut_shape_tg] else: pres_lut_shape_a = DataElementX(pres_lut_shape_tg, dictionary_VR(pres_lut_shape_tg), '') old_pls = pres_lut_shape_a.value if photo_in_v == 'MONOCHROME2' and old_pls != 'IDENTITY': new_pls = 'IDENTITY' pres_lut_shape_a.value = new_pls fixed = True elif photo_in_v == 'MONOCHROME1' and old_pls != 'INVERSE': new_pls = 'INVERSE' pres_lut_shape_a.value = new_pls fixed = True if fixed: ds[pres_lut_shape_tg] = pres_lut_shape_a msg = ErrorInfo() msg.msg = 'General Fix - {}'.format( "<PresentationLUTShape> is wrong or absent") msg.fix = "fixed by setting the <PresentationLUTShape>"\ " from '{}' to '{}'".format(old_pls, new_pls) log.append(msg.getWholeMessage()) return fixed
def subfix_HasTrailingNulls(elem: DataElementX) -> bool: fixed = False if elem.is_empty: return False if type(elem.value) == MultiValue: v = elem.value else: v = [elem.value] for i in range(0, len(v)): if type(v[i]) == str or type(v[i]) == bytes: l = len(v[i]) idx = l - 1 while idx >= 0: if v[i][idx] == 0: idx -= 1 else: break idx = idx + 1 if idx < l: v[i] = v[i][:idx] if len(v) % 2 == 1: v[i] = v[i] + b'\x20' fixed = True if fixed: if type(elem.value) == MultiValue: elem.value = v else: elem.value = v[0] return fixed
def CodeSeqItemGenerator(code_value: str, code_meaning: str, coding_shceme_designator: str) -> Dataset: output = Dataset() cv = Dictionary.tag_for_keyword('CodeValue') cm = Dictionary.tag_for_keyword('CodeMeaning') cs = Dictionary.tag_for_keyword('CodingSchemeDesignator') output[cv] = DataElementX(cv, Dictionary.dictionary_VR(cv), code_value) output[cm] = DataElementX(cm, Dictionary.dictionary_VR(cm), code_meaning) output[cs] = DataElementX(cs, Dictionary.dictionary_VR(cs), coding_shceme_designator) return output
def add_anatomy(ds: Dataset, BodyPartExamined_value: str, AnatomicRegionSequence_value: tuple, log: list, check_consistency: bool = True): bpe = tag_for_keyword('BodyPartExamined') ars = tag_for_keyword('AnatomicRegionSequence') old_bpe, old_ars = get_old_anatomy(ds) if old_bpe is None: if check_consistency: new_bpe, new_ars = \ CorrectAnatomicInfo( BodyPartExamined_value, AnatomicRegionSequence_value) else: new_bpe, new_ars = (BodyPartExamined_value, AnatomicRegionSequence_value) else: new_bpe, new_ars = CorrectAnatomicInfo(old_bpe, old_ars) if old_bpe != new_bpe and new_bpe is not None: bpe_a = DataElementX(bpe, dictionary_VR(bpe), new_bpe) old_bpe_txt = old_bpe if bpe not in ds else ds[bpe].value ds[bpe] = bpe_a msg = ErrorInfo() msg.msg = 'General Fix - {}'.format("<BodyPartExamined> is absent") msg.fix = "fixed by setting the <BodyPartExamined>"\ "from {} to '{}'".format(old_bpe_txt, new_bpe) log.append(msg.getWholeMessage()) if is_accurate_code_seq(new_ars) and not is_code_equal(old_ars, new_ars): code_value, code_meaning, coding_scheme_designator = new_ars if ars in ds: old_item_text = subfix_CodeSeqItem2txt(ds[ars], 0) else: old_item_text = 'None' new_item = CodeSeqItemGenerator(str(code_value), code_meaning, coding_scheme_designator) ars_a = DataElementX(ars, 'SQ', DicomSequence([ new_item, ])) ds[ars] = ars_a msg = ErrorInfo() msg.msg = 'General Fix - {}'.format( "<AnatomicRegionSequence> is absent") msg.fix = "fixed by setting the <AnatomicRegionSequence>"\ "from {} to '{}'".format( old_item_text, subfix_CodeSeqItem2txt(ars_a, 0)) log.append(msg.getWholeMessage()) AddLaterality(ds, log)
def generalfix_VM1(ds, log): fixed = False elemsTobeCorrected = [] for key, a in ds.items(): a = ds[key] if key.is_private: continue try: dvr = dictionary_VM(key) except (BaseException): return fixed if dvr != '1': continue if a.VM <= 1: continue if (a.VR != 'LT' or a.VR != 'LO'): continue concat = '/'.join(a.value) ds[key] = DataElementX(key, a.VR, concat) fixed = True err = "<{}> {}".format(a.keyword, validate_vr.tag2str(a.tag)) msg = ErrorInfo( "General Fix - Value Multiplicity for {} "\ "is not allowed to be more than one".format(err), "fixed by concatenating all itmes into one {}".format(concat)) log.append(msg.getWholeMessage()) fixed = True return fixed
def generalfix_CheckAndFixModality(ds: Dataset, log: list) -> bool: fixed = False modality_sop = { CTImageStorageSOPClassUID: 'CT', MRImageStorageSOPClassUID: 'MR', PETImageStorageSOPClassUID: 'PT', } if 'SOPClassUID' in ds: sop_class = ds['SOPClassUID'].value else: return False if sop_class not in modality_sop: return False mod_tg = tag_for_keyword('Modality') if mod_tg in ds: modality = ds[mod_tg].value else: modality = '' if modality == '' or modality != modality_sop[sop_class]: ds[mod_tg] = DataElementX(mod_tg, dictionary_VR(mod_tg), modality_sop[sop_class]) msg = ErrorInfo() msg.msg = 'General Fix - {}'.format("<Modality> is wrong or absent") msg.fix = "fixed by reading the <SOPClassUID> and setting <Modality>"\ " from '{}' to '{}'".format(modality, modality_sop[sop_class]) log.append(msg.getWholeMessage()) fixed = True return fixed
def subfix_ReplaceSlashWithBackslash(attrib: DataElementX, log: list) -> bool: fixed = False msg = mesgtext_cc.ErrorInfo('General Fix -', '') fix = 'fixed attribute <{}> ({}) value by changing slash to backslash {} -> {}' if type(attrib.value) == MultiValue: tmp = attrib.value else: tmp = [attrib.value] for idx in range(0, len(tmp)): old_val = tmp[idx] if type(tmp[idx]) == str: tmp[idx] = tmp[idx].replace('/', '\\') elif type(tmp[idx]) == bytes: x = bytearray() for elem in tmp[idx]: if elem == ord('/'): x.append(ord('\\')) else: x.append(elem) tmp[idx] = bytes(x) if old_val != tmp[idx]: msg.fix = fix.format(attrib.name, idx + 1, old_val, tmp[idx]) log.append(msg.getWholeMessage()) fixed = True if type(attrib.value) != MultiValue: attrib.value = tmp[0] return fixed
def subfix_AddOrChangeAttrib(ds: Dataset, log: list, error_regexp: str, fix_message: str, keyword: str, value) -> bool: fixed = False t = Dictionary.tag_for_keyword(keyword) if t is None: return False desc = Dictionary.dictionary_description(t) ErrorOccured = False log_l = len(log) for i in range(0, log_l): if re.match(error_regexp, log[i]) is not None: msg = mesgtext_cc.ErrorInfo(log[i], fix_message) log[i] = msg.getWholeMessage() ErrorOccured = True if ErrorOccured: if keyword in ds: ds[keyword].value = value # just modify fixed = True else: vr = Dictionary.dictionary_VR(t) vm = 1 elem = DataElementX(t, vr, value) ds[keyword] = elem fixed = True return fixed
def fix_ByAddingEmptyAttrib(ds: Dataset, element: str) -> str: reason = '' ttag = tag_for_keyword(element) if ttag is not None: vr = Dic.dictionary_VR(ttag) element = DataElementX(ttag, vr, '') element.value = element.empty_value ds[ttag] = element reason += "fixed by adding empty attribute" return reason
def fix_ReferencedImageSequence(ds, log: list) -> bool: # This patch is a prticular fixing procedure to replace SOPInstanceUID # with ReferencedSOPInstanceUID and SOPClassUID with ReferencedSOPClassUID fixed = False kw = 'ReferencedImageSequence' tg = Dictionary.tag_for_keyword(kw) if tg not in ds: return True val = ds[tg].value ref_cls_kw = 'ReferencedSOPClassUID' ref_cls_tg = Dictionary.tag_for_keyword(ref_cls_kw) ref_inst_kw = 'ReferencedSOPInstanceUID' ref_inst_tg = Dictionary.tag_for_keyword(ref_inst_kw) i = 0 while i < len(val): item = val[i] if 'SOPInstanceUID' in item: msg = mesgtext_cc.ErrorInfo() msg.msg = "Item {}/{} in <ReferencedImageSequence> holds "\ "<SOPInstanceUID> instead of <ReferencedSOPInstanceUID>".format(i + 1, len(val)) msg.fix = "fixed by changing the attribute into <ReferencedSOPInstanceUID>" log.append(msg.getWholeMessage()) item[ref_inst_tg] = DataElementX(ref_inst_tg, 'UI', item['SOPInstanceUID'].value) del item['SOPInstanceUID'] fixed = True if 'SOPClassUID' in item: msg = mesgtext_cc.ErrorInfo() msg.msg = "Item {}/{} in <ReferencedImageSequence> holds "\ "<SOPClassUID> instead of <ReferencedSOPClassUID>".format(i + 1, len(val)) msg.fix = "fixed by changing the attribute into <ReferencedSOPClassUID>" log.append(msg.getWholeMessage()) item[ref_cls_tg] = DataElementX(ref_cls_tg, 'UI', item['SOPClassUID'].value) del item['SOPClassUID'] fixed = True i += 1 return fixed
def subfix_UpdateOrInsertCodeAttribute(seqelem: DataElementX, index: int, kw: str, value: str) -> str: text_fun = lambda ds, att: '{}: {}\t'.format(att, ds[att]) out_msg = '' if kw in seqelem.value[index]: out_msg = " {} modified <{}> -> <{}>".format( kw, seqelem.value[index][kw].value, value) seqelem.value[index][kw].value = value else: out_msg = "{} = <{}> was added".format(kw, value) newtag = Dictionary.tag_for_keyword(kw) newvr = Dictionary.dictionary_VR(newtag) elem = DataElementX(newtag, newvr, value) seqelem.value[index].add(elem) return out_msg
def generalfix_RealWorldValueMappingSequence(ds, log): kw = 'RealWorldValueMappingSequence' tg = tag_for_keyword(kw) if tg in ds: v = ds[tg].value for i, item in enumerate(v): in_key = 'LUTLabel' in_tg = tag_for_keyword(in_key) if in_tg not in item: new_el = DataElementX(in_tg, 'SH', 'Unknown') item[in_tg] = new_el err = "<{}> {}".format(in_key, validate_vr.tag2str(in_tg)) msg = ErrorInfo( "General Fix - The item number {} lacks {}".format(i, err), "fixed by adding a new element with value <{}>".format( new_el.value)) log.append(msg.getWholeMessage())
def put_attribute_in_path(ds: Dataset, path: list, a: DataElementX): if not path: if a.tag not in ds: ds[a.tag] = a else: kw = path.pop(0) tg = Dictionary.tag_for_keyword(kw) vr = Dictionary.dictionary_VR(tg) if vr == 'SQ': if tg in ds and ds[tg].VM > 0: inner_sq = ds[tg] item = inner_sq.value[0] else: item = Dataset() new_element = DataElementX(tg, vr, Sequence([item])) ds[tg] = new_element put_attribute_in_path(item, path, a)
def generalfix_MisplacedAttributes(ds: Dataset, log: list): paths = {} current_folder = os.path.dirname(__file__) with open(os.path.join(current_folder, 'config.json')) as json_file: fix_config = json.load(json_file) get_all_kw_paths(ds, [], paths) standard_ds = get_full_attrib_list(ds) not_in_std = {} misplaced = {} for kw, pp in paths.items(): tg = tag_for_keyword(kw) if kw not in standard_ds: not_in_std[kw] = pp else: std__ = standard_ds[kw] if len(std__) > 1: continue for parent, path in pp: correct_path = std__[0]['path'] if path != correct_path: if kw not in misplaced: misplaced[kw] = [(parent, path, correct_path)] else: misplaced[kw].append((parent, path, correct_path)) for kw, val in misplaced.items(): if not fix_config["MisplacedAttributes"][ "DisplaceIfAttributeIsInWrongPath"]: break for parent, path, correct_path in val: # print(parent, path, correct_path) delem = parent[kw] del parent[kw] new_paretn = ds inner_kw = kw parent_ds = ds for p in correct_path: if p not in parent_ds: new_ds = Dataset() parent_ds[p] = DataElementX(tag_for_keyword(p), 'SQ', DataElementSequence([new_ds])) elif parent_ds[p].is_empty: new_ds = Dataset() parent_ds[p].value = DataElementSequence([new_ds]) parent_ds = parent_ds[p].value[0] if kw not in parent_ds: parent_ds[kw] = delem msg = ErrorInfo( "General Fix - The DICOM attribute <{}> is not in " "correct place. " "current-path = {} vs. correct-path = {}".format( kw, path, correct_path), "fixed by attribute displacement") else: msg = ErrorInfo( "General Fix - The DICOM attribute <{}> is not in " "correct place. " "current-path = {} vs. correct-path = {}".format( kw, path, correct_path), "couldn't fix because there is already " "an attribute in correct place") log.append(msg.getWholeMessage()) for kw, val in not_in_std.items(): if not fix_config["MisplacedAttributes"]["RemoveIfAttributeIsNotInIOD"]: break for parent, path in val: if kw in parent: del parent[kw] msg = ErrorInfo( "General Fix - The keyword <{}> is not in " "standard DICOM IOD".format(kw), "fixed by removing the attribute") log.append(msg.getWholeMessage())
def subfix_checkandfixBasicCodeSeq(seqelem: DataElementX, log: list) -> bool: fixed = False msg = mesgtext_cc.ErrorInfo("General Fix - Remove empty code seq ") items_to_be_deleted = [] text_fun = lambda ds, att: '{}: <{}>\t'.format(att, ds[att].value) subfix_UpdateSRTCodes(seqelem, log) for i, item in enumerate(seqelem.value, 1): hasCodeValue = True if "CodeValue" in item else False emptyCodeValue = True if not hasCodeValue else item[ "CodeValue"].is_empty textCodeValue = '' if not hasCodeValue else text_fun(item, "CodeValue") hasCodeMeaning = True if "CodeMeaning" in item else False emptyCodeMeaning = True if not hasCodeMeaning else item[ "CodeMeaning"].is_empty textCodeMeaning = '' if not hasCodeMeaning else text_fun( item, "CodeMeaning") hasLongCodeValue = True if "LongCodeValue" in item else False emptyLongCodeValue = True if not hasLongCodeValue else item[ "LongCodeValue"].is_empty textLongCodeValue = '' if not hasLongCodeValue else text_fun( item, "LongCodeValue") hasURNCodeValue = True if "URNCodeValue" in item else False emptyURNCodeValue = True if not hasURNCodeValue else item[ "CodURNCodeValue"].is_empty textURNCodeValue = '' if not hasURNCodeValue else text_fun( item, "CodURNCodeValue") hasCodingSchemeDesignator = True if "CodingSchemeDesignator" in item else False emptyCodingSchemeDesignator = True if not hasCodingSchemeDesignator else item[ "CodingSchemeDesignator"].is_empty textCodingSchemeDesignator = '' if not hasCodingSchemeDesignator else text_fun( item, "CodingSchemeDesignator") hasCodingSchemeVersion = True if "CodingSchemeVersion" in item else False emptyCodingSchemeVersion = True if not hasCodingSchemeVersion else item[ "CodingSchemeVersion"].is_empty textCodingSchemeVersion = '' if not hasCodingSchemeVersion else text_fun( item, "CodingSchemeVersion") hasCodeValue = hasCodeValue or hasLongCodeValue or hasURNCodeValue emptyCodeValue = not (not emptyCodeValue or not emptyLongCodeValue or not emptyURNCodeValue) textCodeValue = (textCodeValue + textLongCodeValue + textURNCodeValue) bit_code = numpy.uint8(0) if emptyCodeValue: bit_code = bit_code | 0x1 if emptyCodeMeaning: bit_code = bit_code | 0x10 if emptyCodingSchemeDesignator: bit_code = bit_code | 0x100 state_text = subfix_CodeSeqItem2txt(seqelem, i - 1) state_text = ". Item current value is: " + state_text if state_text != '' else '' if hasCodingSchemeVersion and emptyCodingSchemeVersion: del item["CodingSchemeVersion"] msg.fix = "Empty CodingSchemeVersion in item {} of {} was deleted".format( i, seqelem.name) + state_text del_msg = msg.getWholeMessage() log.append(del_msg) if bit_code == 0x111 or bit_code == 0x110 or bit_code == 0x011 or \ bit_code == 0x101 or bit_code == 0x001 or bit_code == 0x010: items_to_be_deleted.append(item) msg.fix = "Item {} of {} was deleted".format(i, seqelem.keyword) del_msg = msg.getWholeMessage() log.append(del_msg) fixed = True if bit_code == 0x100: if hasCodingSchemeDesignator: item["CodingSchemeDesignator"].value = '99LOCAL' msg.fix = "The value for CodingSchemeDesignator in "\ "item {} of {} was modified to 99LOCAL".format( i, seqelem.keyword ) + state_text del_msg = msg.getWholeMessage() log.append(del_msg) else: kw = "CodingSchemeDesignator" new_tag = Dictionary.tag_for_keyword(kw) new_vr = Dictionary.dictionary_VR(new_tag) new_elem = DataElementX(new_tag, new_vr, "99LOCAL") item[kw] = new_elem msg.fix = "An element of CodingSchemeDesignator with value "\ "<99LOCAL> in item {} of {} was added".format( i, seqelem.keyword ) + state_text del_msg = msg.getWholeMessage() log.append(del_msg) fixed = True for el in items_to_be_deleted: seqelem.value.remove(el)