Example #1
0
    def from_file(cls, fp):
        """Instantiate a DicomBase instance from a filepath or file-like
        object.
        """
        dataset = pydicom.dcmread(fp, force=True)

        return cls(dataset)
Example #2
0
def is_anonymised_file(filepath, ignore_private_tags=False):
    r"""Check whether a DICOM file has been (fully) anonymised.

    This function specifically checks whether the DICOM file has been
    anonymised using a PyMedPhys anonymiser. It is very likely that it
    will return ``False`` for an anonymous DICOM file that was
    anonymised using a different tool.

    Parameters
    ----------
    filepath : ``str`` or ``pathlib.Path``
        The path to the DICOM file to check for anonymity.

    ignore_private_tags : ``bool``, optional
        If set to ``False``, ``is_anonymised_file()`` will return
        ``False`` if any private (non-standard) DICOM tags exist in the
        DICOM file. Set to ``True`` to ignore private tags when checking
        for anonymity. Do so with caution, since private tags may
        contain identifying information. Defaults to ``False``.

    Returns
    -------
    is_anonymised : ``bool``
        ``True`` if the DICOM dataset read from ``filepath`` has been
        anonymised, ``False`` otherwise.
    """
    ds = pydicom.dcmread(str(filepath))

    return is_anonymised_dataset(ds, ignore_private_tags=ignore_private_tags)
Example #3
0
def main():
    root = tk.Tk()
    root.withdraw()

    path = tk.filedialog.askopenfilename()
    path = Path(path)
    path = str(path)
    ds = pydicom.dcmread(path)

    mcs = ModulationComplexity(ds=ds)
    mcs_all = mcs.calcMCS()

    bc = BeamComplexity(ds=ds)
    bc_all = bc.calcBeamComplexity()

    beamNames = mcs_all[0].keys()

    n = 0
    for b in beamNames:
        beamType = mcs_all[1].get(b)
        mcs = round(mcs_all[0].get(b), 3)
        ba = round(bc_all[0].get(b), 1)
        bi = round(bc_all[1].get(b), 2)
        bm = round(bc_all[2].get(b), 3)
        n = n + 1

        print(f"""
    Printing beam complexity report:
        Beam name: {b}
        Beam type: {beamType}

        Modulation Complexity Score: {mcs}
        Beam Area: {ba}
        Beam irregularity: {bi}
        Beam modulation: {bm}""")
Example #4
0
def load_dicom_into_deque(filepaths):
    dicom_datasets_initial_read = [
        pydicom.dcmread(filepath, force=True) for filepath in filepaths
    ]

    dicom_datasets = convert_datasets_to_deque(dicom_datasets_initial_read)

    return dicom_datasets
Example #5
0
def assign2machine(source_file: str, machine_file: str):
    """Assign a DICOM RT Plan file to a specific machine. The source file is overwritten to contain
    the machine of the machine file.

    Parameters
    ----------
    source_file : str
        Path to the DICOM RTPlan file that contains the fields/plan desired
        (e.g. a Winston Lutz set of fields or Varian's default PF files).
    machine_file : str
        Path to a DICOM RTPlan file that has the desired machine. This is easily obtained from pushing a plan from the TPS
        for that specific machine. The file must contain at least one valid field.
    """
    dcm_source = pydicom.dcmread(source_file)
    dcm_machine = pydicom.dcmread(machine_file)
    for beam in dcm_source.BeamSequence:
        beam.TreatmentMachineName = dcm_machine.BeamSequence[
            0].TreatmentMachineName
    dcm_source.save_as(source_file)
Example #6
0
def load_dicom_file(filepath):
    dicom_dataset = pydicom.dcmread(filepath,
                                    force=True,
                                    stop_before_pixels=True)
    return dicom_dataset
Example #7
0
def anonymise_file(
    dicom_filepath,
    output_filepath=None,
    delete_original_file=False,
    anonymise_filename=True,
    replace_values=True,
    keywords_to_leave_unchanged=(),
    delete_private_tags=True,
    delete_unknown_tags=None,
    replacement_strategy=None,
    identifying_keywords=None,
):
    r"""A simple tool to anonymise a DICOM file.

    Parameters
    ----------
    dicom_filepath : ``str`` or ``pathlib.Path``
        The path to the DICOM file to be anonymised.

    delete_original_file : ``bool``, optional
        If `True` and anonymisation completes successfully, then the
        original DICOM is deleted. Defaults to ``False``.

    anonymise_filename : ``bool``, optional
        If ``True``, the DICOM filename is replaced by a filename of the
        form:

        "<2 char DICOM modality>.<SOP Instance UID>_Anonymised.dcm".

        E.g.: "RP.2.16.840.1.113669.[...]_Anonymised.dcm"

        This ensures that the filename contains no identifying
        information. If set to ``False``, ``anonymise_file()`` simply
        appends "_Anonymised" to the original DICOM filename. Defaults
        to ``True``.

    replace_values : ``bool``, optional
        If set to ``True``, DICOM tags will be anonymised using dummy
        "anonymous" values. This is often required for commercial
        software to successfully read anonymised DICOM files. If set to
        ``False``, anonymised tags are simply given empty string values.
        Defaults to ``True``.

    keywords_to_leave_unchanged : ``sequence``, optional
        A sequence of DICOM keywords (corresponding to tags) to exclude
        from anonymisation. Private and unknown tags can be supplied.
        Empty by default.

    delete_private_tags : ``bool``, optional
        A boolean to flag whether or not to remove all private
        (non-standard) DICOM tags from the DICOM file. These may
        also contain identifying information. Defaults to ``True``.

    delete_unknown_tags : ``bool``, pseudo-optional
        If left as the default value of ``None`` and ``ds`` contains
        tags that are not present in PyMedPhys' copy of ``pydicom``'s
        DICOM dictionary, ``anonymise_dataset()`` will raise an error.
        The user must then either pass ``True`` or ``False`` to proceed.
        If set to ``True``, all unrecognised tags that haven't been
        listed in ``keywords_to_leave_unchanged`` will be deleted. If
        set to ``False``, these tags are simply ignored. Pass ``False``
        with caution, since unrecognised tags may contain identifying
        information.

    replacement_strategy: ``dict`` (keys are VR, value is dispatch function), optional
        If left as the default value of ``None``, the hardcode replacement strategy is used.

    identifying_keywords: ``list``, optional
        If left as None, the default values for/list of identifying keywords are used

    Returns
    -------
    ``str``
    The file path of the anonymised file
    """
    dicom_filepath = str(dicom_filepath)

    ds = pydicom.dcmread(dicom_filepath, force=True)

    anonymise_dataset(
        ds=ds,
        replace_values=replace_values,
        keywords_to_leave_unchanged=keywords_to_leave_unchanged,
        delete_private_tags=delete_private_tags,
        delete_unknown_tags=delete_unknown_tags,
        copy_dataset=False,
        replacement_strategy=replacement_strategy,
        identifying_keywords=identifying_keywords,
    )

    if output_filepath is None:
        output_filepath = dicom_filepath
    else:
        os.makedirs(os.path.split(output_filepath)[0], exist_ok=True)

    if anonymise_filename:
        filepath_used = core.create_filename_from_dataset(
            ds, dirpath=dirname(output_filepath))
    else:
        filepath_used = output_filepath

    dicom_anon_filepath = core.label_dicom_filepath_as_anonymised(
        filepath_used)

    print(f"{dicom_filepath} --> {dicom_anon_filepath}")

    ds.save_as(dicom_anon_filepath)

    if delete_original_file:
        remove_file(dicom_filepath)

    return dicom_anon_filepath
Example #8
0
def _zip_pseudo_fifty_mbytes(file_buffer_list: list,
                             zipfile_path: str,
                             zip_bytes_io=None):
    """Pseudonymises the contents of the file_buffer_list (list of DICOM files)
    and places the pseudonymised files in to a zip.

    Parameters
    ----------
    file_buffer_list : list
        List of DICOM file buffers from streamlit file_uploader to pseudonymise
    zipfile_path : str
        Location to write the zip file so that it can be downloaded.
        Basename provides default name to use for downloading
    zip_bytes_io : io.BytesIO, optional
        An in memory file like object to be used for storing the Zip instead of the
        zipfile_path.  Highly desirable because the zip written to zipfile_path can't
        be deleted from this module.

    """

    bad_data = False
    file_count = 0
    keywords = pseudonymisation_api.get_default_pseudonymisation_keywords()
    keywords.remove("PatientSex")
    strategy = pseudonymisation_api.pseudonymisation_dispatch
    zip_stream = zipfile_path
    if zip_bytes_io is not None:
        zip_stream = zip_bytes_io

    with ZipFile(zip_stream, mode="w", compression=ZIP_DEFLATED) as myzip:
        for uploaded_file_buffer in file_buffer_list:
            file_count += 1

            # don't close the buffers.  Streamlit provides the user with control over that.
            # might be appropriate to close the buffers in some circumstances,
            # but then when the user goes to close the buffer (click x on screen)
            # there will be an error.

            try:
                original_file_name = uploaded_file_buffer.name
                ds_input: pydicom.FileDataset = pydicom.dcmread(
                    uploaded_file_buffer, force=True)

                anonymise_dataset(
                    ds_input,
                    delete_private_tags=True,
                    delete_unknown_tags=True,
                    copy_dataset=
                    False,  # do the work in place.  less memory used and we're disposing shortly anyway
                    identifying_keywords=keywords,
                    replacement_strategy=strategy,
                )
                temp_anon_filepath = build_pseudonymised_file_name(ds_input)
                in_memory_temp_file = io.BytesIO()
                anon_filename = pathlib.Path(temp_anon_filepath).name
                pydicom.dcmwrite(in_memory_temp_file, ds_input)
            except (KeyError, IOError, ValueError) as e_info:
                print(e_info)
                print(f"While processing {original_file_name}")
                bad_data = True
                break
            myzip.writestr(
                anon_filename,
                in_memory_temp_file.getvalue(),
                compress_type=ZIP_DEFLATED,
            )
            in_memory_temp_file.close()
    return bad_data
Example #9
0
def read_dicom3D(direc, i_option):
    # item = 0
    for subdir, dirs, files in os.walk(direc):  # pylint: disable = unused-variable
        k = 0
        for file in tqdm(sorted(files)):
            # print('filename=', file)
            if os.path.splitext(file)[1] == ".dcm":
                dataset = pydicom.dcmread(direc + file)
                if k == 0:
                    ArrayDicom = np.zeros(
                        (dataset.Rows, dataset.Columns, 0),
                        dtype=dataset.pixel_array.dtype,
                    )
                    tmp_array = dataset.pixel_array
                    if i_option.startswith(("y", "yeah", "yes")):
                        max_val = np.amax(tmp_array)
                        tmp_array = tmp_array / max_val
                        min_val = np.amin(tmp_array)
                        tmp_array = tmp_array - min_val
                        tmp_array = 1 - tmp_array  # inverting the range

                        # min_val = np.amin(tmp_array)  # normalizing
                        # tmp_array = tmp_array - min_val
                        # tmp_array = tmp_array / (np.amax(tmp_array))
                        tmp_array = u.norm01(tmp_array)
                    else:
                        # min_val = np.amin(tmp_array)
                        # tmp_array = tmp_array - min_val
                        # tmp_array = tmp_array / (np.amax(tmp_array))
                        tmp_array = u.norm01(tmp_array)  # just normalize
                    ArrayDicom = np.dstack((ArrayDicom, tmp_array))
                    # print("item thickness [mm]=", dataset.SliceThickness)
                    SID = dataset.RTImageSID
                    dx = 1 / (SID * (1 / dataset.ImagePlanePixelSpacing[0]) / 1000)
                    dy = 1 / (SID * (1 / dataset.ImagePlanePixelSpacing[1]) / 1000)
                    print("pixel spacing row [mm]=", dx)
                    print("pixel spacing col [mm]=", dy)
                else:
                    tmp_array = dataset.pixel_array
                    if i_option.startswith(("y", "yeah", "yes")):
                        max_val = np.amax(tmp_array)
                        tmp_array = tmp_array / max_val
                        min_val = np.amin(tmp_array)
                        tmp_array = tmp_array - min_val
                        tmp_array = 1 - tmp_array  # inverting the range

                        # min_val = np.amin(tmp_array)  # normalizing
                        # tmp_array = tmp_array - min_val
                        # tmp_array = tmp_array / (np.amax(tmp_array))
                        tmp_array = u.norm01(tmp_array)
                    else:
                        # min_val = np.amin(tmp_array)
                        # tmp_array = tmp_array - min_val
                        # tmp_array = tmp_array / (np.amax(tmp_array))  # just normalize
                        tmp_array = u.norm01(tmp_array)
                    ArrayDicom = np.dstack((ArrayDicom, tmp_array))
            k = k + 1

    xfield, yfield, rotfield = image_analyze(ArrayDicom, i_option)

    multi_slice_viewer(ArrayDicom, dx, dy)

    if np.shape(xfield)[2] == 2:
        fig, peak_figs, junctions_figs = merge_view_vert(xfield, dx, dy)
        with PdfPages(direc + "jaws_X_report.pdf") as pdf:
            pdf.savefig(fig)
            # for i in range(0, len(peak_figs)):
            for _, f in enumerate(peak_figs):
                pdf.savefig(f)

            # for i in range(0, len(junctions_figs)):
            for _, f in enumerate(junctions_figs):
                pdf.savefig(f)

            plt.close()

    else:
        print(
            "X jaws data analysis not completed please verify that you have two X jaws images. For more information see manual."
        )

    if np.shape(yfield)[2] == 4:
        fig, peak_figs, junctions_figs = merge_view_horz(yfield, dx, dy)
        # print('peak_figs********************************************************=', len(peak_figs),peak_figs)
        with PdfPages(direc + "jaws_Y_report.pdf") as pdf:
            pdf.savefig(fig)
            # for i in range(0, len(peak_figs)):
            for _, f in enumerate(peak_figs):
                pdf.savefig(f)

            for _, f in enumerate(junctions_figs):
                pdf.savefig(f)

            plt.close()

    else:
        print(
            "Y jaws data analysis not completed please verify that you have four Y jaws images. For more information see manual."
        )

    if np.shape(rotfield)[2] == 4:
        fig, peak_figs, junctions_figs = merge_view_filtrot(rotfield, dx, dy)

        with PdfPages(direc + "jaws_FR_report.pdf") as pdf:
            pdf.savefig(fig)
            for _, f in enumerate(peak_figs):
                pdf.savefig(f)

            for _, f in enumerate(junctions_figs):
                pdf.savefig(f)

            plt.close()

    else:
        print(
            "Field rotation data analysis not completed please verify that you have four field rotation images. For more information see manual."
        )
Example #10
0
def openPlan(plan):
    ds = pydicom.dcmread(plan)
    return ds