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
示例#9
0
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)