3

I've successfully built dicoms with one images but I cannot find a way to add more... I think the problem may be in my pixel array, can anyone help me correct it ?

# Populate required values for file meta information
meta = pydicom.Dataset()
meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
meta.MediaStorageSOPClassUID = pydicom._storage_sopclass_uids.MRImageStorage
meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()

# build dataset
ds = Dataset()
ds.file_meta = meta
ds.fix_meta_info()

# unknown options
ds.is_little_endian = True
ds.is_implicit_VR = False
ds.SOPClassUID = pydicom._storage_sopclass_uids.MRImageStorage
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
ds.StudyInstanceUID = pydicom.uid.generate_uid()
ds.FrameOfReferenceUID = pydicom.uid.generate_uid()
ds.BitsStored = 16
ds.BitsAllocated = 16
ds.SamplesPerPixel = 1
ds.HighBit = 15
ds.ImagesInAcquisition = "1"
ds.InstanceNumber = 1
ds.ImagePositionPatient = r"0\0\1"
ds.ImageOrientationPatient = r"1\0\0\0\-1\0"
ds.ImageType = r"ORIGINAL\PRIMARY\AXIAL"
ds.RescaleIntercept = "0"
ds.RescaleSlope = "1"
ds.PixelRepresentation = 1

# Case options
ds.PatientName = "Anonymous"
ds.PatientID = "123456"
ds.Modality = "MR"
ds.StudyDate = '20200225'
ds.ContentDate = '20200225'

def ensure_even(stream):
    # Very important for some viewers
    if len(stream) % 2:
        return stream + b"\x00"
    return stream

pixel_data_list = []
for root, dir, filenames in walk(folder):
    filenames.sort(key=natural_keys)
    for filename in filenames:
        filename = folder + filename
        # convert image to grayscale
        img = Image.open(filename).convert('L')
        img.save(filename)

        # open image, decode and ensure_even stream
        with open(filename, 'rb') as f:
            arr = decode(f)

        pixel_data_list.append(arr.tobytes())

# required for pixel handler
ds.BitsStored = 8
ds.BitsAllocated = 8
ds.HighBit = 7
ds.PixelRepresentation = 0

# grayscale without compression
ds.PhotometricInterpretation = "MONOCHROME2"
ds.SamplesPerPixel = 1  # 1 color = 1 sampleperpixel
ds.file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian
ds.PixelData = array(pixel_data_list)
ds.NumberOfFrames = len(pixel_data_list)

# Image shape
ds['PixelData'].is_undefined_length = False
ds.Columns = img.width
ds.Rows = img.height

# validate and save
pydicom.dataset.validate_file_meta(ds.file_meta, enforce_standard=True)
new_filename = filename.replace('.jpg', name + '.dcm')
ds.save_as(new_filename, write_like_original=False)

Also can't seem to compress the images, only monochrome works (see tries here https://stackoverflow.com/a/68939321/1827162)... That's not as problematic though, the most important thing is to be able to add multiple images. Thanks for any pointer !

Johann
  • 73
  • 7

1 Answers1

2

Pixel Data should be bytes. If your transfer syntax is uncompressed then you need to concatenate the data together and if the length of the concatenated data is odd then you must add a trailing padding byte:

# For uncompressed transfer syntaxes only!
pixel_data = b"".join(pixel_data_list)
ds.PixelData = pixel_data + b"\x00" if len(pixel_data) % 2 else pixel_data

For compressed transfer syntaxes you must use encapsulation

scaramallion
  • 1,251
  • 1
  • 6
  • 14
  • 1
    Awesome ! Thanks it works now^^ That's good progress – Johann Aug 27 '21 at 13:13
  • Do you know why I only see one image with a viewer ? ds.NumberOfFrames = len(pixel_data_list) ds.ImagesInAcquisition = len(pixel_data_list) don't seem to do the trick... – Johann Aug 27 '21 at 13:14
  • I mean the reader only shows one instance. How are the instances defined ? Reading the docs but it's hard to find the answer – Johann Aug 27 '21 at 14:16
  • An instance (SOP Instance) is a single dataset. I don't think *MR Image Storage* is typically multi-framed, does the viewer support it? Maybe you want *Enhanced MR*? – scaramallion Aug 29 '21 at 22:15
  • The viewer supports it (GXD5) I've tried ds.SOPClassUID = pydicom._storage_sopclass_uids.EnhancedMRImageStorage And added all the required values like ds.SharedFunctionalGroupsSequence = "" ds.VolumetricProperties = "MIXED" ds.VolumeBasedCalculationTechnique = "NONE" Also added ContentTime. I don't know what to put in ds.DimensionOrganizationSequence though. Now the file closes as soon as it opens, something corrupted the file ? This is so complicated... Thanks a lot for your help – Johann Aug 30 '21 at 08:16
  • I have a feeling that I'm only missing correct values for SharedFunctionalGroupsSequence and DimensionOrganizationSequence to make it work – Johann Aug 30 '21 at 08:27