Esempio n. 1
0
    def _convert_value(self, val):
        """Convert `val` to an appropriate type and return the result.

        Uses the element's VR in order to determine the conversion method and
        resulting type.
        """
        if self.VR == 'SQ':  # a sequence - leave it alone
            from pydicom.sequence import Sequence
            if isinstance(val, Sequence):
                return val
            else:
                return Sequence(val)

        # if the value is a list, convert each element
        try:
            val.append
        except AttributeError:  # not a list
            return self._convert(val)
        else:
            return MultiValue(lambda x: self._convert(x), val)
Esempio n. 2
0
def MultiString(val, valtype=str):
    """Split a bytestring by delimiters if there are any

    val -- DICOM bytestring to split up
    valtype -- default str, but can be e.g.
    UID to overwrite to a specific type
    """
    # Remove trailing blank used to pad to even length
    # 2005.05.25: also check for trailing 0, error made
    # in PET files we are converting

    if val and (val.endswith(' ') or val.endswith('\x00')):
        val = val[:-1]
    splitup = val.split("\\")

    if len(splitup) == 1:
        val = splitup[0]
        return valtype(val) if val else val
    else:
        return MultiValue(valtype, splitup)
Esempio n. 3
0
def convert_PN(byte_string,
               encodings=None):
    """Read and return string(s) as PersonName instance(s)"""

    def get_valtype(x):
        if not in_py2:
            return PersonName(x, encodings).decode()
        return PersonName(x, encodings)

    # XXX - We have to replicate MultiString functionality
    # here because we can't decode easily here since that
    # is performed in PersonNameUnicode
    if byte_string.endswith((b' ', b'\x00')):
        byte_string = byte_string[:-1]

    splitup = byte_string.split(b"\\")

    if len(splitup) == 1:
        return get_valtype(splitup[0])
    else:
        return MultiValue(get_valtype, splitup)
Esempio n. 4
0
    def copy_contours(self):
        '''
        copy contours to rs
        :param self:
        :return:
        '''
        ROIContourSequence  = Sequence()
        #ContourSequence     = Sequence()

        StructureSetROISequence = Sequence()
        for i in range( len( self.contours)):
            StructureSetROI = Dataset()
            StructureSetROI.ROINumber    = self.contours[i]['number']
            StructureSetROI.ROIName      = self.contours[i]['name']
            StructureSetROI.ROIGenerationAlgorithm = 'MANUAL'
            StructureSetROI.ReferencedFrameOfReferenceUID = ''
            StructureSetROISequence.append(StructureSetROI)

            ContourSequence = Sequence()
            for k in range( len( self.contours[i]['contour'])):
                contour_slice = Dataset()
                contour_slice.ContourData = MultiValue( dicom.valuerep.DSfloat, self.contours[i]['contour'][k])
                contour_slice.ContourGeometricType = 'CLOSED_PLANAR'
                contour_slice.NumberOfContourPoints = len( contour_slice.ContourData) /3 ########wrong

                contour_slice.ContourImageSequence = Sequence()

                ContourSequence.append( contour_slice)

            ROIContour = Dataset()
            ROIContour.ContourSequence = ContourSequence
            ROIContour.ROIDisplayColor = self.rs_generic.structure.ROIContourSequence[i].ROIDisplayColor
            ROIContour.ReferencedROINumber = self.rs_generic.structure.ROIContourSequence[i].ReferencedROINumber
            ROIContourSequence.append( ROIContour)


        self.rs.structure.ROIContourSequence = ROIContourSequence
        self.rs.structure.StructureSetROISequence = StructureSetROISequence

        return 0
Esempio n. 5
0
def convert_ATvalue(
    byte_string: bytes,
    is_little_endian: bool,
    struct_format: Optional[str] = None
) -> Union[BaseTag, MutableSequence[BaseTag]]:
    """Return a decoded 'AT' value.

    Parameters
    ----------
    byte_string : bytes
        The encoded 'AT' element value.
    is_little_endian : bool
        ``True`` if the value is encoded as little endian, ``False`` otherwise.
    struct_format : str, optional
        Not used.

    Returns
    -------
    BaseTag or MultiValue of BaseTag
        The decoded value(s).
    """
    length = len(byte_string)
    if length == 4:
        return convert_tag(byte_string, is_little_endian)

    # length > 4
    if length % 4 != 0:
        logger.warn(
            "Expected length to be multiple of 4 for VR 'AT', "
            f"got length {length}"
        )
    return MultiValue(
        Tag,
        [
            convert_tag(byte_string, is_little_endian, offset=x)
            for x in range(0, length, 4)
        ]
    )
Esempio n. 6
0
    def _convert_value(self, val: Any) -> Any:
        """Convert `val` to an appropriate type and return the result.

        Uses the element's VR in order to determine the conversion method and
        resulting type.
        """
        if self.VR == VR_.SQ:  # a sequence - leave it alone
            from pydicom.sequence import Sequence
            if isinstance(val, Sequence):
                return val

            return Sequence(val)

        # if the value is a list, convert each element
        try:
            val.append
        except AttributeError:  # not a list
            return self._convert(val)
        if len(val) == 1:
            return self._convert(val[0])
        return MultiValue(self._convert,
                          val,
                          validation_mode=self.validation_mode)
Esempio n. 7
0
def convert_text(byte_string, encodings=None):
    """Return a decoded text VR value, ignoring backslashes.

    Text VRs are 'SH', 'LO' and 'UC'.

    Parameters
    ----------
    byte_string : bytes or str
        The encoded text VR element value.
    encodings : list of str, optional
        A list of the character encoding schemes used to encode the value.

    Returns
    -------
    str or list of str
        The decoded value(s).
    """
    values = byte_string.split(b'\\')
    values = [convert_single_string(value, encodings) for value in values]
    if len(values) == 1:
        return values[0]
    else:
        return MultiValue(str, values)
Esempio n. 8
0
 def testSetIndex(self):
     """MultiValue: Setting list item converts it to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival[1] = '7'
     assert isinstance(multival[1], IS)
     assert 7 == multival[1]
Esempio n. 9
0
 def testAppend(self):
     """MultiValue: Append of item converts it to required type..."""
     multival = MultiValue(IS, [1, 5, 10])
     multival.append('5')
     assert isinstance(multival[-1], IS)
     assert 5 == multival[-1]
Esempio n. 10
0
 def testLimits(self, enforce_valid_values):
     """MultiValue: Raise error if any item outside DICOM limits...."""
     with pytest.raises(OverflowError):
         MultiValue(IS, [1, -2**31 - 1])
Esempio n. 11
0
 def testMultiDS(self):
     """MultiValue: Multi-valued data elements can be created........"""
     multival = MultiValue(DS, ['11.1', '22.2', '33.3'])
     for val in multival:
         assert isinstance(val, (DSfloat, DSdecimal))
Esempio n. 12
0
 def testLimits(self):
     """MultiValue: Raise error if any item outside DICOM limits...."""
     with pytest.raises(OverflowError):
         MultiValue(IS, [1, -2**31 - 1], validation_mode=config.RAISE)
Esempio n. 13
0
def dump_dicom(data, folder, spacing=(1, 1, 1),
               origin=(0, 0, 0), intercept=0, slope=1):
    """ Dump 3D scan in dicom format.

    Parameters
    ----------
    data : ndarray
        3D numpy array containing ct scan's data.
    folder : str
        folder where dicom files will be dumped.
    spacing : ArrayLike
        ndarray of shape (3,) that contains spacing along z, y, x axes.
    origin : ArrayLike
        ndarray of shape (3,) that contains origin for z, y, x axes.
    interception : float
        interception value. Default is 0.
    slope : float
        slope value. Default is 1.
    """
    spacing = np.array(spacing).reshape(-1)
    origin = np.array(origin).reshape(-1)

    if not os.path.exists(folder):
        os.makedirs(folder)

    num_slices = data.shape[0]
    scan_id = np.random.randint(2 ** 16)
    for i in range(num_slices):
        slice_name = (
            hex(scan_id + i)
            .replace('x', '')
            .upper()
            .zfill(8)
        )
        filename = os.path.join(folder, slice_name)
        pixel_array = (data[i, ...] - intercept) / slope
        locZ, locY, locX = (float(origin[0] + spacing[0] * i),
                            float(origin[1]), float(origin[2]))

        file_meta = Dataset()
        file_meta.MediaStorageSOPClassUID = 'Secondary Capture Image Storage'
        file_meta.MediaStorageSOPInstanceUID = (
            hex(scan_id)
            .replace('x', '')
            .upper()
            .zfill(8)
        )
        file_meta.ImplementationClassUID = slice_name

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

        dataset.PixelData = pixel_array.astype(np.uint16).tostring()
        dataset.RescaleSlope = slope
        dataset.RescaleIntercept = intercept

        dataset.ImagePositionPatient = MultiValue(type_constructor=float,
                                                  iterable=[locZ, locY, locX])

        dataset.PixelSpacing = MultiValue(type_constructor=float,
                                          iterable=[float(spacing[1]),
                                                    float(spacing[2])])
        dataset.SliceThickness = float(spacing[0])

        dataset.Modality = 'WSD'
        dataset.Columns = pixel_array.shape[0]
        dataset.Rows = pixel_array.shape[1]
        dataset.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
        dataset.PixelRepresentation = 1
        dataset.BitsAllocated = 16
        dataset.BitsStored = 16
        dataset.SamplesPerPixel = 1

        write_file(filename, dataset)
Esempio n. 14
0
 def testSetIndex(self):
     """MultiValue: Setting list item converts it to required type"""
     multival = MultiValue(IS, [1, 5, 10])
     multival[1] = '7'
     self.assertTrue(isinstance(multival[1], IS))
     self.assertEqual(multival[1], 7, "Item set by index is not correct value")
Esempio n. 15
0
 def testAppend(self):
     """MultiValue: Append of item converts it to required type..."""
     multival = MultiValue(IS, [1, 5, 10])
     multival.append('5')
     self.assertTrue(isinstance(multival[-1], IS))
     self.assertEqual(multival[-1], 5, "Item set by append is not correct value")
Esempio n. 16
0
    def copy_from_rs(self):
        '''
        Just change the contour in the structure, not create a new structure
        :return:
        '''
        K = 20

        # for i in range(1, 5):
        #     self.rs.structure.ROIContourSequence[i].ContourSequence = self.rs.structure.ROIContourSequence[
        #                                                                   i].ContourSequence[K:K]

        #points = [[8, 9, -238],[8,9, -241],[8,9,-244]]
        #points = [[8, 9, -238], [8, 9, -241], [8, 9, -244]]
        # points = [[8.057, -194.784, -238], [8.545, -194.784, -238], [14.648, -195.272, -238]]
        #points = [[8, -194, -238], [8, -194, -241], [14, -195, -244]]
        #import numpy as np
        #points = 10 * np.random.random([K, 3] )
        # for i in [0,1,2,3,4]:
        #     for k in range( K):
        #         contour =  MultiValue( dicom.valuerep.DSfloat, np.array(self.contours[i]['contour'][k][:30])*1)
        #         #contour = MultiValue( dicom.valuerep.DSfloat, points[k])
        #         print contour
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourData = contour
        #     self.rs.structure.ROIContourSequence[i].ContourSequence = self.rs.structure.ROIContourSequence[i].ContourSequence[:K]

        # for i in [0,1,2,3,4]:
        #     for k in range( K):
        #         contour =  MultiValue( dicom.valuerep.DSfloat, np.array(self.contours[i]['contour'][k])*10)
        #         # contour = MultiValue( dicom.valuerep.DSfloat, points[k])
        #         print contour
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourData = contour
        #     #     self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourData = self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourData
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourImageSequence =  self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourImageSequence
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourGeometricType = self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourGeometricType
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].NumberOfContourPoints = self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].NumberOfContourPoints
        #     self.rs.structure.ROIContourSequence[i].ContourSequence = self.rs.structure.ROIContourSequence[i].ContourSequence[:K]

        # self.rs.structure.ROIContourSequence[i] = self.rs_test.structure.ROIContourSequence[i]

        # for i in range(len( self.contours)):
        #     for k in range(K): #range( len( self.contours[i]['contour'])):
        #         contour =  MultiValue( dicom.valuerep.DSfloat, np.array(self.contours[i]['contour'][k]))
        #         # contour = MultiValue( dicom.valuerep.DSfloat, points[k])
        #         #print contour
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourData = contour
        #     #     self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourData = self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourData
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourImageSequence =  Sequence() #self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourImageSequence
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].ContourGeometricType = 'CLOSED_PLANAR'#self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourGeometricType
        #         self.rs.structure.ROIContourSequence[i].ContourSequence[k].NumberOfContourPoints = len(contour)/3 #self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].NumberOfContourPoints
        #     self.rs.structure.ROIContourSequence[i].ContourSequence = self.rs.structure.ROIContourSequence[i].ContourSequence[:K]

        for i in range(len(self.contours)):
            self.rs.structure.ROIContourSequence[i].ContourSequence = Sequence(
            )
            for k in range(len(self.contours[i]['contour'])):
                contour = MultiValue(dicom.valuerep.DSfloat,
                                     np.array(self.contours[i]['contour'][k]))
                # contour = MultiValue( dicom.valuerep.DSfloat, points[k])
                #print contour
                ContourSequence = Dataset()
                ContourSequence.ContourData = contour
                ContourSequence.ContourImageSequence = Sequence(
                )  #self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourImageSequence
                ContourSequence.ContourGeometricType = 'CLOSED_PLANAR'  #self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].ContourGeometricType
                ContourSequence.NumberOfContourPoints = len(
                    contour
                ) / 3  #self.rs_test.structure.ROIContourSequence[i].ContourSequence[k].NumberOfContourPoints
                self.rs.structure.ROIContourSequence[i].ContourSequence.append(
                    ContourSequence)
Esempio n. 17
0
 def testMultiDS(self):
     """MultiValue: Multi-valued data elements can be created........"""
     multival = MultiValue(DS, ['11.1', '22.2', '33.3'])
     for val in multival:
         self.assertTrue(isinstance(val, (DSfloat, DSdecimal)),
                         "Multi-value DS item not converted to DS")
Esempio n. 18
0
def rtDicomFromPreviousFolder(input_folder,
                              ctr_folder_names,
                              ctr_names,
                              base_img,
                              masks_np,
                              output_file_name,
                              prefix_name=''):
    '''

    :param input_folder: Input folder where the ORIGINAL DICOMS are stored
    :param ctr_folder_names:  Name of folders where to search for contours
    :param ctr_names:  Original contour names (this should have the same order as the contours in masks_np)
    :param base_img:  Itk base image to use for obtaining the positions
    :param masks_np:  This is the numpy 4D binary array with the masks for each contour (ctr, slice, w, h)
    :param prefix_name:  Additional prefix string to use in the new names of the contours
    :return:
    '''

    print('Getting pydicom DataSequence from previous data...')

    # Gets the proper rt_folder to look for contours
    cont_file = getLatestContourFolder(ctr_folder_names, input_folder)
    lstFilesContour = io_com.get_dicom_files_in_folder(
        join(input_folder, cont_file))

    # ===================== Contours ==================
    ds = pydicom.read_file(lstFilesContour[0])  # Reads original dataset

    final_ROIContourSequence_values = [
    ]  # This should be an array of datasets and will be replaced from the original FileDataset

    # Iterate over the ROIContourSequences (ak the contours) Type: Sequence
    for new_idx_ctr, c_ctr_name in enumerate(ctr_names):
        old_idx_ctr = [
            i for i, x in enumerate(ds.StructureSetROISequence)
            if x.ROIName == c_ctr_name
        ]

        if len(old_idx_ctr) == 0:  # If the ROI was not found
            print(
                F'ERROR the contour {c_ctr_name} was not found on previous RTStructure'
            )
        else:
            # If the ROI is found, the we use it as a template and just modify the positions of the contours
            old_idx_ctr = old_idx_ctr[0]  # Get just the index
            ds.StructureSetROISequence[
                new_idx_ctr].ROIName == c_ctr_name  # Use the new name for this index
            ds.StructureSetROISequence[
                new_idx_ctr].ROINumber == new_idx_ctr + 1  # Use the new name for this index
            ds.Manufacturer = 'Deep Learning Biomarkers Group'
            ds.SeriesDate = str(datetime.date.today().strftime('%Y%m%d'))
            ds.SeriesDescription = 'NN Prediction'
            ds.StructureSetName = 'NN Prediction'

            print(F'\t Found {c_ctr_name} with idx {old_idx_ctr}!!!')

            # Copy the ROIContourSequence of the OLD contour (VERY IMPORTANT STEP, we initalize with everything that was there before)
            old_ROIContour_ds = ds.ROIContourSequence[old_idx_ctr]
            ds.StructureSetROISequence[
                old_idx_ctr].ROIName = F'{prefix_name}{c_ctr_name}'

            cur_mask = masks_np[
                new_idx_ctr, :, :, :]  # Get the proper mask data

            slices_w_data = np.unique(np.where(cur_mask > 0)[0])
            cur_ContourSequence_values = []

            # Use the first contour sequence as template (IMPORTANT)
            old_ContourSequence_ds = old_ROIContour_ds.ContourSequence[0]
            # Iterate slices with some contour inside. Transform the mask to contour and add it to the sequence
            for idx_slice, slice in enumerate(slices_w_data):
                # for idx_slice,slice in enumerate([82]):

                # Get a contour from the 2D mask using OpenCV
                ctr_pts = getContourMinPosFrom2DMask(cur_mask[slice, :, :])
                # --- Use to visualize the contoured points for each slice and contour -----------
                # import matplotlib.pyplot as plt
                # plt.imshow(cur_mask[slice,:,:])
                # plt.title(slice)
                # for c_ctr in ctr_pts:
                #     plt.scatter(c_ctr[:,0,0],c_ctr[:,0,1],s=1)
                # plt.show()

                tot_ctrs = len(ctr_pts)
                # Iterate over EACH contour and create a dataset for each of them
                for idx_ctr in range(tot_ctrs):
                    cur_contourdata_ds = Dataset(
                    )  # This is the Contour data for this ContourSequence
                    # Copy the desired values from the OLD ContourSequence
                    cur_contourdata_ds.ContourGeometricType = old_ContourSequence_ds.ContourGeometricType

                    contourdata_values = []

                    tot_pts = ctr_pts[idx_ctr].shape[0]
                    this_slice_indx_pos = []

                    # Add the 'desired' format of (x,y,z) for each contour point
                    cur_ctr_pts = ctr_pts[idx_ctr]
                    for cur_pt in cur_ctr_pts:
                        this_slice_indx_pos.append(
                            [int(cur_pt[0][0]),
                             int(cur_pt[0][1]),
                             int(slice)])
                    # We add again the first point of each contour at the end (trying to 'close' the contour)
                    this_slice_indx_pos.append([
                        int(cur_ctr_pts[0][0][0]),
                        int(cur_ctr_pts[0][0][1]),
                        int(slice)
                    ])

                    # Transform each point into a physical position
                    for c_pos in this_slice_indx_pos:
                        phys_pos = base_img.TransformIndexToPhysicalPoint(
                            c_pos)
                        contourdata_values.append(phys_pos[0])
                        contourdata_values.append(phys_pos[1])
                        contourdata_values.append(phys_pos[2])

                    # Copy the list of physical positions in the variable ContourData of the dataset
                    cur_contourdata_ds.ContourData = MultiValue(
                        pydicom.valuerep.DSfloat, contourdata_values)
                    cur_contourdata_ds.NumberOfContourPoints = tot_pts

                    # Append this Dataset into the list of slices with date
                    cur_ContourSequence_values.append(cur_contourdata_ds)

            old_ROIContour_ds.ContourSequence = Sequence(
                cur_ContourSequence_values
            )  # Replace the desired sequence with the new values
            final_ROIContourSequence_values.append(
                old_ROIContour_ds)  # Append it to the ROIContourSquence values

    # Replace the original Sequence of ROIContours with the new ones. These were initialized with the previous values
    # and only the Contour Data was replaced
    new_ROIContourSequence = Sequence(final_ROIContourSequence_values)
    ds.ROIContourSequence = new_ROIContourSequence  # Replace old ROIContourSequence with new one

    pydicom.dcmwrite(output_file_name, ds)