Ejemplo n.º 1
0
    def read_image(self):
        self.recvImages += 1
        logging.info("<-- Received MRD_MESSAGE_ISMRMRD_IMAGE (1022)")
        # return ismrmrd.Image.deserialize_from(self.read)

        # Explicit version of deserialize_from() for more verbose debugging
        logging.debug("   Reading in %d bytes of image header",
                      ctypes.sizeof(ismrmrd.ImageHeader))
        header_bytes = self.read(ctypes.sizeof(ismrmrd.ImageHeader))

        attribute_length_bytes = self.read(ctypes.sizeof(ctypes.c_uint64))
        attribute_length = ctypes.c_uint64.from_buffer_copy(
            attribute_length_bytes)
        logging.debug("   Reading in %d bytes of attributes",
                      attribute_length.value)

        attribute_bytes = self.read(attribute_length.value)
        if (attribute_length.value > 25000):
            logging.debug("   Attributes (truncated): %s",
                          attribute_bytes[0:24999].decode('utf-8'))
        else:
            logging.debug("   Attributes: %s", attribute_bytes.decode('utf-8'))

        image = ismrmrd.Image(header_bytes, attribute_bytes.decode('utf-8'))

        logging.info(
            "    Image is size %d x %d x %d with %d channels of type %s",
            image.matrix_size[0], image.matrix_size[1], image.matrix_size[2],
            image.channels, ismrmrd.get_dtype_from_data_type(image.data_type))

        def calculate_number_of_entries(nchannels, xs, ys, zs):
            return nchannels * xs * ys * zs

        nentries = calculate_number_of_entries(image.channels,
                                               *image.matrix_size)
        nbytes = nentries * ismrmrd.get_dtype_from_data_type(
            image.data_type).itemsize

        logging.debug("Reading in %d bytes of image data", nbytes)
        data_bytes = self.read(nbytes)

        image.data.ravel()[:] = np.frombuffer(
            data_bytes,
            dtype=ismrmrd.get_dtype_from_data_type(image.data_type))

        if self.savedata is True:
            if self.dset is None:
                self.create_save_file()

            image.attribute_string = ismrmrd.Meta.deserialize(
                image.attribute_string.split(
                    '\x00', 1)[0]).serialize()  # Strip off null teminator
            self.dset.append_image("images_%d" % image.image_series_index,
                                   image)

        return image
Ejemplo n.º 2
0
    def read_image(self):
        logging.info("<-- Received MRD_MESSAGE_ISMRMRD_IMAGE (1022)")
        # return ismrmrd.Image.deserialize_from(self.read)

        # Explicit version of deserialize_from() for more verbose debugging
        logging.debug("   Reading in %d bytes of image header",
                      ctypes.sizeof(ismrmrd.ImageHeader))
        header_bytes = self.read(ctypes.sizeof(ismrmrd.ImageHeader))

        attribute_length_bytes = self.read(ctypes.sizeof(ctypes.c_uint64))
        attribute_length = ctypes.c_uint64.from_buffer_copy(
            attribute_length_bytes)
        logging.debug("   Reading in %d bytes of attributes",
                      attribute_length.value)

        attribute_bytes = self.read(attribute_length.value)
        logging.debug("   Attributes: %s", attribute_bytes)

        image = ismrmrd.Image(header_bytes, attribute_bytes.decode('utf-8'))

        logging.debug("    Image is size %d x %d x %d of type %s",
                      image.matrix_size[0], image.matrix_size[1],
                      image.matrix_size[2],
                      ismrmrd.get_dtype_from_data_type(image.data_type))

        def calculate_number_of_entries(nchannels, xs, ys, zs):
            return nchannels * xs * ys * zs

        nentries = calculate_number_of_entries(image.channels,
                                               *image.matrix_size)
        nbytes = nentries * ismrmrd.get_dtype_from_data_type(
            image.data_type).itemsize

        logging.debug("Reading in %d bytes of image data", nbytes)
        data_bytes = self.read(nbytes)

        image.data.ravel()[:] = np.frombuffer(
            data_bytes,
            dtype=ismrmrd.get_dtype_from_data_type(image.data_type))

        if (self.savedata is True):
            self.dset.append_image("images_%d" % image.image_series_index,
                                   image)

        return image
def process_image(image, config, metadata):
    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Incoming image data of type %s",
                  ismrmrd.get_dtype_from_data_type(image.data_type))

    # Extract image data itself
    data = image.data
    logging.debug("Original image data is size %s" % (data.shape, ))
    np.save(debugFolder + "/" + "imgOrig.npy", data)

    # Normalize and convert to int16
    data = data.astype(np.float64)
    data *= 32767 / data.max()
    data = np.around(data)
    data = data.astype(np.int16)

    # Invert image contrast
    data = 32767 - data
    data = np.abs(data)
    data = data.astype(np.int16)
    np.save(debugFolder + "/" + "imgInverted.npy", data)

    # Create new MRD instance for the inverted image
    imageInverted = ismrmrd.Image.from_array(data.transpose())
    data_type = imageInverted.data_type

    np.save(debugFolder + "/" + "imgInvertedMrd.npy", imageInverted.data)

    # Copy the fixed header information
    oldHeader = image.getHead()
    oldHeader.data_type = data_type
    imageInverted.setHead(oldHeader)

    # Set ISMRMRD Meta Attributes
    meta = ismrmrd.Meta({
        'DataRole': 'Image',
        'ImageProcessingHistory': ['FIRE', 'PYTHON'],
        'WindowCenter': '16384',
        'WindowWidth': '32768'
    })
    xml = meta.serialize()
    logging.debug("Image MetaAttributes: %s", xml)
    logging.debug("Image data has %d elements", image.data.size)

    imageInverted.attribute_string = xml

    return imageInverted
Ejemplo n.º 4
0
def process_image(images, connection, config, metadata):
    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Processing data with %d images of type %s", len(images),
                  ismrmrd.get_dtype_from_data_type(images[0].data_type))

    # Extract image data into a 5D array of size [img cha z y x]
    data = np.stack([img.data for img in images])
    head = [img.getHead() for img in images]
    meta = [ismrmrd.Meta.deserialize(img.attribute_string) for img in images]

    # Reformat data to the more intuitive [x y z cha img]
    data = data.transpose()

    # Reformat data again to [y x z cha img], i.e. [row col] for the first two
    # dimensions.  Note we will need to undo this later prior to sending back
    # to the client
    data = data.transpose((1, 0, 2, 3, 4))

    # Display MetaAttributes for first image
    logging.debug("MetaAttributes[0]: %s", ismrmrd.Meta.serialize(meta[0]))

    # Optional serialization of ICE MiniHeader
    if 'IceMiniHead' in meta[0]:
        logging.debug("IceMiniHead[0]: %s",
                      base64.b64decode(meta[0]['IceMiniHead']).decode('utf-8'))

    logging.debug("Original image data is size %s" % (data.shape, ))
    np.save(debugFolder + "/" + "imgOrig.npy", data)

    # Normalize and convert to int16
    data = data.astype(np.float64)
    data *= 32767 / data.max()
    data = np.around(data)
    data = data.astype(np.int16)

    # Invert image contrast
    data = 32767 - data
    data = np.abs(data)
    data = data.astype(np.int16)
    np.save(debugFolder + "/" + "imgInverted.npy", data)

    # Reformat data from [row col z cha img] back to [x y z cha img] before sending back to client
    data = data.transpose((1, 0, 2, 3, 4))

    currentSeries = 0

    # Re-slice back into 2D images
    imagesOut = [None] * data.shape[-1]
    for iImg in range(data.shape[-1]):
        # Create new MRD instance for the inverted image
        # NOTE: from_array() takes input data as [x y z coil], which is
        # different than the internal representation in the "data" field as
        # [coil z y x].  However, we already transposed this data when
        # extracting it earlier.
        imagesOut[iImg] = ismrmrd.Image.from_array(data[..., iImg])
        data_type = imagesOut[iImg].data_type

        # Create a copy of the original fixed header and update the data_type
        # (we changed it to int16 from all other types)
        oldHeader = head[iImg]
        oldHeader.data_type = data_type

        # Unused example, as images are grouped by series before being passed into this function now
        # oldHeader.image_series_index = currentSeries

        # Increment series number when flag detected (i.e. follow ICE logic for splitting series)
        if mrdhelper.get_meta_value(meta[iImg], 'IceMiniHead') is not None:
            if mrdhelper.extract_minihead_bool_param(
                    base64.b64decode(
                        meta[iImg]['IceMiniHead']).decode('utf-8'),
                    'BIsSeriesEnd') is True:
                currentSeries += 1

        imagesOut[iImg].setHead(oldHeader)

        # Create a copy of the original ISMRMRD Meta attributes and update
        tmpMeta = meta[iImg]
        tmpMeta['DataRole'] = 'Image'
        tmpMeta['ImageProcessingHistory'] = ['PYTHON', 'INVERT']
        tmpMeta['WindowCenter'] = '16384'
        tmpMeta['WindowWidth'] = '32768'
        tmpMeta['SequenceDescriptionAdditional'] = 'FIRE'
        tmpMeta['Keep_image_geometry'] = 1
        # tmpMeta['ROI_example']                    = create_example_roi(data.shape)

        # Example for setting colormap
        # tmpMeta['LUTFileName']            = 'MicroDeltaHotMetal.pal'

        # Add image orientation directions to MetaAttributes if not already present
        if tmpMeta.get('ImageRowDir') is None:
            tmpMeta['ImageRowDir'] = [
                "{:.18f}".format(oldHeader.read_dir[0]),
                "{:.18f}".format(oldHeader.read_dir[1]),
                "{:.18f}".format(oldHeader.read_dir[2])
            ]

        if tmpMeta.get('ImageColumnDir') is None:
            tmpMeta['ImageColumnDir'] = [
                "{:.18f}".format(oldHeader.phase_dir[0]),
                "{:.18f}".format(oldHeader.phase_dir[1]),
                "{:.18f}".format(oldHeader.phase_dir[2])
            ]

        metaXml = tmpMeta.serialize()
        logging.debug("Image MetaAttributes: %s",
                      xml.dom.minidom.parseString(metaXml).toprettyxml())
        logging.debug("Image data has %d elements", imagesOut[iImg].data.size)

        imagesOut[iImg].attribute_string = metaXml

    return imagesOut
Ejemplo n.º 5
0
def process_image(images, config, metadata):
    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Incoming image data of type %s",
                  ismrmrd.get_dtype_from_data_type(images[0].data_type))

    # Display MetaAttributes for first image
    tmpMeta = ismrmrd.Meta.deserialize(images[0].attribute_string)
    logging.debug("MetaAttributes[0]: %s", ismrmrd.Meta.serialize(tmpMeta))

    # Optional serialization of ICE MiniHeader
    if 'IceMiniHead' in tmpMeta:
        logging.debug("IceMiniHead[0]: %s",
                      base64.b64decode(tmpMeta['IceMiniHead']).decode('utf-8'))

    slice = [img.slice for img in images]
    phase = [img.phase for img in images]

    # Process each group of venc directions separately
    unique_venc_dir = np.unique([
        ismrmrd.Meta.deserialize(img.attribute_string)['FlowDirDisplay']
        for img in images
    ])

    # Start the phase images at series 10.  When interpreted by FIRE, images
    # with the same image_series_index are kept in the same series, but the
    # absolute series number isn't used and can be arbitrary
    last_series = 10
    imagesOut = []
    for venc_dir in unique_venc_dir:
        # data array has dimensions [x y sli phs]
        # info lists has dimensions [sli phs]
        data = np.zeros((images[0].data.shape[2], images[0].data.shape[3],
                         max(slice) + 1, max(phase) + 1), images[0].data.dtype)
        head = [[None] * (max(phase) + 1) for _ in range(max(slice) + 1)]
        meta = [[None] * (max(phase) + 1) for _ in range(max(slice) + 1)]

        for img, sli, phs in zip(images, slice, phase):
            if ismrmrd.Meta.deserialize(
                    img.attribute_string)['FlowDirDisplay'] == venc_dir:
                data[:, :, sli, phs] = img.data
                head[sli][phs] = img.getHead()
                meta[sli][phs] = ismrmrd.Meta.deserialize(img.attribute_string)

        logging.debug("Phase data with venc encoding %s is size %s" % (
            venc_dir,
            data.shape,
        ))
        np.save(debugFolder + "/" + "data_" + venc_dir + ".npy", data)

        # Mask out data with high mean temporal diff
        threshold = 250
        data_meandiff = np.mean(np.abs(np.diff(data, 3)), 3)
        data_masked = data
        data_masked[(data_meandiff > threshold)] = 2048
        np.save(debugFolder + "/" + "data_" + venc_dir + ".npy", data_masked)

        # Normalize and convert to int16
        data_masked = (data_masked.astype(np.float64) - 2048) * 32767 / 2048
        data_masked = np.around(data_masked).astype(np.int16)

        # Re-slice back into 2D images
        for sli in range(data_masked.shape[2]):
            for phs in range(data_masked.shape[3]):
                # Create new MRD instance for the processed image
                tmpImg = ismrmrd.Image.from_array(data_masked[..., sli,
                                                              phs].transpose())

                # Set the header information
                tmpHead = head[sli][phs]
                tmpHead.data_type = tmpImg.getHead().data_type
                tmpHead.image_index = phs + sli * data_masked.shape[3]
                tmpHead.image_series_index = last_series
                tmpImg.setHead(tmpHead)

                # Set ISMRMRD Meta Attributes
                tmpMeta = meta[sli][phs]
                tmpMeta['DataRole'] = 'Image'
                tmpMeta['ImageProcessingHistory'] = ['FIRE', 'PYTHON']
                tmpMeta['WindowCenter'] = '16384'
                tmpMeta['WindowWidth'] = '32768'

                xml = tmpMeta.serialize()
                logging.debug("Image MetaAttributes: %s", xml)
                tmpImg.attribute_string = xml
                imagesOut.append(tmpImg)

        last_series += 1
    return imagesOut
def process_image(images, config, metadata):
    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Incoming image data of type %s",
                  ismrmrd.get_dtype_from_data_type(images[0].data_type))

    # Display MetaAttributes for first image
    meta = ismrmrd.Meta.deserialize(images[0].attribute_string)
    logging.debug("MetaAttributes: %s", ismrmrd.Meta.serialize(meta))

    # Optional serialization of ICE MiniHeader
    if 'IceMiniHead' in meta:
        logging.debug("IceMiniHead: %s",
                      base64.b64decode(meta['IceMiniHead']).decode('utf-8'))

    # Extract image data into a 5D array of size [img cha z y x]
    data = np.stack([img.data for img in images])
    head = [img.getHead() for img in images]

    logging.debug("Original image data is size %s" % (data.shape, ))
    np.save(debugFolder + "/" + "imgOrig.npy", data)

    # Normalize and convert to int16
    data = data.astype(np.float64)
    data *= 32767 / data.max()
    data = np.around(data)
    data = data.astype(np.int16)

    # Invert image contrast
    data = 32767 - data
    data = np.abs(data)
    data = data.astype(np.int16)
    np.save(debugFolder + "/" + "imgInverted.npy", data)

    # Re-slice back into 2D images
    imagesOut = [None] * data.shape[0]
    for iImg in range(data.shape[0]):
        # Create new MRD instance for the inverted image
        imagesOut[iImg] = ismrmrd.Image.from_array(data[iImg, ...].transpose())
        data_type = imagesOut[iImg].data_type

        # Copy the fixed header information
        oldHeader = head[iImg]
        oldHeader.data_type = data_type

        imagesOut[iImg].setHead(oldHeader)

        # Set ISMRMRD Meta Attributes
        meta = ismrmrd.Meta({
            'DataRole': 'Image',
            'ImageProcessingHistory': ['FIRE', 'PYTHON'],
            'WindowCenter': '16384',
            'WindowWidth': '32768'
        })
        xml = meta.serialize()
        logging.debug("Image MetaAttributes: %s", xml)
        logging.debug("Image data has %d elements", imagesOut[iImg].data.size)

        imagesOut[iImg].attribute_string = xml

    return imagesOut
Ejemplo n.º 7
0
def process_image(images, connection, config, metadata):
    # Start timer
    tic = perf_counter()

    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Processing data with %d images of type %s", len(images),
                  ismrmrd.get_dtype_from_data_type(images[0].data_type))

    # Display MetaAttributes for first image
    tmpMeta = ismrmrd.Meta.deserialize(images[0].attribute_string)
    logging.debug("MetaAttributes[0]: %s", ismrmrd.Meta.serialize(tmpMeta))

    # Optional serialization of ICE MiniHeader
    if 'IceMiniHead' in tmpMeta:
        logging.debug("IceMiniHead[0]: %s",
                      base64.b64decode(tmpMeta['IceMiniHead']).decode('utf-8'))

    # Extract some indices for the images
    slice = [img.slice for img in images]
    phase = [img.phase for img in images]

    # Process each group of venc directions separately
    unique_venc_dir = np.unique([
        ismrmrd.Meta.deserialize(img.attribute_string)['FlowDirDisplay']
        for img in images
    ])

    # Measure processing time
    toc = perf_counter()
    strProcessTime = "Total processing time: %.2f ms" % ((toc - tic) * 1000.0)
    logging.info(strProcessTime)

    # Send this as a text message back to the client
    connection.send_logging(constants.MRD_LOGGING_INFO, strProcessTime)

    # Start the phase images at series 10.  When interpreted by FIRE, images
    # with the same image_series_index are kept in the same series, but the
    # absolute series number isn't used and can be arbitrary
    last_series = 10
    imagesOut = []
    for venc_dir in unique_venc_dir:
        # data array has dimensions [row col sli phs], i.e. [y x sli phs]
        # info lists has dimensions [sli phs]
        data = np.zeros((images[0].data.shape[2], images[0].data.shape[3],
                         max(slice) + 1, max(phase) + 1), images[0].data.dtype)
        head = [[None] * (max(phase) + 1) for _ in range(max(slice) + 1)]
        meta = [[None] * (max(phase) + 1) for _ in range(max(slice) + 1)]

        for img, sli, phs in zip(images, slice, phase):
            if ismrmrd.Meta.deserialize(
                    img.attribute_string)['FlowDirDisplay'] == venc_dir:
                # print("sli phs", sli, phs)
                data[:, :, sli, phs] = img.data
                head[sli][phs] = img.getHead()
                meta[sli][phs] = ismrmrd.Meta.deserialize(img.attribute_string)

        logging.debug("Phase data with venc encoding %s is size %s" % (
            venc_dir,
            data.shape,
        ))
        np.save(debugFolder + "/" + "data_" + venc_dir + ".npy", data)

        # Mask out data with high mean temporal diff
        threshold = 250
        data_meandiff = np.mean(np.abs(np.diff(data, 3)), 3)
        data_masked = data
        data_masked[(data_meandiff > threshold)] = 2048
        np.save(debugFolder + "/" + "data_masked_" + venc_dir + ".npy",
                data_masked)

        # Normalize and convert to int16
        data_masked = (data_masked.astype(np.float64) - 2048) * 32767 / 2048
        data_masked = np.around(data_masked).astype(np.int16)

        # Re-slice back into 2D images
        for sli in range(data_masked.shape[2]):
            for phs in range(data_masked.shape[3]):
                # Create new MRD instance for the processed image
                # NOTE: from_array() takes input data as [x y z coil], which is
                # different than the internal representation in the "data" field as
                # [coil z y x], so we need to transpose
                tmpImg = ismrmrd.Image.from_array(data_masked[..., sli,
                                                              phs].transpose())

                # Set the header information
                tmpHead = head[sli][phs]
                tmpHead.data_type = tmpImg.getHead().data_type
                tmpHead.image_index = phs + sli * data_masked.shape[3]
                tmpHead.image_series_index = last_series
                tmpImg.setHead(tmpHead)

                # Set ISMRMRD Meta Attributes
                tmpMeta = meta[sli][phs]
                tmpMeta['DataRole'] = 'Image'
                tmpMeta['ImageProcessingHistory'] = ['FIRE', 'PYTHON']
                tmpMeta['WindowCenter'] = '16384'
                tmpMeta['WindowWidth'] = '32768'
                tmpMeta['Keep_image_geometry'] = 1

                # Add image orientation directions to MetaAttributes if not already present
                if tmpMeta.get('ImageRowDir') is None:
                    tmpMeta['ImageRowDir'] = [
                        "{:.18f}".format(tmpHead.read_dir[0]),
                        "{:.18f}".format(tmpHead.read_dir[1]),
                        "{:.18f}".format(tmpHead.read_dir[2])
                    ]

                if tmpMeta.get('ImageColumnDir') is None:
                    tmpMeta['ImageColumnDir'] = [
                        "{:.18f}".format(tmpHead.phase_dir[0]),
                        "{:.18f}".format(tmpHead.phase_dir[1]),
                        "{:.18f}".format(tmpHead.phase_dir[2])
                    ]

                xml = tmpMeta.serialize()
                logging.debug("Image MetaAttributes: %s", xml)
                tmpImg.attribute_string = xml
                imagesOut.append(tmpImg)

        last_series += 1
    return imagesOut
Ejemplo n.º 8
0
def process_image(images, config, metadata):
    # Create folder, if necessary
    if not os.path.exists(debugFolder):
        os.makedirs(debugFolder)
        logging.debug("Created folder " + debugFolder +
                      " for debug output files")

    logging.debug("Processing data with %d images of type %s", len(images),
                  ismrmrd.get_dtype_from_data_type(images[0].data_type))

    # Extract image data into a 5D array of size [img cha z y x]
    data = np.stack([img.data for img in images])
    head = [img.getHead() for img in images]
    meta = [ismrmrd.Meta.deserialize(img.attribute_string) for img in images]

    # Reformat data to the more intuitive [x y z cha img]
    data = data.transpose()

    # Reformat data again to [y x z cha img], i.e. [row col] for the first two
    # dimensions.  Note we will need to undo this later prior to sending back
    # to the client
    data = data.transpose((1, 0, 2, 3, 4))

    # Display MetaAttributes for first image
    logging.debug("MetaAttributes[0]: %s", ismrmrd.Meta.serialize(meta[0]))

    # Optional serialization of ICE MiniHeader
    if 'IceMiniHead' in meta[0]:
        logging.debug("IceMiniHead[0]: %s",
                      base64.b64decode(meta[0]['IceMiniHead']).decode('utf-8'))

    logging.debug("Original image data is size %s" % (data.shape, ))
    np.save(debugFolder + "/" + "imgOrig.npy", data)

    if data.shape[3] != 1:
        logging.error("Multi-channel data is not supported")
        return []

    # Normalize to (0.0, 1.0) as expected by get_cmap()
    data = data.astype(float)
    data -= data.min()
    data *= 1 / data.max()

    # Apply colormap
    cmap = plt.get_cmap('jet')
    rgb = cmap(data)

    # Remove alpha channel
    # Resulting shape is [row col z rgb img]
    rgb = rgb[..., 0:-1]
    rgb = rgb.transpose((0, 1, 2, 5, 4, 3))
    rgb = np.squeeze(rgb, 5)

    # MRD RGB images must be uint16 in range (0, 255)
    rgb *= 255
    data = rgb.astype(np.uint16)
    np.save(debugFolder + "/" + "imgRGB.npy", data)

    # Reformat data from [row col z cha img] back to [x y z cha img] before sending back to client
    data = data.transpose((1, 0, 2, 3, 4))

    currentSeries = 0

    # Re-slice back into 2D images
    imagesOut = [None] * data.shape[-1]
    for iImg in range(data.shape[-1]):
        # Create new MRD instance for the new image
        # NOTE: from_array() takes input data as [x y z coil], which is
        # different than the internal representation in the "data" field as
        # [coil z y x].  However, we already transposed this data when
        # extracting it earlier.
        imagesOut[iImg] = ismrmrd.Image.from_array(data[..., iImg])
        data_type = imagesOut[iImg].data_type

        # Create a copy of the original fixed header and update the data_type
        # (we changed it to int16 from all other types)
        oldHeader = head[iImg]
        oldHeader.data_type = data_type

        # Set RGB parameters
        oldHeader.image_type = 6  # To be defined as ismrmrd.IMTYPE_RGB
        oldHeader.channels = 3  # RGB "channels".  This is set by from_array, but need to be explicit as we're copying the old header instead

        # Increment series number when flag detected (i.e. follow ICE logic for splitting series)
        if mrdhelper.get_meta_value(meta[iImg], 'IceMiniHead') is not None:
            if mrdhelper.extract_minihead_bool_param(
                    base64.b64decode(
                        meta[iImg]['IceMiniHead']).decode('utf-8'),
                    'BIsSeriesEnd') is True:
                currentSeries += 1

        imagesOut[iImg].setHead(oldHeader)

        # Create a copy of the original ISMRMRD Meta attributes and update
        tmpMeta = meta[iImg]
        tmpMeta['DataRole'] = 'Image'
        tmpMeta['ImageProcessingHistory'] = ['PYTHON', 'RGB']
        tmpMeta['SequenceDescriptionAdditional'] = 'FIRE_RGB'
        tmpMeta['Keep_image_geometry'] = 1

        # Add image orientation directions to MetaAttributes if not already present
        if tmpMeta.get('ImageRowDir') is None:
            tmpMeta['ImageRowDir'] = [
                "{:.18f}".format(oldHeader.read_dir[0]),
                "{:.18f}".format(oldHeader.read_dir[1]),
                "{:.18f}".format(oldHeader.read_dir[2])
            ]

        if tmpMeta.get('ImageColumnDir') is None:
            tmpMeta['ImageColumnDir'] = [
                "{:.18f}".format(oldHeader.phase_dir[0]),
                "{:.18f}".format(oldHeader.phase_dir[1]),
                "{:.18f}".format(oldHeader.phase_dir[2])
            ]

        metaXml = tmpMeta.serialize()
        logging.debug("Image MetaAttributes: %s",
                      xml.dom.minidom.parseString(metaXml).toprettyxml())
        logging.debug("Image data has %d elements", imagesOut[iImg].data.size)

        imagesOut[iImg].attribute_string = metaXml

    return imagesOut