def fix_SOPReferencedMacro(ds: Dataset,
                           log: list,
                           suggested_SOPClassUID: dict = {}):
    kw = 'ReferencedStudySequence'
    tg = tag_for_keyword(kw)
    if tg not in ds:
        return
    val = ds[tg].value
    if len(val) == 0:
        del ds[tg]
        return
    i = 0

    while i < len(val):
        item = val[i]
        msg = ErrorInfo()
        if 'ReferencedSOPInstanceUID' not in item:
            msg.msg = 'General Fix - item {}/{} <ReferencedStudySequence>'\
                ' lacks <ReferencedSOPInstanceUID> attribute'.format(i + 1, len(val))
            msg.fix = 'fixed by removint the item'
            log.append(msg.getWholeMessage())
            val.pop(i)
            continue
        if item['ReferencedSOPInstanceUID'].is_empty:
            msg.msg = 'General Fix - item {}/{} <ReferencedStudySequence>'\
                ' holds an empty <ReferencedSOPInstanceUID> attribute'.format(i + 1, len(val))
            msg.fix = 'fixed by removint the item'
            log.append(msg.getWholeMessage())
            val.pop(i)
            continue
        if 'ReferencedSOPClassUID' not in item or\
                item['ReferencedSOPClassUID'].is_empty:
            uid = item['ReferencedSOPInstanceUID'].value
            msg.msg = 'General Fix - item {}/{} <ReferencedStudySequence>'\
                ' lacks <ReferencedSOPClassUID> attribute'.format(i + 1, len(val))
            if uid not in suggested_SOPClassUID:
                msg.fix = 'fixed by removint the item'
                log.append(msg.getWholeMessage())
                val.pop(i)
                continue
            else:
                msg.fix = 'fixed by querying the attribute '\
                    'ReferencedSOPClassUID filling it with {}'.format(
                        suggested_SOPClassUID[uid]
                    )
                log.append(msg.getWholeMessage())
                item['ReferencedSOPClassUID'].value = suggested_SOPClassUID[
                    uid]
        i += 1
    if len(val) == 0:
        msg = ErrorInfo()
        msg.msg = 'General Fix - Attribute <{}> is empty '.format(kw)
        msg.fix = 'fixed by removint the attribute'
        log.append(msg.getWholeMessage())
        del ds[tg]
    return
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 AddLaterality(ds: Dataset, log: list):
    needlaterality = checkLaterality(ds, 0, ds)
    old_laterality = None if "Laterality" not in ds else ds['Laterality'].value
    AddImageLateralityForBoth = False
    if needlaterality:
        if old_laterality is None:
            AddImageLateralityForBoth = True
        if old_laterality is not None:
            if old_laterality not in ['L', 'R']:
                if old_laterality.lower() == 'left':
                    new_laterality = 'L'
                elif old_laterality.lower() == 'right':
                    new_laterality = 'R'
                else:
                    new_laterality = None
                if new_laterality is not None:
                    msg = ErrorInfo()
                    msg.msg = 'General Fix - {}'.format(
                        "<Laterality> holds wrong value")
                    msg.fix = "fixed by setting the <Laterality>"\
                        "from {} to '{}'".format(old_laterality, new_laterality)
                    log.append(msg.getWholeMessage())
                    ds['Laterlaity'].value = new_laterality
                    return
                else:
                    AddImageLateralityForBoth = True
            else:
                return
    elif 'Laterality' in ds:
        del ds['Laterality']
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_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 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 generalfix_RemoveUnwanterPixelAspctRatio(ds: Dataset, log: list) -> bool:
    fixed = False
    kw = "PixelAspectRatio"
    is_one_to_one = False
    if kw in ds:
        elem = ds[kw]
        if type(elem) == MultiValue:
            if len(elem) == 2:
                is_one_to_one = (elem.value[0] == elem.value[2])
        if (Condition_UnwantedPixelAspectRatioWhenPixelSpacingPresent(
                ds, ds, ds
        ) or Condition_UnwantedPixelAspectRatioWhenImagerPixelSpacingPresent(
                ds, ds, ds
        ) or Condition_UnwantedPixelAspectRatioWhenNominalScannedPixelSpacingPresent(
                ds, ds, ds
        ) or Condition_UnwantedPixelAspectRatioWhenSharedPixelMeasuresMacro(
                ds, ds, ds
        ) or Condition_UnwantedPixelAspectRatioWhenPerFramePixelMeasuresMacro(
                ds, ds, ds) or
                Condition_UnwantedPixelAspectRatioWhenMPEG2MPHLTransferSyntax(
                    ds, ds, ds) or is_one_to_one):
            msg = ErrorInfo()
            msg.msg = '{} Error - {}'.format(
                ErrorType.BadValue.value,
                "<PixelAspectRatio> is 1:1 or redundant")
            msg.fix = "fixed by removing the attribute"
            log.append(msg.getWholeMessage())
            del ds["PixelAspectRatio"]
            fixed = True
    return fixed
def generalfix_TrailingNulls(ds: Dataset, log: list) -> bool:
    fixed = False
    elemsTobeCorrected = []
    for key, a in ds.items():
        a = ds[key]
        if key.is_private:
            continue
        if a.VR == 'UI' or a.VR == 'OB' or a.VR == 'OW' or a.VR == 'UN':
            continue
        if type(a) == pydicom.dataelem.RawDataElement:
            a = pydicom.dataelem.DataElement_from_raw(a)
        if type(a.value) == Sequence:
            for item in a.value:
                fixed = fixed or generalfix_TrailingNulls(item, log)
        elif type(a.value) == Dataset:
            fixed = fixed or generalfix_TrailingNulls(a.value, log)
        else:
            partial_fixed = subfix_HasTrailingNulls(a)
            if partial_fixed:

                err = "<{}> {}".format(a.keyword, validate_vr.tag2str(a.tag))
                msg = ErrorInfo(
                    "General Fix - Trailing null bytesz",
                    "fixed by removing the trailing null bytes for {}".format(
                        err))
                log.append(msg.getWholeMessage())
                elemsTobeCorrected.append(a)
                fixed = True
    return fixed
예제 #9
0
def verifyType2C(ds: Dataset, module: str, element: str, verbose: bool,
                 log: list, fix_trivials: bool, condition_function, mbpo: bool,
                 parent_ds: Dataset, root_ds: Dataset, multiplicityMin: uint32,
                 multiplicityMax: uint32) -> bool:
    #    Type 2C - Conditional Data Element (May be Empty)

    err_not_exists = False
    err_vr = False
    err_vm = False
    reason = ""
    if condition_function == 0:
        conditionNotSatisfied = True
    else:
        conditionNotSatisfied = not condition_function(ds, parent_ds, root_ds)
    if element in ds:
        elem = ds[element]
    else:
        if not conditionNotSatisfied:
            reason = "Error - T<{}> {}"
            reason = reason.format(ErrorType.Type2CAbsent.value,
                                   MMsgDC("MissingAttribute"))
        err_not_exists = True
    if not err_not_exists:
        if condition_function != 0 and conditionNotSatisfied and not mbpo:
            reason = "Error - T<{}> {}"
            reason = reason.format(
                ErrorType.Type2CPresent.value,
                MMsgDC(
                    "AttributePresentWhenConditionUnsatisfiedWithoutMayBePresentOtherwise"
                ))
            if fix_trivials:
                tmp = ErrorInfo(reason + "{} {}", fix_by_removing(ds, element))
                reason = tmp.getWholeMessage()
        if elem.is_empty:
            if not verifyVR(elem, module, element, verbose, log):
                reason = "Error - T<{}> {}"
                reason = reason.format(ErrorType.Type2CVR.value,
                                       MMsgDC("BadValueRepresentation"))
                err_vr = True
            else:
                if not verifyVM(elem, module, element, verbose, log,
                                multiplicityMin, multiplicityMax, "source"):
                    reason = "Error - T<{}> {}"
                    reason = reason.format(
                        ErrorType.Type2CVM.value,
                        MMsgDC("BadAttributeValueMultiplicity"))
                    err_vm = True
    if len(reason) != 0:
        if fix_trivials:
            ViolationMessage(reason, MMsgDC("Type2C"), module, element, log,
                             verbose, True)
        else:
            ViolationMessage(reason, MMsgDC("Type2C"), module, element, log,
                             verbose)
    else:
        ValidMessage(MMsgDC("Type2C"), module, element, log, verbose)
    return len(reason) == 0
def generalfix_RemoveEmptyCodes(parent_ds: Dataset,
                                log: list,
                                kw_to_be_removed: list = []) -> bool:
    global iod_dict
    if 'SOPClassUID' in parent_ds:
        iod_dict = get_full_attrib_list(parent_ds)

    fixed = False
    for key, elem in parent_ds.items():
        elem = parent_ds[key]
        if type(elem) == pydicom.dataelem.RawDataElement:
            elem = pydicom.dataelem.DataElement_from_raw(elem)
        if type(elem.value) == Sequence:
            if elem.keyword.endswith("CodeSequence"):
                if iod_dict is None or elem.keyword not in iod_dict:
                    type_ = None
                else:
                    type_ = iod_dict[elem.keyword][0]['type']
                if type_ != '1' or type_ != '1C' or type_ != '2'\
                        or type_ != '2C':
                    fixed = fixed or subfix_checkandfixBasicCodeSeq(elem, log)
                if elem.is_empty:
                    kw_to_be_removed.append((parent_ds, elem.keyword))
            else:
                for (item, idx) in zip(elem.value, range(0, len(elem.value))):
                    fixed = fixed or generalfix_RemoveEmptyCodes(
                        item, log, kw_to_be_removed)
        elif type(elem.value) == Dataset:
            fixed = fixed or generalfix_RemoveEmptyCodes(
                elem.value,
                log,
                kw_to_be_removed,
            )
    if 'SOPClassUID' in parent_ds:
        for ddss, k in kw_to_be_removed:
            if k in ddss:
                a = ddss[k]
                # print('{} is present and going to be removed'.format(k))
            else:
                # print('{} is not present and going to continue anyway'.format(k))
                continue
            if iod_dict is None or k not in iod_dict:
                type_ = None
            else:
                type_ = iod_dict[k][0]['type']
            if type_ != '1' or type_ != '1C' or type_ != '2' or type_ != '2C':
                err = "{} <{}>".format(validate_vr.tag2str(a.tag), a.keyword)
                msg = ErrorInfo(
                    "General Fix - Attribute type SQ has no items",
                    "fixed by removing the whole attribute for {}".format(err))
                log.append(msg.getWholeMessage())
                del ddss[k]
 def check(v, n, N, a):
     u = v.upper()
     if u != v:
         err = "<{}> {}".format(a.keyword, validate_vr.tag2str(a.tag))
         msg = ErrorInfo(
             "General Fix - {} lower case is not allowed for Code "\
                 "String itme number {}/ {}".format(err, n, N),
             "fixed by capitalizing the value from {} to {}".format(
                 v, u))
         log.append(msg.getWholeMessage())
         return u
     else:
         return None
def generalfix_WindowWidth(ds, log):
    fixed = False
    wwkw = 'WindowWidth'
    wwtg = tag_for_keyword(wwkw)
    if wwtg in ds:
        if ds[wwtg].value == 0:
            ds[wwtg].value = 1
            err = "<{}> {}".format(wwkw, validate_vr.tag2str(wwtg))
            msg = ErrorInfo(
                "General Fix - Window width {} "\
                    "is not allowed to be 0".format(err),
                "fixed by replacing it with 1")
            log.append(msg.getWholeMessage())
            fixed = True
    return fixed
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())
예제 #14
0
def verifyType2(ds: Dataset, module: str, element: str, verbose: bool,
                log: list, fix_trivials: bool, multiplicityMin: uint32,
                multiplicityMax: uint32) -> bool:
    # Type 2 - Required Data Element (May be Empty)
    err_vr = False
    err_vm = False
    err_not_exists = False
    reason = ""
    if element in ds:
        elem = ds[element]
    else:
        reason = "Error - T<{}> {}"
        reason = reason.format(ErrorType.Type2.value,
                               MMsgDC("MissingAttribute"))
        err_not_exists = True
        if fix_trivials:  # add an empty attrib
            tmp = ErrorInfo(reason + "{} {}",
                            fix_ByAddingEmptyAttrib(ds, element))
            reason = tmp.getWholeMessage()
    if not err_not_exists:
        # do not check emptiness
        if not elem.is_empty:
            if not verifyVR(elem, module, element, verbose, log):
                reason = "Error - T<{}> {}"
                reason = reason.format(ErrorType.Type2VR.value,
                                       MMsgDC("BadValueRepresentation"))
                err_vr = True
            else:
                if not verifyVM(elem, module, element, verbose, log,
                                multiplicityMin, multiplicityMax):
                    reason = "Error - T<{}> {}"
                    reason = reason.format(
                        ErrorType.Type2VM.value,
                        MMsgDC("BadAttributeValueMultiplicity"))
                    err_vm = True
    if len(reason) != 0:
        if fix_trivials:
            ViolationMessage(reason, MMsgDC("Type2"), module, element, log,
                             verbose, True)
        else:
            ViolationMessage(reason, MMsgDC("Type2"), module, element, log,
                             verbose)
    else:
        ValidMessage(MMsgDC("Type2"), module, element, log, verbose)
    return len(reason) == 0
 def check(v, n, N, a):
     v = str(v)
     if len(v) > 16:
         f = float(v)
         u = '{:1.10E}'.format(f)
         n = 10
         while len(u) > 16 and n > 0:
             n -= 1
             ptrn = '{{:1.{}E}}'.format(n)
             u = ptrn.format(f)
         err = "<{}> {}".format(a.keyword, validate_vr.tag2str(a.tag))
         msg = ErrorInfo(
             "General Fix - {} value length greater than 16 is not allowed"\
                 "for DS value representation in itme number {}/ {}".format(
                     err, n, N),
             "fixed by modifying the precision from {} to {}".format(
                 v, u))
         log.append(msg.getWholeMessage())
         return u
     else:
         return None
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())