-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from kristofferknudsen/update-example-metadata
Updated example reconstrutions to provide better image headers for the produced images.
- Loading branch information
Showing
3 changed files
with
76 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}" |