コード例 #1
0
    def add_dimension_organization(
            self, dim_organization: DimensionOrganizationSequence) -> None:
        """Adds a dimension organization sequence to the dataset.

        This methods registers the (0x0020, 0x9164) DimensionOrganizationUID
        and appends all items from the sequence to (0x0020, 0x9222)
        DimensionIndexSequence.

        Args:
            dim_organization: A `DimensionOrganizationSequence` with one or
                more dimension items configured.
        """
        if 'DimensionOrganizationSequence' not in self:
            self.DimensionOrganizationSequence = pydicom.Sequence()
            self.DimensionIndexSequence = pydicom.Sequence()

        for item in self.DimensionOrganizationSequence:
            if item.DimensionOrganizationUID == dim_organization[
                    0].DimensionOrganizationUID:
                raise ValueError(
                    'Dimension organization with UID '
                    f'{item.DimensionOrganizationUID} already exists')

        item = pydicom.Dataset()
        item.DimensionOrganizationUID = dim_organization[
            0].DimensionOrganizationUID
        self.DimensionOrganizationSequence.append(item)
        self.DimensionIndexSequence.extend(dim_organization)
コード例 #2
0
 def _create_empty_pixel_measures_sequence(self) -> pydicom.Dataset:
     ds = pydicom.Dataset()
     ds.SharedFunctionalGroupsSequence = pydicom.Sequence([pydicom.Dataset()])
     ds.SharedFunctionalGroupsSequence[0].PixelMeasuresSequence = pydicom.Sequence(
         [pydicom.Dataset()]
     )
     return ds
コード例 #3
0
def get_local_dicom_sequence(dir_entry_folder,
                             rgx_dicom=re.compile(r'^i\d+\.MRDC\.\d+$'),
                             presort=True):
    """Get the DICOM Sequence in a given DirEntry folder whose DICOM Datasets that match the provided Regex

    :param dir_entry_folder: A DirEntry folder holding DICOM Datasets that will be bundled as a DICOM Sequence
    :type  dir_entry_folder: DirEntry
    :param rgx_dicom: A Regex matching the DICOM filenames
    :type  rgx_dicom: Regex
    :param presort: A boolean flag for sorting the DICOM Datasets within the DICOM Sequence before returning it
    :type  presort: boolean

    :return: pydicom Sequence of DICOM Datasets (where a DICOM "dataset" is a DICOM file)
    :rtype: pydicom Sequence
    """
    subitems = get_local_subitems(dir_entry_folder)
    dicom_subfiles = get_local_subfiles(subitems, rgx_dicom)
    if presort:
        sorted_dicom_subfiles = sorted(
            dicom_subfiles, key=lambda f: int(f.name.split(".")[-1]))
        dicom_datasets = map(
            lambda dicom_subfile: get_local_dicom_dataset(dicom_subfile),
            sorted_dicom_subfiles)
    else:
        dicom_datasets = map(
            lambda dicom_subfile: get_local_dicom_dataset(dicom_subfile),
            dicom_subfiles)

    return pydicom.Sequence(dicom_datasets)
コード例 #4
0
def from_dcmqi_metainfo(metainfo: Union[dict, str]) -> pydicom.Dataset:
    """Converts a `metainfo.json` file from the dcmqi project to a
    `pydicom.Dataset` with the matching DICOM data elements set from JSON.

    Those JSON files can be easilly created using the segmentation editor
    tool from QIICR/dcmqi:
    http://qiicr.org/dcmqi/#/seg
    When converting the JSON to a DICOM dataset, the validity of the provided
    JSON document is ensured using the official JSON schema files from the
    dcmqi project.

    Args:
        metainfo: Either a `str` for a file path to read from or a `dict`
            with the JSON already imported or constructed in source code.

    Returns:
        A `pydicom.Dataset` containg all values from the JSON document and
        some defaults if the elements were not available.
    """
    # Add convienence loader of JSON dictionary
    if isinstance(metainfo, str):
        with open(metainfo) as ifile:
            metainfo = json.load(ifile)
    assert isinstance(metainfo, dict)

    # Validate dictionary against dcmqi JSON schemas
    validator = _create_validator()
    if not validator.is_valid(metainfo):
        raise NotImplementedError()

    # Create dataset from provided JSON
    dataset = pydicom.Dataset()
    tags_with_defaults = [
        ("BodyPartExamined", ""),
        ("ClinicalTrialCoordinatingCenterName", ""),
        ("ClinicalTrialSeriesID", "Session1"),
        ("ClinicalTrialTimePointID", "1"),
        ("ContentCreatorName", "Reader1"),
        ("ContentDescription", "Image segmentation"),
        ("ContentLabel", "SEGMENTATION"),
        ("InstanceNumber", "1"),
        ("SeriesDescription", "Segmentation"),
        ("SeriesNumber", "300"),
    ]

    for tag_name, default_value in tags_with_defaults:
        dataset.__setattr__(tag_name, metainfo.get(tag_name, default_value))

    if len(metainfo["segmentAttributes"]) > 1:
        raise ValueError(
            "Only metainfo.json files written for single-file input are supported"
        )

    dataset.SegmentSequence = pydicom.Sequence(
        [_create_segment_dataset(x) for x in metainfo["segmentAttributes"][0]])

    return dataset
コード例 #5
0
def _create_code_sequence(data: dict) -> pydicom.Sequence:
    """Helper function for creating a DICOM sequence from JSON attributes.

    Returns:
        A `pydicom.Sequence` with a single `pydicom.Dataset` item containing
        all attributes from the JSON document.
    """
    dataset = pydicom.Dataset()
    for key in data:
        dataset.__setattr__(key, data[key])
    return pydicom.Sequence([dataset])
コード例 #6
0
    def add_instance_reference(self, dataset: pydicom.Dataset) -> bool:
        """Adds an instance to the (0x0008,0x1115) ReferencedSeriesSequence.

        Args:
            dataset: A `pydicom.Dataset` DICOM image which should be added
                to the segmentation dataset.

        Returns:
            Returns `True`, if `dataset` was added as a reference.
        """
        if 'ReferencedSeriesSequence' not in self:
            self.ReferencedSeriesSequence = pydicom.Sequence()

        for series_item in self.ReferencedSeriesSequence:
            if series_item.SeriesInstanceUID != dataset.SeriesInstanceUID:
                continue

            for instance_item in series_item.ReferencedInstanceSequence:
                if instance_item.ReferencedSOPInstanceUID == dataset.SOPInstanceUID:
                    return False

            # Series found, but instance is missing
            break
        else:
            # Series not yet referenced, create a new series item
            series_item = pydicom.Dataset()
            series_item.SeriesInstanceUID = dataset.SeriesInstanceUID
            series_item.ReferencedInstanceSequence = pydicom.Sequence([])
            self.ReferencedSeriesSequence.append(series_item)

        # Instance not yet referenced, create a new instance item
        instance_item = pydicom.Dataset()
        instance_item.ReferencedSOPClassUID = dataset.SOPClassUID
        instance_item.ReferencedSOPInstanceUID = dataset.SOPInstanceUID
        series_item.ReferencedInstanceSequence.append(instance_item)

        return True
コード例 #7
0
ファイル: writer_utils.py プロジェクト: vsaase/pydicom-seg
def set_shared_functional_groups_sequence(
    target: pydicom.Dataset, segmentation: sitk.Image
) -> None:
    spacing = segmentation.GetSpacing()

    dataset = pydicom.Dataset()
    dataset.PixelMeasuresSequence = [pydicom.Dataset()]
    dataset.PixelMeasuresSequence[0].PixelSpacing = [f"{x:e}" for x in spacing[:2]]
    dataset.PixelMeasuresSequence[0].SliceThickness = f"{spacing[2]:e}"
    dataset.PixelMeasuresSequence[0].SpacingBetweenSlices = f"{spacing[2]:e}"
    dataset.PlaneOrientationSequence = [pydicom.Dataset()]
    dataset.PlaneOrientationSequence[
        0
    ].ImageOrientationPatient = sitk_to_dcm_orientation(segmentation)

    target.SharedFunctionalGroupsSequence = pydicom.Sequence([dataset])
コード例 #8
0
def set_shared_functional_groups_sequence(target: pydicom.Dataset,
                                          segmentation: sitk.Image):
    spacing = segmentation.GetSpacing()

    dataset = pydicom.Dataset()
    dataset.PixelMeasuresSequence = [pydicom.Dataset()]
    dataset.PixelMeasuresSequence[0].PixelSpacing = [
        f'{x:e}' for x in spacing[:2]
    ]
    dataset.PixelMeasuresSequence[0].SliceThickness = f'{spacing[2]:e}'
    dataset.PixelMeasuresSequence[0].SpacingBetweenSlices = f'{spacing[2]:e}'
    dataset.PlaneOrientationSequence = [pydicom.Dataset()]
    dataset.PlaneOrientationSequence[0].ImageOrientationPatient = [
        f'{x:e}' for x in np.ravel(segmentation.GetDirection())[:6]
    ]

    target.SharedFunctionalGroupsSequence = pydicom.Sequence([dataset])
コード例 #9
0
ファイル: dicomlib.py プロジェクト: gpilab/core-nodes
def sqlist_to_sequence(slist):
    temp = []
    for item in slist:
        ds = pydicom.dataset.Dataset()
        for key in item.keys():
            tag = key_to_Tag(key)
            VR = item[key][1]
            val = item[key][2]
            if VR == 'SQ':
                val = sqlist_to_sequence(val)
            elif (VR == 'UI'):
                val = uidname_to_val(val)
            else:
                val = eval(val)
            ds.add_new(tag, VR, val)
        temp.append(ds)
    return pydicom.Sequence(temp)
コード例 #10
0
def ConvertDataset(ds: pydicom.Dataset) -> pydicom.Dataset:
    msg = 'tag VR length value value_tell is_implicit_VR is_little_endian used_in_verification'

    MyRawDataElement = namedtuple('RawDataElement', msg)
    if type(ds) == pydicom.dataset.FileDataset:
        ds.file_meta = ConvertDataset(ds.file_meta)
    already_coverted = False
    for key, val in ds.items():
        try:
            elem = ds[key]
            already_coverted = True
        except KeyError:
            elem = val
            already_coverted = False

        if type(elem.value) == pydicom.Sequence:
            new_seq_elem = DataElementX(elem.tag, elem.VR, pydicom.Sequence())
            for item in elem.value:
                new_seq_elem.value.append(ConvertDataset(item))
            ds[key] = new_seq_elem
        elif type(elem.value) == pydicom.Dataset:
            ds_elem = DataElementX(elem.tag, elem.VR, pydicom.Dataset())
            ds_elem = ConvertDataset(elem.value)
            ds[key] = ds_elem
        else:
            if type(elem) == pydicom.dataelem.RawDataElement:
                # new_elem = pydicom.dataelem.RawDataElement(elem.tag, elem.VR, elem.length, elem.value,
                # elem.value_tell, elem.is_implicit_VR, elem.is_little_endian, False)

                # ds[key] = elem
                new_elem = DataElementX(elem.tag,
                                        'UN',
                                        elem.value,
                                        already_converted=already_coverted)
            elif type(elem) == pydicom.DataElement:
                new_elem = DataElementX(elem.tag,
                                        elem.VR,
                                        elem.value,
                                        already_converted=already_coverted)
                ds[key] = new_elem
    return ds
コード例 #11
0
    def add_frame(
            self,
            data: np.ndarray,
            referenced_segment: int,
            referenced_images: List[pydicom.Dataset] = None
    ) -> pydicom.Dataset:
        """Adds a frame to the dataset.

        Adds the frame data to the PixelData element after encoding the data
        into the correct format for the configured segmentation type.
        Each referenced image is also registered in (0x0008,0x1115)
        ReferencedSeriesSequence.

        Args:
            data: A `np.ndarray` with integer data type for binary
                or floating point data type for fractional segmentations.
            referenced_segment: An integer number for the segment to which
                this frame belongs. The segment must exist in (0x0062, 0x0002)
                SegmentSequence.
            referenced_images: A list of `pydicom.Dataset` source images, which
                map to the frame.

        Returns:
            A `pydicom.Dataset` with pre-initialized values for an item in
            (0x5200,0x9230) PerFrameFunctionalGroupsSequence, which needs
            further configuration based on writer dependent strategy.
        """
        referenced_images = referenced_images or []

        if len(data.shape) != 2:
            raise ValueError('Invalid frame data shape, expecting 2D images')

        if data.shape[0] != self.Rows or data.shape[1] != self.Columns:
            raise ValueError(
                f'Invalid frame data shape, expecting {self.Rows}x{self.Columns} images'
            )

        # TODO Optimize packing/unpacking/concatenation by using a io.BytesIO
        # TODO stream and track free bits in the last byte
        if self.SegmentationType == SegmentationType.BINARY.value:
            if not np.issubdtype(data.dtype, np.integer):
                raise ValueError(
                    'Binary segmentation data requires an integer data type')
            data = np.greater(data, 0, dtype=np.uint8)

            self._frames.append(data)
            self.PixelData = np.packbits(np.ravel(self._frames),
                                         bitorder='little').tobytes()
        else:
            if not np.issubdtype(data.dtype, np.floating):
                raise ValueError(
                    'Fractional segmentation data requires a floating point data type'
                )
            data = np.clip(data, 0.0, 1.0)
            data *= self.MaximumFractionalValue
            data = data.astype(np.uint8)

            self._frames.append(data.ravel())
            self.PixelData = np.concatenate(self._frames).tobytes()

        # A frame was added to the dataset
        self.NumberOfFrames += 1

        # Update (0x5200,0x9230) PerFunctionalGroupsSequence
        frame_fg_item = pydicom.Dataset()
        if referenced_segment not in [
                x.SegmentNumber for x in self.SegmentSequence
        ]:
            raise IndexError('Segment not found in SegmentSequence')
        frame_fg_item.SegmentIdentificationSequence = pydicom.Sequence(
            [pydicom.Dataset()])
        frame_fg_item.SegmentIdentificationSequence[
            0].ReferencedSegmentNumber = referenced_segment

        # Each frame requires references to the original DICOM files
        derivation_image = pydicom.Dataset()
        derivation_image.SourceImageSequence = pydicom.Sequence()

        for referenced_image in referenced_images:
            # Update (0x0008,0x1115) ReferencedSeriesSequence for each referenced image
            self.add_instance_reference(referenced_image)

            # Add referenced image to SourceImageSequence
            ref = pydicom.Dataset()
            ref.ReferencedSOPClassUID = referenced_image.SOPClassUID
            ref.ReferencedSOPInstanceUID = referenced_image.SOPInstanceUID
            ref.PurposeOfReferenceCodeSequence = CodeSequence(
                '121322', 'DCM', 'Source image for image processing operation')
            derivation_image.SourceImageSequence.append(ref)
        derivation_image.DerivationCodeSequence = CodeSequence(
            '113076', 'DCM', 'Segmentation')
        frame_fg_item.DerivationImageSequence = pydicom.Sequence(
            [derivation_image])
        self.PerFrameFunctionalGroupsSequence.append(frame_fg_item)

        return frame_fg_item
def labelvol_to_rtstruct(roi_vol,
                         aff,
                         refdcm_file,
                         filename,
                         ordered_slices,
                         uid_base='1.2.826.0.1.3680043.9.7147.',
                         seriesDescription='test rois',
                         structureSetLabel='RTstruct',
                         structureSetName='my rois',
                         connect_holes=True,
                         roinames=None,
                         roidescriptions=None,
                         roigenerationalgs=None,
                         roi_colors=[['255', '0', '0'], ['0', '0', '255'],
                                     ['0', '255', '0'], ['255', '0', '255'],
                                     ['255', '255', '0'], ['0', '255', '255']],
                         tags_to_copy=[
                             'PatientName', 'PatientID', 'AccessionNumber',
                             'StudyID', 'StudyDescription', 'StudyDate',
                             'StudyTime', 'SeriesDate', 'SeriesTime'
                         ],
                         tags_to_add=None):
    """Convert a 3D array with integer ROI label to RTstruct

  Parameters
  ---------

  roi_vol : 3d numpy integer array 
    in LPS orientation containing the ROI labels
    0 is considered background
    1  ...          ROI-1
    n  ...          ROI-n


  aff : 2d 4x4 numpy array
      affine matrix that maps from voxel to (RAS) world coordinates
  
  refdcm_file : string or list
    A single reference dicom file or a list of reference files (multiple CT slices)
    From this file several dicom tags are copied (e.g. the FrameOfReferenceUID).
    In case a list of files is given, the dicom tags are copied from the first file,
    however all SOPInstanceUIDs are added to the ContourImageSequence (needed for some
    RT systems)

  filename : string
    name of the output rtstruct file

  uid_base : string, optional
    uid base used to generate some dicom UID

  seriesDescription : string, optional
    dicom series description for the rtstruct series

  structureSetLabel, structureSetName : string, optional
    Label and Name of the structSet

  connect_holes : bool, optional
    whether to connect inner holes to their outer parents contour - default: True
    this connection is needed to show holes correctly in MIM

  roinames, roidescriptions, roigenerationalgs : lists, optional
    containing strings for ROIName, ROIDescription and ROIGenerationAlgorithm
  
  roi_colors: list of lists containing 3 integer strings (0 - 255), optional
    used as ROI display colors

  tags_to_copy: list of strings, list optional
    extra dicom tags to copy from the refereced dicom file

  tags_to_add: dictionary
    with valid dicom tags to add in the header
  """

    roinumbers = np.unique(roi_vol)
    roinumbers = roinumbers[roinumbers > 0]
    nrois = len(roinumbers)

    if isinstance(refdcm_file, list):
        refdcm = pydicom.read_file(refdcm_file[0])
    else:
        refdcm = pydicom.read_file(refdcm_file)

    file_meta = pydicom.Dataset()

    file_meta.ImplementationClassUID = uid_base + '1.1.1'
    file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3'
    file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid(
        '1.2.840.10008.5.1.4.1.1.481.3.')

    ds = pydicom.FileDataset(filename, {},
                             file_meta=file_meta,
                             preamble=b"\0" * 128)

    ds.Modality = 'RTSTRUCT'
    ds.SeriesDescription = seriesDescription

    #--- copy dicom tags from reference dicom file
    for tag in tags_to_copy:
        if tag in refdcm:
            setattr(ds, tag, refdcm.data_element(tag).value)
        else:
            warnings.warn(
                tag + ' not in reference dicom file -> will not be written')

    ds.StudyInstanceUID = refdcm.StudyInstanceUID
    ds.SeriesInstanceUID = pydicom.uid.generate_uid(uid_base)

    ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3'
    ds.SOPInstanceUID = pydicom.uid.generate_uid(uid_base)

    ds.StructureSetLabel = structureSetLabel
    ds.StructureSetName = structureSetName

    dfr = pydicom.Dataset()
    dfr.FrameOfReferenceUID = refdcm.FrameOfReferenceUID

    ds.ReferencedFrameOfReferenceSequence = pydicom.Sequence([dfr])

    if tags_to_add is not None:
        for tag, value in tags_to_add.items():
            setattr(ds, tag, value)

    #######################################################################
    #######################################################################
    # write the ReferencedFrameOfReferenceSequence

    contourImageSeq = pydicom.Sequence()

    if isinstance(refdcm_file, list):
        # in case we got all reference dicom files we add all SOPInstanceUIDs
        # otherwise some RT planning systems refuse to read the RTstructs
        for fname in refdcm_file:
            with pydicom.read_file(fname) as tmpdcm:
                tmp = pydicom.Dataset()
                tmp.ReferencedSOPClassUID = tmpdcm.SOPClassUID
                tmp.ReferencedSOPInstanceUID = tmpdcm.SOPInstanceUID
                contourImageSeq.append(tmp)
    else:
        tmp = pydicom.Dataset()
        tmp.ReferencedSOPClassUID = refdcm.SOPClassUID
        tmp.ReferencedSOPInstanceUID = refdcm.SOPInstanceUID
        contourImageSeq.append(tmp)

    tmp2 = pydicom.Dataset()
    tmp2.SeriesInstanceUID = refdcm.SeriesInstanceUID
    tmp2.ContourImageSequence = contourImageSeq

    tmp3 = pydicom.Dataset()
    tmp3.ReferencedSOPClassUID = '1.2.840.10008.3.1.2.3.1'
    # TODO SOP just copied from MIM rtstructs
    tmp3.ReferencedSOPInstanceUID = refdcm.StudyInstanceUID
    tmp3.RTReferencedSeriesSequence = pydicom.Sequence([tmp2])

    tmp4 = pydicom.Dataset()
    tmp4.FrameOfReferenceUID = refdcm.FrameOfReferenceUID
    tmp4.RTReferencedStudySequence = pydicom.Sequence([tmp3])

    ds.ReferencedFrameOfReferenceSequence = pydicom.Sequence([tmp4])

    #######################################################################
    #######################################################################

    ds.StructureSetROISequence = pydicom.Sequence()
    ds.ROIContourSequence = pydicom.Sequence()

    if roinames is None: roinames = ['ROI-' + str(x) for x in roinumbers]
    if roidescriptions is None:
        roidescriptions = ['ROI-' + str(x) for x in roinumbers]
    if roigenerationalgs is None:
        roigenerationalgs = len(roinumbers) * ['MANUAL']

    # loop over the ROIs
    for iroi, roinumber in enumerate(roinumbers):
        dssr = pydicom.Dataset()
        dssr.ROINumber = roinumber
        dssr.ROIName = roinames[iroi]
        dssr.ROIDescription = roidescriptions[iroi]
        dssr.ROIGenerationAlgorithm = roigenerationalgs[iroi]
        dssr.ReferencedFrameOfReferenceUID = dfr.FrameOfReferenceUID

        ds.StructureSetROISequence.append(dssr)

        #######################################################################
        #######################################################################
        # write ROIContourSequence containing the actual 2D polygon points of the ROI

        ds_contour = pydicom.Dataset()
        ds_contour.ReferencedSOPClassUID = refdcm.SOPClassUID  ###

        # generate binary volume for the current ROI
        bin_vol = (roi_vol == dssr.ROINumber).astype(int)

        # find the bounding box in the last direction
        ob_sls = find_objects(bin_vol)
        z_start = min([x[2].start for x in ob_sls])
        z_end = max([x[2].stop for x in ob_sls])

        ds_roi_contour = pydicom.Dataset()
        ds_roi_contour.ROIDisplayColor = roi_colors[iroi % len(roi_colors)]
        ds_roi_contour.ReferencedROINumber = dssr.ROINumber
        ds_roi_contour.ContourSequence = pydicom.Sequence()

        # loop over the slices in the 2 direction to create 2D polygons
        for sl in np.arange(z_start, z_end):
            bin_slice = bin_vol[:, :, sl]

            if bin_slice.max() > 0:
                contours = binary_2d_image_to_contours(
                    bin_slice, connect_holes=connect_holes)

                for ic in range(len(contours)):
                    npoints = contours[ic].shape[0]

                    contour = np.zeros((npoints, 3))

                    for ipoint in range(npoints):
                        contour[ipoint, :] = (aff @ np.concatenate(
                            (contours[ic][ipoint, :], [sl, 1])))[:-1]

                    dsci = pydicom.Dataset()
                    dsci.ReferencedSOPInstanceUID = ordered_slices[sl][0]
                    dsci.ContourGeometricType = 'CLOSED_PLANAR'
                    dsci.NumberOfContourPoints = contour.shape[0]
                    dsci.ContourImageSequence = pydicom.Sequence([ds_contour])
                    dsci.ContourData = contour.flatten().tolist()

                    # ContourImageSequence contains 1 element per 2D contour
                    ds_roi_contour.ContourSequence.append(dsci)

        # has to contain one element per ROI
        ds.ROIContourSequence.append(ds_roi_contour)

    #######################################################################
    #######################################################################

    pydicom.filewriter.write_file(os.path.join('.', filename),
                                  ds,
                                  write_like_original=False)

    return z_start, z_end
コード例 #13
0
ファイル: pbs_plan_file.py プロジェクト: xfyecn/GateTools
    def __init__(self, fname, spotspecs=None, uid=None, verbose=False):
        """
        Create a fake treatment plan DICOM file for unit testing.  The spot
        specs are a list of dictionaries, one dictionary per beam.  Each beam
        dictionary contains optional entries for gantry angle, patient support
        angle and isocenter position and an obligatory list of "controlpoints".

        A file with filename `fname` should not yet exist. A DICOM file with
        name `fname` will be written by _test_plan_creator, and also be deleted
        when the creator goes out of scope.
        """
        assert (sys.version_info.major == 3)
        self.verbose = verbose
        assert (not os.path.exists(fname))
        self.dcm_filename = fname
        classUID = '1.2.840.10008.5.1.4.1.1.481.8' if uid is None else uid
        instanceUID = pydicom.uid.generate_uid()

        # File meta info data elements
        file_meta = pydicom.Dataset()
        file_meta.MediaStorageSOPClassUID = str(classUID)
        file_meta.MediaStorageSOPInstanceUID = str(instanceUID)
        file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
        #FIXME: we probably need to apply for an official UID here
        file_meta.ImplementationClassUID = '1.2.826.0.1.3680043.1.2.100.6.40.0.76'
        file_meta.ImplementationVersionName = 'DicomObjects.NET'

        self.ds = pydicom.FileDataset(self.dcm_filename, {},
                                      file_meta=file_meta,
                                      preamble=b"\0" * 128)
        self.ds.SOPClassUID = classUID
        self.ds.SOPInstanceUID = instanceUID
        self.ds.PatientName = "unit test"
        self.ds.PatientID = "42"
        #print("trying to fix 'is_little_endian'")

        if spotspecs:
            self.ds.IonBeamSequence = pydicom.Sequence()
            self.ds.NumberOfBeams = len(spotspecs)
            for beamdata in spotspecs:
                beam = pydicom.Dataset()
                beam.BeamName = beamdata.get('Nm', 'noname')
                beam.BeamNumber = beamdata.get('Nr', -1)
                beam.NumberOfIonControlPoints = len(beamdata["controlpoints"])
                beam.IonControlPointSequence = pydicom.Sequence()
                for i, icpdata in enumerate(beamdata["controlpoints"]):
                    icp = pydicom.Dataset()
                    if i == 0:
                        if 'G' in beamdata:
                            icp.GantryAngle = beamdata['G']
                        if 'P' in beamdata:
                            icp.PatientSupportAngle = beamdata['P']
                        if "I" in beamdata:
                            icp.IsocenterPosition = list(
                                [str(v) for v in beamdata.get('I')])
                        else:
                            icp.IsocenterPosition = ""
                    icp.NominalBeamEnergy = icpdata.get('E', 100.)
                    if 'T' in icpdata:
                        icp.ScanSpotTuneID = icpdata['T']
                    #unfortunately, this does not work
                    icp.ScanSpotMetersetWeights = [
                        float(v) for v in icpdata['SSW']
                    ]
                    icp.ScanSpotPositionMap = [
                        float(v) for v in np.array(icpdata['SSP']).flatten()
                    ]
                    icp.NumberOfScanSpotPositions = int(
                        icpdata.get('NSSP',
                                    len(icp.ScanSpotPositionMap) / 2))
                    beam.IonControlPointSequence.append(icp)
                self.ds.IonBeamSequence.append(beam)
        else:
            self.ds.NumberOfBeams = 0
        if verbose:
            logger.info("number of beams is {}".format(self.ds.NumberOfBeams))
        dt = datetime.datetime.now()
        self.ds.ContentDate = dt.strftime('%Y%m%d')
        timeStr = dt.strftime('%H%M%S.%f')  # long format with micro seconds
        self.ds.ContentTime = timeStr
        self.ds.is_little_endian = True
        self.ds.is_implicit_VR = True
        self.ds.fix_meta_info()
        self.ds.save_as(self.dcm_filename)
        if self.verbose:
            logger.info("wrote dicom file {}".format(self.dcm_filename))
コード例 #14
0
    def __init__(self, *,
                 rows: int,
                 columns: int,
                 segmentation_type: SegmentationType,
                 segmentation_fractional_type: SegmentationFractionalType = SegmentationFractionalType.PROBABILITY,
                 max_fractional_value: int = 255):
        """Initializes the segmentation dataset.

        Args:
            rows: Number of rows in a frame/image (y-axis)
            columns: Number of columns in a frame/image (x-axis)
            segmentation_type: Either `SegmentationType.BINARY` or
                `SegmentationType.FRACTIONAL` depending on the data to encode.
            segmentation_fractional_type: If `segmentation_type == SegmentationType.FRACTIONAL`,
                then the fractional type indicates the semantic meaning. Can be
                either `PROBABILITY` or `OCCUPANCY`.
            max_fractional_value: Fractional data is expected to be within
                `[0.0, 1.0]` and will be rescaled to `[0, max_fractional_value]`.
        """
        super().__init__()

        self._frames: List[np.ndarray] = []

        self.SpecificCharacterSet = 'ISO_IR 100'
        self.SOPClassUID = SegmentationStorage
        self.SOPInstanceUID = pydicom.uid.generate_uid()
        self._set_file_meta()

        # General Series module
        self.Modality = 'SEG'
        self.SeriesInstanceUID = pydicom.uid.generate_uid()
        self.SeriesNumber = 1

        # Generate SOP and Series and General Image timestamps
        timestamp = datetime.now()
        self.InstanceCreationDate = timestamp.strftime('%Y%m%d')
        self.InstanceCreationTime = timestamp.strftime('%H%M%S.%f')
        self.SeriesDate = self.InstanceCreationDate
        self.SeriesTime = self.InstanceCreationTime
        self.ContentDate = self.InstanceCreationDate
        self.ContentTime = self.InstanceCreationTime

        # Frame of Reference module
        self.FrameOfReferenceUID = pydicom.uid.generate_uid()

        # Enhanced General Equipment module
        # http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.5.2.html#table_C.7-8b
        self.Manufacturer = 'pydicom-seg'
        self.ManufacturerModelName = '[email protected]/razorx89/pydicom-seg.git'
        self.DeviceSerialNumber = '0'
        self.SoftwareVersions = __version__

        # Image Pixel module
        if rows <= 0 or columns <= 0:
            raise ValueError('Rows and columns must be larger than zero')
        self.Rows = rows
        self.Columns = columns
        self.PixelData = b''

        # Segmentation Image module
        # http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.8.20.2.html#table_C.8.20-2
        self.ImageType = ['DERIVED', 'PRIMARY']
        self.InstanceNumber = '1'
        self.ContentLabel = 'SEGMENTATION'
        self.ContentDescription = ''
        self.ContentCreatorName = ''
        self.SamplesPerPixel = 1
        self.PhotometricInterpretation = 'MONOCHROME2'
        self.PixelRepresentation = 0
        self.LossyImageCompression = '00'
        self.SegmentSequence = pydicom.Sequence()

        self.SegmentationType = segmentation_type.value
        if segmentation_type == SegmentationType.BINARY:
            # Binary segmentations are always bit-packed
            self.BitsAllocated = 1
            self.BitsStored = 1
            self.HighBit = 0
        else:
            # Fractional segmentations are always 8-bit unsigned
            self.SegmentationFractionalType = segmentation_fractional_type.value
            if max_fractional_value < 1 or max_fractional_value > 255:
                raise ValueError('Invalid maximum fractional value for 8-bit unsigned int data')
            self.MaximumFractionalValue = max_fractional_value
            self.BitsAllocated = 8
            self.BitsStored = 8
            self.HighBit = 7

        # Multi-frame Functional Groups module
        self.SharedFunctionalGroupsSequence = pydicom.Sequence([pydicom.Dataset()])
        self.PerFrameFunctionalGroupsSequence = pydicom.Sequence()
        self.NumberOfFrames = 0
コード例 #15
0
def deidentifier(src_path, newptID):
    ds = pydicom.dcmread(src_path)  ## defer_size could improve performance
    ds.remove_private_tags(
    )  # this will get rid of all private tags, this will cause some loss of information but this is inevitable for good de-identification

    if (0x40, 0x275) in ds:
        del ds[(0x40,
                0x275)]  # ugly way to get arround key error of (0x40,0x275)

    for row in ds.iterall():
        # if tag of data_element is in X_list it will be deleted
        if row.tag in Tag_dict["X_list"]:
            delattr(ds, row.keyword)

        # if tag of data_elem is in Z_list it shall be set to an empty string
        elif row.tag in Tag_dict["Z_list"]:
            row.value = ""

        # if tag in in D_list it shall be set to a dummy value
        elif row.tag in Tag_dict["D_list"]:
            # sequences needed some extra touch, since they need to be changed to an empty sequence
            if row.VR == "SQ":
                row.value = pydicom.Sequence(
                    [pydicom.Dataset()])  # Might cause issues at some point

            # gets the dummie values from VR_dummies by VR
            else:
                row.value = VR_dummies[row.VR]

        # if tag of data_elem is in U_list a new UID needs to be created, keeping the internal consistency
        elif row.tag in Tag_dict["U_list"]:
            if row.value not in known_UIDS.keys():
                new_UID = "2.25." + str(uuid.uuid4().int).lstrip(
                    "0")  # or use pydicom.uid.generate(prefix = None)
                '''
				In accordance with the NEMA standard of creating UID by using Universeally Unique Identifier (UUID)
				http://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_B.2.html
				'''
                known_UIDS[row.value] = new_UID
                row.value = new_UID

            else:
                new_UID = known_UIDS[row.value]
                row.value = new_UID

    # two hardcoded group removals for the 50xx (Curve Data) and (60xx,3000)[Overlay Data] and (60xx,4000)[Overlay Comments] groups of the nema standards:
            group = row.tag.group
            if group >= 0x5000 and group < 0x5100:
                delattr(ds, row.keyword)
            if group >= 0x6000 and group < 0x6100 and (
                    row.tag.element == 0x3000 or row.tag.element == 0x4000):
                delattr(ds, row.keyword)
    # sets PatientID and PatientName to a newptID given when calling this function
    ds.PatientID = newptID
    ds.PatientName = newptID
    # removes all dates mentioned in the seriesDescription in the fromat of "digitdigit nonwhitespacecharacter digitdigit nonwhitespacecharacter digitdigitdigitdigit"
    if (0x008, 0x103E) in ds:
        ds.SeriesDescription = re.sub(r"\d{2}\S\d{2}\S\d{4}", "",
                                      ds.SeriesDescription)

    return ds
コード例 #16
0
    def __init__(
            self,
            *,
            rows: int,
            columns: int,
            segmentation_type: SegmentationType,
            segmentation_fractional_type:
        SegmentationFractionalType = SegmentationFractionalType.PROBABILITY,
            reference_dicom: Optional[pydicom.Dataset] = None,
            max_fractional_value: int = 255):
        super().__init__()

        self._frames: List[np.ndarray] = []
        if reference_dicom:
            writer_utils.import_hierarchy(target=self,
                                          reference=reference_dicom,
                                          import_frame_of_reference=True,
                                          import_series=False)
        else:
            logger.warning('No source images provided, cannot import patient '\
                'and study level information.')

        self.SpecificCharacterSet = 'ISO_IR 100'
        self.SOPClassUID = SegmentationStorage
        self.SOPInstanceUID = pydicom.uid.generate_uid()
        self._set_file_meta()

        # General Series module
        self.Modality = 'SEG'
        self.SeriesInstanceUID = pydicom.uid.generate_uid()
        self.SeriesNumber = 1

        # Generate SOP and Series and General Image timestamps
        timestamp = datetime.now()
        self.InstanceCreationDate = timestamp.strftime('%Y%m%d')
        self.InstanceCreationTime = timestamp.strftime('%H%M%S.%f')
        self.SeriesDate = self.InstanceCreationDate
        self.SeriesTime = self.InstanceCreationTime
        self.ContentDate = self.InstanceCreationDate
        self.ContentTime = self.InstanceCreationTime

        # Frame of Reference module
        self.FrameOfReferenceUID = pydicom.uid.generate_uid()

        # Enhanced General Equipment module
        # http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.5.2.html#table_C.7-8b
        self.Manufacturer = 'pydicom-seg'
        self.ManufacturerModelName = 'https://github.com/razorx89/pydicom-seg'
        self.DeviceSerialNumber = '0'
        self.SoftwareVersions = __version__

        # Image Pixel module
        if rows <= 0 or columns <= 0:
            raise ValueError('Rows and columns must be larger than zero')
        self.Rows = rows
        self.Columns = columns
        self.PixelData = b''

        # Segmentation Image module
        # http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.8.20.2.html#table_C.8.20-2
        self.ImageType = ['DERIVED', 'PRIMARY']
        self.InstanceNumber = '1'
        self.ContentLabel = 'SEGMENTATION'
        self.ContentDescription = ''
        self.ContentCreatorName = ''
        self.SamplesPerPixel = 1
        self.PhotometricInterpretation = 'MONOCHROME2'
        self.PixelRepresentation = 0
        self.LossyImageCompression = '00'
        self.SegmentSequence = pydicom.Sequence()

        self.SegmentationType = segmentation_type.value
        if segmentation_type == SegmentationType.BINARY:
            # Binary segmentations are always bit-packed
            self.BitsAllocated = 1
            self.BitsStored = 1
            self.HighBit = 0
        else:
            # Fractional segmentations are always 8-bit unsigned
            self.SegmentationFractionalType = segmentation_fractional_type.value
            if max_fractional_value < 1 or max_fractional_value > 255:
                raise ValueError(
                    'Invalid maximum fractional value for 8-bit unsigned int data'
                )
            self.MaximumFractionalValue = max_fractional_value
            self.BitsAllocated = 8
            self.BitsStored = 8
            self.HighBit = 7

        # Multi-frame Functional Groups module
        self.SharedFunctionalGroupsSequence = pydicom.Sequence(
            [pydicom.Dataset()])
        self.PerFrameFunctionalGroupsSequence = pydicom.Sequence()
        self.NumberOfFrames = 0
コード例 #17
0
import pydicom as pdcm
import PIL.Image as IMG
import numpy as np
import cv2
import os
pdcm.Sequence()
file_path = r"C:\Users\MERT\Desktop\Biokido\Bacak"
file_path_list = os.listdir(file_path)
cnt_volume_array = np.array([])
closed_cnt = []
pixel_counter = 0


def PolygonArea(corners):
    n = len(corners)
    area = 0.0
    for i in range(n):
        j = (i + 1) % n
        area += corners[i][0] * corners[j][1]
        area -= corners[j][0] * corners[i][1]
    area = abs(area) / 2.0
    return area


for i in range(len(file_path_list)):
    file_name = file_path_list[i]
    im = cv2.imread(file_path + "/" + file_name)
    im = cv2.fastNlMeansDenoisingColored(im, None, 10, 10, 7, 21)
    roi = cv2.selectROI("contour", im)
    cv2.waitKey(1)
    cv2.destroyAllWindows()
コード例 #18
0
                    else:
                        ref.add_new(0x300a0018, 'DS', [0, 0, 0])

                    ref.add_new(0x300a0020, 'CS', 'ORGAN_AT_RISK')
                    ref.add_new(
                        0x300a0023, 'DS',
                        beamset.Prescription.PrimaryDosePrescription.DoseValue
                        / 100)
                    ref.add_new(
                        0x300a002c, 'DS',
                        beamset.Prescription.PrimaryDosePrescription.DoseValue
                        / 100)

                    if 'DoseReferenceSequence' not in ds:
                        ds.add_new(0x300a0010, 'SQ', pydicom.Sequence([ref]))
                        expected.add(ds[0x300a0010])

                    else:
                        if 'DoseReferenceStructureType' not in ds.DoseReferenceSequence[0] or \
                                ds.DoseReferenceSequence[0].DoseReferenceStructureType != \
                                ref.DoseReferenceStructureType:
                            expected.add(ref[0x300a0014])

                        if 'DoseReferenceDescription' not in ds.DoseReferenceSequence[0] or \
                                ds.DoseReferenceSequence[0].DoseReferenceDescription != ref.DoseReferenceDescription:
                            expected.add(ref[0x300a0016])

                        if 'DoseReferencePointCoordinates' not in ds.DoseReferenceSequence[0] or \
                                ds.DoseReferenceSequence[0].DoseReferencePointCoordinates != \
                                ref.DoseReferencePointCoordinates: