Skip to content

Commit

Permalink
Merge pull request #5 from kristofferknudsen/update-example-metadata
Browse files Browse the repository at this point in the history
Updated example reconstrutions to provide better image headers for the produced images.
  • Loading branch information
dchansen authored Sep 30, 2021
2 parents 88ce3c7 + 2868f52 commit 57dfba0
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 64 deletions.
28 changes: 17 additions & 11 deletions gadgetron/examples/recon_acquisitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import time
import logging
import ismrmrd
import itertools

import numpy as np

Expand All @@ -26,12 +27,11 @@ def scaling_factor(acq):
return np.sqrt(2 * acq.sample_time_us * noise_bandwidth / noise_dwell_time)

def calculate_whitening_transformation(noise):
noise = np.asmatrix(noise)
covariance = (1.0 / (noise.shape[1] - 1)) * (noise * noise.H)
covariance = (1.0 / (noise.shape[1] - 1)) * np.dot(noise, np.transpose(np.conjugate(noise)))
return np.linalg.inv(np.linalg.cholesky(covariance))

def apply_whitening_transformation(acq):
return np.asarray(scaling_factor(acq) * noise_matrix * np.asmatrix(acq.data))
return scaling_factor(acq) * np.dot(noise_matrix, acq.data)

def noise_adjust(acq):
if noise_matrix is not None:
Expand Down Expand Up @@ -83,19 +83,18 @@ def accumulate_acquisitions(acquisitions, header):
def assemble_buffer(acqs):
logging.debug(f"Assembling buffer from {len(acqs)} acquisitions.")

number_of_channels = acqs[0].data.shape[0]
number_of_samples = acqs[0].data.shape[1]
number_of_channels, number_of_samples = acqs[0].data.shape

buffer = np.zeros(
(number_of_channels,
number_of_samples,
matrix_size.z,
matrix_size.y,
matrix_size.z),
number_of_samples),
dtype=np.complex64
)

for acq in acqs:
buffer[:, :, acq.idx.kspace_encode_step_1, acq.idx.kspace_encode_step_2] = acq.data
buffer[:, acq.idx.kspace_encode_step_2, acq.idx.kspace_encode_step_1, :] = acq.data

return buffer

Expand All @@ -106,7 +105,10 @@ def assemble_buffer(acqs):
accumulated_acquisitions = []


def reconstruct_images(buffers):
def reconstruct_images(buffers, header):

indices = itertools.count(start=1)
field_of_view = header.encoding[0].reconSpace.fieldOfView_mm

def reconstruct_image(kspace_data):
# Reconstruction is an inverse fft in this case.
Expand All @@ -122,7 +124,11 @@ def combine_channels(image_data):
for reference, data in buffers:
yield ismrmrd.image.Image.from_array(
combine_channels(reconstruct_image(data)),
acquisition=reference
acquisition=reference,
image_index=next(indices),
image_type=ismrmrd.IMTYPE_MAGNITUDE,
field_of_view=(field_of_view.x, field_of_view.y, field_of_view.z),
transpose = False # Squelch deprecation warning; we are in line with future behaviour.
)


Expand All @@ -140,7 +146,7 @@ def recon_acquisitions(connection):
acquisitions = noise_adjustment(acquisitions, connection.header)
acquisitions = remove_oversampling(acquisitions, connection.header)
buffers = accumulate_acquisitions(acquisitions, connection.header)
images = reconstruct_images(buffers)
images = reconstruct_images(buffers, connection.header)

for image in images:
logging.debug("Sending image back to client.")
Expand Down
110 changes: 58 additions & 52 deletions gadgetron/examples/recon_buffers.py
Original file line number Diff line number Diff line change
@@ -1,93 +1,99 @@

import time
import logging
import numpy as np
import gadgetron

import ismrmrd
import time
from multimethod import multimethod
import gadgetron
import itertools
import multimethod

import numpy as np

def buffer_from_bucket(bucket, header):
matrix_size = header.encoding[0].encodedSpace.matrixSize
acqs = bucket.data

logging.debug(f"Assembling buffer from bucket containing {len(acqs)} acquisitions.")
def prepare_buffers(input, header):

number_of_channels = acqs[0].data.shape[0]
number_of_samples = acqs[0].data.shape[1]
buffer = np.zeros(
(number_of_samples, matrix_size.y, matrix_size.z, number_of_channels, 1),
dtype=np.complex64
)
def buffer_from_bucket(bucket):
matrix_size = header.encoding[0].encodedSpace.matrixSize
acquisitions = bucket.data

for acq in acqs:
buffer[:, acq.idx.kspace_encode_step_1, acq.idx.kspace_encode_step_2, :, 0] = acq.data.T
logging.debug(f"Assembling buffer from bucket containing {len(acquisitions)} acquisitions.")

return buffer, bucket.data[0].getHead()
channels, samples = acquisitions[0].data.shape
buffer = np.zeros(
(channels, matrix_size.z, matrix_size.y, samples),
dtype=np.complex64
)

for acq in acquisitions:
buffer[:, acq.idx.kspace_encode_step_2, acq.idx.kspace_encode_step_1, :] = acq.data

def reconstruct_image(kspace_data):
return gadgetron.util.cifftn(kspace_data, axes=[0, 1, 2])
return buffer, acquisitions[0]

def buffer_from_buffer(buffer):
return buffer.data.reshape(buffer.data.shape[:4]).transpose(), buffer.headers.item(0)

def combine_channels(image):
return np.sqrt(np.sum(np.abs(image) ** 2, axis=3))
@multimethod.multimethod
def prepare_buffers(bucket: gadgetron.types.AcquisitionBucket):
yield buffer_from_bucket(bucket)

@multimethod.multimethod
def prepare_buffers(recon_data: gadgetron.types.ReconData):
yield from (buffer_from_buffer(bit.data) for bit in recon_data)

def split_image_array(image_data):
image_5d_view = image_data.reshape(*image_data.shape[:4], -1)
return [image_5d_view[..., i] for i in range(image_5d_view.shape[4])]
for item in input:
yield from prepare_buffers(item)


def create_ismrmrd_image(image_data, reference_acquisition_header):
return ismrmrd.image.Image.from_array(image_data, acquisition=reference_acquisition_header)
def reconstruct_images(buffers, header):

indices = itertools.count(start=1)
field_of_view = header.encoding[0].reconSpace.fieldOfView_mm

def reconstruct_buffer(kspace_data, reference_header):
print(kspace_data.shape)
image_data = reconstruct_image(kspace_data)
image_data = combine_channels(image_data)
image_data_list = split_image_array(image_data)
images = [create_ismrmrd_image(img, reference_header) for img in image_data_list]
return images
def reconstruct_buffer(data):
return gadgetron.util.cifftn(data, axes=[1, 2, 3])

def flatten(lists):
return sum(lists,[])
def combine_channels(data):
return np.sqrt(np.sum(np.square(np.abs(data)), axis=0))

@multimethod
def reconstruct(buffers: gadgetron.types.ReconData, header):
return flatten([[image for image in reconstruct_buffer(buffer.data.data, buffer.data.headers.flat[0])] for buffer in
buffers])
def create_ismrmrd_image(data, reference):
return ismrmrd.image.Image.from_array(
data,
acquisition=reference,
image_index=next(indices),
image_type=ismrmrd.IMTYPE_MAGNITUDE,
field_of_view=(field_of_view.x, field_of_view.y, field_of_view.z),
transpose=False
)

def build_image(buffer, reference):

@multimethod
def reconstruct(bucket: gadgetron.types.AcquisitionBucket, header):
kspace_data, acq_header = buffer_from_bucket(bucket, header)
return reconstruct_buffer(kspace_data, acq_header)
buffer = reconstruct_buffer(buffer)
buffer = combine_channels(buffer)

return create_ismrmrd_image(buffer, reference)

def send_images(connection, images):
for img in images:
print(img.getHead())
connection.send(img)
for buffer, reference in buffers:
yield build_image(buffer, reference)


def recon_buffers(connection):
logging.info("Python reconstruction running - reconstructing images from acquisition buffers.")

start = time.time()
input = iter(connection)
buffers = prepare_buffers(input, connection.header)
images = reconstruct_images(buffers, connection.header)

start = time.time()

for data in connection:
images = reconstruct(data, connection.header)
send_images(connection, images)
for image in images:
connection.send(image)

logging.info(f"Python reconstruction done. Duration: {(time.time() - start):.2f} s")


#######################################################################################################################
# This allows the file to be run directly as a server


if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
gadgetron.external.listen(9100, recon_buffers)
2 changes: 1 addition & 1 deletion gadgetron/version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

major = 1
minor = 3
build = 5
build = 6

version = f"{major}.{minor}.{build}"

0 comments on commit 57dfba0

Please sign in to comment.