Пример #1
0
def convert_struct(plan, export_path):

    # Check that the plan has a primary image, as we can't create a meaningful RTSTRUCT without it:
    if not plan.primary_image:
        plan.logger.error(
            "No primary image found for plan. Unable to generate RTSTRUCT.")
        return

    patient_info = plan.pinnacle.patient_info

    struct_sop_instuid = plan.struct_inst_uid

    # Populate required values for file meta information
    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = RTStructSOPClassUID
    file_meta.TransferSyntaxUID = GTransferSyntaxUID
    file_meta.MediaStorageSOPInstanceUID = struct_sop_instuid
    file_meta.ImplementationClassUID = GImplementationClassUID

    struct_filename = "RS." + struct_sop_instuid + ".dcm"

    ds = FileDataset(struct_filename, {},
                     file_meta=file_meta,
                     preamble=b"\x00" * 128)
    ds = FileDataset(struct_filename, {},
                     file_meta=file_meta,
                     preamble=b"\x00" * 128)

    struct_series_instuid = pydicom.uid.generate_uid()
    ds.ReferencedStudySequence = Sequence()

    # not sure what I want here, going off of template dicom file
    ds.SpecificCharacterSet = "ISO_IR 100"
    ds.InstanceCreationDate = time.strftime("%Y%m%d")
    ds.InstanceCreationTime = time.strftime("%H%M%S")
    ds.SOPClassUID = RTStructSOPClassUID
    ds.SOPInstanceUID = struct_sop_instuid
    ds.Modality = RTSTRUCTModality
    ds.AccessionNumber = ""
    ds.Manufacturer = Manufacturer  # from sample dicom file, maybe should change?
    # not sure where to get information for this element can find this and
    # read in from
    ds.StationName = "adacp3u7"
    # ds.ManufacturersModelName = 'Pinnacle3'
    ReferencedStudy1 = Dataset()
    ds.ReferencedStudySequence.append(ReferencedStudy1)
    # Study Component Management SOP Class (chosen from template)
    ds.ReferencedStudySequence[
        0].ReferencedSOPClassUID = "1.2.840.10008.3.1.2.3.2"
    ds.ReferencedStudySequence[
        0].ReferencedSOPInstanceUID = plan.primary_image.image_info[0][
            "StudyInstanceUID"]
    ds.StudyInstanceUID = plan.primary_image.image_info[0]["StudyInstanceUID"]
    ds.SeriesInstanceUID = struct_series_instuid

    ds.PatientID = patient_info["MedicalRecordNumber"]
    ds.ReferringPhysiciansName = patient_info["ReferringPhysician"]
    ds.PhysiciansOfRecord = patient_info["RadiationOncologist"]
    ds.StudyDescription = patient_info["Comment"]
    ds.PatientSex = patient_info["Gender"][0]
    ds.PatientBirthDate = patient_info["DOB"]
    ds.StructureSetLabel = plan.plan_info["PlanName"]
    ds.StudyID = plan.primary_image.image["StudyID"]

    datetimesplit = plan.plan_info["ObjectVersion"]["WriteTimeStamp"].split()
    # Read more accurate date from trial file if it is available
    trial_info = plan.trial_info
    if trial_info:
        datetimesplit = trial_info["ObjectVersion"]["WriteTimeStamp"].split()

    study_date = datetimesplit[0].replace("-", "")
    study_time = datetimesplit[1].replace(":", "")

    ds.StructureSetDate = study_date
    ds.StructureSetTime = study_time
    ds.StudyDate = study_date
    ds.StudyTime = study_time
    ds.ManufacturersModelName = plan.plan_info["ToolType"]
    ds.SoftwareVersions = plan.plan_info["PinnacleVersionDescription"]
    ds.StructureSetName = "POIandROI"
    ds.SeriesNumber = "1"
    ds.PatientName = patient_info["FullName"]

    ds.ReferencedFrameOfReferenceSequence = Sequence()
    ReferencedFrameofReference = Dataset()
    ds.ReferencedFrameOfReferenceSequence.append(ReferencedFrameofReference)
    ds.ReferencedFrameOfReferenceSequence[
        0].FrameOfReferenceUID = plan.primary_image.image_info[0]["FrameUID"]
    ds.ReferencedFrameOfReferenceSequence[
        0].RTReferencedStudySequence = Sequence()

    RTReferencedStudy = Dataset()
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence.append(
        RTReferencedStudy)
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].ReferencedSOPClassUID = "1.2.840.10008.3.1.2.3.2"
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].ReferencedSOPInstanceUID = plan.primary_image.image_info[0][
            "StudyInstanceUID"]
    ds.StudyInstanceUID = plan.primary_image.image_info[0]["StudyInstanceUID"]
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].RTReferencedSeriesSequence = Sequence()

    RTReferencedSeries = Dataset()
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].RTReferencedSeriesSequence.append(RTReferencedSeries)
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].RTReferencedSeriesSequence[
            0].SeriesInstanceUID = plan.primary_image.image_info[0][
                "SeriesUID"]
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].RTReferencedSeriesSequence[0].ContourImageSequence = Sequence()

    for info in plan.primary_image.image_info:
        contour_image = Dataset()
        contour_image.ReferencedSOPClassUID = "1.2.840.10008.5.1.4.1.1.2"
        contour_image.ReferencedSOPInstanceUID = info["InstanceUID"]
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
            0].RTReferencedSeriesSequence[0].ContourImageSequence.append(
                contour_image)

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

    # Determine ISO Center
    find_iso_center(plan)

    ds = read_points(ds, plan)
    ds = read_roi(ds, plan)

    # find out where to get if its been approved or not
    # find out how to insert proper 'CodeString' here
    ds.ApprovalStatus = "UNAPPROVED"
    # Set the transfer syntax

    ds.is_little_endian = True
    ds.is_implicit_VR = True

    # Save the RTDose Dicom File
    output_file = os.path.join(export_path, struct_filename)
    plan.logger.info("Creating Struct file: %s \n" % (output_file))
    ds.save_as(output_file)
Пример #2
0
    def create_dicom_base(self):
        if _dicom_loaded is False:
            raise ModuleNotLoadedError("Dicom")
        if self.header_set is False:
            raise InputError("Header not loaded")

        # TODO tags + code datatypes are described here:
        # https://www.dabsoft.ch/dicom/6/6/#(0020,0012)
        # datatype codes are described here:
        # ftp://dicom.nema.org/medical/DICOM/2013/output/chtml/part05/sect_6.2.html

        meta = Dataset()
        meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'  # CT Image Storage
        # Media Storage SOP Instance UID tag 0x0002,0x0003 (type UI - Unique Identifier)
        meta.MediaStorageSOPInstanceUID = self._ct_sop_instance_uid
        meta.ImplementationClassUID = "1.2.3.4"
        meta.TransferSyntaxUID = uid.ImplicitVRLittleEndian  # Implicit VR Little Endian - Default Transfer Syntax
        ds = FileDataset("file", {}, file_meta=meta, preamble=b"\0" * 128)
        ds.PatientName = self.patient_name
        if self.patient_id in (None, ''):
            ds.PatientID = datetime.datetime.today().strftime('%Y%m%d-%H%M%S')
        else:
            ds.PatientID = self.patient_id  # Patient ID tag 0x0010,0x0020 (type LO - Long String)
        ds.PatientSex = ''  # Patient's Sex tag 0x0010,0x0040 (type CS - Code String)
        #                      Enumerated Values: M = male F = female O = other.
        ds.PatientBirthDate = '19010101'
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.AccessionNumber = ''
        ds.is_little_endian = True
        ds.is_implicit_VR = True
        ds.SOPClassUID = '1.2.3'  # !!!!!!!!
        # SOP Instance UID tag 0x0008,0x0018 (type UI - Unique Identifier)
        ds.SOPInstanceUID = self._ct_sop_instance_uid

        # Study Instance UID tag 0x0020,0x000D (type UI - Unique Identifier)
        # self._dicom_study_instance_uid may be either set in __init__ when creating new object
        #   or set when import a DICOM file
        #   Study Instance UID for structures is the same as Study Instance UID for CTs
        ds.StudyInstanceUID = self._dicom_study_instance_uid

        # Series Instance UID tag 0x0020,0x000E (type UI - Unique Identifier)
        # self._ct_dicom_series_instance_uid may be either set in __init__ when creating new object
        #   or set when import a DICOM file
        #   Series Instance UID for structures might be different than Series Instance UID for CTs
        ds.SeriesInstanceUID = self._ct_dicom_series_instance_uid

        # Study Instance UID tag 0x0020,0x000D (type UI - Unique Identifier)
        ds.FrameofReferenceUID = '1.2.3'  # !!!!!!!!!
        ds.StudyDate = datetime.datetime.today().strftime('%Y%m%d')
        ds.StudyTime = datetime.datetime.today().strftime('%H%M%S')
        ds.PhotometricInterpretation = 'MONOCHROME2'
        ds.SamplesPerPixel = 1
        ds.ImageOrientationPatient = ['1', '0', '0', '0', '1', '0']
        ds.Rows = self.dimx
        ds.Columns = self.dimy
        ds.SliceThickness = str(self.slice_distance)
        ds.PixelSpacing = [self.pixel_size, self.pixel_size]

        # Add eclipse friendly IDs
        ds.StudyID = '1'  # Study ID tag 0x0020,0x0010 (type SH - Short String)
        ds.ReferringPhysiciansName = 'py^trip'  # Referring Physician's Name tag 0x0008,0x0090 (type PN - Person Name)
        ds.PositionReferenceIndicator = ''  # Position Reference Indicator tag 0x0020,0x1040
        ds.SeriesNumber = '1'  # SeriesNumber tag 0x0020,0x0011 (type IS - Integer String)

        return ds
Пример #3
0
    def add_default_elements(self):
        filename = os.path.join(
            self.save_to_dir,
            "compressed_instance_" + str(self.instance_cnt) + ".dcm")
        ds = FileDataset(filename, {},
                         preamble=b"\0" * 128,
                         is_implicit_VR=self.IS_IMPLICIT_VR,
                         is_little_endian=self.IS_LITTLE_ENDIAN)
        ds.SpecificCharacterSet = 'ISO_IR 100'
        # ds.ImageType = ['ORIGINAL', 'PRIMARY', 'VOLUME', 'NONE']
        ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.77.1.6'  # VL Whole Slide Microscopy Image Storage
        # ds.SOPInstanceUID = '1.2.276.0.7230010.3.1.4.296485376.1.1484917438.721089'
        ds.StudyDate = '20170120'
        ds.SeriesDate = '20170120'
        ds.ContentDate = '20170120'
        ds.AcquisitionDateTime = '20170120130353.000000'
        ds.StudyTime = '130353.000000'
        ds.SeriesTime = '130353.000000'
        ds.ContentTime = '130353.000000'
        ds.AccessionNumber = '123456789'
        ds.Modality = 'SM'
        ds.Manufacturer = 'MyManufacturer'
        ds.ReferringPhysicianName = 'SOME^PHYSICIAN'
        ds.ManufacturerModelName = 'MyModel'
        ds.VolumetricProperties = 'VOLUME'
        if self.JPEG_COMPRESS:
            ds.PatientName = 'compressed_' + self._wsi_fn_
            ds.PatientID = 'compressed_' + self._wsi_fn_
        else:
            ds.PatientName = 'uncompressed_' + self._wsi_fn_
            ds.PatientID = 'uncompressed_' + self._wsi_fn_
        ds.PatientBirthDate = '19700101'
        ds.PatientSex = 'M'
        ds.DeviceSerialNumber = 'MySerialNumber'
        ds.SoftwareVersions = 'MyVersion'
        ds.AcquisitionDuration = 100
        ds.StudyInstanceUID = '1.2.276.0.7230010.3.1.2.296485376.1.1484917433.721084'
        # ds.SeriesInstanceUID = '1.2.276.0.7230010.3.1.3.296485376.1.1484917433.721085'
        ds.StudyID = 'NONE'
        # ds.SeriesNumber = 1
        ds.PatientOrientation = ''
        ds.ImageComments = 'http://openslide.cs.cmu.edu/download/openslide-testdata/Aperio/'

        ds_DimensionOrganization = Dataset()
        ds_DimensionOrganization.DimensionOrganizationUID = '1.2.276.0.7230010.3.1.4.296485376.1.1484917433.721087'
        ds.DimensionOrganizationSequence = Sequence([ds_DimensionOrganization])

        ds_DimensionIndex0 = Dataset()
        ds_DimensionIndex0.DimensionOrganizationUID = '1.2.276.0.7230010.3.1.4.296485376.1.1484917433.721087'
        ds_DimensionIndex0.DimensionIndexPointer = Tag(0x0048021E)
        ds_DimensionIndex0.FunctionalGroupPointer = Tag(0x0048021A)
        ds_DimensionIndex1 = Dataset()
        ds_DimensionIndex1.DimensionOrganizationUID = '1.2.276.0.7230010.3.1.4.296485376.1.1484917433.721087'
        ds_DimensionIndex1.DimensionIndexPointer = Tag(0x0048021F)
        ds_DimensionIndex1.FunctionalGroupPointer = Tag(0x0048021A)
        ds.DimensionIndexSequence = Sequence(
            [ds_DimensionIndex0, ds_DimensionIndex1])

        ds_WholeSlideMicroscopyImageFrameType = Dataset()
        ds_WholeSlideMicroscopyImageFrameType.FrameType = [
            'ORIGINAL', 'PRIMARY', 'VOLUME', 'NONE'
        ]
        ds.WholeSlideMicroscopyImageFrameTypeSequence = Sequence(
            [ds_WholeSlideMicroscopyImageFrameType])

        ds.SamplesPerPixel = 3
        # ds.PhotometricInterpretation = 'RGB'
        ds.PhotometricInterpretation = 'MONOCHROME2'

        ds.PlanarConfiguration = 0
        ds.Rows = self.patch_size[1]
        ds.Columns = self.patch_size[0]
        ds.BitsAllocated = 8
        ds.BitsStored = 8
        ds.HighBit = 7
        ds.PixelRepresentation = 0
        ds.BurnedInAnnotation = 'NO'
        ds.LossyImageCompression = '01'
        ds.LossyImageCompressionRatio = 10
        ds.LossyImageCompressionMethod = 'ISO_10918_1'
        # ds.LossyImageCompressionMethod = 'ISO_14495_1'
        ds.ContainerIdentifier = 'CI_12345'
        ds.IssuerOfTheContainerIdentifierSequence = []
        ds.ContainerTypeCodeSequence = []
        ds.AcquisitionContextSequence = []
        ds.ColorSpace = 'sRGB'

        ds_SpecimenDescription = Dataset()
        ds_SpecimenDescription.SpecimenIdentifier = 'Specimen^Identifier'
        ds_SpecimenDescription.SpecimenUID = '1.2.276.0.7230010.3.1.4.3252829876.4112.1426166133.871'
        ds_SpecimenDescription.IssuerOfTheSpecimenIdentifierSequence = []
        ds_SpecimenDescription.SpecimenPreparationSequence = []
        ds.SpecimenDescriptionSequence = Sequence([ds_SpecimenDescription])

        ds.ImagedVolumeWidth = 15
        ds.ImagedVolumeHeight = 15
        ds.ImagedVolumeDepth = 1

        ds_TotalPixelMatrixOrigin = Dataset()
        ds_TotalPixelMatrixOrigin.XOffsetInSlideCoordinateSystem = 20
        ds_TotalPixelMatrixOrigin.YOffsetInSlideCoordinateSystem = 40
        ds.TotalPixelMatrixOriginSequence = Sequence(
            [ds_TotalPixelMatrixOrigin])

        ds.SpecimenLabelInImage = 'NO'
        ds.FocusMethod = 'AUTO'
        ds.ExtendedDepthOfField = 'NO'
        ds.ImageOrientationSlide = ['0', '-1', '0', '-1', '0', '0']

        ds_OpticalPath = Dataset()
        ds_IlluminationTypeCode = Dataset()
        ds_IlluminationTypeCode.CodeValue = '111744'
        ds_IlluminationTypeCode.CodingSchemeDesignator = 'DCM'
        ds_IlluminationTypeCode.CodeMeaning = 'Brightfield illumination'
        ds_OpticalPath.IlluminationTypeCodeSequence = Sequence(
            [ds_IlluminationTypeCode])
        ds_OpticalPath.ICCProfile = b'RGB'
        ds_OpticalPath.OpticalPathIdentifier = '1'
        ds_OpticalPath.OpticalPathDescription = 'Brightfield'
        ds_IlluminationColorCode = Dataset()
        ds_IlluminationColorCode.CodeValue = 'R-102C0'
        ds_IlluminationColorCode.CodingSchemeDesignator = 'SRT'
        ds_IlluminationColorCode.CodeMeaning = 'Full Spectrum'
        ds_OpticalPath.IlluminationColorCodeSequence = Sequence(
            [ds_IlluminationColorCode])
        ds.OpticalPathSequence = Sequence([ds_OpticalPath])

        ds_PixelMeasures = Dataset()
        ds_PixelMeasures.SliceThickness = 1
        ds_PixelMeasures.PixelSpacing = ['0.00025', '0.00025']
        PixelMeasuresSequence = Sequence([ds_PixelMeasures])
        ds_OpticalPathIdentification = Dataset()
        ds_OpticalPathIdentification.OpticalPathIdentifier = '1'
        OpticalPathIdentificationSequence = Sequence(
            [ds_OpticalPathIdentification])

        SharedFunctionalGroupsSequence = Dataset()
        SharedFunctionalGroupsSequence.OpticalPathIdentificationSequence = OpticalPathIdentificationSequence
        SharedFunctionalGroupsSequence.PixelMeasuresSequence = PixelMeasuresSequence
        ds.SharedFunctionalGroupsSequence = Sequence(
            [SharedFunctionalGroupsSequence])
        return ds
Пример #4
0
def main(dir, args):

    energies = [90 + i*5 for i in range(0,29)] # in MeV, they go from 90 to 230 in steps of 5 MeV
    lat_sigma = [6 for energy in energies] # in mm
    print(energies, lat_sigma)

    parser = argparse.ArgumentParser(description='Generate an ideal CT of a water cube and a dummy RTplan with certain energy layers, defined in script. Every energy layer has a single spot at (0,0)')

    parser.add_argument('--outdir', dest='outdir'   , type=str, required=True )
    parser.add_argument('--inDate', dest='inDate'   , type=str, required=False, default='20191011')
    parser.add_argument('--stDate', dest='stDate'   , type=str, required=False, default='20191011')
    parser.add_argument('--seDate', dest='seDate'   , type=str, required=False, default='20191011')
    parser.add_argument('--acDate', dest='acDate'   , type=str, required=False, default='20191011')
    parser.add_argument('--coDate', dest='coDate'   , type=str, required=False, default='20191011')
    parser.add_argument('--inTime', dest='inTime'   , type=str, required=False, default='150000')
    parser.add_argument('--stTime', dest='stTime'   , type=str, required=False, default='150000')
    parser.add_argument('--seTime', dest='seTime'   , type=str, required=False, default='150000')
    parser.add_argument('--acTime', dest='acTime'   , type=str, required=False, default='150000')
    parser.add_argument('--coTime', dest='coTime'   , type=str, required=False, default='150000')
    parser.add_argument('--access', dest='access'   , type=str, required=False, default='0')
    parser.add_argument('--manuf' , dest='manuf'    , type=str, required=False, default='MGH Physics Research')
    parser.add_argument('--station',dest='station'  , type=str, required=False, default='Nashua')
    parser.add_argument('--institution' , dest='institution', type=str, required=False, default='rbe')
    parser.add_argument('--instaddr' , dest='instaddr', type=str, required=False, default='30 Fruit St, Boston MA 02114, USA')
    parser.add_argument('--physician' , dest='physician', type=str, required=False, default='')
    parser.add_argument('--stDesc', dest='stDesc'  , type=str, required=False, default='For commissioning')
    parser.add_argument('--seDesc', dest='seDesc'  , type=str, required=False, default='Water cube 50x50x50cm3')
    parser.add_argument('--operat', dest='operat'  , type=str, required=False, default='FHG')
    parser.add_argument('--manmod', dest='manmod'  , type=str, required=False, default='2019')
    parser.add_argument('--patname',dest='patname' , type=str, required=False, default='Cube^Water')
    parser.add_argument('--patid' , dest='patid'   , type=str, required=False, default='WaterCube50')
    parser.add_argument('--birth' , dest='birth'   , type=str, required=False, default='N/A')
    parser.add_argument('--age'   , dest='age'     , type=str, required=False, default='N/A')
    parser.add_argument('--sex'   , dest='sex'     , type=str, required=False, default='N/A')
    parser.add_argument('--anon'  , dest='anon'    , type=str, required=False, default='NO')
    parser.add_argument('--sw'    , dest='sw'      , type=str, required=False, default='pydicom1.2.2')
    parser.add_argument('--lastCal',dest='lastCal' , type=str, required=False, default='')
    parser.add_argument('--stuid' , dest='stuid'   , type=str, required=False, default=uid.generate_uid())
    parser.add_argument('--seuid' , dest='seuid'   , type=str, required=False, default=uid.generate_uid())
    parser.add_argument('--stid'  , dest='stid'    , type=str, required=False, default='1')
    parser.add_argument('--sen'   , dest='sen'     , type=str, required=False, default='1')
    parser.add_argument('--acqn'  , dest='acqn'    , type=str, required=False, default='1')
    parser.add_argument('--forUID', dest='forUID'  , type=str, required=False, default=uid.generate_uid())
    parser.add_argument('--RTlabel',dest='RTlabel' , type=str, required=False, default='Test')
    parser.add_argument('--RTname', dest='RTname'  , type=str, required=False, default='Pristine test')
    parser.add_argument('--RTdesc', dest='RTdesc'  , type=str, required=False, default='Dummy energies')
    parser.add_argument('--RTdate', dest='RTdate'  , type=str, required=False, default='20191011')
    parser.add_argument('--RTtime', dest='RTtime'  , type=str, required=False, default='150000')
    parser.add_argument('--RTgeom', dest='RTgeom'  , type=str, required=False, default='PATIENT')
    parser.add_argument('--sadX'  , dest='sadX'    , type=float, required=False, default=2000.0)
    parser.add_argument('--sadY'  , dest='sadY'    , type=float, required=False, default=1800.0)
    parser.add_argument('--nBlocks',dest='nBlocks' , type=int, required=False, default=0)
    parser.add_argument('--isoX'  , dest='isoX'    , type=float, required=False, default=0.0)
    parser.add_argument('--isoY'  , dest='isoY'    , type=float, required=False, default=0.0)
    parser.add_argument('--isoZ'  , dest='isoZ'    , type=float, required=False, default=0.0)
    parser.add_argument('--snout' , dest='snout'   , type=str, required=False, default='')
    parser.add_argument('--nRS'   , dest='nRS'     , type=int, required=False, default=0)
    parser.add_argument('--rsID'  , dest='rsID'    , type=str, required=False, default='')
    parser.add_argument('--snPos' , dest='snPos'   , type=float, required=False, default=200)
    parser.add_argument('--seX'   , dest='seX'     , type=float, required=False, default=0.0)
    parser.add_argument('--seY'   , dest='seY'     , type=float, required=False, default=0.0)
    parser.add_argument('--seZ'   , dest='seZ'     , type=float, required=False, default=0.0)
    parser.add_argument('--ssd'   , dest='ssd'     , type=float, required=False, default=2000.0)
    parser.add_argument('--beam'        , dest='beam'       , type=str  , required=False, nargs='?', default='G000'  )
    parser.add_argument('--machine'     , dest='machine'    , type=str  , required=False, nargs='?', default='1.1')
    parser.add_argument('--dosimeter'   , dest='dosimeter'  , type=str  , required=False, nargs='?', default='NP'    )
    parser.add_argument('--tuneid'      , dest='tuneid'     , type=str  , required=False, nargs='?', default='Tune'  )
    parser.add_argument('--gangle'      , dest='gangle'     , type=int  , required=False, nargs='?', default=0       )
    args = parser.parse_args(args)

    # Check output dir
    if not os.path.exists(args.outdir):
        print('Creating output folder',args.outdir)
        os.mkdir(args.outdir)
        os.mkdir(os.path.join(args.outdir,'ct'))
    else:
        print('Writing to preexisting output folder',args.outdir)

    # Define geometry of water cube
    rows = 512
    columns = 512
    slices = 512
    wrows = 1 # mm
    wcolumns = 1 # mm
    wslices = 1 # mm
    margin = 6 # pixels of air around water cube, so that water cube is 50cm * 50cm * 50cm

    cube = np.full((slices, rows, columns), -1000, dtype='int16') # -1000 HU as initialization value (air)
    cube[margin:-margin,margin:-margin,margin:-margin] = 0 # 0 HU cube (water)

    # Generate CT image
    for sl in range(slices):
        # ~ break
        output_file = os.path.join(args.outdir,'ct',str(sl+1)+'.dcm')
        image = cube[sl]
        print(output_file,image.shape)

        meta = Dataset()
        meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'#'CT Image Storage'
        meta.MediaStorageSOPInstanceUID = uid.generate_uid()
        meta.ImplementationClassUID = uid.PYDICOM_IMPLEMENTATION_UID
        meta.TransferSyntaxUID = uid.ImplicitVRLittleEndian
        ds = FileDataset(output_file, {}, file_meta=meta, preamble=b"\0" * 128)

        ds.SpecificCharacterSet     = 'ISO_IR 100'
        ds.ImageType                = ['DERIVED','SECONDARY','AXIAL']
        ds.InstanceCreationDate     = args.inDate
        ds.InstanceCreationTime     = args.inTime
        ds.SOPClassUID              = ds.file_meta.MediaStorageSOPClassUID
        ds.SOPInstanceUID           = ds.file_meta.MediaStorageSOPInstanceUID
        ds.StudyDate                = args.stDate
        ds.SeriesDate               = args.seDate
        ds.AcquisitionDate          = args.acDate
        ds.ContentDate              = args.coDate
        ds.StudyTime                = args.stTime
        ds.SeriesTime               = args.seTime
        ds.AcquisitionTime          = args.acTime
        ds.ContentTime              = args.coTime
        ds.AccessionNumber          = args.access
        ds.Modality                 = 'CT'
        ds.Manufacturer             = args.manuf
        ds.InstitutionName          = args.institution
        ds.InstitutionAddress       = args.instaddr
        ds.ReferringPhysicianName   = args.physician
        ds.StationName              = args.station
        ds.StudyDescription         = args.stDesc
        ds.SeriesDescription        = args.seDesc
        ds.OperatorsName            = args.operat
        ds.ManufacturerModelName    = args.manmod
        ds.PatientName              = args.patname
        ds.PatientID                = args.patid
        ds.PatientBirthDate         = args.birth
        ds.PatientSex               = args.sex
        ds.PatientAge               = args.age
        ds.PatientIdentityRemoved   = args.anon
        ds.AdditionalPatientHistory = 'Pseudo-CT'
        if args.anon == "YES":
            ds.DeidentificationMethod   = "Manual"
        ds.SliceThickness           = wslices
        # ~ ds.FocalSpots
        # ~ ds.KVP
        # ~ ds.DataCollectionDiameter
        # ~ ds.ReconstructionDiameter
        ds.SoftwareVersions         = args.sw
        # ~ ds.DistanceSourceToDetector
        # ~ ds.DistanceSourceToPatient
        # ~ ds.GantryDetectorTilt
        # ~ ds.ExposureTime
        # ~ ds.XRayTubeCurrent
        # ~ ds.RotationDirection
        # ~ ds.ConvolutionKerne
        # ~ ds.FilterType
        ds.ProtocolName             = 'RESEARCH'
        # ~ ds.ScanOptions = 'AXIAL MODE'
        ds.DateOfLastCalibration    = args.lastCal
        ds.PatientPosition          = 'HFS'
        ds.StudyInstanceUID         = args.stuid
        ds.SeriesInstanceUID        = args.seuid
        ds.StudyID                  = args.stid
        ds.SeriesNumber             = args.sen
        ds.AcquisitionNumber        = args.acqn
        ds.InstanceNumber           = sl + 1
        # Zero (origin) is at surface of water on anterior side, and centered in the other axes. Image position refers to center point of corner voxel.
        ds.ImagePositionPatient     = [(-columns/2+0.5)*wcolumns,(-margin+0.5)*wrows, (slices/2-0.5-sl)*wslices]
        ds.ImageOrientationPatient  = ['1', '0', '0', '0', '1', '0']
        ds.FrameOfReferenceUID      = args.forUID
        ds.PositionReferenceIndicator = ''#'OM'
        ds.SliceLocation            = ds.ImagePositionPatient[2]

        ds.SamplesPerPixel          = 1
        ds.PhotometricInterpretation= 'MONOCHROME2'
        ds.Rows                     = rows
        ds.Columns                  = columns
        ds.PixelSpacing             = [wcolumns, wrows]
        ds.BitsAllocated            = 16
        ds.BitsStored               = 16
        ds.HighBit                  = 15
        ds.PixelRepresentation      = 1
        ds.SmallestImagePixelValue  = np.amin(image).tobytes()
        ds.LargestImagePixelValue   = np.amax(image).tobytes()
        ds.WindowCenter             = 0
        ds.WindowWidth              = 1000
        ds.RescaleIntercept         = 0
        ds.RescaleSlope             = 1
        ds.RescaleType              = "HU"
        ds.PixelData                = image.tobytes()
        # ~ ds.PixelPaddingValue        = -2000

        ds.save_as(output_file, False)

    # Generate RTplan with certain energies
    meta = Dataset()
    meta.MediaStorageSOPClassUID    = '1.2.840.10008.5.1.4.1.1.481.8' #RT Ion Plan Storage
    meta.MediaStorageSOPInstanceUID = uid.generate_uid()
    meta.ImplementationClassUID     = uid.PYDICOM_IMPLEMENTATION_UID
    meta.TransferSyntaxUID = uid.ImplicitVRLittleEndian
    output_file = os.path.join(args.outdir,'rtplan.dcm')
    ds = FileDataset(output_file, {}, file_meta=meta, preamble=b"\0" * 128)

    ds.SpecificCharacterSet     = 'ISO_IR 100'
    ds.InstanceCreationDate     = args.inDate
    ds.InstanceCreationTime     = args.inTime
    ds.SOPClassUID              = ds.file_meta.MediaStorageSOPClassUID
    ds.SOPInstanceUID           = ds.file_meta.MediaStorageSOPInstanceUID
    ds.StudyDate                = args.stDate
    ds.SeriesDate               = args.seDate
    ds.StudyTime                = args.stTime
    ds.SeriesTime               = args.seTime
    ds.AccessionNumber          = ''
    ds.Modality                 = 'RTPLAN'
    ds.Manufacturer             = args.manuf
    ds.InstitutionName          = args.institution
    ds.ReferringPhysicianName   = args.physician
    ds.StudyDescription         = args.stDesc
    ds.SeriesDescription        = args.seDesc
    ds.OperatorsName            = args.operat
    ds.ManufacturerModelName    = args.manmod
    ds.PatientName              = args.patname
    ds.PatientID                = args.patid
    ds.PatientBirthDate         = args.birth
    ds.PatientSex               = args.sex
    ds.PatientAge               = args.age
    ds.PatientIdentityRemoved   = args.anon
    if args.anon=="YES":
        ds.DeidentificationMethod   = "Manual"
    ds.SoftwareVersions         = args.sw
    ds.DateOfLastCalibration    = args.lastCal
    ds.StudyInstanceUID         = args.stuid
    ds.SeriesInstanceUID        = args.seuid
    ds.StudyID                  = args.stid
    ds.SeriesNumber             = args.sen
    ds.InstanceNumber           = args.acqn
    ds.FrameOfReferenceUID      = args.forUID
    ds.PositionReferenceIndicator = ''
    ds.RTPlanLabel              = args.RTlabel
    ds.RTPlanName               = args.RTname
    ds.RTPlanDescription        = args.RTdesc
    ds.RTPlanDate               = args.RTdate
    ds.RTPlanTime               = args.RTtime
    ds.RTPlanGeometry           = args.RTgeom

    ds.FractionGroupSequence = Sequence()
    dsfx = Dataset()
    dsfx.FractionGroupNumber      = 1
    dsfx.FractionGroupDescription = ''
    dsfx.NumberOfFractionsPlanned = 1
    dsfx.NumberOfBeams            = 1
    dsfx.NumberOfBrachyApplicationSetups = 0
    dsfx.ReferencedBeamSequence = Sequence()
    dsfx_b = Dataset()
    dsfx_b.BeamDoseSpecificationPoint = [args.isoX,args.isoY,args.isoZ]
    dsfx_b.BeamDose = 1 #dummy
    dsfx_b.BeamMeterset = float(len(energies)*1e9)#1e9 protons per spot
    dsfx_b.ReferencedBeamNumber = 1
    dsfx.ReferencedBeamSequence.append(dsfx_b)
    ds.FractionGroupSequence.append(dsfx)

    ds.PatientSetupSequence = Sequence()
    pss = Dataset()
    pss.PatientPosition = 'HFS'
    pss.PatientSetupNumber = 1
    pss.PatientSetupLabel = 'Standard'
    ds.PatientSetupSequence.append(pss)

    ds.IonBeamSequence = Sequence()
    be = Dataset()
    ds.IonBeamSequence.append(be)
    be.BeamName = args.beam
    be.IonControlPointSequence = Sequence()
    be.TreatmentMachineName = args.machine
    be.InstitutionName = args.institution
    be.PrimaryDosimeterUnit = args.dosimeter
    be.Manufacturer = args.manuf
    be.InstitutionName  = args.institution
    be.ManufacturerModelName  = args.machine
    be.InstitutionAddress = args.instaddr
    be.TreatmentMachineName   = args.machine
    be.PrimaryDosimeterUnit   = args.dosimeter
    be.BeamNumber             = '1'
    be.BeamName               = args.beam
    be.BeamDescription        = 'Gantry from top'
    be.BeamType               = 'STATIC'
    be.RadiationType          = 'PROTON'
    be.TreatmentDeliveryType  = 'TREATMENT'
    be.NumberOfWedges         = 0
    be.NumberOfCompensators   = 0
    be.NumberOfBoli           = 0
    be.NumberOfBlocks         = args.nBlocks
    be.FinalCumulativeMetersetWeight = int(1e9*len(energies)) # 1e9 protons per spot (energy)
    be.NumberOfControlPoints  = 2*len(energies)
    be.ScanMode                   = 'MODULATED'
    be.VirtualSourceAxisDistances = [args.sadX, args.sadY]
    if not args.snout == '':
        be.SnoutSequence = Sequence()
        sds = Dataset()
        sds.AccessoryCode = args.snout
        sds.SnoutID = args.snout
        be.SnoutSequence.append(sds)
    be.NumberOfRangeShifters   = args.nRS
    if args.nRS == 1:
        be.RangeShifterSequence = Sequence()
        rsds = Dataset()
        rsds.AccessoryCode = 'Undefined Accessory Code'
        rsds.RangeShifterNumber = 1
        rsds.RangeShifterID = args.rsID
        rsds.RangeShifterType = 'BINARY'
        be.RangeShifterSequence.append(rsds)
    be.NumberOfLateralSpreadingDevices = 0
    be.NumberOfRangeModulators = 0
    be.PatientSupportType = 'TABLE'
    cweight = 0
    for i,energy in enumerate(energies[::-1]):
        for j in range(2):
            icpoi = Dataset()
            icpoi.NominalBeamEnergyUnit = 'MEV'
            icpoi.ControlPointIndex = i*2 + j
            icpoi.NominalBeamEnergy = str(energy)
            if j == 0:
                icpoi.GantryAngle = args.gangle
                icpoi.GantryRotationDirection = 'NONE'
                icpoi.BeamLimitingDeviceAngle = 0
                icpoi.BeamLimitingDeviceRotationDirection = 'NONE'
                icpoi.PatientSupportAngle = 0
                icpoi.PatientSupportRotationDirection = 'NONE'
                icpoi.TableTopVerticalPosition     = 0
                icpoi.TableTopLongitudinalPosition = 0
                icpoi.TableTopLateralPosition      = 0
                icpoi.IsocenterPosition            = [args.isoX,args.isoY,args.isoZ]
                icpoi.SurfaceEntryPoint            = [args.seX,args.seY,args.seZ]
                icpoi.SourceToSurfaceDistance      = args.ssd
            icpoi.CumulativeMetersetWeight = cweight
            if j == 0:
                icpoi.TableTopPitchAngle = 0
                icpoi.TableTopPitchRotationDirection = 'NONE'
                icpoi.TableTopRollAngle  = 0
                icpoi.TableTopRollRotationDirection = 'NONE'
                icpoi.GantryPitchAngle = 0.0
                icpoi.GantryPitchRotationDirection = 'NONE'
                icpoi.SnoutPosition      = args.snPos
            icpoi.ScanSpotTuneID = args.tuneid
            icpoi.NumberOfScanSpotPositions = 1
            icpoi.ScanSpotPositionMap = [0.0, 0.0]
            icpoi.ScanSpotMetersetWeights = 1e9 if j == 0 else 0
            cweight += icpoi.ScanSpotMetersetWeights
            icpoi.ScanningSpotSize = [lat_sigma[len(energies)-i-1],lat_sigma[len(energies)-i-1]]
            icpoi.NumberOfPaintings = 1
            be.IonControlPointSequence.append(icpoi)
    be.ReferencedPatientSetupNumber = 1
    be.ReferencedToleranceTableNumber = 0

    ds.ApprovalStatus = 'UNAPPROVED'
    # ~ ds.ReviewDate     = args.inDate
    # ~ ds.ReviewTime     = args.inTime
    # ~ ds.ReviewerName   = 'You'

    ds.save_as(output_file, False)
    print('Done, saved to',output_file)
Пример #5
0
def write_dicom(ods, anon_values, out_dir, grouping):
    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = 'Secondary Capture Image Storage'
    file_meta.MediaStorageSOPInstanceUID = str(anon_values['sopID'])
    file_meta.ImplementationClassUID = '0.0'
    file_meta.TransferSyntaxUID = ods.file_meta.TransferSyntaxUID if "TransferSyntaxUID" in ods.file_meta else "0.0"

    ds = FileDataset(anon_values['studyID'], {},
                     file_meta=file_meta,
                     preamble=ods.preamble)

    ds.StudyInstanceUID = str(anon_values['studyID'])
    ds.SeriesInstanceUID = str(anon_values['seriesID'])
    ds.SOPInstanceUID = str(anon_values['sopID'])
    ds.SOPClassUID = 'Secondary Capture Image Storage'
    ds.AccessionNumber = str(anon_values['accession'])
    ds.PatientID = str(anon_values['mrn'])
    ds.StudyID = str(anon_values['studyID'])

    ds.PatientName = str(anon_values['mrn'])
    ds.ReferringPhysicianName = ""
    ds.StudyDate = "000000"
    ds.StudyTime = "000000"
    ds.PatientBirthTime = "000000.000000"
    ds.PatientBirthDate = "00000000"
    ds.PatientAge = utils.calculate_age(
        ods.StudyDate, ods.PatientBirthDate) if (
            "StudyDate" in ods and "PatientBirthDate" in ods) else ""
    ds.PatientSex = ods.PatientSex if "PatientSex" in ods else ""

    ds.StudyDescription = ods.StudyDescription if "StudyDescription" in ods else ""
    ds.SeriesDescription = ods.SeriesDescription if "SeriesDescription" in ods else ""
    ds.Modality = ods.Modality if "Modality" in ods else ""
    ds.SeriesNumber = ods.SeriesNumber if "SeriesNumber" in ods else ""
    ds.InstanceNumber = ods.InstanceNumber if "InstanceNumber" in ods else ""

    ds.PlanarConfiguration = ods.PlanarConfiguration if "PlanarConfiguration" in ods else ""
    ds.ViewPosition = ods.ViewPosition if "ViewPosition" in ods else ""
    ds.PatientOrientation = ods.PatientOrientation if "PatientOrientation" in ods else ""

    ds.SamplesPerPixel = ods.SamplesPerPixel if "SamplesPerPixel" in ods else ""
    ds.PhotometricInterpretation = ods.PhotometricInterpretation if "PhotometricInterpretation" in ods else ""
    ds.PixelRepresentation = ods.PixelRepresentation if "PixelRepresentation" in ods else ""
    ds.ImagerPixelSpacing = ods.ImagerPixelSpacing if "ImagerPixelSpacing" in ods else ""
    ds.HighBit = ods.HighBit if "HighBit" in ods else ""
    ds.BitsStored = ods.BitsStored if "BitsStored" in ods else ""
    ds.BitsAllocated = ods.BitsAllocated if "BitsAllocated" in ods else ""
    ds.Columns = ods.Columns if "Columns" in ods else ""
    ds.Rows = ods.Rows if "Rows" in ods else ""

    ds.SpecificCharacterSet = ods.SpecificCharacterSet if "SpecificCharacterSet" in ods else ""
    ds.SecondaryCaptureDeviceManufctur = 'Python 3.X'
    ds.PresentationLUTShape = ods.PresentationLUTShape if "PresentationLUTShape" in ods else ""
    ds.KVP = ods.KVP if "KVP" in ods else ""
    ds.XRayTubeCurrent = ods.XRayTubeCurrent if "XRayTubeCurrent" in ods else ""
    ds.ExposureTime = ods.ExposureTime if "ExposureTime" in ods else ""
    ds.Exposure = ods.Exposure if "Exposure" in ods else ""
    ds.ExposureControlMode = ods.ExposureControlMode if "ExposureControlMode" in ods else ""
    ds.RelativeXRayExposure = ods.RelativeXRayExposure if "RelativeXRayExposure" in ods else ""
    ds.FocalSpots = ods.FocalSpots if "FocalSpots" in ods else ""
    ds.AnodeTargetMaterial = ods.AnodeTargetMaterial if "AnodeTargetMaterial" in ods else ""
    ds.BodyPartThickness = ods.BodyPartThickness if "BodyPartThickness" in ods else ""
    ds.CompressionForce = ods.CompressionForce if "CompressionForce" in ods else ""
    ds.PaddleDescription = ods.PaddleDescription if "PaddleDescription" in ods else ""
    ds.BurnedInAnnotation = ""
    ds.DistanceSourceToDetector = ods.DistanceSourceToDetector if "DistanceSourceToDetector" in ods else ""
    ds.DistanceSourceToPatient = ods.DistanceSourceToPatient if "DistanceSourceToPatient" in ods else ""
    ds.PositionerPrimaryAngle = ods.PositionerPrimaryAngle if "PositionerPrimaryAngle" in ods else ""
    ds.PositionerPrimaryAngleDirection = ods.PositionerPrimaryAngleDirection if "PositionerPrimaryAngleDirection" in ods else ""
    ds.PositionerSecondaryAngle = ods.PositionerSecondaryAngle if "PositionerSecondaryAngle" in ods else ""
    ds.ImageLaterality = ods.ImageLaterality if "ImageLaterality" in ods else ""
    ds.BreastImplantPresent = ods.BreastImplantPresent if "BreastImplantPresent" in ods else ""
    ds.Manufacturer = ods.Manufacturer if "Manufacturer" in ods else ""
    ds.ManufacturerModelName = ods.ManufacturerModelName if "ManufacturerModelName" in ods else ""
    ds.EstimatedRadiographicMagnificationFactor = ods.EstimatedRadiographicMagnificationFactor if "EstimatedRadiographicMagnificationFactor" in ods else ""
    ds.DateOfLastDetectorCalibration = ods.DateOfLastDetectorCalibration if "DateOfLastDetectorCalibration" in ods else ""

    filename = utils.clean_string('m' + str(anon_values['mrn']) + '_a' +
                                  str(anon_values['accession']) + '_st' +
                                  str(anon_values['studyID']) + "_se" +
                                  str(anon_values['seriesID']) + "_i" +
                                  str(anon_values['sopID']) + "_" +
                                  str(ds.SeriesNumber) + "_" +
                                  str(ds.InstanceNumber) + "_" +
                                  str(ds.Modality) + "_" +
                                  str(ds.ViewPosition) + ".dcm")

    # Create study directory, if it doesn't already exist.
    if grouping == 'a':
        utils.make_dirs(os.path.join(out_dir, str(anon_values['accession'])))
        out_path = os.path.join(out_dir, str(anon_values['accession']),
                                filename)
    elif grouping == 's':
        utils.make_dirs(os.path.join(out_dir, str(anon_values['studyID'])))
        out_path = os.path.join(out_dir, str(anon_values['studyID']), filename)
    elif grouping == 'm':
        utils.make_dirs(os.path.join(out_dir, str(anon_values['mrn'])))
        out_path = os.path.join(out_dir, str(anon_values['mrn']), filename)
    else:
        out_path = os.path.join(out_dir, filename)

    if 'PixelData' in ods:
        ds.PixelData = ''
        pixel_array = ods.pixel_array
        f = h5py.File('{}.hdf5'.format(out_path[0:-4]))
        f.create_dataset("pixel_array",
                         pixel_array.shape,
                         data=pixel_array,
                         dtype=str(pixel_array.dtype),
                         compression="gzip",
                         shuffle=True)
    ds.save_as(out_path, write_like_original=False)
Пример #6
0
def __write_dicom_series_file_nrrd(series_dataset, dst_one_case_file_path):
    print('start: write dicom series')
    if not os.path.exists(dst_one_case_file_path):
        os.makedirs(dst_one_case_file_path)
    dataset_list = []

    sop_instance_info_list = __generate_sop_instance_info_nrrd(series_dataset)
    study_instance_uid = pydicom.uid.generate_uid()
    series_instance_uid = pydicom.uid.generate_uid()
    frame_of_reference_uid = pydicom.uid.generate_uid()
    volume_size = series_dataset[1]['sizes']  # [width height slices]
    for j in range(volume_size[2]):
        sop_instance_uid = sop_instance_info_list[j][1]
        filename = os.path.join(dst_one_case_file_path,
                                'CT.' + sop_instance_uid + '.dcm')

        file_meta = Dataset()
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
        file_meta.MediaStorageSOPInstanceUID = sop_instance_uid
        file_meta.ImplementationClassUID = '1.2.246.352.70.2.1.7'

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

        ds.InstanceNumber = sop_instance_info_list[j][0]
        ds.SliceLocation = sop_instance_info_list[j][2]
        ds.ImagePositionPatient = sop_instance_info_list[j][3]

        ds.fix_meta_info()
        ds.is_little_endian = True
        ds.is_implicit_VR = True
        ds.StudyInstanceUID = study_instance_uid
        ds.SeriesInstanceUID = series_instance_uid
        ds.FrameOfReferenceUID = frame_of_reference_uid
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.ImageType = 'ORIGINAL\\PRIMARY\\AXIAL\\HELIX'
        dt = datetime.datetime.now()
        datestr = dt.strftime('%Y%m%d')
        timestr1 = dt.strftime('%H%M%S.%f')
        timestr2 = dt.strftime('%H%M%S')

        ds.InstanceCreationDate = datestr
        ds.InstanceCreationTime = timestr1
        ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
        ds.SOPInstanceUID = sop_instance_uid
        ds.StudyDate = datestr
        ds.SeriesDate = datestr
        ds.ContentDate = datestr
        ds.StudyTime = timestr2
        ds.SeriesTime = timestr2
        ds.ContentTime = timestr2
        ds.AccessionNumber = '116939'
        ds.Modality = 'CT'
        ds.Manufacturer = 'Philips'
        ds.InstitutionName = 'Cancer Hosipital'
        ds.ReferringPhysicianName = '79309'
        ds.StationName = '-'
        ds.StudyDescription = '-'
        ds.PatientName = 'patientxxx'
        ds.PatientID = 'patientxxx'
        ds.PatientBirthDate = '19000101'
        ds.PatientBirthTime = '000000'
        ds.PatientAge = '119'
        ds.PatientSex = '-'
        ds.PatientSize = 0
        ds.PatientWeight = 0
        ds.PatientAddress = '-'
        ds.PatientComments = '-'
        ds.SliceThickness = series_dataset[1]['space directions'][2][2]
        ds.KVP = 1111111
        patient_position = series_dataset[1]['space']
        #if patient_position == 'left-posterior-superior':
        ds.PatientPosition = 'HFS'
        ds.StudyID = '-'
        ds.SeriesNumber = 1
        ds.ImageOrientationPatient = '1\\0\\0\\0\\1\\0'
        ds.SamplesPerPixel = 1
        ds.Rows = series_dataset[1]['sizes'][1]
        ds.Columns = series_dataset[1]['sizes'][0]
        spacing_x = series_dataset[1]['space directions'][0][0]
        spacing_y = series_dataset[1]['space directions'][1][1]
        ds.PixelSpacing = '%f\\%f' % (spacing_x, spacing_y)
        ds.BitsAllocated = 16
        ds.BitsStored = 12
        ds.HighBit = 11
        ds.PixelRepresentation = 0
        ds.WindowCenter = 60
        ds.WindowWidth = 350
        ds.RescaleIntercept = -1024
        ds.RescaleSlope = 1
        ds.PixelData = __create_pixel_data_array_nrrd(series_dataset, j)
        ds.save_as(filename, write_like_original=False)
        dataset_list.append(ds)
    print('end: write dicom series')
    return dataset_list
Пример #7
0
def create_image_files(image, export_path):

    # TODO: Fix this function, output not working
    image.logger.warn(
        "Creating image files: The output of these are not correct!")

    patient_info = image.pinnacle.patient_info
    image_header = image.image_header
    image_info = image.image_info
    image_set = image.image_set
    currentpatientposition = image_header["patient_position"]

    modality = "CT"
    try:
        # Also should come from header file, but not always present
        modality = image_header["modality"]
    except:
        pass  # Incase it is not present in header

    img_file = os.path.join(image.path,
                            "ImageSet_%s.img" % (image.image["ImageSetID"]))
    if os.path.isfile(img_file):
        allframeslist = []
        pixel_array = np.fromfile(img_file, dtype=np.short)
        # will loop over every frame
        for i in range(0, int(image_header["z_dim"])):
            frame_array = pixel_array[i * int(image_header["x_dim"]) *
                                      int(image_header["y_dim"]):(i + 1) *
                                      int(image_header["x_dim"]) *
                                      int(image_header["y_dim"])]
            allframeslist.append(frame_array)
    image.logger.debug("Length of frames list: " + str(len(allframeslist)))
    image.logger.debug(image_info[0])

    curframe = 0
    for info in image_info:
        sliceloc = -info["TablePosition"] * 10
        instuid = info["InstanceUID"]
        seriesuid = info["SeriesUID"]
        classuid = info["ClassUID"]
        frameuid = info["FrameUID"]
        studyinstuid = info["StudyInstanceUID"]
        slicenum = info["SliceNumber"]

        dateofscan = image_set["scan_date"]
        timeofscan = image_set["scan_time"]

        file_meta = Dataset()
        file_meta.MediaStorageSOPClassUID = classuid
        file_meta.MediaStorageSOPInstanceUID = instuid
        file_meta.TransferSyntaxUID = GTransferSyntaxUID
        # this value remains static since implementation for creating
        # file is the same
        file_meta.ImplementationClassUID = GImplementationClassUID

        image_file_name = modality + "." + instuid + ".dcm"
        ds = FileDataset(image_file_name, {},
                         file_meta=file_meta,
                         preamble=b"\x00" * 128)

        ds.SpecificCharacterSet = "ISO_IR 100"
        ds.ImageType = ["ORIGINAL", "PRIMARY", "AXIAL"]
        ds.AccessionNumber = ""
        ds.SOPClassUID = classuid
        ds.SOPInstanceUID = instuid
        ds.StudyDate = dateofscan
        ds.SeriesDate = dateofscan
        ds.AcquisitionDate = dateofscan
        ds.ContentDate = dateofscan
        ds.AcquisitionTime = timeofscan
        ds.Modality = modality
        # This should come from Manufacturer in header, but for some
        # patients it isn't set??
        ds.Manufacturer = ""
        ds.StationName = modality
        ds.PatientsName = patient_info["FullName"]
        ds.PatientID = patient_info["MedicalRecordNumber"]
        ds.PatientsBirthDate = patient_info["DOB"]
        ds.BitsAllocated = 16
        ds.BitsStored = 16
        ds.HighBit = 15
        ds.PixelRepresentation = 1
        ds.RescaleIntercept = -1024
        ds.RescaleSlope = 1.0
        # ds.kvp = ?? This should be peak kilovoltage output of x ray
        # generator used
        ds.PatientPosition = currentpatientposition
        # this is probably x_pixdim * xdim = y_pixdim * ydim
        ds.DataCollectionDiameter = (float(image_header["x_pixdim"]) * 10 *
                                     float(image_header["x_dim"]))
        # ds.SpatialResolution = 0.35  # ???????
        # # ds.DistanceSourceToDetector = #???
        # # ds.DistanceSourceToPatient = #????
        # ds.GantryDetectorTilt = 0.0  # ??
        # ds.TableHeight = -158.0  # ??
        # ds.RotationDirection = "CW"  # ???
        # ds.ExposureTime = 1000  # ??
        # ds.XRayTubeCurrent = 398  # ??
        # ds.GeneratorPower = 48  # ??
        # ds.FocalSpots = 1.2  # ??
        # ds.ConvolutionKernel = "STND"  # ????
        ds.SliceThickness = float(image_header["z_pixdim"]) * 10
        ds.NumberOfSlices = int(image_header["z_dim"])
        # ds.StudyInstanceUID = studyinstuid
        # ds.SeriesInstanceUID = seriesuid
        ds.FrameOfReferenceUID = info["FrameUID"]
        ds.StudyInstanceUID = info["StudyInstanceUID"]
        ds.SeriesInstanceUID = info["SeriesUID"]
        # problem, some of these are repeated in image file so not sure
        # what to do with that
        ds.InstanceNumber = slicenum
        ds.ImagePositionPatient = [
            -float(image_header["x_pixdim"]) * 10 *
            float(image_header["x_dim"]) / 2,
            -float(image_header["y_pixdim"]) * 10 *
            float(image_header["y_dim"]) / 2,
            sliceloc,
        ]
        if "HFS" in currentpatientposition or "FFS" in currentpatientposition:
            ds.ImageOrientationPatient = [1.0, 0.0, 0.0, 0.0, 1.0, -0.0]
        elif "HFP" in currentpatientposition or "FFP" in currentpatientposition:
            ds.ImageOrientationPatient = [-1.0, 0.0, 0.0, 0.0, -1.0, -0.0]
        ds.PositionReferenceIndicator = "LM"  # ???
        ds.SliceLocation = sliceloc
        ds.SamplesPerPixel = 1
        ds.PhotometricInterpretation = "MONOCHROME2"
        ds.Rows = int(image_header["x_dim"])
        ds.Columns = int(image_header["y_dim"])
        ds.PixelSpacing = [
            float(image_header["x_pixdim"]) * 10,
            float(image_header["y_pixdim"]) * 10,
        ]

        ds.PixelData = allframeslist[curframe].tostring()

        output_file = os.path.join(export_path, image_file_name)
        image.logger.info("Creating image: " + output_file)
        ds.save_as(output_file)
        curframe = curframe + 1
Пример #8
0
def convert_dose(plan, export_path):

    # Check that the plan has a primary image, as we can't create a meaningful RTDOSE without it:
    if not plan.primary_image:
        plan.logger.error(
            "No primary image found for plan. Unable to generate RTDOSE.")
        return

    patient_info = plan.pinnacle.patient_info
    plan_info = plan.plan_info
    trial_info = plan.trial_info
    machine_info = plan.machine_info
    image_info = plan.primary_image.image_info[0]

    patient_position = plan.patient_position

    # Get the UID for the Dose and the Plan
    doseInstanceUID = plan.dose_inst_uid
    planInstanceUID = plan.plan_inst_uid

    # Populate required values for file meta information
    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = RTDoseSOPClassUID
    file_meta.TransferSyntaxUID = GTransferSyntaxUID
    file_meta.MediaStorageSOPInstanceUID = doseInstanceUID
    file_meta.ImplementationClassUID = GImplementationClassUID

    # Create the FileDataset instance (initially no data elements, but
    # file_meta supplied)
    RDfilename = "RD." + file_meta.MediaStorageSOPInstanceUID + ".dcm"
    ds = FileDataset(RDfilename, {},
                     file_meta=file_meta,
                     preamble=b"\x00" * 128)
    ds.SpecificCharacterSet = "ISO_IR 100"
    ds.InstanceCreationDate = time.strftime("%Y%m%d")
    ds.InstanceCreationTime = time.strftime("%H%M%S")

    ds.SOPClassUID = RTDoseSOPClassUID  # RT Dose Storage
    ds.SOPInstanceUID = doseInstanceUID
    datetimesplit = plan_info["ObjectVersion"]["WriteTimeStamp"].split()
    # Read more accurate date from trial file if it is available
    trial_info = plan.trial_info
    if trial_info:
        datetimesplit = trial_info["ObjectVersion"]["WriteTimeStamp"].split()

    ds.StudyDate = datetimesplit[0].replace("-", "")
    ds.StudyTime = datetimesplit[1].replace(":", "")
    ds.AccessionNumber = ""
    ds.Modality = RTDOSEModality
    ds.Manufacturer = Manufacturer
    ds.OperatorsName = ""
    ds.ManufacturerModelName = plan_info["ToolType"]
    ds.SoftwareVersions = [plan_info["PinnacleVersionDescription"]]
    ds.PhysiciansOfRecord = patient_info["RadiationOncologist"]
    ds.PatientName = patient_info["FullName"]
    ds.PatientBirthDate = patient_info["DOB"]
    ds.PatientID = patient_info["MedicalRecordNumber"]
    ds.PatientSex = patient_info["Gender"][0]

    ds.SliceThickness = trial_info["DoseGrid .VoxelSize .Z"] * 10
    ds.SeriesInstanceUID = doseInstanceUID

    ds.StudyInstanceUID = image_info["StudyInstanceUID"]
    ds.FrameOfReferenceUID = image_info["FrameUID"]
    ds.StudyID = plan.primary_image.image["StudyID"]

    # Assume zero struct shift for now (may not the case for versions below Pinnacle 9)
    x_shift = 0
    y_shift = 0
    if patient_position == "HFP" or patient_position == "FFS":
        dose_origin_x = -trial_info["DoseGrid .Origin .X"] * 10
    elif patient_position == "HFS" or patient_position == "FFP":
        dose_origin_x = trial_info["DoseGrid .Origin .X"] * 10

    if patient_position == "HFS" or patient_position == "FFS":
        dose_origin_y = -trial_info["DoseGrid .Origin .Y"] * 10
    elif patient_position == "HFP" or patient_position == "FFP":
        dose_origin_y = trial_info["DoseGrid .Origin .Y"] * 10

    if patient_position == "HFS" or patient_position == "HFP":
        dose_origin_z = -trial_info["DoseGrid .Origin .Z"] * 10
    elif patient_position == "FFS" or patient_position == "FFP":
        dose_origin_z = trial_info["DoseGrid .Origin .Z"] * 10

    # Image Position (Patient) seems off, so going to calculate shift assuming
    # dose origin in center and I want outer edge
    ydoseshift = (trial_info["DoseGrid .VoxelSize .Y"] * 10 *
                  trial_info["DoseGrid .Dimension .Y"] -
                  trial_info["DoseGrid .VoxelSize .Y"] * 10)
    zdoseshift = (trial_info["DoseGrid .VoxelSize .Z"] * 10 *
                  trial_info["DoseGrid .Dimension .Z"] -
                  trial_info["DoseGrid .VoxelSize .Z"] * 10)

    if patient_position == "HFS":
        ds.ImagePositionPatient = [
            dose_origin_x,
            dose_origin_y - ydoseshift,
            dose_origin_z - zdoseshift,
        ]
    elif patient_position == "HFP":
        ds.ImagePositionPatient = [
            dose_origin_x,
            dose_origin_y + ydoseshift,
            dose_origin_z - zdoseshift,
        ]
    elif patient_position == "FFS":
        ds.ImagePositionPatient = [
            dose_origin_x,
            dose_origin_y - ydoseshift,
            dose_origin_z + zdoseshift,
        ]
    elif patient_position == "FFP":
        ds.ImagePositionPatient = [
            dose_origin_x,
            dose_origin_y + ydoseshift,
            dose_origin_z + zdoseshift,
        ]

    # Read this from CT DCM if available?
    if "HFS" in patient_position or "FFS" in patient_position:
        ds.ImageOrientationPatient = [1.0, 0.0, 0.0, 0.0, 1.0, -0.0]
    elif "HFP" in patient_position or "FFP" in patient_position:
        ds.ImageOrientationPatient = [-1.0, 0.0, 0.0, 0.0, -1.0, -0.0]

    # Read this from CT DCM if available
    ds.PositionReferenceIndicator = ""
    ds.SamplesPerPixel = 1
    ds.PhotometricInterpretation = "MONOCHROME2"

    ds.NumberOfFrames = int(
        trial_info["DoseGrid .Dimension .Z"])  # is this Z dimension???
    # Using y for Rows because that's what's in the exported dicom file for
    # test patient
    ds.Rows = int(trial_info["DoseGrid .Dimension .Y"])
    ds.Columns = int(trial_info["DoseGrid .Dimension .X"])
    ds.PixelSpacing = [
        trial_info["DoseGrid .VoxelSize .X"] * 10,
        trial_info["DoseGrid .VoxelSize .Y"] * 10,
    ]
    ds.BitsAllocated = 16  # ????
    ds.BitsStored = 16  # ???
    ds.HighBit = 15  # ???
    ds.PixelRepresentation = 0
    ds.DoseUnits = "GY"  # 'RELATIVE'#'GY'
    ds.DoseType = "PHYSICAL"
    ds.DoseSummationType = "PLAN"

    # TODO: need to look at what is required from this block
    ds.ReferencedRTPlanSequence = Sequence()
    ReferencedRTPlan1 = Dataset()
    ds.ReferencedRTPlanSequence.append(ReferencedRTPlan1)
    ds.ReferencedRTPlanSequence[0].ReferencedSOPClassUID = RTPlanSOPClassUID
    ds.ReferencedRTPlanSequence[0].ReferencedSOPInstanceUID = planInstanceUID
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence = Sequence()
    ReferencedFractionGroup1 = Dataset()
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence.append(
        ReferencedFractionGroup1)
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence[
        0].ReferencedBeamSequence = Sequence()
    ReferencedBeam1 = Dataset()
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence[
        0].ReferencedBeamSequence.append(ReferencedBeam1)
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence[
        0].ReferencedBeamSequence[0].ReferencedBeamNumber = 0
    ds.ReferencedRTPlanSequence[0].ReferencedFractionGroupSequence[
        0].ReferencedFractionGroupNumber = "1"
    ds.TissueHeterogeneityCorrection = "IMAGE"

    frameoffsetvect = []
    for p in range(0, int(trial_info["DoseGrid .Dimension .Z"])):
        frameoffsetvect.append(
            p * float(trial_info["DoseGrid .VoxelSize .X"] * 10))
    ds.GridFrameOffsetVector = frameoffsetvect

    # Array in which to sum the dose values of all beams
    summed_pixel_values = []

    # For each beam in the trial, convert the dose from the Pinnacle binary
    # file and sum together
    beam_list = trial_info["BeamList"] if trial_info["BeamList"] else []
    if len(beam_list) == 0:
        plan.logger.warning(
            "No Beams found in Trial. Unable to generate RTDOSE.")
        return None

    for beam in beam_list:

        plan.logger.info("Exporting Dose for beam: " + beam["Name"])

        # Get the binary file for this beam
        binary_id = re.findall("\\d+", beam["DoseVolume"])[0]
        binary_file = os.path.join(
            plan.path, "plan.Trial.binary." + str(binary_id).zfill(3))

        # Get the prescription for this beam (need this for number of fractions)
        prescription = [
            p for p in trial_info["PrescriptionList"]
            if p["Name"] == beam["PrescriptionName"]
        ][0]

        # Get the prescription point
        plan.logger.debug("PrescriptionPointName: {0}".format(
            beam["PrescriptionPointName"]))
        points = plan.points
        prescription_point = []
        for p in points:
            if p["Name"] == beam["PrescriptionPointName"]:
                plan.logger.debug("Presc Point: {0} {1} {2} {3}".format(
                    p["Name"], p["XCoord"], p["YCoord"], p["ZCoord"]))
                prescription_point = plan.convert_point(p)
                break

        if len(prescription_point) < 3:
            plan.logger.warning(
                "No valid prescription point found for beam! Beam will be ignored for Dose conversion. Dose will most likely be incorrect"
            )
            continue

        plan.logger.debug("Presc Point Dicom: {0} {1}".format(
            p["Name"], prescription_point))
        total_prescription = (beam["MonitorUnitInfo"]["PrescriptionDose"] *
                              prescription["NumberOfFractions"])
        plan.logger.debug("Total Prescription {0}".format(total_prescription))

        # Read the dose into a grid, so that we can interpolate for the prescription point and determine the MU for the grid
        dose_grid = np.zeros((
            trial_info["DoseGrid .Dimension .X"],
            trial_info["DoseGrid .Dimension .Y"],
            trial_info["DoseGrid .Dimension .Z"],
        ))
        spacing = [
            trial_info["DoseGrid .VoxelSize .X"] * 10,
            trial_info["DoseGrid .VoxelSize .Y"] * 10,
            trial_info["DoseGrid .VoxelSize .Z"] * 10,
        ]
        origin = [
            ds.ImagePositionPatient[0],
            ds.ImagePositionPatient[1],
            ds.ImagePositionPatient[2],
        ]
        if os.path.isfile(binary_file):
            with open(binary_file, "rb") as b:
                for z in range(trial_info["DoseGrid .Dimension .Z"] - 1, -1,
                               -1):
                    for y in range(0, trial_info["DoseGrid .Dimension .Y"]):
                        for x in range(0,
                                       trial_info["DoseGrid .Dimension .X"]):
                            data_element = b.read(4)
                            value = struct.unpack(">f", data_element)[0]
                            dose_grid[x, y, z] = value
        else:
            plan.logger.warning("Dose file not found")
            plan.logger.error("Skipping generating RTDOSE")
            return None

        # Get the index within that grid of the dose reference point
        idx = [0.0, 0.0, 0.0]
        for i in range(3):
            idx[i] = -(origin[i] - prescription_point[i]) / spacing[i]
        plan.logger.debug(
            "Index of prescription point within grid: {0}".format(idx))

        # Trilinear interpolation of that point within the dose grid
        cgy_mu = trilinear_interpolation(idx, dose_grid)
        plan.logger.debug("cgy_mu: {0}".format(cgy_mu))

        # Now that we have the cgy/mu value of the dose reference point, we can
        # extract an accurate value for MU
        beam_mu = (total_prescription /
                   cgy_mu) / prescription["NumberOfFractions"]
        plan.logger.debug("Beam MU: {0}".format(beam_mu))

        pixel_data_list = []
        for z in range(trial_info["DoseGrid .Dimension .Z"] - 1, -1, -1):
            for y in range(0, trial_info["DoseGrid .Dimension .Y"]):
                for x in range(0, trial_info["DoseGrid .Dimension .X"]):
                    value = (float(prescription["NumberOfFractions"]) *
                             dose_grid[x, y, z] * beam_mu / 100)
                    pixel_data_list.append(value)

        ds.FrameIncrementPointer = ds.data_element("GridFrameOffsetVector").tag

        main_pix_array = []
        for h in range(0, trial_info["DoseGrid .Dimension .Z"]):
            pixelsforframe = []
            for k in range(
                    0,
                    trial_info["DoseGrid .Dimension .X"] *
                    trial_info["DoseGrid .Dimension .Y"],
            ):

                pixelsforframe.append(
                    float(
                        pixel_data_list[h *
                                        trial_info["DoseGrid .Dimension .Y"] *
                                        trial_info["DoseGrid .Dimension .X"] +
                                        k]))

            main_pix_array = main_pix_array + list(reversed(pixelsforframe))

        main_pix_array = list(reversed(main_pix_array))

        # Add the values from this beam to the summed values
        if len(summed_pixel_values) == 0:
            summed_pixel_values = main_pix_array
        else:
            for i in range(0, len(summed_pixel_values)):
                summed_pixel_values[
                    i] = summed_pixel_values[i] + main_pix_array[i]

    # Compute the scaling factor
    scale = max(summed_pixel_values) / 16384
    ds.DoseGridScaling = scale
    plan.logger.debug("Dose Grid Scaling: {0}".format(ds.DoseGridScaling))

    pixel_binary_block = bytes()

    # Scale by the scaling factor
    pixelvaluelist = []
    for pp, element in enumerate(summed_pixel_values, 0):

        if scale != 0:
            element = round(element / scale)
        else:
            element = 0
        pixelvaluelist.append(int(element))

    # Set the PixelData
    pixel_binary_block = struct.pack("%sh" % len(pixelvaluelist),
                                     *pixelvaluelist)
    ds.PixelData = pixel_binary_block

    # Save the RTDose Dicom File
    output_file = os.path.join(export_path, RDfilename)
    plan.logger.info("Creating Dose file: %s \n" % (output_file))
    ds.save_as(output_file)
Пример #9
0
def convert_plan(plan, export_path):

    # Check that the plan has a primary image, as we can't create a meaningful RTPLAN without it:
    if not plan.primary_image:
        plan.logger.error(
            "No primary image found for plan. Unable to generate RTPLAN.")
        return

    # TODO Fix the RTPLAN export functionality and remove this warning
    plan.logger.warning(
        "RTPLAN export functionality is currently not validated and not stable. Use with caution."
    )

    patient_info = plan.pinnacle.patient_info
    plan_info = plan.plan_info
    trial_info = plan.trial_info
    image_info = plan.primary_image.image_info[0]
    machine_info = plan.machine_info

    patient_position = plan.patient_position

    # Get the UID for the Plan
    planInstanceUID = plan.plan_inst_uid

    # Populate required values for file meta information
    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = RTPlanSOPClassUID
    file_meta.TransferSyntaxUID = GTransferSyntaxUID
    file_meta.MediaStorageSOPInstanceUID = planInstanceUID
    file_meta.ImplementationClassUID = GImplementationClassUID

    # Create the FileDataset instance (initially no data elements, but
    # file_meta supplied)
    RPfilename = "RP." + file_meta.MediaStorageSOPInstanceUID + ".dcm"
    ds = FileDataset(RPfilename, {},
                     file_meta=file_meta,
                     preamble=b"\x00" * 128)

    ds.SpecificCharacterSet = "ISO_IR 100"
    ds.InstanceCreationDate = time.strftime("%Y%m%d")
    ds.InstanceCreationTime = time.strftime("%H%M%S")

    ds.SOPClassUID = RTPlanSOPClassUID  # RT Plan Storage
    ds.SOPInstanceUID = planInstanceUID

    datetimesplit = plan_info["ObjectVersion"]["WriteTimeStamp"].split()
    datetimesplit = plan_info["ObjectVersion"]["WriteTimeStamp"].split()

    # Read more accurate date from trial file if it is available
    trial_info = plan.trial_info
    if trial_info:
        datetimesplit = trial_info["ObjectVersion"]["WriteTimeStamp"].split()

    ds.StudyDate = datetimesplit[0].replace("-", "")
    ds.StudyTime = datetimesplit[1].replace(":", "")
    ds.AccessionNumber = ""
    ds.Modality = RTPLANModality
    ds.Manufacturer = Manufacturer
    ds.OperatorsName = ""
    ds.ManufacturersModelName = plan_info["ToolType"]
    ds.SoftwareVersions = [plan_info["PinnacleVersionDescription"]]
    ds.PhysiciansOfRecord = patient_info["RadiationOncologist"]
    ds.PatientName = patient_info["FullName"]
    ds.PatientBirthDate = patient_info["DOB"]
    ds.PatientID = patient_info["MedicalRecordNumber"]
    ds.PatientSex = patient_info["Gender"][0]

    ds.StudyInstanceUID = image_info["StudyInstanceUID"]
    ds.SeriesInstanceUID = planInstanceUID
    ds.StudyID = plan.primary_image.image["StudyID"]

    ds.FrameOfReferenceUID = image_info["FrameUID"]
    ds.PositionReferenceIndicator = ""

    ds.RTPlanLabel = plan.plan_info["PlanName"] + ".0"
    ds.RTPlanName = plan.plan_info["PlanName"]
    ds.RTPlanDescription = plan.pinnacle.patient_info["Comment"]
    ds.RTPlanDate = ds.StudyDate
    ds.RTPlanTime = ds.StudyTime

    # ds.PlanIntent = "" #Not sure where to get this informationd, will likely
    # be 'CURATIVE' or 'PALIATIVE'
    ds.RTPlanGeometry = "PATIENT"
    # ds.DoseReferenceSequence = Sequence() #figure out what goes in DoseReferenceSequence... Should be like a target volume and reference point I think...
    # ds.ToleranceTableSequence = Sequence() #figure out where to get this
    # information
    ds.FractionGroupSequence = Sequence()
    ds.BeamSequence = Sequence()
    ds.PatientSetupSequence = Sequence()  # need one per beam
    ds.ReferencedStructureSetSequence = Sequence()
    ReferencedStructureSet1 = Dataset()
    ds.ReferencedStructureSetSequence.append(ReferencedStructureSet1)
    ds.ReferencedStructureSetSequence[
        0].ReferencedSOPClassUID = RTStructSOPClassUID
    ds.ReferencedStructureSetSequence[
        0].ReferencedSOPInstanceUID = plan.struct_inst_uid
    ds.ApprovalStatus = "UNAPPROVED"  # find out where to get this information

    ds.FractionGroupSequence.append(Dataset())
    ds.FractionGroupSequence[0].ReferencedBeamSequence = Sequence()

    metersetweight = ["0"]

    num_fractions = 0
    beam_count = 0
    beam_list = trial_info["BeamList"] if trial_info["BeamList"] else []
    if len(beam_list) == 0:
        plan.logger.warning(
            "No Beams found in Trial. Unable to generate RTPLAN.")
        return None
    for beam in beam_list:

        beam_count = beam_count + 1

        plan.logger.info("Exporting Plan for beam: " + beam["Name"])

        ds.PatientSetupSequence.append(Dataset())
        ds.PatientSetupSequence[beam_count -
                                1].PatientPosition = patient_position
        ds.PatientSetupSequence[beam_count - 1].PatientSetupNumber = beam_count

        ds.FractionGroupSequence[0].ReferencedBeamSequence.append(Dataset())
        ds.FractionGroupSequence[0].ReferencedBeamSequence[
            beam_count - 1].ReferencedBeamNumber = beam_count
        ds.BeamSequence.append(Dataset())
        # figure out what to put here
        ds.BeamSequence[beam_count - 1].Manufacturer = Manufacturer
        ds.BeamSequence[beam_count - 1].BeamNumber = beam_count
        ds.BeamSequence[beam_count - 1].TreatmentDeliveryType = "TREATMENT"
        ds.BeamSequence[beam_count -
                        1].ReferencedPatientSetupNumber = beam_count
        ds.BeamSequence[beam_count - 1].SourceAxisDistance = "1000"
        ds.BeamSequence[beam_count - 1].FinalCumulativeMetersetWeight = "1"
        ds.BeamSequence[beam_count - 1].PrimaryDosimeterUnit = "MU"
        ds.BeamSequence[beam_count - 1].PrimaryFluenceModeSequence = Sequence()
        ds.BeamSequence[beam_count - 1].PrimaryFluenceModeSequence.append(
            Dataset())
        ds.BeamSequence[
            beam_count -
            1].PrimaryFluenceModeSequence[0].FluenceMode = "STANDARD"

        ds.BeamSequence[beam_count - 1].BeamName = beam["FieldID"]
        ds.BeamSequence[beam_count - 1].BeamDescription = beam["Name"]

        if "Photons" in beam["Modality"]:
            ds.BeamSequence[beam_count - 1].RadiationType = "PHOTON"
        elif "Electrons" in beam["Modality"]:
            ds.BeamSequence[beam_count - 1].RadiationType = "ELECTRON"
        else:
            ds.BeamSequence[beam_count - 1].RadiationType = ""

        if "STATIC" in beam["SetBeamType"].upper():
            ds.BeamSequence[beam_count -
                            1].BeamType = beam["SetBeamType"].upper()
        else:
            ds.BeamSequence[beam_count - 1].BeamType = "DYNAMIC"

        ds.BeamSequence[beam_count - 1].TreatmentMachineName = beam[
            "MachineNameAndVersion"].partition(":")[0]

        doserefpt = None
        for point in plan.points:
            if point["Name"] == beam["PrescriptionPointName"]:
                doserefpt = plan.convert_point(point)
                plan.logger.debug("Dose reference point found: " +
                                  point["Name"])

        if not doserefpt:
            plan.logger.debug("No dose reference point, setting to isocenter")
            doserefpt = plan.iso_center

        plan.logger.debug("Dose reference point: " + str(doserefpt))

        ds.FractionGroupSequence[0].ReferencedBeamSequence[
            beam_count - 1].BeamDoseSpecificationPoint = doserefpt

        ds.BeamSequence[beam_count - 1].ControlPointSequence = Sequence()

        cp_manager = {}
        if "CPManagerObject" in beam["CPManager"]:
            cp_manager = beam["CPManager"]["CPManagerObject"]
        else:
            cp_manager = beam["CPManager"]

        numctrlpts = cp_manager["NumberOfControlPoints"]
        currentmeterset = 0.0
        plan.logger.debug("Number of control points: " + str(numctrlpts))

        x1 = ""
        x2 = ""
        y1 = ""
        y2 = ""
        leafpositions = []
        for cp in cp_manager["ControlPointList"]:

            metersetweight.append(cp["Weight"])

            if x1 == "":
                x1 = -cp["LeftJawPosition"] * 10
            if x2 == "":
                x2 = cp["RightJawPosition"] * 10
            if y2 == "":
                y2 = cp["TopJawPosition"] * 10
            if y1 == "":
                y1 = -cp["BottomJawPosition"] * 10

            points = cp["MLCLeafPositions"]["RawData"]["Points[]"].split(",")
            p_count = 0
            leafpositions1 = []
            leafpositions2 = []
            for p in points:
                leafpoint = float(p.strip())
                # logger.debug("leafpoints: ", leafpoints)
                if p_count % 2 == 0:
                    leafpositions1.append(-leafpoint * 10)
                else:
                    leafpositions2.append(leafpoint * 10)
                p_count += 1

                if p_count == len(points):
                    leafpositions1 = list(reversed(leafpositions1))
                    leafpositions2 = list(reversed(leafpositions2))
                    leafpositions = leafpositions1 + leafpositions2

            gantryangle = cp["Gantry"]
            colangle = cp["Collimator"]
            psupportangle = cp["Couch"]
            numwedges = 0
            if (cp["WedgeContext"]["WedgeName"] == "No Wedge"
                    or cp["WedgeContext"]["WedgeName"] == ""):
                wedgeflag = False
                plan.logger.debug("Wedge is no name")
                numwedges = 0
            elif ("edw" in cp["WedgeContext"]["WedgeName"]
                  or "EDW" in cp["WedgeContext"]["WedgeName"]):
                plan.logger.debug("Wedge present")
                wedgetype = "DYNAMIC"
                wedgeflag = True
                numwedges = 1
                wedgeangle = cp["WedgeContext"]["Angle"]
                wedgeinorout = ""
                wedgeinorout = cp["WedgeContext"]["Orientation"]
                if "WedgeBottomToTop" == wedgeinorout:
                    wedgename = (cp["WedgeContext"]["WedgeName"].upper() +
                                 wedgeangle + "IN")
                    wedgeorientation = (
                        "0")  # temporary until I find out what to put here
                elif "WedgeTopToBottom" == wedgeinorout:
                    wedgename = (cp["WedgeContext"]["WedgeName"].upper() +
                                 wedgeangle + "OUT")
                    wedgeorientation = "180"
                plan.logger.debug("Wedge name = ", wedgename)
            elif "UP" in cp["WedgeContext"]["WedgeName"]:
                plan.logger.debug("Wedge present")
                wedgetype = "STANDARD"
                wedgeflag = True
                numwedges = 1
                wedgeangle = cp["WedgeContext"]["Angle"]
                wedgeinorout = ""
                wedgeinorout = cp["WedgeContext"]["Orientation"]
                if int(wedgeangle) == 15:
                    numberinname = "30"
                elif int(wedgeangle) == 45:
                    numberinname = "20"
                elif int(wedgeangle) == 30:
                    numberinname = "30"
                elif int(wedgeangle) == 60:
                    numberinname = "15"
                if "WedgeRightToLeft" == wedgeinorout:
                    wedgename = "W" + str(
                        int(wedgeangle)) + "R" + numberinname  # + "U"
                    wedgeorientation = (
                        "90")  # temporary until I find out what to put here
                elif "WedgeLeftToRight" == wedgeinorout:
                    wedgename = "W" + str(
                        int(wedgeangle)) + "L" + numberinname  # + "U"
                    wedgeorientation = "270"
                elif "WedgeTopToBottom" == wedgeinorout:
                    wedgename = ("W" + str(int(wedgeangle)) + "OUT" +
                                 numberinname)  # + "U"
                    wedgeorientation = (
                        "180")  # temporary until I find out what to put here
                elif "WedgeBottomToTop" == wedgeinorout:
                    wedgename = ("W" + str(int(wedgeangle)) + "IN" +
                                 numberinname)  # + "U"
                    wedgeorientation = (
                        "0")  # temporary until I find out what to put here
                plan.logger.debug("Wedge name = ", wedgename)

        # Get the prescription for this beam
        prescription = [
            p for p in trial_info["PrescriptionList"]
            if p["Name"] == beam["PrescriptionName"]
        ][0]

        # Get the machine name and version and energy name for this beam
        machinenameandversion = beam["MachineNameAndVersion"].split(": ")
        machinename = machinenameandversion[0]
        machineversion = machinenameandversion[1]
        machineenergyname = beam["MachineEnergyName"]

        beam_energy = re.findall(r"[-+]?\d*\.\d+|\d+",
                                 beam["MachineEnergyName"])[0]

        # Find the DosePerMuAtCalibration parameter from the machine data
        dose_per_mu_at_cal = -1
        if (machine_info["Name"] == machinename
                and machine_info["VersionTimestamp"] == machineversion):

            for energy in machine_info["PhotonEnergyList"]:

                if energy["Name"] == machineenergyname:
                    dose_per_mu_at_cal = energy["PhysicsData"]["OutputFactor"][
                        "DosePerMuAtCalibration"]
                    plan.logger.debug("Using DosePerMuAtCalibration of: " +
                                      str(dose_per_mu_at_cal))

        prescripdose = beam["MonitorUnitInfo"]["PrescriptionDose"]
        normdose = beam["MonitorUnitInfo"]["NormalizedDose"]

        if normdose == 0:
            ds.FractionGroupSequence[0].ReferencedBeamSequence[
                beam_count - 1].BeamMeterset = 0
        else:
            ds.FractionGroupSequence[0].ReferencedBeamSequence[
                beam_count - 1].BeamDose = (prescripdose / 100)
            ds.FractionGroupSequence[0].ReferencedBeamSequence[
                beam_count -
                1].BeamMeterset = prescripdose / (normdose *
                                                  dose_per_mu_at_cal)

        gantryrotdir = "NONE"
        if (
                "GantryIsCCW" in cp_manager
        ):  # This may be a problem here!!!! Not sure how to Pinnacle does this, could be 1 if CW, must be somewhere that states if gantry is rotating or not
            if cp_manager["GantryIsCCW"] == 1:
                gantryrotdir = "CC"
        if "GantryIsCW" in cp_manager:
            if cp_manager["GantryIsCW"] == 1:
                gantryrotdir = "CW"

        plan.logger.debug("Beam MU: " +
                          str(ds.FractionGroupSequence[0].
                              ReferencedBeamSequence[beam_count -
                                                     1].BeamMeterset))

        doserate = 0
        if "DoseRate" in beam:  # TODO What to do if DoseRate isn't available in Beam?
            doserate = beam["DoseRate"]
        if ("STEP" in beam["SetBeamType"].upper()
                and "SHOOT" in beam["SetBeamType"].upper()):
            plan.logger.debug("Using Step & Shoot")

            ds.BeamSequence[beam_count -
                            1].NumberOfControlPoints = numctrlpts * 2
            ds.BeamSequence[beam_count -
                            1].SourceToSurfaceDistance = beam["SSD"] * 10

            if numwedges > 0:
                ds.BeamSequence[beam_count - 1].WedgeSequence = Sequence()
                ds.BeamSequence[beam_count - 1].WedgeSequence.append(
                    Dataset()
                )  # I am assuming only one wedge per beam (which makes sense because you can't change it during beam)
                ds.BeamSequence[beam_count - 1].WedgeSequence[
                    0].WedgeNumber = 1  # might need to change this
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeType = wedgetype
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeAngle = wedgeangle
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeID = wedgename
                ds.BeamSequence[
                    beam_count -
                    1].WedgeSequence[0].WedgeOrientation = wedgeorientation
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeFactor = ""

            metercount = 1
            for j in range(0, numctrlpts * 2):
                ds.BeamSequence[beam_count - 1].ControlPointSequence.append(
                    Dataset())
                ds.BeamSequence[
                    beam_count -
                    1].ControlPointSequence[j].ControlPointIndex = j
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].BeamLimitingDevicePositionSequence = Sequence()
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].ReferencedDoseReferenceSequence = Sequence()
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].ReferencedDoseReferenceSequence.append(Dataset())
                if j % 2 == 1:  # odd number control point
                    currentmeterset = currentmeterset + float(
                        metersetweight[metercount])
                    metercount = metercount + 1

                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].CumulativeMetersetWeight = currentmeterset
                ds.BeamSequence[
                    beam_count -
                    1].ControlPointSequence[j].ReferencedDoseReferenceSequence[
                        0].CumulativeDoseReferenceCoefficient = currentmeterset
                ds.BeamSequence[
                    beam_count -
                    1].ControlPointSequence[j].ReferencedDoseReferenceSequence[
                        0].ReferencedDoseReferenceNumber = "1"

                if j == 0:  # first control point beam meterset always zero
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].NominalBeamEnergy = beam_energy
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].DoseRateSet = doserate

                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].GantryRotationDirection = "NONE"
                    # logger.debug("Gantry angle list length: ", len(gantryangles))
                    # logger.debug("current controlpoint: ", j)
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].GantryAngle = gantryangle
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDeviceAngle = colangle
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDeviceRotationDirection = "NONE"
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].SourceToSurfaceDistance = (
                            beam["SSD"] * 10)

                    if numwedges > 0:
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence = Sequence()
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence.append(Dataset())
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence[0].WedgePosition = "IN"
                        ds.BeamSequence[
                            beam_count -
                            1].ControlPointSequence[j].WedgePositionSequence[
                                0].ReferencedWedgeNumber = "1"

                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # This will be the x jaws
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # this will be the y jaws
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].RTBeamLimitingDeviceType = "ASYMX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].LeafJawPositions = [x1, x2]
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            1].RTBeamLimitingDeviceType = "ASYMY"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            1].LeafJawPositions = [y1, y2]

                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # this will be the MLC
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            2].RTBeamLimitingDeviceType = "MLCX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            2].LeafJawPositions = leafpositions
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].SourceToSurfaceDistance = (
                            beam["SSD"] * 10)
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDeviceRotationDirection = "NONE"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].PatientSupportAngle = psupportangle
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].PatientSupportRotationDirection = "NONE"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].IsocenterPosition = plan.iso_center
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].GantryRotationDirection = gantryrotdir
                else:
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset()
                        )  # This will be the mlcs for control points other than the first
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].RTBeamLimitingDeviceType = "MLCX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].LeafJawPositions = leafpositions
                ds.BeamSequence[beam_count - 1].NumberOfWedges = (
                    numwedges
                )  # this is temporary value, will read in from file later
                ds.BeamSequence[beam_count -
                                1].NumberOfCompensators = "0"  # Also temporary
                ds.BeamSequence[beam_count - 1].NumberOfBoli = "0"
                ds.BeamSequence[beam_count - 1].NumberOfBlocks = "0"  # Temp
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence = Sequence()
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    0].RTBeamLimitingDeviceType = "ASYMX"
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    1].RTBeamLimitingDeviceType = "ASYMY"
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    2].RTBeamLimitingDeviceType = "MLCX"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[0].NumberOfLeafJawPairs = "1"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[1].NumberOfLeafJawPairs = "1"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[2].NumberOfLeafJawPairs = (
                        p_count / 2)
                bounds = [
                    "-200",
                    "-190",
                    "-180",
                    "-170",
                    "-160",
                    "-150",
                    "-140",
                    "-130",
                    "-120",
                    "-110",
                    "-100",
                    "-95",
                    "-90",
                    "-85",
                    "-80",
                    "-75",
                    "-70",
                    "-65",
                    "-60",
                    "-55",
                    "-50",
                    "-45",
                    "-40",
                    "-35",
                    "-30",
                    "-25",
                    "-20",
                    "-15",
                    "-10",
                    "-5",
                    "0",
                    "5",
                    "10",
                    "15",
                    "20",
                    "25",
                    "30",
                    "35",
                    "40",
                    "45",
                    "50",
                    "55",
                    "60",
                    "65",
                    "70",
                    "75",
                    "80",
                    "85",
                    "90",
                    "95",
                    "100",
                    "110",
                    "120",
                    "130",
                    "140",
                    "150",
                    "160",
                    "170",
                    "180",
                    "190",
                    "200",
                ]
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    2].LeafPositionBoundaries = bounds
        else:
            plan.logger.debug("Not using Step & Shoot")
            ds.BeamSequence[beam_count -
                            1].NumberOfControlPoints = numctrlpts + 1
            ds.BeamSequence[beam_count -
                            1].SourceToSurfaceDistance = beam["SSD"] * 10
            if numwedges > 0:
                ds.BeamSequence[beam_count - 1].WedgeSequence = Sequence()
                ds.BeamSequence[beam_count - 1].WedgeSequence.append(Dataset())
                # I am assuming only one wedge per beam (which makes sense
                # because you can't change it during beam)
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeNumber = 1
                # might need to change this
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeType = wedgetype
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeAngle = wedgeangle
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeID = wedgename
                ds.BeamSequence[
                    beam_count -
                    1].WedgeSequence[0].WedgeOrientation = wedgeorientation
                ds.BeamSequence[beam_count -
                                1].WedgeSequence[0].WedgeFactor = ""
            for j in range(0, numctrlpts + 1):
                ds.BeamSequence[beam_count - 1].ControlPointSequence.append(
                    Dataset())
                ds.BeamSequence[
                    beam_count -
                    1].ControlPointSequence[j].ControlPointIndex = j
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].BeamLimitingDevicePositionSequence = Sequence()
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].ReferencedDoseReferenceSequence = Sequence()
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].ReferencedDoseReferenceSequence.append(Dataset())
                ds.BeamSequence[beam_count - 1].ControlPointSequence[
                    j].CumulativeMetersetWeight = metersetweight[j]
                if j == 0:  # first control point beam meterset always zero
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].NominalBeamEnergy = beam_energy
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].DoseRateSet = doserate

                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].GantryRotationDirection = "NONE"
                    # logger.debug("Gantry angle list length: ", len(gantryangles))
                    # logger.debug("current controlpoint: ", j)
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].GantryAngle = gantryangle
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDeviceAngle = colangle
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].SourceToSurfaceDistance = (
                            beam["SSD"] * 10)
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].ReferencedDoseReferenceSequence[
                            0].CumulativeDoseReferenceCoefficient = "0"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].ReferencedDoseReferenceSequence[
                            0].ReferencedDoseReferenceNumber = "1"
                    if numwedges > 0:
                        WedgePosition1 = Dataset()
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence = Sequence()
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence.append(WedgePosition1)
                        ds.BeamSequence[beam_count - 1].ControlPointSequence[
                            j].WedgePositionSequence[0].WedgePosition = "IN"
                        ds.BeamSequence[
                            beam_count -
                            1].ControlPointSequence[j].WedgePositionSequence[
                                0].ReferencedWedgeNumber = "1"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # This will be the x jaws
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # this will be the y jaws
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].RTBeamLimitingDeviceType = "ASYMX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].LeafJawPositions = [x1, x2]
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            1].RTBeamLimitingDeviceType = "ASYMY"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            1].LeafJawPositions = [y1, y2]
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(
                            Dataset())  # this will be the MLC
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            2].RTBeamLimitingDeviceType = "MLCX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            2].LeafJawPositions = leafpositions
                    ds.BeamSequence[
                        beam_count -
                        1].ControlPointSequence[j].SourceToSurfaceDistance = (
                            beam["SSD"] * 10)
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDeviceRotationDirection = "NONE"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].PatientSupportAngle = psupportangle
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].PatientSupportRotationDirection = "NONE"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].IsocenterPosition = plan.iso_center
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].GantryRotationDirection = gantryrotdir
                    ds.BeamSequence[beam_count - 1].NumberOfWedges = numwedges

                    ds.BeamSequence[beam_count - 1].NumberOfCompensators = (
                        "0"
                    )  # this is temporary value, will read in from file later
                    ds.BeamSequence[beam_count -
                                    1].NumberOfBoli = "0"  # Also temporary
                    ds.BeamSequence[beam_count -
                                    1].NumberOfBlocks = "0"  # Temp
                else:
                    # This will be the mlcs for control points other than the first
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence.append(Dataset())
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].RTBeamLimitingDeviceType = "MLCX"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].BeamLimitingDevicePositionSequence[
                            0].LeafJawPositions = leafpositions
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].ReferencedDoseReferenceSequence[
                            0].CumulativeDoseReferenceCoefficient = "1"
                    ds.BeamSequence[beam_count - 1].ControlPointSequence[
                        j].ReferencedDoseReferenceSequence[
                            0].ReferencedDoseReferenceNumber = "1"

                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence = Sequence()
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count -
                                1].BeamLimitingDeviceSequence.append(Dataset())
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    0].RTBeamLimitingDeviceType = "ASYMX"
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    1].RTBeamLimitingDeviceType = "ASYMY"
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    2].RTBeamLimitingDeviceType = "MLCX"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[0].NumberOfLeafJawPairs = "1"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[1].NumberOfLeafJawPairs = "1"
                ds.BeamSequence[
                    beam_count -
                    1].BeamLimitingDeviceSequence[2].NumberOfLeafJawPairs = (
                        p_count / 2)
                bounds = [
                    "-200",
                    "-190",
                    "-180",
                    "-170",
                    "-160",
                    "-150",
                    "-140",
                    "-130",
                    "-120",
                    "-110",
                    "-100",
                    "-95",
                    "-90",
                    "-85",
                    "-80",
                    "-75",
                    "-70",
                    "-65",
                    "-60",
                    "-55",
                    "-50",
                    "-45",
                    "-40",
                    "-35",
                    "-30",
                    "-25",
                    "-20",
                    "-15",
                    "-10",
                    "-5",
                    "0",
                    "5",
                    "10",
                    "15",
                    "20",
                    "25",
                    "30",
                    "35",
                    "40",
                    "45",
                    "50",
                    "55",
                    "60",
                    "65",
                    "70",
                    "75",
                    "80",
                    "85",
                    "90",
                    "95",
                    "100",
                    "110",
                    "120",
                    "130",
                    "140",
                    "150",
                    "160",
                    "170",
                    "180",
                    "190",
                    "200",
                ]
                ds.BeamSequence[beam_count - 1].BeamLimitingDeviceSequence[
                    2].LeafPositionBoundaries = bounds
            ctrlptlist = False
            wedgeflag = False
            numwedges = 0
            beginbeam = False

        # Get the prescription for this beam
        prescription = [
            p for p in trial_info["PrescriptionList"]
            if p["Name"] == beam["PrescriptionName"]
        ][0]
        num_fractions = prescription["NumberOfFractions"]

    ds.FractionGroupSequence[0].FractionGroupNumber = 1
    ds.FractionGroupSequence[0].NumberOfFractionsPlanned = num_fractions
    ds.FractionGroupSequence[0].NumberOfBeams = beam_count
    ds.FractionGroupSequence[0].NumberOfBrachyApplicationSetups = "0"

    # Save the RTPlan Dicom File
    output_file = os.path.join(export_path, RPfilename)
    plan.logger.info("Creating Plan file: %s \n" % (output_file))
    ds.save_as(output_file)
Пример #10
0
info_mask.PatientBirthDate= info.PatientBirthDate
info_mask.PatientSex= info.PatientSex
if 'PatientAge' in info:
    info_mask.PatientAge= info.PatientAge

if 'PatientWeight' in info:
    info_mask.PatientWeight= info.PatientWeight

info_mask.StudyID=info.StudyID

info_mask.ImageType='DERIVED\PRIMARY'
info_mask.InstanceCreatorUID='1.2.276.0.7230010.3'
info_mask.SOPClassUID='1.2.840.10008.5.1.4.1.1.66.4'
info_mask.SOPInstanceUID= instanceuid

info_mask.AccessionNumber=info.AccessionNumber
info_mask.Modality='SEG'
info_mask.Manufacturer='Stanford University'

info_mask.ManufacturerModelName= 'ePAD Matlab'
info_mask.DeviceSerialNumber='SN123456'
info_mask.SoftwareVersion='1.0'
info_mask.StudyInstanceUID=info.StudyInstanceUID
info_mask.SeriesInstanceUID= dicomuid
info_mask.SeriesNumber= 1000
info_mask.ContentDate=datetime.today().strftime('%Y%m%d')
info_mask.StudyDate=info.StudyDate
info_mask.SeriesDate=datetime.today().strftime('%Y%m%d')
info_mask.AcquisitionDate=datetime.today().strftime('%Y%m%d')
currentTime=datetime.today().strftime('%H%M%S.')
f = datetime.today().strftime('%f')
Пример #11
0
    def create_dicom_base(self):
        if _dicom_loaded is False:
            raise ModuleNotLoadedError("Dicom")
        if self.header_set is False:
            raise InputError("Header not loaded")

        # TODO tags + code datatypes are described here:
        # https://www.dabsoft.ch/dicom/6/6/#(0020,0012)
        # datatype codes are described here:
        # ftp://dicom.nema.org/medical/DICOM/2013/output/chtml/part05/sect_6.2.html

        meta = Dataset()
        meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'  # CT Image Storage
        # Media Storage SOP Instance UID tag 0x0002,0x0003 (type UI - Unique Identifier)
        meta.MediaStorageSOPInstanceUID = self._ct_sop_instance_uid
        meta.ImplementationClassUID = "1.2.3.4"
        meta.TransferSyntaxUID = uid.ImplicitVRLittleEndian  # Implicit VR Little Endian - Default Transfer Syntax
        ds = FileDataset("file", {}, file_meta=meta, preamble=b"\0" * 128)
        ds.PatientName = self.patient_name
        if self.patient_id in (None, ''):
            ds.PatientID = datetime.datetime.today().strftime('%Y%m%d-%H%M%S')
        else:
            ds.PatientID = self.patient_id  # Patient ID tag 0x0010,0x0020 (type LO - Long String)
        ds.PatientSex = ''  # Patient's Sex tag 0x0010,0x0040 (type CS - Code String)
        #                      Enumerated Values: M = male F = female O = other.
        ds.PatientBirthDate = '19010101'
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.AccessionNumber = ''
        ds.is_little_endian = True
        ds.is_implicit_VR = True
        ds.SOPClassUID = '1.2.3'  # !!!!!!!!
        # SOP Instance UID tag 0x0008,0x0018 (type UI - Unique Identifier)
        ds.SOPInstanceUID = self._ct_sop_instance_uid

        # Study Instance UID tag 0x0020,0x000D (type UI - Unique Identifier)
        # self._dicom_study_instance_uid may be either set in __init__ when creating new object
        #   or set when import a DICOM file
        #   Study Instance UID for structures is the same as Study Instance UID for CTs
        ds.StudyInstanceUID = self._dicom_study_instance_uid

        # Series Instance UID tag 0x0020,0x000E (type UI - Unique Identifier)
        # self._ct_dicom_series_instance_uid may be either set in __init__ when creating new object
        #   or set when import a DICOM file
        #   Series Instance UID for structures might be different than Series Instance UID for CTs
        ds.SeriesInstanceUID = self._ct_dicom_series_instance_uid

        # Study Instance UID tag 0x0020,0x000D (type UI - Unique Identifier)
        ds.FrameofReferenceUID = '1.2.3'  # !!!!!!!!!
        ds.StudyDate = datetime.datetime.today().strftime('%Y%m%d')
        ds.StudyTime = datetime.datetime.today().strftime('%H%M%S')
        ds.PhotometricInterpretation = 'MONOCHROME2'
        ds.SamplesPerPixel = 1
        ds.ImageOrientationPatient = ['1', '0', '0', '0', '1', '0']
        ds.Rows = self.dimx
        ds.Columns = self.dimy
        ds.SliceThickness = str(self.slice_distance)
        ds.PixelSpacing = [self.pixel_size, self.pixel_size]

        # Add eclipse friendly IDs
        ds.StudyID = '1'  # Study ID tag 0x0020,0x0010 (type SH - Short String)
        ds.ReferringPhysiciansName = 'py^trip'  # Referring Physician's Name tag 0x0008,0x0090 (type PN - Person Name)
        ds.PositionReferenceIndicator = ''  # Position Reference Indicator tag 0x0020,0x1040
        ds.SeriesNumber = '1'  # SeriesNumber tag 0x0020,0x0011 (type IS - Integer String)

        return ds
Пример #12
0
def saveDICOM_sliced_CT(image_np,
                        voxel_size,
                        image_position_patient,
                        output_path,
                        output_name,
                        orig_Z_pos=0,
                        patient_ID_in='SynthAttCorr_Test',
                        StudyInstanceUID='',
                        SeriesInstanceUID='',
                        TYPE_INT=np.int16,
                        vervose=True):
    '''Saves a 3D numpy array as a CT DICOM file.
    '''

    # Get info type
    info_bits = np.iinfo(TYPE_INT)
    Vol_CT_max_val = np.max(image_np)

    # Create output folder
    OUTPUT_DCM_FOLDER = os.path.join(output_path, output_name)
    File_mng.check_create_path('OUTPUT_DCM_FOLDER',
                               OUTPUT_DCM_FOLDER,
                               clear_folder=True)

    # get current time
    now = datetime.datetime.now()

    # Number of slices to save
    num_CT_slices = image_np.shape[Z]

    # Save each slice
    for idx_slice_CT in range(num_CT_slices):

        print('Slice: %d/%d       ' % (idx_slice_CT + 1, num_CT_slices),
              end='')
        print('', end='\r')

        OUTPUT_DCM = os.path.join(OUTPUT_DCM_FOLDER, '%04d.dcm' % idx_slice_CT)

        slice_aux = image_np[:, :, idx_slice_CT]

        # This code was taken from the output of a valid CT file
        # I do not know what the long dotted UIDs mean, but this code works...
        file_meta = Dataset()
        # (0008, 0016) SOP Class UID                       UI: CT Image Storage
        file_meta.MediaStorageSOPClassUID = 'CT Image Storage'
        # (0008, 0018) SOP Instance UID                    UI: 1.3.6.1.4.1.14519.5.2.1.3320.3273.139372384049551777032016175435
        file_meta.MediaStorageSOPInstanceUID = '1.3.6.1.4.1.14519.5.2.1.3320.3273.139372384049551777032016175435'

        #     file_meta.ImplementationClassUID = '1.2.840.10008.5.1.4.1.1.20'

        file_meta.TransferSyntaxUID = dicom.uid.ImplicitVRLittleEndian

        ds = FileDataset(OUTPUT_DCM, {},
                         file_meta=file_meta,
                         preamble=b'\x00' * 128)
        ds.Modality = 'CT'
        ds.ContentDate = str(datetime.date.today()).replace('-', '')
        ds.ContentTime = str(time.time())  #milliseconds since the epoch

        # (0020, 000d) Study Instance UID                  UI: 1.3.6.1.4.1.14519.5.2.1.3320.3273.231445815234682119538863169983
        if StudyInstanceUID == '':
            StudyInstanceUID = '1.3.6.1.4.1.14519.5.2.1.3320.3273.231445815234682119538863169983'
        ds.StudyInstanceUID = StudyInstanceUID
        # (0020, 000e) Series Instance UID                 UI: 1.3.6.1.4.1.14519.5.2.1.3320.3273.330516103388011054891344582212
        if SeriesInstanceUID == '':
            SeriesInstanceUID = '1.3.6.1.4.1.14519.5.2.1.3320.3273.330516103388011054891344582212'
        ds.SeriesInstanceUID = SeriesInstanceUID

        # (0008, 1155) Referenced SOP Instance UID         UI: 1.3.6.1.4.1.14519.5.2.1.3320.3273.275604822259752169794323488440
        ds.SOPInstanceUID = '1.3.6.1.4.1.14519.5.2.1.3320.3273.2756048222597%d' % np.random.randint(
            low=52169794323488440, high=92169794323488440)
        # ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.20'

        # These are the necessary imaging components of the FileDataset object.

        # (0028, 0002) Samples per Pixel                   US: 1
        ds.SamplesPerPixel = 1
        # (0028, 0004) Photometric Interpretation          CS: 'MONOCHROME2'
        ds.PhotometricInterpretation = "MONOCHROME2"

        # (0028, 0103) Pixel Representation                US: 1
        ds.PixelRepresentation = 1

        # (0028, 0100) Bits Allocated                      US: 16
        ds.BitsAllocated = 16
        # (0028, 0101) Bits Stored                         US: 16
        ds.BitsStored = 16
        # (0028, 0102) High Bit                            US: 15
        ds.HighBit = 15

        # (0028, 0010) Rows                                US: 512
        ds.Rows = slice_aux.shape[X]
        # (0028, 0011) Columns                             US: 512
        ds.Columns = slice_aux.shape[Y]

        # (0028, 0030) Pixel Spacing                       DS: ['0.976562', '0.976562']
        ds.PixelSpacing = [str(voxel_size[X]), str(voxel_size[Y])]

        # (0018, 0088) Spacing Between Slices              DS: "3.260000"
        ds.SpacingBetweenSlices = b'00000'

        # (0018, 0050) Slice Thickness                     DS: "1.250000"
        ds.SliceThickness = str(voxel_size[Z])

        ds.ImageOrientationPatient = [
            '1.000000', '0.000000', '0.000000', '0.000000', '1.000000',
            '0.000000'
        ]

        ds.ImagePositionPatient = [
            str(image_position_patient[X]).encode(),
            str(image_position_patient[Y]).encode(),
            str(orig_Z_pos + (idx_slice_CT * voxel_size[Z])).encode()
        ]

        # Otros agregados

        # (0008, 0050) Accession Number                    SH: ''
        ds.AccessionNumber = ''
        # (0008, 0022) Acquisition Date                    DA: '20001007'
        ds.AcquisitionDate = now.strftime("%Y%m%d")
        # (0008, 0032) Acquisition Time                    TM: '110409.543667'
        ds.AcquisitionTime = now.strftime("%H%M%S")

        # (0008, 0008) Image Type                          CS: ['ORIGINAL', 'PRIMARY', 'AXIAL']
        ds.ImageType = ['ORIGINAL', 'PRIMARY', 'AXIAL']
        # (0008, 0012) Instance Creation Date              DA: '20001007'
        ds.InstanceCreationDate = now.strftime("%Y%m%d")
        # (0008, 0013) Instance Creation Time              TM: '110451'
        ds.InstanceCreationTime = now.strftime("%H%M%S")
        # (0020, 0013) Instance Number                     IS: "207"
        ds.InstanceNumber = str(idx_slice_CT)

        ds.Manufacturer = 'UTN-UTT-CNEA-CONICET'

        # (0010, 0020) Patient ID                          LO: 'C3N-02285'
        ds.PatientID = patient_ID_in
        # (0010, 0010) Patient's Name                      PN: 'C3N-02285'
        ds.PatientName = output_name

        # (0018, 5100) Patient Position                    CS: 'HFS'
        ds.PatientPosition = 'HFS'
        # (0018, 1030) Protocol Name                       LO: '4.1 PET_WB LOW BMI'
        ds.ProtocolName = 'Synth. CT'

        # (0018, 1100) Reconstruction Diameter             DS: "500.000000"
        ds.ReconstructionDiameter = '500.000000'
        # (0008, 0090) Referring Physician's Name          PN: ''
        ds.ReferringPhysicianName = ''

        # (0008, 0021) Series Date                         DA: '20001007'
        ds.SeriesDate = now.strftime("%Y%m%d")
        ds.SeriesNumber = ''
        # (0008, 0031) Series Time                         TM: '110352'
        ds.SeriesTime = now.strftime("%H%M%S")
        # (0020, 1041) Slice Location                      DS: "-216.500"
        ds.SliceLocation = str(orig_Z_pos + (idx_slice_CT * voxel_size[Z]))
        # (0008, 0020) Study Date                          DA: '20001007'
        ds.StudyDate = now.strftime("%Y%m%d")
        # (0008, 1030) Study Description                   LO: 'PET WB LOW BMI'
        ds.StudyDescription = 'synthetic CT created from NAC PET'
        ds.StudyID = ''
        # (0008, 0030) Study Time                          TM: '110246'
        ds.StudyTime = now.strftime("%H%M%S")

        # (0028, 1052) Rescale Intercept                   DS: "-1024"
        ds.RescaleIntercept = "-1024"
        # (0028, 1053) Rescale Slope                       DS: "1"
        ds.RescaleSlope = "1"  #str(Vol_CT_max_val/info_bits.max)
        # (0028, 1054) Rescale Type                        LO: 'HU'
        ds.RescaleType = 'HU'

        #     slice_aux = ((slice_aux/Vol_CT_max_val)*info_bits.max)+1024
        #     slice_aux = (((slice_aux+1024)/Vol_CT_max_val)*info_bits.max)
        slice_aux = (slice_aux + 1024).astype(np.int16)
        #     print(slice_aux.max(),slice_aux.min())

        # volume_aux = np.swapaxes(volume_aux,0,1)
        # volume_aux = np.swapaxes(volume_aux,0,2)

        ds.PixelData = slice_aux.tostring()

        ds.save_as(OUTPUT_DCM)

    print('done.')

    return
Пример #13
0
def convert_npy_to_dicom(fname,
                         npy_array,
                         slice_thickness=None,
                         pixel_spacing=None):
    """
    convert npy array to dicom
    :param fname: file name
    :param npy_array: npy array
    :param slice_thickness: slice thickness
    :param pixel_spacing: pixel spacing
    :return:  dcm
    """

    uint16_img = np.array(npy_array)
    uint16_img = ((uint16_img - uint16_img.min()) /
                  (uint16_img.max() - uint16_img.min()) * (2**16 - 1)).astype(
                      np.uint16)
    dim = len(uint16_img.shape)
    if dim == 1:
        raise Exception('Cannot convert 1D array to dicom')
    elif dim == 2:
        uint16_img = uint16_img[np.newaxis, :, :]
    elif dim > 3:
        raise Exception('{}D array is not supported.'.format(dim))
    x_min = npy_array.min()
    x_max = npy_array.max()
    x_max_min = x_max - x_min
    t_max = (2**16) - 1
    slope = x_max_min / t_max
    intercept = x_min

    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = '0.0.000.000000.0.0.0.0.0.00'
    file_meta.MediaStorageSOPInstanceUID = \
        '333.333.0.0.0.333.333333333.{}'.format(
            datetime.now().timestamp())
    file_meta.ImplementationClassUID = '0.0.0.0'
    dcm = FileDataset(fname, {}, file_meta=file_meta, preamble=b'\0' * 128)

    dcm.Modality = 'OT'
    dcm.ImageType = ['ORIGINAL', 'PRIMARY']

    dcm.ContentDate = datetime.now().strftime('%Y%m%d')
    dcm.ContentTime = datetime.now().strftime('%H%M%S')
    dcm.InstanceCreationDate = datetime.now().strftime('%Y%m%d')
    dcm.InstanceCreationTime = datetime.now().strftime('%H%M%S')
    dcm.SeriesDate = datetime.now().strftime('%Y%m%d')
    dcm.SeriesTime = datetime.now().strftime('%H%M%S')
    dcm.AcquisitionTime = datetime.now().strftime('%H%M%S')
    dcm.PatientName = os.path.basename(fname)
    dcm.PatientBirthDate = datetime.now().strftime('%Y%m%d')
    dcm.PatientAge = '000Y'
    dcm.PatientSize = 1
    dcm.PatientWeight = 1
    dcm.PatientID = os.path.basename(fname)
    dcm.PatientSex = 'O'
    dcm.StudyDescription = os.path.basename(fname)
    dcm.StudyDate = datetime.now().strftime('%Y%m%d')
    dcm.StudyTime = datetime.now().strftime('%H%M%S')
    dcm.StudyID = os.path.basename(fname)
    dcm.SeriesDescription = os.path.basename(fname)
    dcm.SamplesPerPixel = 1
    dcm.PhotometricInterpretation = 'MONOCHROME1'
    dcm.PixelRepresentation = 0  # unsigned 0, signed 1
    dcm.HighBit = 16
    dcm.BitsStored = 16
    dcm.BitsAllocated = 16
    dcm.SmallestImagePixelValue = uint16_img.min()
    dcm.LargestImagePixelValue = uint16_img.max()
    dcm.Columns = uint16_img.shape[2]
    dcm.Rows = uint16_img.shape[1]
    dcm.NumberOfFrames = uint16_img.shape[0]
    dcm.NumberOfSlices = uint16_img.shape[0]
    dcm.ImagesInAquisition = uint16_img.shape[0]
    dcm.RescaleIntercept = intercept
    dcm.RescaleSlope = slope
    dcm.SliceVector = (np.arange(uint16_img.shape[0]) + 1).tolist()
    dcm.FrameIncrementPointer = [(0x0054, 0x0080)]

    dcm.PixelData = uint16_img.tostring()
    dcm.SliceThickness = 1 if slice_thickness is None else slice_thickness
    ps = 1 if pixel_spacing is None else pixel_spacing
    if isinstance(ps, list) or isinstance(ps, np.ndarray):
        dcm.PixelSpacing = [ps[0], ps[1]]
    else:
        dcm.PixelSpacing = [ps, ps]
    dcm.InstanceCreatorUID = '333.333.0.0.0'
    dcm.SOPClassUID = '0.0.000.00000.0.0.0.0.0.00'
    dcm.SOPInstanceUID = '333.333.0.0.0.{}'.format(datetime.now().timestamp())
    dcm.StudyInstanceUID = '333.333.0.0.0.{}'.format(
        datetime.now().timestamp())
    dcm.SeriesInstanceUID = '333.333.0.0.0.{}.3333'.format(
        datetime.now().timestamp())
    dcm.FrameOfReferenceUID = dcm.StudyInstanceUID
    dcm.SeriesNumber = 0
    dcm.InstanceNumber = 0
    dcm.BodyPartExamined = 'UNKNOWN'
    dcm.Manufacturer = 'DicomConversionUtils'
    dcm.DeviceSerialNumber = ''
    dcm.AcquisitionTerminationCondition = 'MANU'
    dcm.SoftwareVersions = 'UNKNOWN'
    dcm.AccessionNumber = '{:13d}'.format(random.randint(0, 1e13))
    dcm.InstitutionName = 'DicomConversionUtils'

    dcm.save_as(fname)
    return dcm
Пример #14
0
def Edm(input, empty_Name):
    """
    Read single DICOM file, write the meta data to a new dicom file as template for m2d_lossless().
    If input is a DICOM folder, then only use the first DICOM file.
    :param input: a single dicom slice or a folder
    :param empty_Name: the empty dicom name
    """
    start = time.time()
    if (os.path.isfile(input)):
        file = input
    else:
        files = sorted(glob.glob(os.path.join(input, "*.dcm")))
        file = sortDCM(files)[0]

    dataSet = pydicom.dcmread(file)

    fileName = "Edm.dcm"
    file_meta = Dataset()

    # add file meta data
    file_meta.MediaStorageSOPClassUID = dataSet.file_meta.MediaStorageSOPClassUID

    file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
    file_meta.FileMetaInformationVersion = dataSet.file_meta.FileMetaInformationVersion
    file_meta.MediaStorageSOPInstanceUID = dataSet.file_meta.MediaStorageSOPInstanceUID
    file_meta.ImplementationClassUID = dataSet.file_meta.ImplementationClassUID
    file_meta.ImplementationVersionName = dataSet.file_meta.ImplementationVersionName
    ds = FileDataset(fileName, {}, file_meta=file_meta, preamble=b"\0" * 128)

    # add meta data
    ds.ImageType = dataSet.ImageType
    ds.SOPClassUID = dataSet.SOPClassUID
    ds.SOPInstanceUID = dataSet.SOPInstanceUID
    ds.StudyDate = dataSet.StudyDate
    ds.StudyTime = dataSet.StudyTime
    ds.AccessionNumber = dataSet.AccessionNumber
    ds.Manufacturer = dataSet.Manufacturer
    ds.StudyDescription = dataSet.StudyDescription
    ds.SeriesDescription = dataSet.SeriesDescription
    ds.ManufacturerModelName = dataSet.ManufacturerModelName
    ds.PatientName = dataSet.PatientName
    ds.PatientID = dataSet.PatientID
    ds.PatientBirthDate = dataSet.PatientBirthDate
    ds.PatientSex = dataSet.PatientSex
    ds.SliceThickness = dataSet.SliceThickness
    ds.PatientPosition = dataSet.PatientPosition
    ds.StudyInstanceUID = dataSet.StudyInstanceUID
    ds.SeriesInstanceUID = dataSet.SeriesInstanceUID
    ds.StudyID = dataSet.StudyID
    ds.SeriesNumber = dataSet.SeriesNumber
    ds.InstanceNumber = "1"
    ds.ImagePositionPatient = dataSet.ImagePositionPatient
    ds.ImageOrientationPatient = dataSet.ImageOrientationPatient
    ds.FrameOfReferenceUID = dataSet.FrameOfReferenceUID
    ds.PositionReferenceIndicator = dataSet.PositionReferenceIndicator
    ds.PositionReferenceIndicator = "SN"
    ds.SamplesPerPixel = dataSet.SamplesPerPixel
    ds.PhotometricInterpretation = "MONOCHROME2"
    ds.Rows = 0
    ds.Columns = 0
    ds.PixelSpacing = dataSet.PixelSpacing
    ds.BitsAllocated = dataSet.BitsAllocated
    ds.BitsStored = dataSet.BitsStored
    ds.HighBit = dataSet.HighBit
    ds.PixelRepresentation = dataSet.PixelRepresentation
    ds.PixelData = bytes(0)
    ds.save_as(empty_Name)

    print("Cost time (seconds): ", (time.time() - start))
Пример #15
0
def writeDCMheader(filename, dcmStruct):
    logging.info("Setting file meta information...")
    # Populate required values for file meta information
    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3'  # IOP of RT STORAGE
    file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()
    file_meta.ImplementationClassUID = "1.3.6.1.4.1.9590.100.1.3.100.9.4"
    file_meta.TransferSyntaxUID = '1.2.840.10008.1.2.1'
    file_meta.FileMetaInformationVersion = b'\x00\x01'
    file_meta.FileMetaInformationGroupLength = 1

    logging.info("Setting dataset values...")
    # Create the FileDataset instance (initially no data elements, but file_meta supplied)
    ds = FileDataset(filename, {}, file_meta=file_meta, preamble=b"\0" * 128)

    if hasattr(dcmStruct, 'PatientName'):
        ds.PatientName = dcmStruct.PatientName

    if hasattr(dcmStruct, 'PatientID'):
        ds.PatientID = dcmStruct.PatientID

    # Set the transfer syntax
    ds.is_little_endian = True
    ds.is_implicit_VR = False

    # Set creation date/time
    dt = datetime.datetime.now()
    ds.StructureSetDate = dt.strftime('%Y%m%d')
    ds.StudyDate = dt.strftime('%Y%m%d')
    timeStr = dt.strftime('%H%M%S.%f')  # long format with micro seconds
    ds.StructureSetTime = timeStr
    ds.StudyTime = timeStr

    ds.SOPClassUID = file_meta.MediaStorageSOPClassUID
    ds.SOPInstanceUID = pydicom.uid.generate_uid()
    ds.SeriesInstanceUID = pydicom.uid.generate_uid()

    if hasattr(dcmStruct, 'StudyInstanceUID'):
        ds.StudyInstanceUID = dcmStruct.StudyInstanceUID

    if hasattr(dcmStruct, 'PatientSex'):
        ds.PatientSex = dcmStruct.PatientSex

    if hasattr(dcmStruct, 'PatientBirthDate'):
        ds.PatientBirthDate = dcmStruct.PatientBirthDate

    if hasattr(dcmStruct, 'StudyID'):
        ds.StudyID = dcmStruct.StudyID

    if hasattr(dcmStruct, 'ReferringPhysicianName'):
        ds.ReferringPhysicianName = dcmStruct.ReferringPhysicianName

    ds.StudyDescription = 'DLROI'
    ds.SeriesDescription = 'DLROI'

    if hasattr(dcmStruct, 'AccessionNumber'):
        ds.AccessionNumber = dcmStruct.AccessionNumber

    if hasattr(dcmStruct, 'InstitutionName'):
        ds.InstitutionName = dcmStruct.InstitutionName

    if hasattr(dcmStruct, 'StationName'):
        ds.StationName = dcmStruct.StationName

    ds.ColorType = 'grayscale'
    ds.Manufacturer = 'CourtLab - MD Anderson'
    ds.Modality = 'RTSTRUCT'
    ds.SoftwareVersion = 'test_v1'
    ds.StructureSetName = 'ROI'
    ds.StructureSetLabel = 'AutoPlan ROI'

    ds.SeriesNumber = 1
    ds.InstanceNumber = 1
    ds.Rows = 0
    ds.Columns = 0
    ds.BitsAllocated = 16
    ds.BitsStored = 16
    ds.Width = 0
    ds.Height = 0
    ds.BitDepth = 16
    ds.HighBit = 15
    ds.PixelRepresentation = 0

    return ds
Пример #16
0
def newDCM(meta_file, shape):
    """
    Create a new dicom file as template for m2d.
    This dicom file has no pixel data. Pixel data will be filled in m2d method.
    :param meta_file: provide meta data
    :param shape: mgz data shape
    :return: the dataset of dicom file, which could be filled pixel data from mgz file
    """

    fileName = "template.dcm"
    prefix = "1.2.826.0.1.3680043.10.271."
    suffix = str(datetime.datetime.today())[:10].replace('-', '') + str(
        time.time()).replace('.', '')

    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = "1.2.840.10008.5.1.4.1.1.4"  # Standard SOP CLasses: MR Image Storage
    ds = FileDataset(fileName, {}, file_meta=file_meta, preamble=b"\0" * 128)
    ds.SeriesInstanceUID = prefix + suffix  # change Series Instance UID

    ds.SOPClassUID = "1.2.840.10008.5.1.4.1.1.4"  # Standard SOP CLasses: MR Image Storage
    ds.ImageType = ['ORIGINAL', 'PRIMARY', 'OTHER']
    ds.PatientPosition = "HFS"
    ds.Manufacturer = "GE MEDICAL SYSTEMS"
    ds.ManufacturerModelName = "SIGNA EXCITE"
    ds.PositionReferenceIndicator = "SN"
    ds.SliceThickness = 1

    # Set the transfer syntax
    ds.is_little_endian = True
    ds.is_implicit_VR = True
    ds.PixelData = bytes(0)
    ds.Rows = shape[0]
    ds.Columns = shape[1]
    ds.SamplesPerPixel = 1
    ds.PhotometricInterpretation = "MONOCHROME2"
    ds.SeriesDescription = "_m2d"
    ds.PixelSpacing = [1, 1]
    ds.BitsAllocated = 16
    ds.BitsStored = 16
    ds.HighBit = 15
    ds.PixelRepresentation = 1
    ds.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian

    with open(meta_file) as f:
        for line in f:
            index = line.rindex(":")
            key = line[:index].replace(' ', '').lower()
            value = line[index + 1:].strip()
            if (key == "studydate"):
                ds.StudyDate = value
            if (key == "seriesdate"):
                ds.SeriesDate = value
            if (key == "patientbirthdate"):
                ds.PatientBirthDate = value
            if (key == "studytime"):
                ds.StudyTime = value
            if (key == "accessionnumber"):
                ds.AccessionNumber = value
            if (key == "studydescription"):
                ds.StudyDescription = value
            if (key == "seriesdescription"):
                ds.SeriesDescription = value
            if (key == "patientname"):
                ds.PatientName = value
            if (key == "patientid"):
                ds.PatientID = value
            if (key == "seriesnumber"):
                ds.SeriesNumber = value
            if (key == "patientsex"):
                ds.PatientSex = value

    return ds
Пример #17
0
def write_dicom_slice(
        pixel_array,  # 2D array in LP orientation
        filename=None,
        outputdir='mydcmdir',
        suffix='.dcm',
        modality='PT',
        SecondaryCaptureDeviceManufctur='KUL',
        uid_base='1.2.826.0.1.3680043.9.7147.',  # UID root for Georg Schramm
        PatientName='Test^Patient',
        PatientID='08150815',
        AccessionNumber='08150815',
        StudyDescription='test study',
        SeriesDescription='test series',
        PixelSpacing=['1', '1'],
        SliceThickness='1',
        ImagePositionPatient=['0', '0', '0'],
        ImageOrientationPatient=['1', '0', '0', '0', '1', '0'],
        CorrectedImage=['DECY', 'ATTN', 'SCAT', 'DTIM', 'LIN', 'CLN'],
        ImageType='STATIC',
        RescaleSlope=None,
        RescaleIntercept=None,
        StudyInstanceUID=None,
        SeriesInstanceUID=None,
        SOPInstanceUID=None,
        FrameOfReferenceUID=None,
        RadiopharmaceuticalInformationSequence=None,
        PatientGantryRelationshipCodeSequence=None,
        sl=None,
        frm=None,
        verbose=False,
        **kwargs):
    """write a 2D PET dicom slice

  Parameters
  ---------

  pixel_array : 2d numpy array 
    array that contains the image values

  filename : str, optional  
    name of the output dicom file (default: None -> automatically generated)
  
  outputdir : string, optional 
    output directory fir dicom file (default: mydcmdir)

  suffix : string, optional 
    suffix for dicom file (default '.dcm')

  sl, frm : int, optional
    slice and frame numbers that are appended to the file name prefix if given

  SecondaryCaptureDeviceManufctur       --|  
  uid_base                                | 
  PatientName                             | 
  PatientID                               | 
  AccessionNumber                         | 
  StudyDescription                        | 
  SeriesDescription                       | 
  PixelSpacing                            | 
  SliceThickness                          | 
  ImagePositionPatient                    | 
  ImageOrientationPatient                 | 
  CorrectedImage                          | ... dicom tags that should be present in a minimal
  ImageType                               |     dicom header
  RescaleSlope                            |     see function definition for default values
  RescaleIntercept                        |     default None means that they are creacted automatically
  StudyInstanceUID                        | 
  SeriesInstanceUID                       | 
  SOPInstanceUID                          | 
  FrameOfReferenceUID                     | 
  RadiopharmaceuticalInformationSequence  | 
  PatientGantryRelationshipCodeSequence --| 

  **kwargs : additional tags from the standard dicom dictionary to write
             the following tags could be useful:
  StudyDate                              
  StudyTime                              
  SeriesDate                             
  SeriesTime                             
  AcquisitionDate                        
  AcquisitionTime                        
  PatientBirthDate                       
  PatientSex                             
  PatientAge                             
  PatientSize                            
  PatientWeight                          
  ActualFrameDuration                    
  PatientPosition                        
  DecayCorrectionDateTime                
  ImagesInAcquisition                    
  SliceLocation                          
  NumberOfSlices                         
  Units                                  
  DecayCorrection                        
  ReconstructionMethod                   
  FrameReferenceTime                     
  DecayFactor                             
  DoseCalibrationFactor                  
  ImageIndex                             

  Returns
  -------
  str
    containing the ouput file name  

  """
    # create output dir if it does not exist
    if not os.path.exists(outputdir): os.mkdir(outputdir)

    # Populate required values for file meta information
    file_meta = Dataset()

    if modality == 'PT':
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.128'
    elif modality == 'NM':
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.20'
    elif modality == 'CT':
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
    elif modality == 'MR':
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.4'
    else:
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.4'

    # the MediaStorageSOPInstanceUID sould be the same as SOPInstanceUID
    # however it is stored in the meta information header to have faster access
    if SOPInstanceUID is None:
        SOPInstanceUID = dicom.uid.generate_uid(uid_base)
    file_meta.MediaStorageSOPInstanceUID = SOPInstanceUID
    file_meta.ImplementationClassUID = uid_base + '1.1.1'

    prefix = modality
    # append the frame and slice number to the file name prefix if given
    # not needed for the dicom standard but can be usefule for less dicom conform "2D" readers
    if frm is not None: prefix += f'.{frm:03}'
    if sl is not None: prefix += f'.{sl:05}'

    filename = f'{prefix}.{SOPInstanceUID}{suffix}'

    # Create the FileDataset instance (initially no data elements, but file_meta
    # supplied)
    ds = FileDataset(filename, {}, file_meta=file_meta, preamble=b"\0" * 128)

    # Add the data elements -- not trying to set all required here. Check DICOM
    # standard
    ds.PatientName = PatientName
    ds.PatientID = PatientID
    ds.AccessionNumber = AccessionNumber

    ds.Modality = modality
    ds.StudyDescription = StudyDescription
    ds.SeriesDescription = SeriesDescription

    if StudyInstanceUID is None:
        StudyInstanceUID = dicom.uid.generate_uid(uid_base)
    ds.StudyInstanceUID = StudyInstanceUID

    if SeriesInstanceUID is None:
        SeriesInstanceUID = dicom.uid.generate_uid(uid_base)
    ds.SeriesInstanceUID = SeriesInstanceUID

    if FrameOfReferenceUID is None:
        FrameOfReferenceUID = dicom.uid.generate_uid(uid_base)
    ds.FrameOfReferenceUID = FrameOfReferenceUID

    ds.SOPInstanceUID = SOPInstanceUID
    ds.SOPClassUID = file_meta.MediaStorageSOPClassUID

    ds.SecondaryCaptureDeviceManufctur = SecondaryCaptureDeviceManufctur

    ## These are the necessary imaging components of the FileDataset object.
    ds.SamplesPerPixel = 1

    if modality == 'PT' or modality == 'NM':
        ds.PhotometricInterpretation = "MONOCHROME1"
    else:
        ds.PhotometricInterpretation = "MONOCHROME2"

    ds.HighBit = 15
    ds.BitsStored = 16
    ds.BitsAllocated = 16

    # PixelRepresentation is 0 for uint16, 1 for int16

    if pixel_array.dtype == np.uint16:
        ds.PixelRepresentation = 0
        ds.RescaleIntercept = 0
        ds.RescaleSlope = 1
    elif pixel_array.dtype == np.int16:
        ds.PixelRepresentation = 1
        ds.RescaleIntercept = 0
        ds.RescaleSlope = 1
    else:
        ds.PixelRepresentation = 0

        # rescale the input pixel array to uint16 if needed
        if RescaleIntercept is None: RescaleIntercept = pixel_array.min()
        if RescaleIntercept != 0:
            pixel_array = 1.0 * pixel_array - RescaleIntercept

        if RescaleSlope is None:
            if pixel_array.max() != 0:
                RescaleSlope = 1.0 * pixel_array.max() / (2**16 - 1)
            else:
                RescaleSlope = 1.0
        if RescaleSlope != 1: pixel_array = 1.0 * pixel_array / RescaleSlope

        pixel_array = pixel_array.astype(np.uint16)

        ds.RescaleIntercept = RescaleIntercept
        ds.RescaleSlope = RescaleSlope

    # we have to transpose the column and row direction in the dicoms
    ds.PixelData = pixel_array.transpose().tobytes()
    ds.Columns = pixel_array.shape[0]
    ds.Rows = pixel_array.shape[1]

    # the pixel spacing also has to be inverted (transposed array saved!)
    ds.PixelSpacing = PixelSpacing[::-1]
    ds.SliceThickness = SliceThickness

    # Set the transfer syntax
    ds.is_little_endian = True
    ds.is_implicit_VR = True

    # Set creation date/time
    dt = datetime.datetime.now()
    timeStr = dt.strftime('%H%M%S.%f')  # long format with micro seconds
    ds.ContentDate = dt.strftime('%Y%m%d')
    ds.ContentTime = timeStr

    # voxel coordinate tags
    ds.ImagePositionPatient = ImagePositionPatient
    ds.ImageOrientationPatient = ImageOrientationPatient

    # special NM tags
    if modality == 'PT' or modality == 'NM':
        ds.CorrectedImage = CorrectedImage
        ds.ImageType = ImageType

        if RadiopharmaceuticalInformationSequence != None:
            rpi = RadiopharmaceuticalInformationSequence
            # this is needed otherwise the dicoms cannot be read
            rpi.is_undefined_length = True
            rpi[0].RadionuclideCodeSequence.is_undefined_length = True

            ds.RadiopharmaceuticalInformationSequence = rpi

    # add all key word arguments to dicom structure
    for key, value in kwargs.items():
        if dicom.datadict.tag_for_keyword(key) != None: setattr(ds, key, value)
        else:
            warnings.warn(
                key +
                ' not in standard dicom dictionary -> will not be written')

    if verbose: print("Writing file", os.path.join(outputdir, filename))
    dicom.filewriter.write_file(os.path.join(outputdir, filename),
                                ds,
                                write_like_original=False)

    return os.path.join(outputdir, filename)
Пример #18
0
def generate_dicom_from_image(image_file, **kwargs):
    suffix = '.dcm'
    filename_little_endian = tempfile.NamedTemporaryFile(suffix=suffix).name
    image_name = image_file
    img = Image.open(image_name)

    # required data elements

    # File meta info data elements
    file_meta = Dataset()
    file_meta.FileMetaInformationGroupLength = 190
    file_meta.FileMetaInformationVersion = b'\x00\x01'
    # secondary capture image storage
    file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.7'
    file_meta.MediaStorageSOPInstanceUID = kwargs['sop_instance_uid']
    file_meta.TransferSyntaxUID = '1.2.840.10008.1.2.1'  # explicit VR Little-endian
    file_meta.ImplementationClassUID = '1.2.840.000000.1.1'  # old - '1.2.840.114202.5.2'
    file_meta.ImplementationVersionName = 'PF.1.0.0'

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

    # kwargs so far = accession, modality, procedure, tech initia, patient
    # name, patient id

    # Main data elements
    ds.ImageType = ['ORIGINAL', 'SECONDARY']
    ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.7'
    ds.SOPInstanceUID = kwargs['sop_instance_uid']
    ds.StudyDate = kwargs['date_of_capture']
    ds.SeriesDate = kwargs['date_of_capture']
    ds.AcquisitionDate = kwargs['date_of_capture']
    ds.StudyTime = kwargs['time_of_capture']
    ds.SeriesTime = kwargs['time_of_capture']
    ds.AcquisitionTime = kwargs['time_of_capture']
    ds.AccessionNumber = kwargs['accession']
    ds.Modality = kwargs['modality']
    ds.ConversionType = 'WSD'
    ds.Manufacturer = 'PACSFORM'
    ds.InstitutionName = 'LXA'
    ds.ReferringPhysicianName = ''
    ds.StationName = 'PACS-FORM-PC01'
    ds.StudyDescription = kwargs['procedure']
    ds.SeriesDescription = 'PACS FORM'
    ds.InstitutionalDepartmentName = 'US'
    ds.OperatorsName = kwargs['tech_initials']
    ds.ManufacturerModelName = ''
    ds.PatientName = kwargs['patient_name']
    ds.PatientID = kwargs['patient_id']
    ds.PatientBirthDate = '19850112'
    ds.PatientSex = ''
    ds.BodyPartExamined = ''
    ds.DeviceSerialNumber = ''
    ds.DateOfSecondaryCapture = kwargs['date_of_capture']
    ds.TimeOfSecondaryCapture = kwargs['time_of_capture']
    ds.SecondaryCaptureDeviceManufacturer = 'Shihab'
    ds.SecondaryCaptureDeviceManufacturerModelName = 'PACSFORM'
    ds.SecondaryCaptureDeviceSoftwareVersions = '1.0.0'
    ds.SoftwareVersions = 'V1.0.0'
    ds.DigitalImageFormatAcquired = ''
    ds.StudyInstanceUID = kwargs['study_instance_uid']
    ds.SeriesInstanceUID = kwargs['series_instance_uid']
    ds.StudyID = ''
    ds.SeriesNumber = "999"
    ds.InstanceNumber = "1"
    ds.PatientOrientation = ''
    ds.SamplesPerPixel = 3  # 1 for grayscale #3 for color
    ds.PhotometricInterpretation = 'RGB'
    ds.Rows = img.size[1]
    ds.Columns = img.size[0]
    ds.BitsAllocated = 8
    ds.BitsStored = 7  # NEED TO FIX THIS
    ds.HighBit = 7
    ds.PixelRepresentation = 0
    ds.PixelData = img.tobytes()
    ds.is_implicit_VR = False
    ds.is_little_endian = True

    return ds
Пример #19
0
def writeDCM(root_Path,doc,pat,data):
    # HUaPath = 'Data/dcmHua/liping(0703)/0014712255/RTSTRUCT_2.16.840.1.113669.2.931128.121126138.20190626165800.28009'
    # RTHuaData = pydicom.read_file(HUaPath, force=True)

    # Create some temporary filenames
    # 获取对应的CT信息
    CTpath = getCTName(root_Path + '/' + doc + '/' + pat, 'CT_')
    CTrefds = pydicom.read_file(CTpath, force=True)

    # 新建RTSTRUCT文件
    # filename = root_Path + '/' + doc + '/' + pat + '/'+'RTSTRUCT_2.16.840.1.113669.2.931128.121126138.20190703172017.764011'
    filename = root_Path + '/' + doc + '/' + pat + '/RTSTRUCT_' + pat + '.dcm'
    RTName = pat + '.dcm'
    # RTName = filename
    dirName = getDirName(root_Path + '/' + doc + '/' + pat)#切片的目录文件.dir的名字
    dirPath = root_Path + '/' + doc + '/' + pat + '/'+dirName
    # print(dirName)
    # dirPath = 'Data\dcmHua\doctor1/0002348871/CT_1.3.12.2.1107.5.1.4.66045.30000014051200211018700037438.dir'


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

    # Specific Character Set
    # ds.SpecificCharacterSet = RTHuaData.SpecificCharacterSet#'ISO_IR 100'#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    try:
        ds.SpecificCharacterSet = CTrefds.SpecificCharacterSet
    except BaseException:
        print('对应CT没有 Specific Character Set属性')

    #SOPInstanceUID
    ds.SOPInstanceUID = RTName


    # InstanceCreationDate
    year = datetime.datetime.now().year
    month = datetime.datetime.now().month
    day = datetime.datetime.now().day
    ds.InstanceCreationDate = str(year) + str(month) + str(day)

    # Instance Creation Time ???????????????????????????????????????????????
    time = datetime.datetime.now()
    ds.InstanceCreationTime =  time

    # SOP Class UID
    ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3' #RTHuaData.SOPClassUID# "RT Structure Set Storage"

    # Study Date
    ds.StudyDate = CTrefds.StudyDate

    # Study Time
    ds.StudyTime = CTrefds.StudyTime

    # Accession Number
    ds.AccessionNumber = CTrefds.AccessionNumber
    # Modality
    ds.Modality = "RTSTRUCT"

    # Manufacturer????????????????????!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    ds.Manufacturer ="UESTC"#'ADAC'

    # Referring Physician's Name
    ds.ReferringPhysicianName =  "Huaxi"

    # Station Name ?????????????????
    ds.StationName =  'pinnsx86c-6' #"UESTC"!!!!!!!!!!!!!!!!!!!!!!!

    # Physician(s) of Record
    ds.PhysiciansOfRecord =  "UESTC"

    # Manufacturer's Model Name
    ds.ManufacturerModelName =  "UESTC"

    # Referenced Study Sequence 写sequence
    beam = Dataset()
    ds.ReferencedStudySequence = Sequence([beam])
    ds.ReferencedStudySequence[0].ReferencedSOPClassUID = "Study Component Management SOP Class"
    ds.ReferencedStudySequence[0].ReferencedSOPInstanceUID = CTrefds.StudyInstanceUID
    # ds.ReferencedStudySequence = RTHuaData.ReferencedStudySequence


    # Patient's Name??????????!!!!!!!!!!!!!!
    ds.PatientName = CTrefds.PatientName#RTHuaData.PatientName#'du qi wen restored^13684p1204224^pelvis^'#RTHuaData.PatientName#'du qi wen restored^13684p1204224^^'#

    # Patient ID
    ds.PatientID = CTrefds.PatientID

    # Patient's Birth Date
    ds.PatientBirthDate = CTrefds.PatientBirthDate

    # Patient's Sex
    ds.PatientSex = CTrefds.PatientSex

    # Software Version
    ds.SoftwareVersions = ['9.2', '9.2']

    # Study Instance UID
    ds.StudyInstanceUID = CTrefds.StudyInstanceUID

    # Series Instance UID ?????????????
    ds.SeriesInstanceUID = RTName#RTHuaData.SeriesInstanceUID#'2.16.840.1.113669.2.931128.121126138.20190703172017.513499'#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    # Study ID
    ds.StudyID = CTrefds.StudyID

    # Series Number
    ds.SeriesNumber = "1"

    # Structure Set Label
    ds.StructureSetLabel = "Plan_1"

    # Structure Set Name
    ds.StructureSetName ="POIandROIandBOLUS"

    # Structure Set Date
    ds.StructureSetDate = ds.InstanceCreationDate

    # Structure Set Time
    ds.StructureSetTime = datetime.datetime.now()

    # Referenced Frame of Reference Sequence
    beam = Dataset()
    ds.ReferencedFrameOfReferenceSequence = Sequence([beam])
    ds.ReferencedFrameOfReferenceSequence[0].FrameOfReferenceUID = CTrefds.FrameOfReferenceUID

    beam2 = Dataset()
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence = Sequence([beam2])
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].ReferencedSOPClassUID = "Study Component Management SOP Class"
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[
        0].ReferencedSOPInstanceUID = CTrefds.StudyInstanceUID

    beam3 = Dataset()
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence = Sequence(
        [beam3])
    ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[
        0].SeriesInstanceUID = dirName

    # 写dir文件
    # 这个sequence包含的是dir文件中的每一个文件名遍历,思路是先读取dir文件,对每一行进行遍历,然后将每一行放入文件的lengthi处
    print(dirPath)
    print('^^^^^^^^^^^^^')
    dirList = open(dirPath, 'r')
    count_i = 0
    for dirname in dirList:
        # print(count_i)
        block_i = Dataset()
        if count_i == 0:
            beam4 = Dataset()
            ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[
                0].ContourImageSequence = Sequence([beam4])
        else:
            ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[
                0].ContourImageSequence.append(block_i)

        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[
            0].ContourImageSequence[count_i].ReferencedSOPClassUID = "CT Image Storage"
        ds.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0].RTReferencedSeriesSequence[
            0].ContourImageSequence[count_i].ReferencedSOPInstanceUID = dirname
        count_i += 1

    # 写标注部位
    # 思路:输入标注部位列表,StructureSetROISequenceList,然后对每一个部位进行填充
    # StructureSetROISequenceList = ['locref', 'Spinal Cord  N', 'Liver  N', 'Kidney L  N', 'Kidney R  N', 'Bladder','Femoral Head R', 'Femoral Head L', 'Rectum', 'CTV', 'PTV']

    StructureSetROISequenceList = getLablePos(data)
    print(StructureSetROISequenceList)
    print('@@@@@@@@@@@@')
    count_j = 0
    for ROISequence in StructureSetROISequenceList:
        block_j = Dataset()
        if count_j == 0:
            beam5 = Dataset()
            ds.StructureSetROISequence = Sequence([beam5])
        else:
            ds.StructureSetROISequence.append(block_j)
        ds.StructureSetROISequence[count_j].ROINumber = count_j
        ds.StructureSetROISequence[count_j].ReferencedFrameOfReferenceUID = CTrefds.FrameOfReferenceUID
        ds.StructureSetROISequence[count_j].ROIName = ROISequence
        count_j += 1

    # 对每一个标注部位开始写contour
    # 思路:先建立部位的列表,每个部位包含有该部位的切片列表,然后每个切片包含contour
    count_ROI = 0
    colorList = [['127', '255', '212'], ['0', '255', '0'], ['0', '0', '255'], ['0', '255', '255'], ['255', '150', '0'],
                 ['128', '0', '255'], ['34', '139', '34'], ['255', '0', '0'], ['127', '255', '212'],
                 ['128', '0', '255'], ['165', '161', '55']]
    # print('*******************%%%%%%%%%%%%%%%%%%%')
    for ROISequence in StructureSetROISequenceList:  # 对部位进行循环
        block_ROI = Dataset()
        if count_ROI == 0:
            beam_ROI = Dataset()
            ds.ROIContourSequence = Sequence([beam_ROI])
        else:
            ds.ROIContourSequence.append((block_ROI))

        ds.ROIContourSequence[count_ROI].ROIDisplayColor = colorList[count_ROI]

        count_Slice = 0
        SliceList = getSliceList(data, doc, pat, ROISequence)
        # print('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&')
        # print(len(SliceList[0][4]))
        # SliceList = np.array(SliceList)
        # print(SliceList)
        # print('&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&')

        # SliceList = ['1.3.12.2.1107.5.1.4.66045.30000014051200211018700037557','1.3.12.2.1107.5.1.4.66045.30000014051200211018700037563']

        # # 对应的contour
        for slice in SliceList:  # 对切片进行循环,这里需要写一个筛选哪些切片有标注,形成一个list
            # print(slice)
            # print('(((((((((((((()))))))))))))))')
            block_Slice = Dataset()
            if count_Slice == 0:
                beam_Slice = Dataset()
                ds.ROIContourSequence[count_ROI].ContourSequence = Sequence([beam_Slice])
            else:
                ds.ROIContourSequence[count_ROI].ContourSequence.append((block_Slice))
            #
            #     # 每张切片单独成一个slice的item,不需要对slice内部进行循环
            #     ''' Png文件存储要求:
            #     1.png文件名是"部位_切片名"
            #     2.写一个文件,读入png及其名称,转换其像素点为dicom坐标contour,判断连通区域,每个联通区域存储成一项,
            #
            #     --RTSTRUCT
            #         --ROI
            #             --slice
            #

            #     '''
            beam_contour = Dataset()
            ds.ROIContourSequence[count_ROI].ContourSequence[count_Slice].ContourImageSequence = Sequence(
                [beam_contour])
            ds.ROIContourSequence[count_ROI].ContourSequence[count_Slice].ContourImageSequence[
                0].ReferencedSOPClassUID = "CT Image Storage"
            ds.ROIContourSequence[count_ROI].ContourSequence[count_Slice].ContourImageSequence[
                0].ReferencedSOPInstanceUID = slice[3]

            ds.ROIContourSequence[count_ROI].ContourSequence[count_Slice].ContourGeometricType = 'CLOSED_PLANAR'
            # contourData = slice[4].split(',')

            ds.ROIContourSequence[count_ROI].ContourSequence[count_Slice].NumberOfContourPoints = len(slice[4]) / 3

            ds.ROIContourSequence[count_ROI].ContourSequence[
                count_Slice].ContourData = slice[4]#mv(DSfloat, contourData)  # ['-39.4297', '-227.672','-1689.79', '-37.346','-228.714', '-1689.79']
            count_Slice += 1

        ds.ROIContourSequence[count_ROI].ReferencedROINumber = count_ROI
        count_ROI += 1





    # ds.ReferencedFrameOfReferenceSequence = RTHuaData.ReferencedFrameOfReferenceSequence
    # ds.StructureSetROISequence = RTHuaData.StructureSetROISequence
    # ds.ROIContourSequence = RTHuaData.ROIContourSequence

    print('&&&&&&&&&&&&&')
    print("Writing test file", filename)
    ds.save_as(filename)
    print("File saved.")

    print('**************')
    # path = pat+'.dcm'
    p = pydicom.read_file(filename)
    print(p)
Пример #20
0
def convert_npy_to_dicom(npy_array, fname=None,
                         slice_thickness=None,
                         pixel_spacing=None,
                         spacing_between_slices=None,
                         single_file_mode=True
                         ):
    """
    convert npy array to dicom
    :param npy_array: npy array
    :param fname: file name
    :param slice_thickness: slice thickness
    :param spacing_between_slices: spacing between slices
    :param pixel_spacing: pixel spacing
    :param single_file_mode: if False, slice by slice dicom files are generated
    :return:  dcm
    """
    uint16_img = np.array(npy_array).astype(float)
    uint16_img = (
            (uint16_img - uint16_img.min()) /
            (uint16_img.max() - uint16_img.min()) * (2 ** 16 - 1)
    ).astype(np.uint16)
    dim = len(uint16_img.shape)
    if dim == 1:
        raise Exception('Cannot convert 1D array to dicom')
    elif dim == 2:
        uint16_img = uint16_img[np.newaxis, :, :]
    elif dim > 3:
        raise Exception('{}D array is not supported.'.format(dim))
    x_min = float(npy_array.min())
    x_max = float(npy_array.max())
    x_max_min = x_max - x_min
    t_max = (2 ** 16) - 1
    slope = x_max_min / t_max
    intercept = x_min
    now = datetime.now().timestamp()

    file_meta = Dataset()
    file_meta.MediaStorageSOPClassUID = '1.2.840.100008.5.1.4.1.1.20'

    file_meta.MediaStorageSOPInstanceUID = f'333.333.0.0.0.333.333333333.{now}'
    file_meta.ImplementationClassUID = '0.0.0.0'
    file_meta.FileMetaInformationGroupLength = 140
    dcm = FileDataset(fname, {}, file_meta=file_meta, preamble=b'\0' * 128)

    dcm.Modality = 'OT'
    if single_file_mode:
        dcm.ImageType = ["DERIVED", "PRIMARY", "RECON TOMO", "EMISSION"]
    else:
        dcm.ImageType = ["DERIVED", "SECONDARY"]

    dcm.ContentDate = datetime.now().strftime('%Y%m%d')
    dcm.ContentTime = datetime.now().strftime('%H%M%S')
    dcm.InstanceCreationDate = datetime.now().strftime('%Y%m%d')
    dcm.InstanceCreationTime = datetime.now().strftime('%H%M%S')
    dcm.SeriesDate = datetime.now().strftime('%Y%m%d')
    dcm.SeriesTime = datetime.now().strftime('%H%M%S')
    dcm.AcquisitionTime = datetime.now().strftime('%H%M%S')
    dcm.PatientName = os.path.basename(fname)
    dcm.PatientBirthDate = datetime.now().strftime('%Y%m%d')
    dcm.PatientAge = '000Y'
    dcm.PatientSize = 1
    dcm.PatientWeight = 1
    dcm.PatientID = os.path.basename(fname)
    dcm.PatientSex = 'O'
    dcm.StudyDescription = os.path.basename(fname)
    dcm.StudyDate = datetime.now().strftime('%Y%m%d')
    dcm.StudyTime = datetime.now().strftime('%H%M%S')
    dcm.StudyID = os.path.basename(fname)
    dcm.SeriesDescription = os.path.basename(fname)
    dcm.SamplesPerPixel = 1
    dcm.PhotometricInterpretation = 'MONOCHROME2'
    dcm.PixelRepresentation = 0  # unsigned 0, signed 1
    dcm.HighBit = 16
    dcm.BitsStored = 16
    dcm.BitsAllocated = 16
    dcm.Columns = uint16_img.shape[2]
    dcm.Rows = uint16_img.shape[1]
    if single_file_mode:
        dcm.NumberOfFrames = uint16_img.shape[0]
        dcm.ImagesInAquisition = uint16_img.shape[0]
        dcm.SliceVector = (np.arange(uint16_img.shape[0]) + 1).tolist()
        dcm.FrameIncrementPointer = [(0x0054, 0x0080)]
    else:
        dcm.NumberOfTimeSlices = 1
        dcm.FrameReferenceTime = 0.
    dcm.ImageOrientationPatient = [1., 0., 0., 0., -1., 0.]
    dcm.SeriesNumber = 0
    dcm.NumberOfSlices = uint16_img.shape[0]
    dcm.RescaleIntercept = intercept
    dcm.RescaleSlope = slope
    dcm.Units = "NONE"
    dcm.DecayCorrection = "NONE"
    dcm.InstanceCreatorUID = '333.333.0.0.0'
    dcm.SOPClassUID = '1.2.840.10008.5.1.4.1.1.20'
    dcm.SliceThickness = 1 if slice_thickness is None else slice_thickness
    if spacing_between_slices is None:
        dcm.SpacingBetweenSlices = 1 if slice_thickness is None else slice_thickness
    else:
        dcm.SpacingBetweenSlices = spacing_between_slices
    ps = 1 if pixel_spacing is None else pixel_spacing
    if isinstance(ps, list) or isinstance(ps, np.ndarray):
        dcm.PixelSpacing = [ps[0], ps[1]]
    else:
        dcm.PixelSpacing = [ps, ps]

    if single_file_mode:
        dcm.PixelData = uint16_img.tostring()
        dcm.StudyInstanceUID = f'333.333.0.0.0.{now}'
        dcm.SeriesInstanceUID = f'333.333.0.0.0.{now}.3333'
        dcm.FrameOfReferenceUID = dcm.StudyInstanceUID
        dcm.SeriesNumber = 0
        dcm.InstanceNumber = 0
        dcm.BodyPartExamined = 'UNKNOWN'
        dcm.Manufacturer = 'DicomConversionUtils'
        dcm.DeviceSerialNumber = ''
        dcm.AcquisitionTerminationCondition = 'MANU'
        dcm.SoftwareVersions = f'{pydicom_ext_version}'
        dcm.AccessionNumber = '{:13d}'.format(random.randint(0, 1e13))
        dcm.InstitutionName = 'DicomConversionUtils'
        dcm.ImagePositionPatient = [0, 0, 0]
        if fname is not None:
            dcm.save_as(fname, write_like_original=False)
        return dcm
    else:
        dcms = []
        for slice_idx in range(uint16_img.shape[0]):
            dcm.SOPInstanceUID = f'333.333.0.0.0.{now}.{slice_idx:06d}'
            dcm.PixelData = uint16_img[slice_idx].tostring()
            dcm.StudyInstanceUID = f'333.333.0.0.0.{now}'
            dcm.SeriesInstanceUID = f'333.333.0.0.0.{now}.3333'
            dcm.FrameOfReferenceUID = dcm.StudyInstanceUID
            dcm.InstanceNumber = slice_idx
            dcm.BodyPartExamined = 'UNKNOWN'
            dcm.Manufacturer = 'DicomConversionUtils'
            dcm.DeviceSerialNumber = ''
            dcm.AcquisitionTerminationCondition = 'MANU'
            dcm.SoftwareVersions = f'{pydicom_ext_version}'
            dcm.AccessionNumber = '{:13d}'.format(random.randint(0, 1e13))
            dcm.InstitutionName = 'DicomConversionUtils'
            dcm.ImageIndex = slice_idx
            if spacing_between_slices is None:
                if slice_thickness is None:
                    dcm.ImagePositionPatient = [0, 0, slice_idx]
                    dcm.SliceLocation = slice_idx
                else:
                    dcm.ImagePositionPatient = [0, 0, slice_idx * slice_thickness]
                    dcm.SliceLocation = slice_idx * slice_thickness
            else:
                dcm.ImagePositionPatient = [0, 0, slice_idx * spacing_between_slices]
                dcm.SliceLocation = slice_idx * spacing_between_slices

            dcms.append(dcm)
            if fname is not None:
                f = copy.copy(fname)
                if ".dcm" in f:
                    f = f.replace(".dcm", f"_{slice_idx:06d}.dcm")
                else:
                    f += f"_{slice_idx:06d}.dcm"
                dcm.save_as(f, write_like_original=False)
        return dcms
Пример #21
0
def __write_dicom_series_file_nii(series_dataset, dst_one_case_file_path):
    print('start: write dicom series')
    if not os.path.exists(dst_one_case_file_path):
        os.makedirs(dst_one_case_file_path)
    dataset_list = []

    hdr = series_dataset.header
    print(hdr)
    sop_instance_info_list = __generate_sop_instance_info_nii(series_dataset)
    study_instance_uid = pydicom.uid.generate_uid()
    series_instance_uid = pydicom.uid.generate_uid()
    frame_of_reference_uid = pydicom.uid.generate_uid()
    volume_size = series_dataset.shape
    for j in range(volume_size[2]):
        sop_instance_uid = sop_instance_info_list[j][1]
        filename = os.path.join(dst_one_case_file_path,
                                'CT.' + sop_instance_uid + '.dcm')

        file_meta = Dataset()
        file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
        file_meta.MediaStorageSOPInstanceUID = sop_instance_uid
        file_meta.ImplementationClassUID = '1.2.246.352.70.2.1.7'

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

        ds.InstanceNumber = sop_instance_info_list[j][0]
        ds.SliceLocation = sop_instance_info_list[j][2]
        ds.ImagePositionPatient = sop_instance_info_list[j][3]

        ds.fix_meta_info()
        ds.is_little_endian = True
        ds.is_implicit_VR = True
        ds.StudyInstanceUID = study_instance_uid
        ds.SeriesInstanceUID = series_instance_uid
        ds.FrameOfReferenceUID = frame_of_reference_uid
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.ImageType = 'ORIGINAL\\PRIMARY\\AXIAL\\HELIX'
        dt = datetime.datetime.now()
        datestr = dt.strftime('%Y%m%d')
        timestr1 = dt.strftime('%H%M%S.%f')
        timestr2 = dt.strftime('%H%M%S')

        ds.InstanceCreationDate = datestr
        ds.InstanceCreationTime = timestr1
        ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
        ds.SOPInstanceUID = sop_instance_uid
        ds.StudyDate = datestr
        ds.SeriesDate = datestr
        ds.ContentDate = datestr
        ds.StudyTime = timestr2
        ds.SeriesTime = timestr2
        ds.ContentTime = timestr2
        ds.AccessionNumber = '116939'
        ds.Modality = 'CT'
        ds.Manufacturer = 'HYHY'  #'Philips'
        ds.InstitutionName = 'Cancer Hosipital'
        ds.ReferringPhysicianName = '79309'
        ds.StationName = '-'
        ds.StudyDescription = '-'
        ds.PatientName = 'patientxxx'
        ds.PatientID = 'patientxxx'
        ds.PatientBirthDate = '19000101'
        ds.PatientBirthTime = '000000'
        ds.PatientAge = '119'
        ds.PatientSex = '1'  #'-'
        ds.PatientSize = 120
        ds.PatientWeight = 100
        ds.PatientAddress = 'some where'
        ds.PatientComments = 'some'
        ds.SliceThickness = series_dataset.header['pixdim'][3]
        ds.KVP = 1111111
        ds.PatientPosition = 'HFS'
        ds.StudyID = '20200423'
        ds.SeriesNumber = 1
        ds.ImageOrientationPatient = '1\\0\\0\\0\\1\\0'
        ds.SamplesPerPixel = 1
        ds.Rows = volume_size[1]
        ds.Columns = volume_size[0]
        spacing_x = series_dataset.header['pixdim'][1]
        spacing_y = series_dataset.header['pixdim'][2]
        ds.PixelSpacing = '%f\\%f' % (spacing_x, spacing_y)

        niiPixelDataType = series_dataset.header['datatype'].dtype
        if (niiPixelDataType == 'uint16' or niiPixelDataType == 'uint8'
            ):  #判断有符号数 无符号数 if(niiPixelDataType.find('uint') == 1 )
            ds.PixelRepresentation = 0
        elif (niiPixelDataType == 'int16' or niiPixelDataType == 'int8'):
            ds.PixelRepresentation = 1

        niiBitPixel = series_dataset.header['bitpix']
        ds.BitsAllocated = niiBitPixel  #16
        ds.BitsStored = niiBitPixel  #12
        ds.HighBit = niiBitPixel - 1  #11

        #ds.WindowCenter = 756#60
        #ds.WindowWidth = 1500#350
        rescale_intercept = series_dataset.header['scl_inter']
        if np.isnan(rescale_intercept):
            ds.RescaleIntercept = 0  #-1024
        else:
            ds.RescaleIntercept = rescale_intercept
        rescale_slope = series_dataset.header['scl_slope']
        if np.isnan(rescale_slope):
            ds.RescaleSlope = 1
        else:
            ds.RescaleSlope = rescale_slope

        ds.PixelData = __create_pixel_data_array_nii(series_dataset, j)
        ds.save_as(filename, write_like_original=False)
        dataset_list.append(ds)
    print('end: write dicom series')
    return dataset_list