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
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
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
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
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
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