Exemple #1
0
def create_dicom(x,
                 filename,
                 sp,
                 sz=None,
                 f=1,
                 study_uid=None,
                 series_uid=None,
                 time=datetime.datetime.now(),
                 storage_directory=None):
    """ Create DICOM format output file from data

	create_dicom(x, filename, sp) creates a new DICOM file with a
	name `filename_0001.dcm' and containing data from x. The pixel scale is
	given by sp which is in mm.

	create_dicom(x, filename, sp, sz, f) creates a new DICOM file with a
	name formed from the given filename and the frame number f, and
	containing data from x. The pixel scale is given by sp, and the frame
	spacing is given by sz, both of which are in mm.

	create_dicom(x, filename, sp, sz, f, study_uid, series_uid, time)
	uses the DICOM UIDs study_uid and series_uid, and also the
	datetime, for the file. This is useful if you want to write several
	frames in the same DICOM series. The UIDs can be generated
	using the DICOMUID function. The time can be generated using datetime.datetime.now().

	optional storage_directory parameter can set the file's storage directory path
	"""

    # check for inputs
    if sz is None:
        sz = sp

    if study_uid is None:
        study_uid = pydicom.uid.generate_uid()

    if series_uid is None:
        series_uid = pydicom.uid.generate_uid()

    # get data with the appropriate limits
    x = x + 1024
    x = np.clip(x, 0, None)
    x = np.clip(x, None, 4096)

    file_meta = Dataset()

    # Initial write to create DICOM file with default settings
    full_filename = filename + '_' + str(f).zfill(4) + '.dcm'
    full_file = full_filename

    #add storage directory if needed
    if storage_directory is not None:
        full_filename = os.path.join(storage_directory, full_filename)

    series_date = time.strftime('%Y%m%d')
    series_time = time.strftime('%H%M%S')

    ds = FileDataset(full_file, {}, file_meta=file_meta, preamble=b"\0" * 128)
    ds.Modality = 'CT'
    ds.ContentDate = str(datetime.date.today()).replace('-', '')
    ds.ContentTime = str(time)  #milliseconds since the epoch
    ds.StudyInstanceUID = study_uid
    ds.SeriesInstanceUID = series_uid
    ds.SOPClassUID = 'CT Image Storage'

    ds.StudyInstanceUID = study_uid
    ds.SeriesInstanceUID = series_uid
    ds.StudyDescription = full_file + ' Study'
    ds.SeriesDescription = full_file + ' Series'
    ds.StudyID = '1'
    ds.SeriesNumber = 1
    ds.StudyDate = series_date
    ds.SeriesDate = series_date
    ds.AcquisitionDate = series_date
    ds.ContentDate = series_date
    ds.StudyTime = series_time
    ds.SeriesTime = series_time
    ds.AcquisitionTime = series_time
    ds.ContentTime = series_time
    ds.PatientName = full_file
    ds.Modality = 'CT'
    ds.RescaleIntercept = '-1024'
    ds.RescaleSlope = '1'
    ds.RescaleType = 'HU'
    ds.WindowWidth = '2000'
    ds.WindowCenter = '0'
    ds.ImagePositionPatient = [0.000, 0.000, float(f * sz)]
    ds.ImageOrientationPatient = [1.000, 0.000, 0.000, 0.000, 1.000, 0.000]
    ds.SpacingBetweenSlices = str(sz)
    ds.SliceThickness = str(sz)
    ds.GantryDetectorTilt = '0'
    ds.SliceLocation = str(f * sz)
    ds.PixelSpacing = [sp, sp]

    ## These are the necessary imaging components of the FileDataset object.
    ds.SamplesPerPixel = 1
    ds.PhotometricInterpretation = "MONOCHROME2"
    ds.PixelRepresentation = 0
    ds.HighBit = 15
    ds.BitsStored = 16
    ds.BitsAllocated = 16
    ds.Columns = x.shape[1]
    ds.Rows = x.shape[0]

    if x.dtype != np.uint16:
        x = x.astype(np.uint16)

    ds.PixelData = x.tostring()

    # write final file with this metadata
    ds.save_as(full_filename)
Exemple #2
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
Exemple #3
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)
Exemple #4
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
Exemple #5
0
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')
currentTime += str(f[0:3])
info_mask.ContentTime=currentTime
info_mask.StudyTime=info.StudyTime
info_mask.SeriesTime=currentTime
info_mask.AcquisitionTime=currentTime
info_mask.InstanceNumber= 1
info_mask.FrameOfReferenceUID= info.FrameOfReferenceUID
info_mask.PositionReferenceIndicator= ''

da1 = Dataset()
da2 = Dataset()
da3 = Dataset()
da4 = Dataset()
Exemple #6
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