diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..657b0bbab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +src/xDynamicSimulation/cDynamicSimulation/Testing/tests_dynamicsimulation.h merge=ours +src/xDynamicSimulation/cDynamicSimulation/Testing/tests_dynamicsimulation.cpp merge=ours +src/xGadgetron/cGadgetron/gadgetron_data_containers.h merge=ours +src/xGadgetron/cGadgetron/gadgetron_data_containers.cpp merge=ours diff --git a/.gitignore b/.gitignore index 19cd698f2..f078ee9e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. +# CMake +*.cmake +*.make +*.include_cache +*.internal # User-specific files *.suo *.user @@ -20,6 +25,14 @@ bld/ [Bb]in/ [Oo]bj/ +# CMake +CMakeCache.txt +CMakeFiles* + +# Makefiles +**/Makefile* + + # Roslyn cache directories *.ide/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9348e8081..aa962405d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,7 @@ else() endif() option(SIRF_INSTALL_DEPENDENCIES "Install dlls etc" ${DEFAULT_SIRF_INSTALL_DEPENDENCIES}) ####### CMake path -set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +set (CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}/cmake") # If OSX give the advanced option to use absolute paths for shared libraries if (APPLE) diff --git a/README.md b/README.md index 2fc02816c..7b3418f04 100644 --- a/README.md +++ b/README.md @@ -43,4 +43,4 @@ what was found/built: [style-badge]: https://api.codacy.com/project/badge/Grade/392861b4085f4f438d12c41029f86b47 [style-link]: https://www.codacy.com/gh/SyneRBI/SIRF?utm_source=github.com&utm_medium=referral&utm_content=SyneRBI/SIRF&utm_campaign=Badge_Grade [zenodo-badge]: https://zenodo.org/badge/DOI/10.5281/zenodo.2707911.svg -[zenodo-link]: https://doi.org/10.5281/zenodo.2707911 +[zenodo-link]: https://doi.org/10.5281/zenodo.2707911 \ No newline at end of file diff --git a/cmake/setup.py.cmake b/cmake/setup.py.cmake index 7689bef4f..4c0857510 100644 --- a/cmake/setup.py.cmake +++ b/cmake/setup.py.cmake @@ -22,6 +22,7 @@ if(BUILD_PYTHON) python_pkg_alias(pyiutilities "sirf.pyiutilities") python_pkg_alias(pReg "sirf.Reg") python_pkg_alias(pyreg "sirf.pyreg") + python_pkg_alias(pysimulation "sirf.pysimulation") # convert to python CSV tuple for setup.py configure_file string(REPLACE ";" "', '" PYTHON_SETUP_PKGS_CSV "${PYTHON_SETUP_PKGS}") set(PYTHON_SETUP_PKGS_CSV "'${PYTHON_SETUP_PKGS_CSV}'") @@ -42,8 +43,13 @@ if(BUILD_PYTHON) if(PYTHONINTERP_FOUND) # python setup.py install if("${PYTHON_STRATEGY}" STREQUAL "SETUP_PY") + message("intalling setup.py") install(CODE "execute_process(COMMAND\n\ +<<<<<<< HEAD + \"${PYTHON_EXECUTABLE}\" setup.py install --prefix ${PYTHON_DEST}\n\ +======= \"${Python_EXECUTABLE}\" setup.py build install\n\ +>>>>>>> rpe-encoding WORKING_DIRECTORY \"${PYTHON_DEST}\")") endif() endif(PYTHONINTERP_FOUND) diff --git a/examples/Python/Simulation/prepare_EPG_input.py b/examples/Python/Simulation/prepare_EPG_input.py new file mode 100644 index 000000000..529ce716f --- /dev/null +++ b/examples/Python/Simulation/prepare_EPG_input.py @@ -0,0 +1,26 @@ +import sys +sys.path.append("/home/sirfuser/devel/buildVM/sources/SIRF/src/xDynamicSimulation/pDynamicSimulation/") + +import numpy as np + +import TissueParameterList as TPL + +root_path = '/media/sf_CCPPETMR/TestData/Input/xDynamicSimulation/pDynamicSimulation/' +xml_path = root_path + 'Cube128/XCAT_TissueParameters_XML.xml' +fpath_output = root_path + 'Fingerprints/XCAT_tissue_parameter_list.npz' + + +# parse file +tpl = TPL.TissueParameterList() +tpl.parse_xml(xml_path) +tpl.print_contents() + +# prepare EPG input array +all_mr_params = tpl.mr_as_array() +mr_params = all_mr_params[:,1:] +mr_params[:,0] /= 100 +mr_params[:,-1] = 0 + +mr_params_unique,idx_inverse = np.unique(mr_params, axis=0, return_inverse=True) + +np.savez(fpath_output, mr_params_full=all_mr_params, mr_parameters=mr_params_unique, unique_idx_inverse=idx_inverse) diff --git a/examples/Python/Simulation/preprocess_speedup_testdata.py b/examples/Python/Simulation/preprocess_speedup_testdata.py new file mode 100644 index 000000000..805ed7527 --- /dev/null +++ b/examples/Python/Simulation/preprocess_speedup_testdata.py @@ -0,0 +1,51 @@ + +import numpy as np +from pathlib import Path +import nibabel as nib + + + +num_slices = 10 +Nz = 128 +slab_start = Nz//2 -num_slices//2 +slab_end = Nz//2 + num_slices//2 + + +def preprocess_mvfs(rootpath, folder_pattern, prefix_output): + + fpath_input = Path(rootpath + folder_pattern) + print("looking in {} ".format(fpath_input)) + list_files = sorted(fpath_input.glob('mvf_*')) + + for f in list_files: + print("loading {}".format(f)) + + mvf = nib.load(str(f)) + print("The input motoinfield has size {}".format(mvf.shape)) + + mvf = mvf.slicer[:,:,slab_start:slab_end,:] + print("The output motoinfield has size {}".format(mvf.shape)) + + fname_output = str(prefix_output + folder_pattern + f.name) + print("Storing to {}".format(fname_output)) + nib.save(mvf, fname_output) + +root_path = '/media/sf_CCPPETMR/TestData/Input/xDynamicSimulation/pDynamicSimulation/' + +fpath_input = root_path + 'Cube128/' +fpath_output = root_path + 'Slab128/' + +foldername_resp = 'mvf_resp/' +foldername_card = 'mvf_card/' + +preprocess_mvfs(fpath_input, foldername_resp, fpath_output) +preprocess_mvfs(fpath_input, foldername_card, fpath_output) + + +fname_segmentation = fpath_input + 'label_volume.nii' +seg = nib.load(fname_segmentation) + +print("The input segmentation has size {}".format(seg.shape)) + +seg = seg.slicer[:,:,slab_start:slab_end] +nib.save(seg, fpath_output + 'label_volume.nii') \ No newline at end of file diff --git a/examples/Python/Simulation/simulate_3D_RPE.py b/examples/Python/Simulation/simulate_3D_RPE.py new file mode 100644 index 000000000..8eb6cd778 --- /dev/null +++ b/examples/Python/Simulation/simulate_3D_RPE.py @@ -0,0 +1,185 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +from pathlib import Path +import numpy as np +import time + + +def read_motionfields(fpath_prefix): + p = sorted( Path(fpath_prefix).glob('*.nii') ) + files = [x for x in p if x.is_file()] + + temp = [] + for f in files: + print("Reading from {} ... ".format(f)) + img = pReg.NiftiImageData3DDisplacement(str(f)) + temp.append(img) + + data = np.array(temp, dtype=object) + return data + +def set_motionfields_from_path(modyn, fpath_prefix): + + assert_validity(modyn, pDS.MRMotionDynamic) + mvfs = read_motionfields(fpath_prefix) + + for m in mvfs: + modyn.add_displacement_field(m) + + +def get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz): + + t_s = np.linspace(t0_s, tmax_s, Nt) + sig = 0.5 * (1 + np.sin( 2*np.pi*f_Hz*t_s)) + return t_s, sig + + +def main(): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Cube128/XCAT_TissueParameters_XML.xml' + + fpath_template_contrast_rawdata = input_fpath_prefix + 'Cube128/CV_nav_cart_128Cube_FLASH_T1_defaultorient.h5' + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID00241_FID69145_Tho_T1_fast_ismrmrd_defaultorient.h5' + + acquisition_ad = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + acquisition_ad = pMR.set_grpe_trajectory(acquisition_ad) + + # configure the simulation + contrast_ad = pMR.AcquisitionData(fpath_template_contrast_rawdata) + contrast_ad = pMR.preprocess_acquisition_data(contrast_ad) + + labels = pReg.NiftiImageData3D( input_fpath_prefix + "Cube128/label_volume_rai.nii" ) + mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + mrsim.set_contrast_template_data(contrast_ad) + mrsim.set_acquisition_template_data(acquisition_ad) + + offset_z_mm = 0 + translation = np.array([0, 0, offset_z_mm]) + euler_angles_deg = np.array([0,0,0]) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + # take CSM from the rawdata itself + # could be replaced if independent way of computing CSM is available + csm = pMR.CoilSensitivityData() + csm.calculate(acquisition_ad) + mrsim.set_csm(csm) + + # set which tissue defines SNR + SNR = 10 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + # configure the surrogates + Nt = 10000 + t0_s = 0 + tmax_s = 60* 5 + + + f_Hz_resp = 0.2 + + t_resp, sig_resp = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_resp) + + + # configure the motion + num_motion_states = 4 + # RESP + num_sim_resp_states = num_motion_states + # resp_motion = pDS.MRMotionDynamic( num_sim_resp_states ) + # resp_motion.set_dynamic_signal(t_resp, sig_resp) + # resp_motion.set_cyclicality(False) + # resp_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_3D_motiondata_r_{}_gt_resp".format(num_sim_resp_states)) + # set_motionfields_from_path(resp_motion, input_fpath_prefix + 'Cube128/mvf_resp/') + # mrsim.add_motion_dynamic(resp_motion) + + # + fname_simulation_output = "output_simulation_3DRPE_motiondata_traj_r{}".format(num_sim_resp_states) + + fname_output = output_fpath_prefix + fname_simulation_output + ".h5" + simulated_file = Path(fname_output) + if not simulated_file.is_file(): + + tstart = time.time() + mrsim.simulate_data() + print("--- Required {} minutes for the simulation.".format( (time.time()-tstart)/60)) + mrsim.write_simulation_results(str(simulated_file)) + else: + print("Skipping simulation since output file already exists.") + + # mrsim.save_motion_ground_truth() + + simulated_data = pMR.AcquisitionData(str(simulated_file)) + + csm.calculate(simulated_data) + + AM = pMR.AcquisitionModel() + AM.set_coil_sensitivity_maps(csm) + AM.set_up(simulated_data, csm) + + recon_img = AM.inverse(simulated_data) + recon_nii = pReg.NiftiImageData3D(recon_img) + recon_nii = recon_nii.abs() + + fname_output = output_fpath_prefix + "recon_" + fname_simulation_output + ".nii" + recon_nii.write(fname_output) + + return 1 + +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/examples/Python/Simulation/simulation_2D_externalcontrast.py b/examples/Python/Simulation/simulation_2D_externalcontrast.py new file mode 100644 index 000000000..c453aaa0b --- /dev/null +++ b/examples/Python/Simulation/simulation_2D_externalcontrast.py @@ -0,0 +1,340 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +import matplotlib.pyplot as plt +from pathlib import Path +import numpy as np +import time +import random + +def create_dummy_mrf_signal(num_tissue_types,num_time_points): + # + labels = np.array([i for i in range(num_tissue_types)]) + time_curve = np.sin(np.array([t for t in range(num_time_points)]) * np.pi / num_time_points) \ + +1j * np.cos(np.array([t for t in range(num_time_points)]) * 2 * np.pi / num_time_points) + + mrf_signal = labels[:,np.newaxis] * time_curve[np.newaxis,:] / num_tissue_types / np.sqrt(2) + + return pDS.ExternalMRSignal(labels, mrf_signal) + +def read_motionfields(fpath_prefix): + p = sorted( Path(fpath_prefix).glob('*.nii') ) + files = [x for x in p if x.is_file()] + + temp = [] + for f in files: + print("Reading from {} ... ".format(f)) + img = pReg.NiftiImageData3DDisplacement(str(f)) + temp.append(img) + + data = np.array(temp, dtype=object) + return data + +def set_motionfields_from_path(modyn, fpath_prefix): + + assert_validity(modyn, pDS.MRMotionDynamic) + mvfs = read_motionfields(fpath_prefix) + + for m in mvfs: + modyn.add_displacement_field(m) + + +def get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz): + + t_s = np.linspace(t0_s, tmax_s, Nt) + sig = 0.5 * (1 + np.sin( 2*np.pi*f_Hz*t_s)) + return t_s, sig + + +def static_MR_fingerprinting(): + + print(" --- Running static MRF simulation.\n") + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + prefix_fpath_input = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + + fpath_output = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + fname_output = fpath_output + 'simulated_static_MR_fingerprinting.h5' + + fname_xml = prefix_fpath_input + 'Slab128/XCAT_TissueParameters_XML.xml' + fname_template_contrast_rawdata = prefix_fpath_input + 'Slab128/CV_nav_cart_128Slab_FLASH_T1_defaultorientation.h5' + fname_template_acquisition_rawdata = prefix_fpath_input + 'General/meas_MID27_CV_11s_TI2153_a6_2x2x8_TR45_FID33312_defaultorientation.h5' + + ## + labels = pReg.NiftiImageData3D( prefix_fpath_input + "Slab128/label_volume_rai.nii") + mrsim = pDS.MRDynamicSimulation(labels, fname_xml) + + print(" --- Loading the template raw data.\n") + + contrast_template = pMR.AcquisitionData(fname_template_contrast_rawdata) + contrast_template = pMR.preprocess_acquisition_data(contrast_template) + + acquisition_template = pMR.AcquisitionData(fname_template_acquisition_rawdata) + acquisition_template = pMR.set_radial2D_trajectory(acquisition_template) + + + # First compute the coil profiles from the rawdata itself. + csm = pMR.CoilSensitivityData() + csm.calculate(acquisition_template) + mrsim.set_csm(csm) + + # + fname_mrf_labels = prefix_fpath_input + 'Fingerprints/XCAT_tissue_parameter_list.npz' + fname_mrf_signal = prefix_fpath_input + 'Fingerprints/XCAT_tissue_parameter_fingerprints.npy' + + mrf_label_input = np.load(fname_mrf_labels) + mrf_labels = mrf_label_input['mr_params_full'][:,0] + unique_inv_idx = mrf_label_input['unique_idx_inverse'] + + epg_simulation = np.array(np.load(fname_mrf_signal), dtype=np.complex64) + + num_lines_kept = 120 + epg_simulation = epg_simulation[:num_lines_kept,:] + + epg_simulation = np.transpose(epg_simulation[:,unique_inv_idx]) + + mrf_signal = pDS.ExternalMRSignal(mrf_labels, epg_simulation) + mrf_dynamic = pDS.ExternalMRContrastDynamic() + mrf_dynamic.add_external_signal(mrf_signal) + mrsim.add_external_contrast_dynamic(mrf_dynamic) + + print(" --- Extracting subset for template acquisition data.\n") + num_acquisitions = epg_simulation.shape[1] + list_acquisitions_to_keep = np.arange(0,num_acquisitions) + acquisition_template = acquisition_template.get_subset(list_acquisitions_to_keep) + + # + mrsim.set_contrast_template_data(contrast_template) + mrsim.set_acquisition_template_data(acquisition_template) + + + offset_centre_mm = 0 + offset_z_mm = 0 + offset_mm = [offset_centre_mm, offset_centre_mm, offset_z_mm] + + translation = np.array(offset_mm) + euler_angles_deg = np.array([0,0,0]) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + mrsim.save_parametermap_ground_truth(fpath_output + "static_fingerprinting_parametermap") + + # set which tissue defines SNR + SNR = 10 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + fname_simulation_output = "simulation_static_MRF" + simulated_file = Path(fpath_output, fname_simulation_output).with_suffix('.h5') + if not simulated_file.is_file(): + print(" --- Simulation of MRF data \n") + tstart = time.time() + mrsim.simulate_data() + print("--- Required {} minutes for the simulation.".format( (time.time()-tstart)/60)) + mrsim.write_simulation_results(str(simulated_file)) + else: + print("Skipping simulation since output file already exists.") + + simulated_data = pMR.AcquisitionData(str(simulated_file)) + csm.calculate(simulated_data) + + AM = pMR.AcquisitionModel() + AM.set_coil_sensitivity_maps(csm) + AM.set_up(simulated_data, csm) + + recon_img = AM.inverse(simulated_data) + recon_nii = pReg.NiftiImageData3D(recon_img) + recon_nii = recon_nii.abs() + + fname_output = fpath_output + "recon_" + fname_simulation_output + ".nii" + recon_nii.write(fname_output) + + return 1 + +def motion_MR_fingerprinting(): + + # fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + # input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + # output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + # fpath_xml = input_fpath_prefix + 'Slab128/XCAT_TissueParameters_XML.xml' + # fpath_template_contrast_rawdata = input_fpath_prefix + 'Slab128/CV_nav_cart_128Slab_FLASH_T1.h5' + + # trajectory_type = 'radial2D' + + # if trajectory_type == 'cartesian': + # fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID29_cart_ref_image_FID78804_ismrmrd.h5' + # else: + # fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID30_rad_2d_uniform_FID78805_ismrmrd.h5' + + # acquisition_ad = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + + # if trajectory_type == 'cartesian': + # acquisition_ad = pMR.preprocess_acquisition_data(acquisition_ad) + # elif trajectory_type == 'radial2D': + # acquisition_ad = pMR.set_radial2D_trajectory(acquisition_ad) + # elif trajectory_type == 'goldenangle2D': + # acquisition_ad = pMR.set_goldenangle2D_trajectory(acquisition_ad) + # else: + # raise ValueError("The trajectory you gave is {}. The only options are cartesian, radial2D or goldenangle2D".format(trajectory_type)) + + # # configure the simulation + # contrast_ad = pMR.AcquisitionData(fpath_template_contrast_rawdata) + # contrast_ad = pMR.preprocess_acquisition_data(contrast_ad) + + # labels = pReg.NiftiImageData3D( input_fpath_prefix + "Slab128/label_volume_ras.nii" ) + # mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + # mrsim.set_contrast_template_data(contrast_ad) + # mrsim.set_acquisition_template_data(acquisition_ad) + + # offset_z_mm = -5 + # translation = np.array([0, 0, offset_z_mm]) + # euler_angles_deg = np.array([0,0,0]) + + # offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + # mrsim.set_offset_trafo(offset_trafo) + + # # take CSM from the rawdata itself + # # could be replaced if independent way of computing CSM is available + # csm = pMR.CoilSensitivityData() + # csm.calculate(acquisition_ad) + # mrsim.set_csm(csm) + + # # set which tissue defines SNR + # SNR = 10 + # SNR_label = 13 + + # mrsim.set_snr(SNR) + # mrsim.set_snr_label(SNR_label) + + # # configure the surrogates + # Nt = 10000 + # t0_s = 0 + # tmax_s = 60* 5 + + # f_Hz_card = 1 + # f_Hz_resp = 0.2 + + # t_resp, sig_resp = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_resp) + # t_card, sig_card = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_card) + + # # configure the motion + # num_motion_states = 2 + # # RESP + # num_sim_resp_states = num_motion_states + # resp_motion = pDS.MRMotionDynamic( num_sim_resp_states ) + # resp_motion.set_dynamic_signal(t_resp, sig_resp) + # resp_motion.set_cyclicality(False) + # resp_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_motiondata_r_{}_gt_resp".format(num_sim_resp_states)) + # set_motionfields_from_path(resp_motion, input_fpath_prefix + 'Slab128/mvf_resp/') + # mrsim.add_motion_dynamic(resp_motion) + + # # external signal + # num_max_labels = 100 + # external_signal = create_dummy_mrf_signal(num_max_labels, acquisition_ad.number()) + + # external_contrast = pDS.ExternalMRContrastDynamic() + # external_contrast.add_external_signal(external_signal) + + # mrsim.add_external_contrast_dynamic(external_contrast) + + + # # CARD + # num_sim_card_states = num_motion_states + + # card_motion = pDS.MRMotionDynamic(num_sim_card_states) + # card_motion.set_dynamic_signal(t_card, sig_card) + # card_motion.set_cyclicality(True) + # card_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_externalcontrast_c_{}_gt_card".format(num_sim_card_states)) + # set_motionfields_from_path(card_motion, input_fpath_prefix + 'Slab128/mvf_card/') + # mrsim.add_motion_dynamic(card_motion) + + # # + # fname_simulation_output = "output_simulation_2D_externalcontrast_traj_{}_r{}_c{}".format(trajectory_type, num_sim_resp_states,num_sim_card_states) + + # fname_output = output_fpath_prefix + fname_simulation_output + ".h5" + # simulated_file = Path(fname_output) + # if not simulated_file.is_file(): + + # tstart = time.time() + # mrsim.simulate_data() + # print("--- Required {} minutes for the simulation.".format( (time.time()-tstart)/60)) + # mrsim.write_simulation_results(str(simulated_file)) + # else: + # print("Skipping simulation since output file already exists.") + + # mrsim.save_motion_ground_truth() + + # simulated_data = pMR.AcquisitionData(str(simulated_file)) + + # csm.calculate(simulated_data) + + # AM = pMR.AcquisitionModel() + # AM.set_coil_sensitivity_maps(csm) + # AM.set_up(simulated_data, csm) + + # recon_img = AM.inverse(simulated_data) + # recon_nii = pReg.NiftiImageData3D(recon_img) + # recon_nii = recon_nii.abs() + + # fname_output = output_fpath_prefix + "recon_" + fname_simulation_output + ".nii" + # recon_nii.write(fname_output) + + return 1 + +def main(): + static_MR_fingerprinting() + # motion_MR_fingerprinting() +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/examples/Python/Simulation/simulation_2D_motiondata.py b/examples/Python/Simulation/simulation_2D_motiondata.py new file mode 100644 index 000000000..f2a605b2a --- /dev/null +++ b/examples/Python/Simulation/simulation_2D_motiondata.py @@ -0,0 +1,208 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +from pathlib import Path +import numpy as np +import time + + +def read_motionfields(fpath_prefix): + p = sorted( Path(fpath_prefix).glob('*.nii') ) + files = [x for x in p if x.is_file()] + + temp = [] + for f in files: + print("Reading from {} ... ".format(f)) + img = pReg.NiftiImageData3DDisplacement(str(f)) + temp.append(img) + + data = np.array(temp, dtype=object) + return data + +def set_motionfields_from_path(modyn, fpath_prefix): + + assert_validity(modyn, pDS.MRMotionDynamic) + mvfs = read_motionfields(fpath_prefix) + + for m in mvfs: + modyn.add_displacement_field(m) + + +def get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz): + + t_s = np.linspace(t0_s, tmax_s, Nt) + sig = 0.5 * (1 + np.sin( 2*np.pi*f_Hz*t_s)) + return t_s, sig + + +def main(): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Cube128/XCAT_TissueParameters_XML.xml' + fpath_template_contrast_rawdata = input_fpath_prefix + 'Cube128/CV_nav_cart_128Cube_FLASH_T1.h5' + + trajectory_type = 'radial2D' + + if trajectory_type == 'cartesian': + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID29_cart_ref_image_FID78804_ismrmrd.h5' + else: + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID30_rad_2d_uniform_FID78805_ismrmrd.h5' + + acquisition_ad = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + + if trajectory_type == 'cartesian': + acquisition_ad = pMR.preprocess_acquisition_data(acquisition_ad) + elif trajectory_type == 'radial2D': + acquisition_ad = pMR.set_radial2D_trajectory(acquisition_ad) + elif trajectory_type == 'goldenangle2D': + acquisition_ad = pMR.set_goldenangle2D_trajectory(acquisition_ad) + else: + raise ValueError("The trajectory you gave is {}. The only options are cartesian, radial2D or goldenangle2D".format(trajectory_type)) + + # configure the simulation + contrast_ad = pMR.AcquisitionData(fpath_template_contrast_rawdata) + contrast_ad = pMR.preprocess_acquisition_data(contrast_ad) + + labels = pReg.NiftiImageData3D( input_fpath_prefix + "Cube128/label_volume.nii" ) + mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + mrsim.set_contrast_template_data(contrast_ad) + mrsim.set_acquisition_template_data(acquisition_ad) + + offset_z_mm = -128 + translation = np.array([0, 0, offset_z_mm]) + euler_angles_deg = np.array([15,15,0]) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + # take CSM from the rawdata itself + # could be replaced if independent way of computing CSM is available + csm = pMR.CoilSensitivityData() + csm.calculate(acquisition_ad) + mrsim.set_csm(csm) + + # set which tissue defines SNR + SNR = 10 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + # configure the surrogates + Nt = 10000 + t0_s = 0 + tmax_s = 60* 5 + + f_Hz_card = 1 + f_Hz_resp = 0.2 + + t_resp, sig_resp = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_resp) + t_card, sig_card = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_card) + + # configure the motion + num_motion_states = 4 + # RESP + num_sim_resp_states = num_motion_states + resp_motion = pDS.MRMotionDynamic( num_sim_resp_states ) + resp_motion.set_dynamic_signal(t_resp, sig_resp) + resp_motion.set_cyclicality(False) + resp_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_motiondata_r_{}_gt_resp".format(num_sim_resp_states)) + set_motionfields_from_path(resp_motion, input_fpath_prefix + 'Cube128/mvf_resp/') + mrsim.add_motion_dynamic(resp_motion) + + # CARD + num_sim_card_states = num_motion_states + + card_motion = pDS.MRMotionDynamic(num_sim_card_states) + card_motion.set_dynamic_signal(t_card, sig_card) + card_motion.set_cyclicality(True) + card_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_motiondata_c_{}_gt_card".format(num_sim_card_states)) + set_motionfields_from_path(card_motion, input_fpath_prefix + 'Cube128/mvf_card/') + mrsim.add_motion_dynamic(card_motion) + + # + fname_simulation_output = "output_simulation_2D_motiondata_traj_{}_r{}_c{}".format(trajectory_type, num_sim_resp_states,num_sim_card_states) + + fname_output = output_fpath_prefix + fname_simulation_output + ".h5" + simulated_file = Path(fname_output) + if not simulated_file.is_file(): + + tstart = time.time() + mrsim.simulate_data() + print("--- Required {} minutes for the simulation.".format( (time.time()-tstart)/60)) + mrsim.write_simulation_results(str(simulated_file)) + else: + print("Skipping simulation since output file already exists.") + + mrsim.save_motion_ground_truth() + + simulated_data = pMR.AcquisitionData(str(simulated_file)) + + csm.calculate(simulated_data) + + AM = pMR.AcquisitionModel() + AM.set_coil_sensitivity_maps(csm) + AM.set_up(simulated_data, csm) + + recon_img = AM.inverse(simulated_data) + recon_nii = pReg.NiftiImageData3D(recon_img) + recon_nii = recon_nii.abs() + + fname_output = output_fpath_prefix + "recon_" + fname_simulation_output + ".nii" + recon_nii.write(fname_output) + + return 1 + +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/examples/Python/Simulation/simulation_2D_noncubic_motiondata.py b/examples/Python/Simulation/simulation_2D_noncubic_motiondata.py new file mode 100644 index 000000000..30944e900 --- /dev/null +++ b/examples/Python/Simulation/simulation_2D_noncubic_motiondata.py @@ -0,0 +1,208 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +from pathlib import Path +import numpy as np +import time + + +def read_motionfields(fpath_prefix): + p = sorted( Path(fpath_prefix).glob('*.nii') ) + files = [x for x in p if x.is_file()] + + temp = [] + for f in files: + print("Reading from {} ... ".format(f)) + img = pReg.NiftiImageData3DDisplacement(str(f)) + temp.append(img) + + data = np.array(temp, dtype=object) + return data + +def set_motionfields_from_path(modyn, fpath_prefix): + + assert_validity(modyn, pDS.MRMotionDynamic) + mvfs = read_motionfields(fpath_prefix) + + for m in mvfs: + modyn.add_displacement_field(m) + + +def get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz): + + t_s = np.linspace(t0_s, tmax_s, Nt) + sig = 0.5 * (1 + np.sin( 2*np.pi*f_Hz*t_s)) + return t_s, sig + + +def main(): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Slab128/XCAT_TissueParameters_XML.xml' + fpath_template_contrast_rawdata = input_fpath_prefix + 'Slab128/CV_nav_cart_128Slab_FLASH_T1.h5' + + trajectory_type = 'radial2D' + + if trajectory_type == 'cartesian': + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID29_cart_ref_image_FID78804_ismrmrd.h5' + else: + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID30_rad_2d_uniform_FID78805_ismrmrd.h5' + + acquisition_ad = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + + if trajectory_type == 'cartesian': + acquisition_ad = pMR.preprocess_acquisition_data(acquisition_ad) + elif trajectory_type == 'radial2D': + acquisition_ad = pMR.set_radial2D_trajectory(acquisition_ad) + elif trajectory_type == 'goldenangle2D': + acquisition_ad = pMR.set_goldenangle2D_trajectory(acquisition_ad) + else: + raise ValueError("The trajectory you gave is {}. The only options are cartesian, radial2D or goldenangle2D".format(trajectory_type)) + + # configure the simulation + contrast_ad = pMR.AcquisitionData(fpath_template_contrast_rawdata) + contrast_ad = pMR.preprocess_acquisition_data(contrast_ad) + + labels = pReg.NiftiImageData3D( input_fpath_prefix + "Slab128/label_volume.nii" ) + mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + mrsim.set_contrast_template_data(contrast_ad) + mrsim.set_acquisition_template_data(acquisition_ad) + + offset_z_mm = 0 + translation = np.array([64, 64, offset_z_mm]) + euler_angles_deg = np.array([0,0,0]) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + # take CSM from the rawdata itself + # could be replaced if independent way of computing CSM is available + csm = pMR.CoilSensitivityData() + csm.calculate(acquisition_ad) + mrsim.set_csm(csm) + + # set which tissue defines SNR + SNR = 10 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + # configure the surrogates + Nt = 10000 + t0_s = 0 + tmax_s = 60* 5 + + f_Hz_card = 1 + f_Hz_resp = 0.2 + + t_resp, sig_resp = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_resp) + t_card, sig_card = get_normed_surrogate_signal(t0_s, tmax_s, Nt, f_Hz_card) + + # configure the motion + num_motion_states = 2 + # RESP + num_sim_resp_states = num_motion_states + resp_motion = pDS.MRMotionDynamic( num_sim_resp_states ) + resp_motion.set_dynamic_signal(t_resp, sig_resp) + resp_motion.set_cyclicality(False) + resp_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_motiondata_r_{}_gt_resp".format(num_sim_resp_states)) + set_motionfields_from_path(resp_motion, input_fpath_prefix + 'Slab128/mvf_resp/') + mrsim.add_motion_dynamic(resp_motion) + + # CARD + num_sim_card_states = num_motion_states + + card_motion = pDS.MRMotionDynamic(num_sim_card_states) + card_motion.set_dynamic_signal(t_card, sig_card) + card_motion.set_cyclicality(True) + card_motion.set_groundtruth_folder_prefix(output_fpath_prefix + "output_simulation_2D_motiondata_c_{}_gt_card".format(num_sim_card_states)) + set_motionfields_from_path(card_motion, input_fpath_prefix + 'Slab128/mvf_card/') + mrsim.add_motion_dynamic(card_motion) + + # + fname_simulation_output = "output_simulation_2D_motiondata_traj_{}_r{}_c{}".format(trajectory_type, num_sim_resp_states,num_sim_card_states) + + fname_output = output_fpath_prefix + fname_simulation_output + ".h5" + simulated_file = Path(fname_output) + if not simulated_file.is_file(): + + tstart = time.time() + mrsim.simulate_data() + print("--- Required {} minutes for the simulation.".format( (time.time()-tstart)/60)) + mrsim.write_simulation_results(str(simulated_file)) + else: + print("Skipping simulation since output file already exists.") + + mrsim.save_motion_ground_truth() + + simulated_data = pMR.AcquisitionData(str(simulated_file)) + + csm.calculate(simulated_data) + + AM = pMR.AcquisitionModel() + AM.set_coil_sensitivity_maps(csm) + AM.set_up(simulated_data, csm) + + recon_img = AM.inverse(simulated_data) + recon_nii = pReg.NiftiImageData3D(recon_img) + recon_nii = recon_nii.abs() + + fname_output = output_fpath_prefix + "recon_" + fname_simulation_output + ".nii" + recon_nii.write(fname_output) + + return 1 + +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/examples/Python/Simulation/simulation_2D_subsampling.py b/examples/Python/Simulation/simulation_2D_subsampling.py new file mode 100644 index 000000000..b05ff64e4 --- /dev/null +++ b/examples/Python/Simulation/simulation_2D_subsampling.py @@ -0,0 +1,156 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt +from numpy.core.numeric import identity + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +from pathlib import Path +import numpy as np +import time + + +def offset_shift_from_nifti(float_img): + + + # rot_angle_rad = np.pi/4 + # ca = np.cos(rot_angle_rad) + # sa = np.sin(rot_angle_rad) + + # tm_affine = np.array([[ca,-sa,0,0], + # [sa,ca,0,0], + # [0,0 ,1,0], + # [0,0 ,0,1]]) + + # return pReg.AffineTransformation(tm_affine) + + shift_array = -1 * float_img.get_voxel_sizes()[1:4] * np.array(float_img.shape)/2 + print("--- Resolution is {} mm".format(float_img.get_voxel_sizes())) + print("--- We will shift by {} mm".format(shift_array)) + + shift_x = pReg.NiftiImageData3D(float_img) + shift_y = pReg.NiftiImageData3D(float_img) + shift_z = pReg.NiftiImageData3D(float_img) + + shift_x.fill(0) + shift_y.fill(0) + shift_z.fill(shift_array[2]) + + dvf = pReg.NiftiImageData3DDisplacement(shift_x, shift_y, shift_z) + + return dvf + + +def read_motionfields(fpath_prefix): + p = sorted( Path(fpath_prefix).glob('*.nii') ) + files = [x for x in p if x.is_file()] + + temp = [] + for f in files: + print("Reading from {} ... ".format(f)) + img = pReg.NiftiImageData3DDisplacement(str(f)) + temp.append(img) + + data = np.array(temp, dtype=object) + return data + + +def main(): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_template_contrast_rawdata = input_fpath_prefix + 'Cube128/CV_nav_cart_128Cube_FLASH_T1.h5' + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID29_cart_ref_image_FID78804_ismrmrd.h5' + + # configure the simulation + contrast_rd = pMR.AcquisitionData(fpath_template_contrast_rawdata) + contrast_rd = pMR.preprocess_acquisition_data(contrast_rd) + + acquisition_rd = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + acquisition_rd = pMR.preprocess_acquisition_data(acquisition_rd) + + img_for_contrast = pMR.ImageData() + img_for_contrast.from_acquisition_data(contrast_rd) + + img_for_acquis = pMR.ImageData() + img_for_acquis.from_acquisition_data(acquisition_rd) + img_for_acquis = img_for_acquis.abs() + + labels = pReg.NiftiImageData3D( input_fpath_prefix + "Cube128/label_volume.nii" ) + labels.write(output_fpath_prefix + 'output_example_cartesian_2D_resampling_Labels.nii') + + mvfs = read_motionfields(input_fpath_prefix + 'Cube128/mvf_resp/') + + resampler = pReg.NiftyResampler() + resampler.set_padding_value(0) + resampler.set_interpolation_type_to_cubic_spline() + + resampler.set_floating_image(labels) + resampler.set_reference_image(labels) + + offset_shift = offset_shift_from_nifti(labels) + resampler.add_transformation(offset_shift) + resampler.process() + + resampler.clear_transformations() + resampler.set_floating_image(resampler.get_output()) + resampler.set_reference_image(img_for_acquis) + resampler.process() + + resampled_img = resampler.get_output() + resampled_img = resampled_img.abs() + resampled_img = pReg.NiftiImageData3D(resampled_img) + + resampled_img.write(output_fpath_prefix + 'output_example_cartesian_2D_resampling.nii') + + + return 1 + +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/examples/Python/Simulation/simulation_geometry.py b/examples/Python/Simulation/simulation_geometry.py new file mode 100644 index 000000000..4dd587458 --- /dev/null +++ b/examples/Python/Simulation/simulation_geometry.py @@ -0,0 +1,161 @@ +''' +bla bla + +Usage: +cartesian_3D_simulation.py [--help | options] + +Options: +--non-interactive do not show plots +''' + +## SyneRBI Synergistic Image Reconstruction Framework (SIRF). +## Copyright 2015 - 2020 Rutherford Appleton Laboratory STFC. +## Copyright 2015 - 2017 University College London. +## Copyright 2015 - 2017 Physikalisch-Technische Bundesanstalt. +## +## This is software developed for the Collaborative Computational +## Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +## (http://www.ccpsynerbi.ac.uk/). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. + +__version__ = '0.1.0' +from docopt import docopt +from numpy.core.numeric import identity + +args = docopt(__doc__, version=__version__) + +from pUtilities import * +import sirf.Reg as pReg +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR + +# import engine module + +# process command-line options +show_plot = not args['--non-interactive'] + +from pathlib import Path +import numpy as np +import nibabel as nib +import time + + +fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' +input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' +output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + +fname_xml = input_fpath_prefix + 'Slab128/XCAT_TissueParameters_XML.xml' +fname_template_contrast = input_fpath_prefix + 'Slab128/CV_nav_cart_128Slab_FLASH_T1.h5' +fname_template_acquisition = input_fpath_prefix + 'General/meas_MID27_CV_11s_TI2153_a6_2x2x8_TR45_FID33312_defaultorientation.h5' +fname_labels = input_fpath_prefix + 'Cube128/label_volume_rai.nii' + + +reorient_label_volume = False +if reorient_label_volume: + + img = nib.load(fname_labels) + data = img.get_fdata() + + data = data[:,:,60:70] + + resolution_mm_per_pixel = np.array([2,2,-2,1]) + offset_mm =(-np.array(data.shape)/2 + 0.5) * resolution_mm_per_pixel[0:3] + + affine = np.diag(resolution_mm_per_pixel) + affine[:3,3] = offset_mm + + # + img = nib.Nifti1Image(data, affine) + hdr = img.header + hdr.set_qform(hdr.get_sform()) + + fname_out = '/media/sf_CCPPETMR/labels.nii' + nib.save(img, fname_out) + + sirf_nii = pReg.NiftiImageData(fname_out) + sirf_nii.print_header() + + + + + + + + +def resample_to_destination_geometry(): + + acquisition_template = pMR.AcquisitionData(fname_template_acquisition) + labels = pReg.NiftiImageData3D( input_fpath_prefix + "Cube128/label_volume_rai.nii") + + dst_img = pMR.ImageData() + dst_img.from_acquisition_data(acquisition_template) + dst_img = pReg.NiftiImageData3D(dst_img) + + resampler = pReg.NiftyResampler() + resampler.set_interpolation_type_to_nearest_neighbour() + + resampler.set_reference_image(dst_img) + resampler.set_floating_image(labels) + + translation = np.array([0,0,100], dtype=np.float32) + euler_angles_deg = np.array([0,0,45], dtype=np.float32) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + resampler.add_transformation(offset_trafo) + + resampler.process() + output = resampler.get_output() + output.write("/media/sf_CCPPETMR/tmp_neartestneighbor.nii") + +def experiments_simulation_geometry(): + + + contrast_template = pMR.AcquisitionData(fname_template_contrast) + acquisition_template = pMR.AcquisitionData(fname_template_acquisition) + + labels = pReg.NiftiImageData3D(fname_labels) + mrsim = pDS.MRDynamicSimulation(labels, fname_xml) + + mrsim.set_contrast_template_data(contrast_template) + mrsim.set_acquisition_template_data(contrast_template) + + # + mrsim.save_parametermap_ground_truth(output_fpath_prefix + "simulation_geometry_contrast_parametermap") + # + mrsim.set_acquisition_template_data(acquisition_template) + mrsim.save_parametermap_ground_truth(output_fpath_prefix + "simulation_geometry_acquisition_parametermap") + + # + offset_x_mm = 0 + offset_y_mm = 0 + offset_z_mm = -10 + + rotation_angles_deg = [15,15,0] + translation = np.array([offset_x_mm, offset_y_mm, offset_z_mm]) + euler_angles_deg = np.array(rotation_angles_deg) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + mrsim.save_parametermap_ground_truth(output_fpath_prefix + "simulation_geometry_acquisition_offset_parametermap") + +def main(): + resample_to_destination_geometry() + # experiments_simulation_geometry() +try: + main() + print('\n=== done with %s' % __file__) + +except error as err: + # display error information + print('??? %s' % err.value) + exit(1) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18e0f7906..0e5b6cc8e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,4 +146,14 @@ else() endif() endif() +########################################################################## +# Simulation # +########################################################################## +option(DISABLE_Simulation "Disable building the SIRF simulation package" OFF) +if (DISABLE_Simulation) + message(STATUS "Gadgetron support disabled.") +else() + ADD_SUBDIRECTORY(xDynamicSimulation) +endif() + ADD_SUBDIRECTORY(common) diff --git a/src/Registration/cReg/ImageWeightedMean.cpp b/src/Registration/cReg/ImageWeightedMean.cpp index 5f2c1cc82..32a3acf80 100644 --- a/src/Registration/cReg/ImageWeightedMean.cpp +++ b/src/Registration/cReg/ImageWeightedMean.cpp @@ -105,7 +105,7 @@ void ImageWeightedMean::check_can_do_mean() const if (_input_image_sptrs.size() == 0) throw std::runtime_error("Need to add images to be able to do weighted mean."); - std::cout << "\nAll images match, we can calculate their weighted average.\n"; + // std::cout << "\nAll images match, we can calculate their weighted average.\n"; } namespace sirf { diff --git a/src/Registration/cReg/NiftyResampler.cpp b/src/Registration/cReg/NiftyResampler.cpp index 669323bbf..f29ccd04b 100644 --- a/src/Registration/cReg/NiftyResampler.cpp +++ b/src/Registration/cReg/NiftyResampler.cpp @@ -112,7 +112,7 @@ void NiftyResampler::set_up() // If no transformations, use identity. if (this->_transformations.size() == 0) { - std::cout << "\nNo transformations set, using identity.\n"; + // std::cout << "\nNo transformations set, using identity.\n"; this->_transformations.push_back(std::make_shared >()); } diff --git a/src/iUtilities/pyiutilities.py b/src/iUtilities/pyiutilities.py new file mode 100644 index 000000000..ba0882b87 --- /dev/null +++ b/src/iUtilities/pyiutilities.py @@ -0,0 +1,187 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 3.0.8 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + + + + + +from sys import version_info +if version_info >= (2, 6, 0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_pyiutilities', [dirname(__file__)]) + except ImportError: + import _pyiutilities + return _pyiutilities + if fp is not None: + try: + _mod = imp.load_module('_pyiutilities', fp, pathname, description) + finally: + fp.close() + return _mod + _pyiutilities = swig_import_helper() + del swig_import_helper +else: + import _pyiutilities +del version_info +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. + + +def _swig_setattr_nondynamic(self, class_type, name, value, static=1): + if (name == "thisown"): + return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name, None) + if method: + return method(self, value) + if (not static): + if _newclass: + object.__setattr__(self, name, value) + else: + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + + +def _swig_setattr(self, class_type, name, value): + return _swig_setattr_nondynamic(self, class_type, name, value, 0) + + +def _swig_getattr_nondynamic(self, class_type, name, static=1): + if (name == "thisown"): + return self.this.own() + method = class_type.__swig_getmethods__.get(name, None) + if method: + return method(self) + if (not static): + return object.__getattr__(self, name) + else: + raise AttributeError(name) + +def _swig_getattr(self, class_type, name): + return _swig_getattr_nondynamic(self, class_type, name, 0) + + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except AttributeError: + class _object: + pass + _newclass = 0 + + + +def newDataHandle(): + return _pyiutilities.newDataHandle() +newDataHandle = _pyiutilities.newDataHandle + +def deleteDataHandle(ptr): + return _pyiutilities.deleteDataHandle(ptr) +deleteDataHandle = _pyiutilities.deleteDataHandle + +def charDataHandle(s): + return _pyiutilities.charDataHandle(s) +charDataHandle = _pyiutilities.charDataHandle + +def intDataHandle(i): + return _pyiutilities.intDataHandle(i) +intDataHandle = _pyiutilities.intDataHandle + +def floatDataHandle(i): + return _pyiutilities.floatDataHandle(i) +floatDataHandle = _pyiutilities.floatDataHandle + +def doubleDataHandle(i): + return _pyiutilities.doubleDataHandle(i) +doubleDataHandle = _pyiutilities.doubleDataHandle + +def charDataFromHandle(ptr): + return _pyiutilities.charDataFromHandle(ptr) +charDataFromHandle = _pyiutilities.charDataFromHandle + +def intDataFromHandle(ptr): + return _pyiutilities.intDataFromHandle(ptr) +intDataFromHandle = _pyiutilities.intDataFromHandle + +def intDataItemFromHandle(ptr, i): + return _pyiutilities.intDataItemFromHandle(ptr, i) +intDataItemFromHandle = _pyiutilities.intDataItemFromHandle + +def uint16DataItemFromHandle(ptr, i): + return _pyiutilities.uint16DataItemFromHandle(ptr, i) +uint16DataItemFromHandle = _pyiutilities.uint16DataItemFromHandle + +def uint32DataItemFromHandle(ptr, i): + return _pyiutilities.uint32DataItemFromHandle(ptr, i) +uint32DataItemFromHandle = _pyiutilities.uint32DataItemFromHandle + +def uint64DataItemFromHandle(ptr, i): + return _pyiutilities.uint64DataItemFromHandle(ptr, i) +uint64DataItemFromHandle = _pyiutilities.uint64DataItemFromHandle + +def floatDataFromHandle(ptr): + return _pyiutilities.floatDataFromHandle(ptr) +floatDataFromHandle = _pyiutilities.floatDataFromHandle + +def floatDataItemFromHandle(ptr, i): + return _pyiutilities.floatDataItemFromHandle(ptr, i) +floatDataItemFromHandle = _pyiutilities.floatDataItemFromHandle + +def floatReDataFromHandle(ptr): + return _pyiutilities.floatReDataFromHandle(ptr) +floatReDataFromHandle = _pyiutilities.floatReDataFromHandle + +def floatImDataFromHandle(ptr): + return _pyiutilities.floatImDataFromHandle(ptr) +floatImDataFromHandle = _pyiutilities.floatImDataFromHandle + +def doubleDataFromHandle(ptr): + return _pyiutilities.doubleDataFromHandle(ptr) +doubleDataFromHandle = _pyiutilities.doubleDataFromHandle + +def doubleReDataFromHandle(ptr): + return _pyiutilities.doubleReDataFromHandle(ptr) +doubleReDataFromHandle = _pyiutilities.doubleReDataFromHandle + +def doubleImDataFromHandle(ptr): + return _pyiutilities.doubleImDataFromHandle(ptr) +doubleImDataFromHandle = _pyiutilities.doubleImDataFromHandle + +def executionStatus(ptr): + return _pyiutilities.executionStatus(ptr) +executionStatus = _pyiutilities.executionStatus + +def executionError(ptr): + return _pyiutilities.executionError(ptr) +executionError = _pyiutilities.executionError + +def executionErrorFile(ptr): + return _pyiutilities.executionErrorFile(ptr) +executionErrorFile = _pyiutilities.executionErrorFile + +def executionErrorLine(ptr): + return _pyiutilities.executionErrorLine(ptr) +executionErrorLine = _pyiutilities.executionErrorLine +# This file is compatible with both classic and new-style classes. + + diff --git a/src/xDynamicSimulation/CMakeLists.txt b/src/xDynamicSimulation/CMakeLists.txt new file mode 100644 index 000000000..821ab0f72 --- /dev/null +++ b/src/xDynamicSimulation/CMakeLists.txt @@ -0,0 +1,28 @@ +#======================================================================== +# Author: Johannes Mayer +# Date: 15.03.2018 +#========================================================================= + +set(CMAKE_POSITION_INDEPENDENT_CODE True) + +if (CMAKE_VERSION VERSION_LESS "3.1") + set (CMAKE_CXX_FLAGS "--std=c++11 ${CMAKE_CXX_FLAGS}") +else () + set (CMAKE_CXX_STANDARD 11) +endif () + +######################################### + +# find_package(HDF5 1.8 COMPONENTS C CXX HL REQUIRED) + + +# include_directories(${PROJECT_SOURCE_DIR}/src/common/include) +# find_package(STIR REQUIRED) + +add_subdirectory(cDynamicSimulation) +if (BUILD_PYTHON) + add_subdirectory(pDynamicSimulation) +endif() + +# set(cDynamicSimulation_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cDynamicSimulation") + diff --git a/src/xDynamicSimulation/cDynamicSimulation/CMakeLists.txt b/src/xDynamicSimulation/cDynamicSimulation/CMakeLists.txt new file mode 100755 index 000000000..8244c0511 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/CMakeLists.txt @@ -0,0 +1,66 @@ +#======================================================================== +# Author: Johannes Mayer +# Date: 15.03.2018 +#========================================================================= + +add_definitions(-DBOOST_ALL_NO_LIB) + +if (SIRF_INSTALL_DEPENDENCIES AND WIN32) + set(Boost_DLL_DIR ${Boost_LIBRARY_DIR_RELEASE}) + message(STATUS "Install boost shared libraries from ${Boost_DLL_DIR} ") + foreach (__boost_lib system filesystem thread date_time chrono) + file(GLOB Boost_DLL "${Boost_DLL_DIR}/boost_${__boost_lib}*.dll") + install( FILES ${Boost_DLL} DESTINATION bin ) + endforeach() + endif() + +include_directories(${PROJECT_SOURCE_DIR}/src/common/include) +include_directories(${PROJECT_SOURCE_DIR}/src/xGadgetron/cGadgetron) +include_directories(${PROJECT_SOURCE_DIR}/src/xSTIR/cSTIR) +include_directories(${PROJECT_SOURCE_DIR}/src/Registration/Reg) +include_directories(${PROJECT_SOURCE_DIR}/src/iUtilities) +include_directories("${GADGETRON_INCLUDE_DIR}/../") + + +set(SRC_FILES_TO_COMPILE + cdynamicsimulation.cpp + dynamicsimulation_x.cpp + auxiliary_input_output.cpp + tissueparameters.cpp + tissuelabelmapper.cpp + contrastgenerator.cpp + phantom_input.cpp + dynamics.cpp + dynsim_noisegenerator.cpp + dynsim_deformer.cpp + volume_orientator.cpp) + +add_library( cdynamicsimulation ${SRC_FILES_TO_COMPILE}) +set (cdynamicsimulation "$$") + +TARGET_INCLUDE_DIRECTORIES(cdynamicsimulation PUBLIC + "$$" +) + +target_include_directories(cdynamicsimulation PUBLIC "${HDF5_INCLUDE_DIRS}") + +target_link_libraries(cdynamicsimulation ISMRMRD::ISMRMRD iutilities cgadgetron cstir Reg "${HDF5_LIBRARIES}" ${HDF5_CXX_LIBRARIES}) + +# Add boost library dependencies +if((CMAKE_VERSION VERSION_LESS 3.5.0) OR (NOT _Boost_IMPORTED_TARGETS)) + # This is harder than it should be on older CMake versions to be able to cope with + # spaces in filenames. + foreach(C SYSTEM FILESYSTEM THREAD DATE_TIME CHRONO) + target_link_libraries(cdynamicsimulation optimized "${Boost_${C}_LIBRARY_RELEASE}") + target_link_libraries(cdynamicsimulation debug "${Boost_${C}_LIBRARY_DEBUG}") + endforeach() +else() + # Nice and simple for recent CMake (which knows about your Boost version) + target_link_libraries(cdynamicsimulation Boost::system Boost::filesystem Boost::thread Boost::date_time Boost::chrono) +endif() + +# Note: cannot use ISMRMRD_LIBRARIES on Windows as it generally contains +# a list of filenames with spaces. There doesn't seem to be a way to pass this through (strange). +# Luckily, we know what libraries it uses + +add_subdirectory(tests) diff --git a/src/xDynamicSimulation/cDynamicSimulation/auxiliary_input_output.cpp b/src/xDynamicSimulation/cDynamicSimulation/auxiliary_input_output.cpp new file mode 100644 index 000000000..6cfbe3ae5 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/auxiliary_input_output.cpp @@ -0,0 +1,113 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.04.05 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + + +#include +#include + +#include "sirf/STIR/stir_types.h" + +using namespace sirf; + + +// ++++++++++++++++++ print IO +++++++++++++ +void print_io::print_voxelized_geometrical_info( const sirf::ImageData& im ) +{ + + std::shared_ptr sptr_geo_info = im.get_geom_info_sptr(); + + VoxelisedGeometricalInfo3D::Offset curr_offset = sptr_geo_info->get_offset(); + VoxelisedGeometricalInfo3D::Spacing curr_spacing = sptr_geo_info->get_spacing(); + VoxelisedGeometricalInfo3D::Size curr_size = sptr_geo_info->get_size(); + VoxelisedGeometricalInfo3D::DirectionMatrix curr_dirmat = sptr_geo_info->get_direction(); + + + std::cout << "Offset: ("; + for( int i=0;i<3; i++) + std::cout << curr_offset[i]<< " / "; + std::cout << ")"<< std::endl; + + std::cout << "Spacing: ("; + for( int i=0;i<3; i++) + std::cout << curr_spacing[i]<< " / "; + std::cout << ")"<< std::endl; + + std::cout << "Size: ("; + for( int i=0;i<3; i++) + std::cout << curr_size[i]<< " / "; + std::cout << ")"<< std::endl; + + std::cout << "Dir mat: \n"; + for( int i=0;i<3; i++) + for( int j=0;j<3; j++) + { + std::cout << curr_dirmat[i][j] << " "; + if(j==2) + std::cout << "\n"; + } + std::cout << "\n"; + +} + +// ++++++++++++++++++ data_IO ++++++++++++++++ + +SignalContainer data_io::read_surrogate_signal( const std::string& filename_time_axis, const std::string& filename_signal ) +{ + std::vector< TimeAxisType > time_points_ms = read_single_column_txt(filename_time_axis); + std::vector< SignalAxisType > signal_points = read_single_column_txt(filename_signal); + + SignalContainer signal; + + if( time_points_ms.size() == signal_points.size()) + { + std::cout << time_points_ms.size() << " signal points read from file." < + + +#include "sirf/iUtilities/DataHandle.h" + +#include "sirf/Reg/AffineTransformation.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/cDynamicSimulation/dynamicsimulation_x.h" +#include "sirf/cDynamicSimulation/contrastgenerator.h" +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/cDynamicSimulation/dynamics.h" + +using namespace sirf; + + +// extern "C" +// void* +// cGT_ISMRMRDAcquisitionsFromFile(const char* file) +// { +// if (!file_exists(file)) +// return fileNotFound(file, __FILE__, __LINE__); +// try { +// shared_ptr +// acquisitions(new AcquisitionsVector); +// acquisitions->read(file); +// return newObjectHandle(acquisitions); +// } +// CATCH; +// } + + +extern "C" +void* cDS_simulateData(void* ptr_sim) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + + aDynamicSimulation& sim = objectFromHandle(h_sim); + sim.simulate_data(); + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_writeSimulationResults(const void* ptr_sim, const char* fname_with_ext) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + aDynamicSimulation& sim = objectFromHandle(h_sim); + sim.write_simulation_results(fname_with_ext); + + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_saveParameterMapsGroundTruth(const void* ptr_sim, const char* fname_prefix) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + sim.save_groud_truth_parameter_maps(fname_prefix); + + return new DataHandle; + } + CATCH; +} + +extern "C" +void* cDS_saveMotionGroundTruth(const void* ptr_sim) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + aDynamicSimulation& sim = objectFromHandle(h_sim); + sim.save_ground_truth_displacements(); + + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_getTissueParameter(const void* ptr_sim, const LabelType label) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + auto sptr_tp = std::make_shared (sim.get_petmr_tissue_parameter(label)); + + return newObjectHandle(sptr_tp); + } + CATCH; +} + +extern "C" +void* cDS_MRDynamicSimulation(const void* ptr_labels, const char* fname_xml) +{ + try { + + CAST_PTR(DataHandle, h_labels, ptr_labels); + std::shared_ptr sptr_labels; + getObjectSptrFromHandle(h_labels, sptr_labels); + + MRContrastGenerator cont_gen(*sptr_labels, fname_xml); + + std::shared_ptr + sptr_ds(new MRDynamicSimulation(cont_gen)); + + return newObjectHandle(sptr_ds); + + } + CATCH; +} + + +extern "C" +void* cDS_setContrastTemplateData(void* ptr_sim, const void* ptr_acqs) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + CAST_PTR(DataHandle, h_acqs, ptr_acqs); + + MRAcquisitionData& ad = objectFromHandle(h_acqs); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + sim.set_contrast_template_rawdata(ad); + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_setAcquisitionTemplateData(void* ptr_sim, const void* ptr_acqs) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + CAST_PTR(DataHandle, h_acqs, ptr_acqs); + + MRAcquisitionData& ad = objectFromHandle(h_acqs); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + sim.set_acquisition_template_rawdata(ad); + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_setCoilmaps(void* ptr_sim, const void* ptr_csm) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + + CAST_PTR(DataHandle, h_csm, ptr_csm); + std::shared_ptr sptr_csm; + getObjectSptrFromHandle(h_csm, sptr_csm); + + sim.set_coilmaps(sptr_csm); + + return new DataHandle; + } + CATCH; +} + +extern "C" +void* cDS_setSNR(void* ptr_sim, size_t ptr_SNR) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + + float const SNR = *((float*) ptr_SNR); + sim.set_SNR(SNR); + + return new DataHandle; + + } + + CATCH; +} + +extern "C" +void* cDS_setNoiseLabel(void* ptr_sim, int const label) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + sim.set_noise_label(label); + + return new DataHandle; + + } + + CATCH; +} + +extern "C" +void* cDS_setOffsetTransformation(void* ptr_sim, const void* ptr_trafo) +{ + try{ + CAST_PTR(DataHandle, h_sim, ptr_sim); + CAST_PTR(DataHandle, h_trafo, ptr_trafo); + + MRDynamicSimulation& sim = objectFromHandle(h_sim); + AffineTransformation& aff = objectFromHandle >(h_trafo); + + sim.set_offset_transformation(aff); + + + return new DataHandle; + } + + CATCH; +} + + +extern "C" +void* cDS_addMRMotionDynamic(void* ptr_sim, void* ptr_dyn) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + std::shared_ptr sptr_dyn; + getObjectSptrFromHandle(h_dyn, sptr_dyn); + + sim.add_dynamic(sptr_dyn); + + return new DataHandle; + + } + + CATCH; +} + + +extern "C" +void* cDS_addMRContrastDynamic(void* ptr_sim, void* ptr_dyn) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + std::shared_ptr sptr_dyn; + getObjectSptrFromHandle(h_dyn, sptr_dyn); + + sim.add_dynamic(sptr_dyn); + + return new DataHandle; + + } + + CATCH; +} + +extern "C" +void* cDS_addExternalContrastDynamic(void* ptr_sim, void* ptr_dyn) +{ + try { + + CAST_PTR(DataHandle, h_sim, ptr_sim); + MRDynamicSimulation& sim = objectFromHandle(h_sim); + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + std::shared_ptr sptr_dyn; + getObjectSptrFromHandle(h_dyn, sptr_dyn); + + sim.add_dynamic(sptr_dyn); + + return new DataHandle; + + } + + CATCH; +} + + +// Dynamics +extern "C" +void* cDS_setDynamicSignal(void* ptr_dyn, size_t ptr_time, size_t ptr_signal, int const num_points) +{ + try { + + float* time = (float*) ptr_time; + float* signal = (float*) ptr_signal; + + typedef std::pair SignalPoint; + typedef std::vector< SignalPoint > SignalContainer; + + SignalContainer surrogate(num_points); + for(size_t i=0; i(h_dyn); + + dyn.set_dynamic_signal(surrogate); + + return new DataHandle; + } + + CATCH; +} + +extern "C" +void* cDS_setMRGroundTruthFolderName(void* ptr_dyn, const char* fpath_output_prefix) +{ + try { + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + MRMotionDynamic& dyn = objectFromHandle(h_dyn); + dyn.set_ground_truth_folder_name(fpath_output_prefix); + + return new DataHandle; + } + + CATCH; +} + +extern "C" +void* cDS_setCyclicality(void* ptr_dyn, bool const cyc) +{ + try { + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + Dynamic& dyn = objectFromHandle(h_dyn); + dyn.set_cyclicality(cyc); + + return new DataHandle; + } + + CATCH; +} + +// Motion Dynamics + +extern "C" +void* cDS_addMRDisplacementField(void* ptr_dyn, const void* ptr_dvf) +{ + try { + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + MRMotionDynamic& dyn = objectFromHandle(h_dyn); + + CAST_PTR(DataHandle, h_dvf, ptr_dvf); + MotionFieldType& dvf = objectFromHandle(h_dvf); + + dyn.add_displacement_field(dvf); + + return new DataHandle; + + } + + CATCH; +} + +// MR Dynamics + +extern "C" +void* cDS_setMRAcquisitions(void* ptr_dyn, void* ptr_ad) +{ + try { + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + MRDynamic& dyn = objectFromHandle(h_dyn); + + CAST_PTR(DataHandle, h_ad, ptr_ad); + MRAcquisitionData& ad = objectFromHandle(h_ad); + + dyn.bin_mr_acquisitions(ad); + + return new DataHandle; + + } + CATCH; +} + +extern "C" +void* cDS_getIdxCorrSizes(void* ptr_dyn, void* ptr_ad, size_t ptr_sizes) +{ + try { + + int* sizes = (int*)ptr_sizes; + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + MRDynamic& dyn = objectFromHandle(h_dyn); + + CAST_PTR(DataHandle, h_ad, ptr_ad); + MRAcquisitionData& ad = objectFromHandle(h_ad); + + dyn.bin_mr_acquisitions(ad); + + std::vector idxcorr_size = dyn.get_idx_corr_sizes(); + memcpy(sizes, &idxcorr_size.front(), dyn.get_num_simul_states()*sizeof(int)); + + return new DataHandle; + } + + CATCH; +} + +extern "C" +void* cDS_getIdxCorr(void* ptr_dyn, int const bin_num, size_t ptr_idx_corr) +{ + try { + + + int* idx_corr = (int*)ptr_idx_corr; + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + MRDynamic& dyn = objectFromHandle(h_dyn); + + std::deque idx = dyn.get_idx_corr(bin_num); + + // memcpy does not seem to work here. + for(int i=0; i + sptr_dyn(new MRMotionDynamic(num_states)); + + return newObjectHandle(sptr_dyn); + } + + CATCH; +} + +extern "C" +void* cDS_MRContrastDynamic( int const num_states ) +{ + try { + std::shared_ptr + sptr_dyn(new MRContrastDynamic(num_states)); + + return newObjectHandle(sptr_dyn); + } + + CATCH; +} + +extern "C" +void* cDS_addDynamicLabel(void* ptr_contrast_dyn, int const label) +{ + try { + CAST_PTR(DataHandle, h_dyn, ptr_contrast_dyn); + MRContrastDynamic& dyn = objectFromHandle(h_dyn); + dyn.add_dynamic_label(label); + + return new DataHandle; + + } + + CATCH; +} + +extern "C" +void* cDS_setMRParameterExtremes(void* ptr_contrast_dyn, void* ptr_tissueparameter_0, void* ptr_tissueparameter_1) +{ + try { + CAST_PTR(DataHandle, h_dyn, ptr_contrast_dyn); + MRContrastDynamic& dyn = objectFromHandle(h_dyn); + + CAST_PTR(DataHandle, h_tp0, ptr_tissueparameter_0); + TissueParameter& tp0 = objectFromHandle(h_tp0); + + CAST_PTR(DataHandle, h_tp1, ptr_tissueparameter_1); + TissueParameter& tp1 = objectFromHandle(h_tp1); + + dyn.set_parameter_extremes(tp0, tp1); + + return new DataHandle; + } + + CATCH; +} + +extern "C" +void* cDS_ExternalMRContrastDynamic( void ) +{ + try { + + std::shared_ptr + sptr_dyn(new ExternalMRContrastDynamic()); + + return newObjectHandle(sptr_dyn); + } + + CATCH; +} + +extern "C" +void* cDS_appendExternalTissueSignal(void* ptr_dyn, int const num_points, size_t ptr_labels, size_t ptr_sig) +{ + try { + + CAST_PTR(DataHandle, h_dyn, ptr_dyn); + ExternalMRContrastDynamic& dyn = objectFromHandle(h_dyn); + + std::vector ext_sig; + + int* labels = (int*) ptr_labels; + complex_float_t* sig = (complex_float_t*) ptr_sig; + + std::uint32_t dummy_time = 0; + for(int i=0; i(h_tissue_parameter); + tp.mr_tissue_.t1_miliseconds_ = T1_ms; + + return new DataHandle; + } + + CATCH; +} + +extern "C" +void* cDS_setSpinDensity(void* ptr_tissue_parameter, float const spin_density) +{ + try { + + CAST_PTR(DataHandle, h_tissue_parameter, ptr_tissue_parameter); + TissueParameter& tp = objectFromHandle(h_tissue_parameter); + tp.mr_tissue_.spin_density_percentH2O_ = spin_density; + + return new DataHandle; + } + + CATCH; +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/contrastgenerator.cpp b/src/xDynamicSimulation/cDynamicSimulation/contrastgenerator.cpp new file mode 100644 index 000000000..59ba4c663 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/contrastgenerator.cpp @@ -0,0 +1,656 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.28 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#include "sirf/cDynamicSimulation/contrastgenerator.h" +#include "sirf/Reg/NiftyResample.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" + +#include +#include +#include +#include +#include +#include +#include + +//#include "Testing/auxiliary_testing_functions.h" + +using namespace std; +using namespace stir; +using namespace sirf; +using namespace ISMRMRD; + + + +AbstractContrastGenerator::AbstractContrastGenerator(const LabelVolume& tissue_labels, const std::string& filename_tissue_parameter_xml) +{ + this->tlm_ = TissueLabelMapper( tissue_labels, filename_tissue_parameter_xml ); + tlm_.map_labels_to_tissue_from_xml(); + +} + +void AbstractContrastGenerator::replace_petmr_tissue_parameters(LabelType label, TissueParameter tiss_param) +{ + this->tlm_.replace_petmr_tissue_parameters(label, tiss_param); +} + +TissueParameter AbstractContrastGenerator::get_petmr_tissue_parameter( LabelType label ) +{ + TissueParameterList all_tissues = this->tlm_.get_tissue_parameter_list(); + std::cout << "Number of different tissues: " << all_tissues.size()<< std::endl; + for( size_t i=0; itlm_.get_segmentation_dimensions(); + + auto sptr_tmp_ad = ad.clone(); + ISMRMRD::IsmrmrdHeader hdr = sptr_tmp_ad->acquisitions_info().get_IsmrmrdHeader(); + ISMRMRD::EncodingSpace es = hdr.encoding[0].reconSpace; + + float const dx = es.fieldOfView_mm.x / es.matrixSize.x; + float const dy = es.fieldOfView_mm.y / es.matrixSize.y; + float const dz = es.fieldOfView_mm.z / es.matrixSize.z; + + // dims[0] == number of dimensions for niftis + es.matrixSize.x = segmentation_dims[1]; + es.matrixSize.y = segmentation_dims[2]; + es.matrixSize.z = segmentation_dims[3]; + + es.fieldOfView_mm.x = es.matrixSize.x * dx; + es.fieldOfView_mm.y = es.matrixSize.y * dy; + es.fieldOfView_mm.z = es.matrixSize.z * dz; + + hdr.encoding[0].reconSpace = es; + hdr.encoding[0].encodedSpace = es; + + std::stringstream xmlhdr; + ISMRMRD::serialize(hdr, xmlhdr); + + sptr_tmp_ad->set_acquisitions_info( xmlhdr.str() ); + + this->sptr_acqu_ = std::move(sptr_tmp_ad->clone()); + this->hdr_ = this->sptr_acqu_->acquisitions_info().get_IsmrmrdHeader(); +} + +void MRContrastGenerator::set_rawdata_header(const ISMRMRD::IsmrmrdHeader& hdr) +{ + this->hdr_ = hdr; +} + +sirf::GadgetronImagesVector& MRContrastGenerator::get_contrast_filled_volumes(bool const resample_output) +{ + if( resample_output == true ) + this->resample_to_template_image(); + + return this->contrast_filled_volumes_; + +} + +void MRContrastGenerator::resample_to_template_image( void ) +{ +} + +void MRContrastGenerator::map_contrast() +{ + this->tlm_.assign_tissues_to_labels(); + this->contrast_filled_volumes_.empty(); + if(sptr_acqu_ == nullptr) + { + std::cout << "Your contrast template acquisition is not yet set." << std::endl; + std::exit( EXIT_FAILURE ); + } + + this->contrast_filled_volumes_ = GadgetronImagesVector(*sptr_acqu_); + + std::vector < complex_float_t > (*contrast_map_function)(std::shared_ptr const ptr_to_tiss_par, const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr); + + ISMRMRD::SequenceParameters const sequ_par = *(this->hdr_.sequenceParameters); + std::string const sequ_name = *(sequ_par.sequence_type); + + if(sequ_name.compare("Flash") == 0) + { + contrast_map_function = &map_flash_contrast; + } + else if (sequ_name.compare("Bssfp") == 0) + { + contrast_map_function = &map_bssfp_contrast; + } + else + { + std::stringstream error_msg_stream; + error_msg_stream << "The header you read in requires a contrast which has not been implemented yet. "; + error_msg_stream << "The demanded sequence type is: " << sequ_name << ". "; + error_msg_stream << "Please give another rawdata header or write the contrast map yourself and add an else if to the map_contrast method."; + throw std::runtime_error( error_msg_stream.str() ); + } + + TissueVector tissue_params = this->tlm_.get_segmentation_tissues(); + size_t const num_voxels = tissue_params.size(); + + std::vector > contrast_vector; + contrast_vector.resize(num_voxels); + + // #pragma omp parallel + for (size_t i= 0; ihdr_); + + size_t const num_contrasts = contrast_vector[0].size(); + + const int* segmentation_dims = this->tlm_.get_segmentation_dimensions(); + + std::vector data_size; + + for( int i_dim=0; i_dim< 8; i_dim++) + { + data_size.push_back( (size_t)segmentation_dims[i_dim] ); + } + + size_t Nz = data_size[3]; + size_t Ny = data_size[2]; + size_t Nx = data_size[1]; + + for(int i=0; icontrast_filled_volumes_.number(); ++i) + { + auto ptr_img = (CFImage*) contrast_filled_volumes_.sptr_image_wrap(i)->ptr_image(); + ptr_img->resize(Nx, Ny, Nz, 1); + uint16_t const current_contast = ptr_img->getContrast(); + + // #pragma omp parallel + for( size_t nz=0; nz curr_voxel = contrast_vector[linear_index_access]; + ptr_img->operator()(nx, ny, nz, 0) = curr_voxel[ current_contast ]; + } + + } + contrast_filled_volumes_.reorient(*(tlm_.get_sptr_geometry())); +} + +void MRContrastGenerator::map_parameters() +{ + parameter_filled_volumes_.empty(); + parameter_filled_volumes_.push_back( get_parameter_map(0)); + parameter_filled_volumes_.push_back( get_parameter_map(1)); + parameter_filled_volumes_.push_back( get_parameter_map(2)); + parameter_filled_volumes_.push_back( get_parameter_map(3)); + parameter_filled_volumes_.push_back( get_parameter_map(4)); +} + + +sirf::NiftiImageData3D MRContrastGenerator::get_parameter_map(const int which_parameter) +{ + this->tlm_.assign_tissues_to_labels(); + TissueVector tissue_params = this->tlm_.get_segmentation_tissues(); + + LabelVolume parameter_map = this->tlm_.get_segmentation_labels(); + + const int num_voxels = parameter_map.get_num_voxels(); + if(tissue_params.size() != num_voxels) + throw std::runtime_error("Your Tissue label mapper gives a different number of voxels compared to your segmentation."); + + // #pragma omp parallel + for( size_t i_vox=0; i_vox MRContrastGenerator::build_label_signal_map(std::vector ext_sig) const { + + TissueParameterList tpl = tlm_.get_tissue_parameter_list(); + + if(ext_sig.size()max_label?tpl[i].label_:max_label; + + std::vector signal_map(max_label+1); + + for(int i=0; i(ext_sig[j]) == curr_label) + { + signal_map[curr_label] = std::get<2>(ext_sig[j]); + ext_sig.erase(ext_sig.begin() + j); + label_found = true; + break; + } + } + if(!label_found) + throw std::runtime_error("Could not find every label in the segmentation in the external signal dictionary."); + } + + return signal_map; +} +void MRContrastGenerator::map_contrast(const std::vector& ext_sig) +{ + std::vector map_label_to_signal = build_label_signal_map(ext_sig); + + this->tlm_.assign_tissues_to_labels(); + this->contrast_filled_volumes_.empty(); + if(sptr_acqu_ == nullptr) + { + std::cout << "Your contrast template acquisition is not yet set." << std::endl; + std::exit( EXIT_FAILURE ); + } + + this->contrast_filled_volumes_ = GadgetronImagesVector(*sptr_acqu_); + + if( contrast_filled_volumes_.number() != 1) + throw std::runtime_error("For external contrast signal mapping please supply template rawdata yiedling only one single image (i.e. only one contrast, repetition etc.)"); + + TissueVector tissue_params = this->tlm_.get_segmentation_tissues(); + size_t const num_voxels = tissue_params.size(); + + std::vector< complex_float_t> magnetisation; + magnetisation.resize(num_voxels); + + // #pragma omp parallel + for (size_t i= 0; ilabel_]; + + const int* segmentation_dims = this->tlm_.get_segmentation_dimensions(); + + size_t Nz = segmentation_dims[3]; + size_t Ny = segmentation_dims[2]; + size_t Nx = segmentation_dims[1]; + + auto ptr_img = (CFImage*) contrast_filled_volumes_.sptr_image_wrap(0)->ptr_image(); + ptr_img->resize(Nx, Ny, Nz, 1); + memcpy(ptr_img->begin(), &magnetisation[0], magnetisation.size() * sizeof(complex_float_t)); + + contrast_filled_volumes_.reorient(*(tlm_.get_sptr_geometry())); +} + +complex_float_t MRContrastGenerator::get_signal_for_tissuelabel( size_t const label ) +{ + + if(sptr_acqu_ == nullptr) + { + std::cout << "Your contrast template acquisition is not yet set." << std::endl; + std::exit( EXIT_FAILURE ); + } + + + auto tissue_list = this->tlm_.get_tissue_parameter_list(); + + TissueParameter tp; + for(size_t i=0; i (*contrast_map_function)(std::shared_ptr const ptr_to_tiss_par, const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr); + + ISMRMRD::SequenceParameters const sequ_par = *(this->hdr_.sequenceParameters); + std::string const sequ_name = *(sequ_par.sequence_type); + + if(sequ_name.compare("Flash") == 0) + { + contrast_map_function = &map_flash_contrast; + } + else if (sequ_name.compare("Bssfp") == 0) + { + contrast_map_function = &map_bssfp_contrast; + } + else + { + std::stringstream error_msg_stream; + error_msg_stream << "The header you read in requires a contrast which has not been implemented yet. "; + error_msg_stream << "The demanded sequence type is: " << sequ_name << ". "; + error_msg_stream << "Please give another rawdata header or write the contrast map yourself and add an else if to the map_contrast method."; + std::cout << error_msg_stream.str() << std::endl; + std::exit( EXIT_FAILURE ); + // throw std::runtime_error( error_msg_stream.str() ); + } + + auto signal_vector = contrast_map_function( std::make_shared(tp), this->hdr_); + + return signal_vector[0]; + +}; + + +std::vector < complex_float_t > map_flash_contrast(std::shared_ptr const ptr_to_tiss_par, const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr) +{ + + SequenceParameters const sequ_par = *(ismrmrd_hdr.sequenceParameters); + AcquisitionSystemInformation const asi = *(ismrmrd_hdr.acquisitionSystemInformation); + + SeqParamType const TE = *(sequ_par.TE); + SeqParamType TR; + + try + { + TR = *(sequ_par.echo_spacing); + + } + catch(const std::runtime_error &e) + { + std::cout << "Caught exception in map_flash_contrast." << std::endl; + std::cout << e.what() < 1 ) + throw std::runtime_error(" More than one echo spacing was given. Please give only one in Flash contrast."); + + SeqParamType const flip_angle_deg = *(sequ_par.flipAngle_deg); + + float const field_strength_t = *(asi.systemFieldStrength_T); + + + if (flip_angle_deg.size() > 1) + throw std::runtime_error(" More than one flip angle was given. Please give only one in Flash contrast."); + + size_t const num_echoes = TE.size(); + + float const spin_dens = ptr_to_tiss_par->mr_tissue_.spin_density_percentH2O_; + float const T1_ms = ptr_to_tiss_par->mr_tissue_.t1_miliseconds_; + float const T2_ms = ptr_to_tiss_par->mr_tissue_.t2_miliseconds_; + float const cs_ppm = ptr_to_tiss_par->mr_tissue_.cs_ppm_; + + std::vector< complex_float_t > contrast; + contrast.resize( num_echoes ); + + complex_float_t const imag_unit(0,1); + float const gyro = 42.58 * 2*M_PI; + + // signal forumla + for( int i_echo = 0; i_echo map_bssfp_contrast( std::shared_ptr const ptr_to_tiss_par, + const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr) +{ + // Signal model based on Haacke/Brown - Magnetic Resonance Imaging, Ch. 18.2.1, Eq. 18.57 f + // Frequency response assumed to be of the form as described in 'Hargreaves et al. "Fat‐suppressed steady‐state free precession imaging using phase detection.", MRM(2003)' + + SequenceParameters const sequ_par = *(ismrmrd_hdr.sequenceParameters); + AcquisitionSystemInformation const asi = *(ismrmrd_hdr.acquisitionSystemInformation); + + SeqParamType const TE = *(sequ_par.TE); + SeqParamType TR; + + try + { + TR = *(sequ_par.echo_spacing); + + } + catch(const std::runtime_error &e) + { + std::cout << "Caught exception in map_bssfp_contrast." << std::endl; + std::cout << e.what() < 1) + throw std::runtime_error(" More than one echo spacing was given. Please give only one in Flash contrast."); + + if (flip_angle_deg.size() > 1) + throw std::runtime_error(" More than one flip angle was given. Please give only one in Flash contrast."); + + size_t const num_echoes = TE.size(); + + float const spin_dens = ptr_to_tiss_par->mr_tissue_.spin_density_percentH2O_; + float const T1_ms = ptr_to_tiss_par->mr_tissue_.t1_miliseconds_; + float const T2_ms = ptr_to_tiss_par->mr_tissue_.t2_miliseconds_; + float const cs_ppm = ptr_to_tiss_par->mr_tissue_.cs_ppm_; + + std::vector< complex_float_t > contrast; + contrast.resize( num_echoes ); + + complex_float_t const imag_unit(0,1); + float const gyro = 42.58*2*M_PI; + + float const E1 = exp( -1.f* TR[0]/T1_ms ); + float const E2 = exp( -1.f* TR[0]/T2_ms ); + + float const sin_term = sin(M_PI/180.f*flip_angle_deg[0]); + float const cos_term = cos(M_PI/180.f*flip_angle_deg[0]); + + + // assuming passband step function behavior for magnetization sign. + float const BSSFPPassbandWidth_Hz = 1000.f * 1.f/TR[0]; + float const off_resonance_Hz = TR[0] * gyro/1000.f * field_strength_t * cs_ppm; + bool const even_band = ((int)std::floor( (off_resonance_Hz + BSSFPPassbandWidth_Hz/2.f) / BSSFPPassbandWidth_Hz ) % 2) ==0; + float const sign_bssfp = even_band? 1.f:-1.f; + + // assuming on resonance (or for fat, outside the signal-response valley) + for( int i_echo = 0; i_echotemplate_pet_image_data_ = sirf::STIRImageData(filename_header_with_ext); + this->template_img_is_set_ = true; + +}; + +std::vector< STIRImageData >& PETContrastGenerator::get_contrast_filled_volumes(bool const resample_output) +{ + if( resample_output == true ) + this->resample_to_template_image(); + + return this->contrast_filled_volumes_; +} + + + +std::vector< int > PETContrastGenerator::get_dimensions( void ) +{ + if( this->template_img_is_set_ ) + { + std::vector< int > dims; + dims.resize(3, 0); + + auto works = this->template_pet_image_data_.get_dimensions(&dims[0]); + + if(works == -1) + throw std::runtime_error("Irregular range of dimensions in PET image data."); + + std::reverse( dims.begin(), dims.end() ); + return dims; + } + else + { + std::stringstream error_msg; error_msg << "From " << __FUNCTION__ << ": please set image template first using the dedicated function."; + throw std::runtime_error( error_msg.str() ); + } +} + +std::vector< float > PETContrastGenerator::get_voxel_sizes( void ) +{ + if( this->template_img_is_set_ ) + { + std::vector< float > vx_size; + vx_size.resize(3, 0); + + this->template_pet_image_data_.get_voxel_sizes(&vx_size[0]); + + return vx_size; + } + else + { + std::stringstream error_msg; error_msg << "From " << __FUNCTION__ << ": please set image template first using the dedicated function."; + throw std::runtime_error( error_msg.str() ); + } +} + +void PETContrastGenerator::map_tissue() +{ + this->map_contrast(); + this->map_attenuation(); +} + +void PETContrastGenerator::map_contrast() +{ + this->contrast_filled_volumes_.clear(); + this->map_tissueparams_member( CASE_MAP_PET_CONTRAST ); +} + +void PETContrastGenerator::map_attenuation() +{ + this->map_tissueparams_member( CASE_MAP_PET_ATTENUATION ); +} + +void PETContrastGenerator::map_tissueparams_member(int const case_map) +{ + using namespace stir; + + if (this->template_img_is_set_) + { + + const int* segmentation_dims = this->tlm_.get_segmentation_dimensions(); + + size_t const num_voxels = size_t( segmentation_dims[1] * segmentation_dims[2] * segmentation_dims[3] ); + + std::vector < float > contrast_img; + contrast_img.resize(num_voxels, 0); + + // STIRImageData pet_img_dat( template_pet_image_data_ ); + LabelVolume nifti_seg = this->tlm_.get_segmentation_labels(); + + nifti_seg.write("temp_niftisegmentation.nii"); + STIRImageData pet_img_dat( "temp_niftisegmentation.nii" ); + + std::vector< float > voxel_sizes(3,0.f); + pet_img_dat.get_voxel_sizes(&voxel_sizes[0]); + float const voxel_volume_ml = voxel_sizes[0] * voxel_sizes[1] * voxel_sizes[2] / 1000.f; + + TissueVector tissue_params = this->tlm_.get_segmentation_tissues(); + + // #pragma omp parallel + for( size_t i_vox=0; i_voxcontrast_filled_volumes_.push_back( pet_img_dat ); + } + else + throw std::runtime_error("To get dimensions of output correct please set image from file as template first using the dedicated method."); + +} + + + +void PETContrastGenerator::resample_to_template_image( void ) +{ + + NiftyResampler resampler; + + resampler.set_interpolation_type_to_cubic_spline(); + resampler.set_reference_image(std::make_shared< sirf::STIRImageData > (this->template_pet_image_data_)); + + auto contrast_volumes = this->get_contrast_filled_volumes(); + + for(int i=0; i( contrast_volumes[i])); + resampler.process(); + const std::shared_ptr sptr_deformed_img = resampler.get_output_sptr(); + + this->contrast_filled_volumes_[i] = sirf::STIRImageData( this->template_pet_image_data_ ); //constructor for STIRImageData from ImageData does not exist yet. + + sptr_deformed_img->copy(sptr_deformed_img->begin(), + this->contrast_filled_volumes_[i].begin(), + this->contrast_filled_volumes_[i].end()); + } +} diff --git a/src/xDynamicSimulation/cDynamicSimulation/dynamics.cpp b/src/xDynamicSimulation/cDynamicSimulation/dynamics.cpp new file mode 100644 index 000000000..3ad069ad1 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/dynamics.cpp @@ -0,0 +1,744 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.08.01 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "sirf/iUtilities/LocalisedException.h" +#include "sirf/common/multisort.h" +#include "sirf/cDynamicSimulation/dynamics.h" + +#include "sirf/Reg/NiftyResample.h" +#include "sirf/Reg/NiftiImageData3D.h" +#include "sirf/Reg/NiftiImageData3DDeformation.h" +#include "sirf/Reg/NiftiImageData3DDisplacement.h" + +#include <_reg_localTrans.h> + +using namespace std; +using namespace sirf; + + +bool sirf::is_in_bin( SignalAxisType const signal, SignalBin const bin) +{ + + auto bin_min = std::get<0>(bin); + auto bin_max = std::get<2>(bin); + + if( bin_min < bin_max ) + return (signal >= bin_min && signal <= bin_max); + else if ( bin_min > bin_max ) + return (signal >= bin_min || signal <= bin_max); + else + return false; +} + +// data are taken from one_dat, just scan_counters are compared +MRDataType sirf::intersect_mr_acquisition_data( const MRAcquisitionData& one_dat, const MRAcquisitionData& other_dat) +{ + typedef std::vector CounterBox; + + CounterBox one_counters, other_counters; + + ISMRMRD::Acquisition acq; + + std::map map_counter_idx; + + for( size_t i=0; i(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins) - 1.f/(2*num_bins); + std::get<1>(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins); + std::get<2>(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins) + 1.f/(2*num_bins); + + std::get<0>(bin) = std::get<0>(bin) < 0 ? ( 1 + std::get<0>(bin) ) : std::get<0>(bin); + std::get<1>(bin) = std::get<1>(bin) < 0 ? ( 1 + std::get<1>(bin) ) : std::get<1>(bin); + std::get<2>(bin) = std::get<2>(bin) < 0 ? ( 1 + std::get<2>(bin) ) : std::get<2>(bin); + + this->signal_bins_.push_back( bin ); + } +} + +void BinProcessor::set_non_cyclic_bins(int const num_bins) +{ + for(int i_state=0; i_state(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins); + std::get<1>(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins) + 1.f/(2*num_bins); + std::get<2>(bin) = SignalAxisType(i_state)/SignalAxisType(num_bins) + 1.f/(num_bins); + + this->signal_bins_.push_back( bin ); + } +} + + +SignalAxisType SurrogateProcessor::linear_interpolate_signal(const TimeAxisType time_point) const +{ + + size_t const num_sig_points = this->signal_.size(); + ASSERT(num_sig_points > 1, "You have at least two signal points before you interpolate between them."); + + size_t first_bigger_than_time_point=-1; + + for( size_t i=0; isignal_[i].first > time_point) + { + first_bigger_than_time_point = i; + break; + } + } + + SignalAxisType interpol_signal; + + if( first_bigger_than_time_point == 0) + interpol_signal = this->signal_[0].second; + else if( first_bigger_than_time_point == -1 ) + interpol_signal = this->signal_[num_sig_points-1].second; + else + { + interpol_signal = signal_[first_bigger_than_time_point-1].second + + (time_point - signal_[first_bigger_than_time_point-1].first ) + *(signal_[first_bigger_than_time_point].second - signal_[first_bigger_than_time_point-1].second) + /(signal_[first_bigger_than_time_point].first - signal_[first_bigger_than_time_point-1].first ); + } + + return interpol_signal; + +} + +// static member variable initialization +std::vector< TimeAxisType > ContrastProcessor::time_points_sampled_ = std::vector{}; + +void ContrastProcessor::set_parameter_extremes(TissueParameter tiss_at_0, TissueParameter tiss_at_1) +{ + this->tissue_parameter_extremes_.first = tiss_at_0; + this->tissue_parameter_extremes_.second = tiss_at_1; +} + + +TissueParameterList ContrastProcessor::get_interpolated_tissue_params(SignalAxisType const signal) const +{ + TissueParameterList tiss_list; + + for(size_t i=0; i< this->list_cont_var_labels_.size(); i++) + { + TissueParameter curr_par = ((1-signal) * this->tissue_parameter_extremes_.first + signal * this->tissue_parameter_extremes_.second); + curr_par.name_ = ""; // name info is lost unfortunately + curr_par.label_ = this->list_cont_var_labels_[i]; + + tiss_list.push_back( curr_par); + } + + return tiss_list; +} + + +int MotionProcessor::num_total_motion_processors_ = 0; + +MotionProcessor::MotionProcessor() +{ + this->which_motion_processor_am_i_ = num_total_motion_processors_; + this->num_total_motion_processors_ += 1; + + this->temp_folder_name_ = setup_tmp_folder_name(); + this->ground_truth_folder_name_ = setup_gt_folder_name(); +} + +MotionProcessor::~MotionProcessor() +{ + // if( this->destroy_upon_deletion_) + // this->delete_temp_folder(); + + this->num_total_motion_processors_ -= 1; +} + + +NiftiImageData3DDeformation MotionProcessor::get_interpolated_deformation_field(const SignalAxisType signal, const bool cyclic) const +{ + if( this->temp_mvf_filenames_.size() == 0 && this->displacement_fields_.size() == 0) + throw std::runtime_error("Before calling get_interpolated_deformation_field: Please use prep_displacement_fields() if the fields are not kept in memory, or set_displacement_fields() if they are."); + + if (signal > 1.f || signal< 0.f) + throw std::runtime_error("Please pass a signal in the range of [0,1]."); + + size_t const num_motion_fields = keep_motion_fields_in_memory_? this->displacement_fields_.size() : this->temp_mvf_filenames_.size(); + + // check in which interval the signal lies + SignalAxisType signal_on_bin_range; + + if( cyclic ) + signal_on_bin_range = num_motion_fields * signal; + else + signal_on_bin_range = (num_motion_fields - 1)* signal; + + int const bin_floor = (int( signal_on_bin_range + 1) -1) % num_motion_fields;; + int const bin_ceil = int( signal_on_bin_range + 1) % num_motion_fields; + + SignalAxisType const linear_interpolation_weight = signal_on_bin_range - bin_floor; + + /// Constructor + sirf::ImageWeightedMean dvf_interpolator; + + if(keep_motion_fields_in_memory_) + { + dvf_interpolator.add_image( this->displacement_fields_.at(bin_floor), 1 - linear_interpolation_weight); + dvf_interpolator.add_image( this->displacement_fields_.at(bin_ceil), linear_interpolation_weight); + } + else + { + dvf_interpolator.add_image( temp_mvf_filenames_.at(bin_floor), 1 - linear_interpolation_weight); + dvf_interpolator.add_image( temp_mvf_filenames_.at(bin_ceil), linear_interpolation_weight); + } + + + dvf_interpolator.process(); + + sirf::NiftiImageData3DDisplacement interpolated_dvf( *dvf_interpolator.get_output_sptr() ); + + return interpolated_dvf.get_as_deformation_field( interpolated_dvf ); +} + +std::string MotionProcessor::setup_tmp_folder_name() +{ + std::string const current_folder_prefix = "temp_folder_motion_dyn_"; + std::stringstream tmp_stream; + tmp_stream << this->temp_folder_prefix_ << current_folder_prefix << this->which_motion_processor_am_i_; + return tmp_stream.str(); + +} + +std::string MotionProcessor::setup_gt_folder_name() +{ + std::string const gt_folder_prefix = "ground_truth_folder_motion_dyn_"; + std::stringstream name_stream; + name_stream << this->temp_folder_prefix_ << gt_folder_prefix << this->which_motion_processor_am_i_; + return name_stream.str(); +} + +bool MotionProcessor::make_ground_truth_folder() const +{ + try + { + std::cout << "Generating ground truth folder " << this->ground_truth_folder_name_ << std::endl; + boost::filesystem::path dir_to_make(this->ground_truth_folder_name_.c_str()); + bool folder_creation_worked = boost::filesystem::create_directories(dir_to_make); + + return folder_creation_worked; + + } + catch(boost::system::error_code& e) + { + std::cout << e.message() << std::endl; + throw e; + } +} + +bool MotionProcessor::make_temp_folder() const +{ + try + { + std::cout << "Generating temporary folder " << this->temp_folder_name_ << std::endl; + boost::filesystem::path dir_to_make(this->temp_folder_name_.c_str()); + bool folder_creation_worked = boost::filesystem::create_directories(dir_to_make); + return folder_creation_worked; + + } + catch(boost::system::error_code& e) + { + std::cout << e.message() << std::endl; + throw e; + } +} + +bool MotionProcessor::delete_temp_folder() const +{ + try + { + boost::filesystem::path dir_to_del( this->temp_folder_name_.c_str() ); + + if( boost::filesystem::exists(dir_to_del) ) + { + std::cout << "Deleting temporary folder " << this->temp_folder_name_ << std::endl; + + bool folder_deletion_worked = boost::filesystem::remove_all(dir_to_del); + return folder_deletion_worked; + } + else + { + std::cout << "Folder " << this->temp_folder_name_ << " does not exist. Deletion omitted" << std::endl; + return false; + } + + } + catch(boost::system::error_code& e) + { + std::cout << e.message() << std::endl; + throw e; + } +} + + +void MotionProcessor::set_displacement_fields(const std::vector< sirf::NiftiImageData3DDisplacement > &input_displacement_fields) +{ + for(size_t i=0; idisplacement_fields_.push_back( input_displacement_fields[i] ); +} + + +sirf::NiftiImageData3DDisplacement MotionProcessor::scale_displacementfields_to_mm( const sirf::NiftiImageData3DDisplacement &dvf ) const +{ + const int* dvf_dims = dvf.get_dimensions() ; + + sirf::VoxelisedGeometricalInfo3D::Spacing voxel_sizes = dvf.get_geom_info_sptr()->get_spacing(); + + if( dvf_dims[5] != 3) + throw std::runtime_error( "The dimensions of your dvf are not 3D in the 4th spot of the dims but instead." ); + + sirf::NiftiImageData3DDisplacement scaled_dvf(dvf); + + for(int nz=0; nz<(int)dvf_dims[3]; nz++) + for(int ny=0; ny<(int)dvf_dims[2]; ny++) + for(int nx=0; nx<(int)dvf_dims[1]; nx++) + { + + for(int nv=0; nvdisplacement_fields_.size() == 0) + throw std::runtime_error("Please call set_displacement_fields() first."); + + std::cout << "Preparing displacement fields ... " <make_temp_folder(); + + if( temp_folder_creation_successful ) + { + for(int i=0; idisplacement_fields_.size(); i++) + { + std::stringstream temp_filename_mvf; + temp_filename_mvf << this->temp_folder_name_ << this->temp_mvf_prefix_ << i; + + this->displacement_fields_[i].write( temp_filename_mvf.str() ); + + temp_filename_mvf << ".nii"; + this->temp_mvf_filenames_.push_back(temp_filename_mvf.str()); + } + } + + else + throw std::runtime_error("The parent directory generation failed. Give a path to which thou hast access rights. Or maybe the directory already exists. This is dangerous. Then you should definitely choose a different temporary folder name."); + + std::vector empty_container; + this->displacement_fields_.swap(empty_container); + } + + std::cout << "... finished." <& gt_signal_points, const bool cyclic) const +{ + bool const correct_for_offset = false; + + this->make_ground_truth_folder(); + + auto minimum_pos = std::min_element(std::begin(gt_signal_points), std::end(gt_signal_points)); + SignalAxisType gt_signal_offset = *minimum_pos; + + std::cout << "Subtracting offset of signal " << gt_signal_offset << std::endl; + + NiftiImageData3DDeformation offset_deformation = this->get_interpolated_deformation_field( gt_signal_offset, cyclic); + + std::shared_ptr > sptr_inverse_offset_deformation; + + if( correct_for_offset ) + sptr_inverse_offset_deformation = std::make_shared >(calc_inverse_offset_deformation( offset_deformation )); + + for( size_t i=0; i gt_deformation_with_offset = this->get_interpolated_deformation_field( gt_signal_points[i], cyclic); + + std::vector< std::shared_ptr > > vec_gt_def_with_offset; + + vec_gt_def_with_offset.push_back(std::make_shared >( gt_deformation_with_offset )); + + if( correct_for_offset ) + vec_gt_def_with_offset.push_back(sptr_inverse_offset_deformation); + + NiftiImageData3DDeformation gt_deformation; + + if( correct_for_offset ) + gt_deformation = NiftiImageData3DDeformation::compose_single_deformation(vec_gt_def_with_offset, *sptr_inverse_offset_deformation); + else + gt_deformation = gt_deformation_with_offset; + + stringstream sstream_output; + sstream_output << this->ground_truth_folder_name_ << "/gt_deformation_state_" << gt_signal_points[i]; + std::cout << sstream_output.str() << std::endl; + + NiftiImageData3DDisplacement const gt_displacement( gt_deformation ); + gt_displacement.write( sstream_output.str() ); + + } +} + + + +NiftiImageData3DDeformation +MotionProcessor::calc_inverse_offset_deformation(NiftiImageData3DDeformation offset_deformation) const +{ + std::shared_ptr sptr_offset_deformation = offset_deformation.get_raw_nifti_sptr(); + std::shared_ptr sptr_inverse_offset_deformation = std::make_shared< nifti_image >(*sptr_offset_deformation); + + float const inversion_tolerance = 0.01f; + + std::cout << "Inverting offset vectorfield ... " << std::endl; + + reg_defFieldInvert(sptr_offset_deformation.get(), sptr_inverse_offset_deformation.get(), inversion_tolerance ); + + std::cout << " ... finished." << std::endl; + + NiftiImageData3DDeformation inverse_offset( *sptr_inverse_offset_deformation ); + + return inverse_offset; +} + + +void MRMotionDynamic::bin_mr_acquisitions( MRAcquisitionData& all_acquisitions ) +{ + std::cout << "Binning motion dynamics\n"; + this->clear_binning_data(); + + if(bp_.get_bins().size() == 1) + { + std::cout << "We have only one bin, we take all the acquisitions" << std::endl; + MRDataType av(all_acquisitions.acquisitions_info()); + ISMRMRD::Acquisition acq; + for(int i=0; i relevant_acq_numbers; + std::deque< size_t > acq_not_binned; + + + for( size_t i=0; i signal_bins = bp_.get_bins(); + this->idx_corr_.resize(signal_bins.size()); + + for( int i_bin=0; i_bin 0 ) + { + auto curr_pos = relevant_acq_numbers[0]; + relevant_acq_numbers.pop_front(); + ISMRMRD::Acquisition acq; + all_acquisitions.get_acquisition( curr_pos, acq ); + + TimeAxisType acq_time_seconds = SIRF_SCANNER_MS_PER_TIC/1000.f * (TimeAxisType)acq.getHead().acquisition_time_stamp - time_offset_seconds; + + SignalAxisType signal_of_acq = this->interpolate_signal(acq_time_seconds); + + if( is_in_bin(signal_of_acq, bin) ) + { + this->idx_corr_[i_bin].push_back(curr_pos); + curr_acq_vector.append_acquisition(acq); + } + else + acq_not_binned.push_back(curr_pos); + } + + relevant_acq_numbers.swap(acq_not_binned); + this->binned_mr_acquisitions_.push_back( curr_acq_vector ); + } +} + +void MRContrastDynamic::bin_mr_acquisitions( MRAcquisitionData& all_acquisitions ) +{ + std::cout << "######################## Binning contrast dynamics\n"; + this->clear_binning_data(); + cp_.empty_timepoints(); + all_acquisitions.sort_by_time(); + + size_t const num_acquis = all_acquisitions.number(); + ISMRMRD::Acquisition acq; + all_acquisitions.get_acquisition(num_acquis-1, acq); + float const t_fin = acq.getHead().acquisition_time_stamp; + + all_acquisitions.get_acquisition(0, acq); + float const t_start = acq.getHead().acquisition_time_stamp; + + TimeAxisType total_time_ms = SIRF_SCANNER_MS_PER_TIC * (t_fin - t_start); + std::vector< size_t > index_lims; + const std::vector signal_bins = bp_.get_bins(); + + this->idx_corr_.resize(signal_bins.size()); + + for( size_t i=0; i(signal_bins[i])*num_acquis ); + cp_.add_timepoint(total_time_ms * std::get<1>(signal_bins[i])); + } + + int start_index = 0; + int stop_index = 0; + + int const num_bins = bp_.get_num_bins(); + + for( size_t i_bin=0; i_binidx_corr_[i_bin].push_back(i); + } + + this->binned_mr_acquisitions_.push_back( av ); + start_index = stop_index; + } +} + +// ++++++++++++++++++++++++++++++++ PET ++++++++++++++++++++++++++++++++++++++++++++++ + + +sirf::TimeBin sirf::intersect_time_intervals( const TimeBin& one_interval, const TimeBin& other_interval) +{ + return intersect_intervals(one_interval, other_interval); +} + +sirf::TimeBinSet sirf::intersect_time_bin_sets( const TimeBinSet& one_set, const TimeBinSet& other_set) +{ + TimeBinSet intersected_set; + for(size_t i=0; i upsampled_time_pts; + std::vector upsampled_signal; + + TimeAxisType current_time = time_interval_total_dynamic_process_seconds.min_; + std::cout << "Upsampling signal to temporal resolution of 1ms." << std::endl; + + while(current_time <= time_interval_total_dynamic_process_seconds.max_) + { + upsampled_time_pts.push_back(current_time); + upsampled_signal.push_back( sp_.linear_interpolate_signal(current_time)); + + current_time +=delta_time_s; + } + + std::cout << "Finished upsampling signal." << std::endl; + + size_t const num_bins = bp_.get_num_bins(); + const std::vector signal_bins = bp_.get_bins(); + + for( size_t i_bin=0; i_bin(bin); + auto bin_max = std::get<2>(bin); + + TimeBin curr_time_interval; + bool new_bin = true; + bool no_intersection = true; + TimeAxisType time_in_bin = 0; + + for(size_t j=0; j= bin_min && curr_sig< bin_max) + { + if(new_bin) + { + curr_time_interval.min_ = upsampled_time_pts[j]; + new_bin = false; + } + + time_in_bin += delta_time_s; + } + else + { + no_intersection = false; + curr_time_interval.max_ = curr_time_interval.min_ + time_in_bin; + time_intervals_for_bin.push_back( curr_time_interval ); + time_in_bin = 0; + new_bin = true; + } + } + + if( no_intersection ) + { + curr_time_interval.max_ = curr_time_interval.min_ + time_in_bin; + time_intervals_for_bin.push_back( curr_time_interval ); + } + + this->binned_time_intervals_.push_back( time_intervals_for_bin ); + } +} + + +sirf::TimeAxisType sirf::get_time_from_between_two_signal_points(SignalAxisType signal, SignalPoint left_point, SignalPoint right_point) +{ + if(std::abs(right_point.second - left_point.second) < 1e-8) + return (right_point.first + left_point.first)/TimeAxisType(2); + else + return (signal-left_point.second) * (right_point.first - left_point.first) / (right_point.second - left_point.second) + left_point.first; +} + +sirf::TimeBinSet PETDynamic::get_time_bin_set_for_state( unsigned int const which_state ) +{ + if(which_state >= binned_time_intervals_.size()) + throw std::runtime_error( " Please give a number not larger than the number of dynamic states-1"); + + + return this->binned_time_intervals_[which_state]; +} + +sirf::TimeAxisType PETDynamic::get_time_spent_in_bin(unsigned int const which_state ) +{ + if(which_state >= binned_time_intervals_.size()) + throw std::runtime_error( " Please give a number not larger than the number of dynamic states-1"); + + + return sirf::get_total_time_in_set( this->binned_time_intervals_[which_state] ); + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/dynamicsimulation_x.cpp b/src/xDynamicSimulation/cDynamicSimulation/dynamicsimulation_x.cpp new file mode 100644 index 000000000..6388e43f2 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/dynamicsimulation_x.cpp @@ -0,0 +1,639 @@ +/* +author: johannes mayer +date: 15. March 2018 + +*/ + +#include +#include +#include + +#include "sirf/cDynamicSimulation/dynamicsimulation_x.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/Reg/NiftiImageData3DDeformation.h" + +#include "sirf/Gadgetron/gadgetron_data_containers.h" + +using namespace sirf; +using std::cout; +using std::endl; + + +#define PET_GLOBAL_NOISE_SCALING 4.0f + +void MRDynamicSimulation::write_simulation_results( const std::string& filename_output_with_h5_extension ) +{ + try + { + cout << "Started writing simulation output to: " << filename_output_with_h5_extension <sptr_simul_data_->number() << endl; + + sptr_simul_data_->sort_by_time(); + sptr_simul_data_->write( filename_output_with_h5_extension.c_str() ); + cout << "Finished writing simulation output."<empty(); + sptr_simul_data_->copy_acquisitions_info(*sptr_source_acquisitions_); + + if(external_contrast_.size() == 1) + { + cout << "Simulating dynamic data acquisition of pre-computed signal... " <bin_mr_acquisitions(*sptr_source_acquisitions_); + this->simulate_external_contrast_motion_dynamics(); + return; + } + else if(external_contrast_.size() > 0) + { + throw std::runtime_error("You supplied more than one external contrast dynamic. Only one is allowed."); + } + else + { + cout << "Simulating dynamic signal generation and data acquisition... " <bin_mr_acquisitions(*sptr_source_acquisitions_); + + for(int i=0; ibin_mr_acquisitions(*sptr_source_acquisitions_); + + this->simulate_simultaneous_motion_contrast_dynamics(); + } +} + + + +void MRDynamicSimulation::simulate_simultaneous_motion_contrast_dynamics() +{ + cout << "Simulating motion and contrast dynamics... " <contrast_dynamics_.size(); + size_t num_contrast_states = (num_contrast_dyns > 0)? contrast_dynamics_[0]->get_num_simul_states() : 1; + + LinearCombiGenerator motionmode_combinator = this->prepare_motion_information(); + + size_t const num_tot_motion_states = motionmode_combinator.get_num_total_combinations(); + std::vector< DimensionsType > all_motion_state_combos = motionmode_combinator.get_all_combinations(); + + + std::vector binned_contrast_acquisitions = (num_contrast_dyns > 0 ) ? contrast_dynamics_[0]->get_binned_mr_acquisitions() : std::vector< AcquisitionsVector >(); + + std::vector< TimeAxisType > sampled_contrast_timepoints = (num_contrast_dyns > 0 ) ? contrast_dynamics_[0]->get_sampled_time_points() : std::vector< TimeAxisType >(0); + + + for( size_t i_motion_state=0; i_motion_state < num_tot_motion_states; i_motion_state++) + { + cout << " ++++++++++++ Acquisition dynamic motion state #" << i_motion_state + 1 << "/" << num_tot_motion_states << "++++++++++++++" << endl; + + DimensionsType current_combination = all_motion_state_combos[i_motion_state]; + AcquisitionsVector acquisitions_for_this_motion_state = this->get_acquisitions_for_motionstate(current_combination); + + if( acquisitions_for_this_motion_state.number() > 0) + { + for( size_t i_contrast_state=0; i_contrast_state0) + { + AcquisitionsVector tmp_acq_for_contrast_dyn = binned_contrast_acquisitions[ i_contrast_state ]; + acquisitions_for_this_contrast_state = intersect_mr_acquisition_data(acquisitions_for_this_contrast_state, tmp_acq_for_contrast_dyn); + } + + cout << "# of mr acquis in this dynamic motion x contrast state: " << acquisitions_for_this_contrast_state.number() << endl; + std::vector > motionfields_for_current_state = + this->get_motionfields_for_motionstate(current_combination); + + if( acquisitions_for_this_contrast_state.number() > 0) + { + TimeAxisType current_time_point = sampled_contrast_timepoints.size()>0 ? sampled_contrast_timepoints[i_contrast_state] : (TimeAxisType)0; + this-> update_tissue_parameters(current_time_point); + + // crucial to call here, as the tissue parameters have been replaced and + // deformation results in motion-deformed images in contrast generator + this->mr_cont_gen_.map_contrast(); + dsd_.deform_contrast_generator(this->mr_cont_gen_, motionfields_for_current_state); + + this->sptr_template_data_ = std::shared_ptr(std::move(acquisitions_for_this_contrast_state.clone())); + this->acquire_raw_data(); + } + } + } + } + + this->noise_generator_.add_noise(*sptr_simul_data_); + + for(size_t i=0; imotion_dynamics_[i]->delete_temp_folder(); + +} + +AcquisitionsVector +MRDynamicSimulation::get_acquisitions_for_motionstate(DimensionsType current_combination) const +{ + AcquisitionsVector acquisitions_for_this_motion_state; + + size_t const num_motion_dyns = this->motion_dynamics_.size(); + for( int i_motion_dyn = 0; i_motion_dynmotion_dynamics_[ i_motion_dyn ]; + int const current_bin = current_combination[ i_motion_dyn ]; + + AcquisitionsVector acquis_in_motion_bin = motion_dyn->get_binned_mr_acquisitions( current_bin ); + if(i_motion_dyn == 0) + acquisitions_for_this_motion_state = intersect_mr_acquisition_data(*sptr_source_acquisitions_, acquis_in_motion_bin); + else + acquisitions_for_this_motion_state = intersect_mr_acquisition_data(acquisitions_for_this_motion_state, acquis_in_motion_bin); + } + + if(num_motion_dyns<1) + acquisitions_for_this_motion_state = intersect_mr_acquisition_data(*sptr_source_acquisitions_,*sptr_source_acquisitions_); + + return acquisitions_for_this_motion_state; +} + +std::vector > +MRDynamicSimulation::get_motionfields_for_motionstate(DimensionsType current_combination) const +{ + std::vector > mvfs; + size_t const num_motion_dyns = this->motion_dynamics_.size(); + + for( int i_motion_dyn = 0; i_motion_dynmotion_dynamics_[i_motion_dyn]; + std::vector< SignalBin > signal_bins = sptr_motion_dyn->get_bins(); + + int const motion_bin_number = current_combination[i_motion_dyn]; + SignalBin bin = signal_bins[ motion_bin_number ]; + + std::cout << "Getting interpolated motion field in state " << std::get<1>(bin) << std::endl; + mvfs.push_back( sptr_motion_dyn->get_interpolated_deformation_field( std::get<1>(bin) ) ); + } + + return mvfs; +} + +LinearCombiGenerator MRDynamicSimulation::prepare_motion_information() +{ + std::vector< int > num_states_per_motion; + size_t const num_motion_dyns = this->motion_dynamics_.size(); + + for(size_t i=0; iget_num_simul_states()); + motion_dynamics_[i]->prep_displacement_fields(); + } + + if(num_states_per_motion.empty()) + num_states_per_motion.push_back(1); + + LinearCombiGenerator lcg(num_states_per_motion); + return lcg; +} + + +void MRDynamicSimulation::update_tissue_parameters(TimeAxisType current_time_point) +{ + for( int i_contrast_dyn = 0; i_contrast_dyncontrast_dynamics_[i_contrast_dyn]; + auto contrast_signal = sptr_contrast_dyn->interpolate_signal(current_time_point); + + TissueParameterList tissueparameter_list_to_replace = sptr_contrast_dyn->get_interpolated_tissue_params( contrast_signal ); + + for( size_t i_tiss=0; i_tiss< tissueparameter_list_to_replace.size(); i_tiss++ ) + { + TissueParameter curr_param = tissueparameter_list_to_replace[i_tiss]; + this->mr_cont_gen_.replace_petmr_tissue_parameters(curr_param.label_, curr_param); + } + } +} + +void MRDynamicSimulation::simulate_external_contrast_motion_dynamics() +{ + + std::shared_ptr sptr_ed = external_contrast_[0]; + const size_t num_simul_states = sptr_ed->get_num_simul_states(); + for(unsigned int i=0; iget_binned_mr_acquisitions(i); + + ISMRMRD::Acquisition acq; + acquisitions_for_this_contrast_state.get_acquisition(0, acq); + uint32_t const timepoint_sec = SIRF_SCANNER_MS_PER_TIC * acq.acquisition_time_stamp()/1000; + + std::vector > current_mvfs; + for( int i_motion_dyn = 0; i_motion_dynget_interpolated_deformation_field_at_timepoint(timepoint_sec)); + + mr_cont_gen_.map_contrast(sptr_ed->get_tissue_signals(i)); + dsd_.deform_contrast_generator(this->mr_cont_gen_, current_mvfs); + + sptr_template_data_ = std::shared_ptr(std::move(acquisitions_for_this_contrast_state.clone())); + acquire_raw_data(); + } +} + +void MRDynamicSimulation::set_noise_scaling() +{ + this->noise_generator_.set_sampling_specific_scaling(RPE_NOISE_SCALING); +} + + +void MRDynamicSimulation::shift_time_start_to_zero( void ) +{ + sptr_source_acquisitions_->sort_by_time(); + + ISMRMRD::Acquisition acq; + sptr_source_acquisitions_->get_acquisition(0, acq); + uint32_t const t0 = acq.acquisition_time_stamp(); + + for(size_t i=0; inumber(); ++i) + { + sptr_source_acquisitions_->get_acquisition(i, acq); + acq.acquisition_time_stamp() -= t0; + sptr_source_acquisitions_->set_acquisition(i, acq); + } +} + +void MRDynamicSimulation::set_acquisition_template_rawdata(const MRAcquisitionData& acquisitions) +{ + sptr_source_acquisitions_ = std::shared_ptr + (std::move(acquisitions.clone())); + + sptr_simul_data_ = std::shared_ptr + (std::move(acquisitions.clone())); + + dsd_.set_template_rawdata(acquisitions); + sptr_simul_data_->empty(); + this->shift_time_start_to_zero(); + +} +void MRDynamicSimulation::set_contrast_template_rawdata(const MRAcquisitionData& acquisitions) +{ + mr_cont_gen_.set_template_rawdata(acquisitions); +} + +void MRDynamicSimulation::set_SNR(float const SNR) +{ + this->noise_generator_.set_SNR(SNR); +} + +void MRDynamicSimulation::set_noise_label(int const label) +{ + auto const signal_in_label = this->mr_cont_gen_.get_signal_for_tissuelabel(label); + auto const abs_signal = std::abs( signal_in_label ); + + std::cout << "Adding signal " << abs_signal << " for label " << label << std::endl; + this->noise_generator_.set_signal_img( abs_signal ); +} + + +void MRDynamicSimulation::acquire_raw_data( void ) +{ + sirf::GadgetronImagesVector contrast_filled_volumes = this->mr_cont_gen_.get_contrast_filled_volumes(); + + this->acq_model_.set_up(this->sptr_template_data_, std::make_shared(contrast_filled_volumes)); + std::shared_ptr sptr_rawdata = acq_model_.fwd(contrast_filled_volumes); + + for(int i=0; inumber();++i) + { + ISMRMRD::Acquisition acq; + sptr_rawdata->get_acquisition(i, acq); + this->sptr_simul_data_->append_acquisition(acq); + } +} + + + +void MRDynamicSimulation::save_ground_truth_displacements( void ) const +{ + for(size_t i=0; imotion_dynamics_.size(); i++) + { + this->motion_dynamics_[i]->save_ground_truth_displacements(); + } +} + +void MRDynamicSimulation::save_groud_truth_parameter_maps( const std::string prefix_output ) +{ + this->mr_cont_gen_.map_parameters(); + std::vector > parameter_maps = this->mr_cont_gen_.get_parameter_filled_volumes(); + + std::vector > average_motion_fields; + for(auto& modyn : motion_dynamics_) + { + average_motion_fields.push_back(modyn->get_average_deformation_field(*sptr_source_acquisitions_)); + } + dsd_.add_offset_deformation(average_motion_fields); + + std::stringstream fname_output; + fname_output << prefix_output << "_spindensity.nii"; + NiftiImageData3D tmp_img; + + tmp_img = dsd_.resample_to_template(parameter_maps[0]); + tmp_img.write(fname_output.str()); + fname_output.str(std::string()); + + fname_output << prefix_output << "_T1_ms.nii"; + tmp_img = dsd_.resample_to_template(parameter_maps[1]); + tmp_img.write(fname_output.str()); + fname_output.str(std::string()); + + fname_output << prefix_output << "_T2_ms.nii"; + tmp_img = dsd_.resample_to_template(parameter_maps[2]); + tmp_img.write(fname_output.str()); + fname_output.str(std::string()); + + fname_output << prefix_output << "_labels.nii"; + tmp_img = dsd_.resample_to_template(parameter_maps[4]); + tmp_img.write(fname_output.str()); + fname_output.str(std::string()); + +} + + +void PETDynamicSimulation::write_simulation_results( const std::string& filename_output_with_extension ) +{ + + std::cout << "Writing PET rawdata ... "; + this->sptr_target_acquisitions_->write( filename_output_with_extension.c_str() ); + std::cout << "finished." << std::endl; + + this->pet_cont_gen_.map_tissue(); + + std::vector< STIRImageData > contrast_filled_volumes = this->pet_cont_gen_.get_contrast_filled_volumes(); + + STIRImageData attenuation_map = contrast_filled_volumes[1]; + + attenuation_map = this->get_reduced_pet_img_in_template_format( attenuation_map ); + + std::stringstream stream_filename_attenuation_map; + stream_filename_attenuation_map << filename_output_with_extension.substr(0, filename_output_with_extension.length()-3); + stream_filename_attenuation_map << "_attenuation_map.hv"; + + attenuation_map.write( stream_filename_attenuation_map.str() ); + +} + + +void PETDynamicSimulation::save_ground_truth_displacements( void ) const +{ + for(size_t i=0; imotion_dynamics_.size(); i++) + { + this->motion_dynamics_[i]->save_ground_truth_displacements(); + } +} + +void PETDynamicSimulation::simulate_statics() +{ + this->pet_cont_gen_.map_tissue(); + this->set_template_acquisition_data(); + this->acquire_raw_data(); + std::cout << "Finished rawdata acquisition." << std::endl; + + this->add_noise(PET_GLOBAL_NOISE_SCALING); +} + +void PETDynamicSimulation::add_noise( void ) +{ + this->sptr_noise_generator_->add_noise( *sptr_target_acquisitions_, *sptr_target_acquisitions_ ); +} + +void PETDynamicSimulation::add_noise( float const scaling_factor ) +{ + sptr_noise_generator_ = std::make_shared< PoissonNoiseGenerator >( scaling_factor ); + this->sptr_noise_generator_->add_noise( *sptr_target_acquisitions_, *sptr_target_acquisitions_ ); +} + +void PETDynamicSimulation::simulate_data( void ) +{ + throw std::runtime_error( "Please give an acquisition time to simulate pet dynamics." ); +} + +void PETDynamicSimulation::simulate_data( size_t const total_scan_time ) +{ + if( motion_dynamics_.size() > 0) + this->simulate_motion_dynamics( total_scan_time ); + else + throw std::runtime_error( "Please only use motion dynamics with PET for the moment." ); +} + +void PETDynamicSimulation::simulate_motion_dynamics(size_t const total_scan_time ) +{ + cout << "Simulating PET motion dynamics... " <pet_cont_gen_.map_tissue(); + + this->set_template_acquisition_data(); + + size_t const num_motion_dynamics = this->motion_dynamics_.size(); + + std::vector< int > all_num_dyn_states; + for(size_t i=0; iget_num_simul_states() <get_num_simul_states()); + motion_dynamics_[i]->prep_displacement_fields(); + + } + + LinearCombiGenerator lcg(all_num_dyn_states); + + size_t const num_total_dyn_states = lcg.get_num_total_combinations(); + std::vector< DimensionsType > all_dyn_state_combos = lcg.get_all_combinations(); + + + for( size_t i_dyn_state=0; i_dyn_state < num_total_dyn_states; i_dyn_state++) + { + std::stringstream output_name_stream; + output_name_stream << this->output_filename_prefix_; + + cout << "Acquisition motion state #" << i_dyn_state+1 << "/" << num_total_dyn_states << endl; + + DimensionsType current_combination = all_dyn_state_combos[i_dyn_state]; + + TimeBinSet acquisition_time_bins_for_this_state = tot_time_interval; + for( int i_motion_dyn = 0; i_motion_dynmotion_dynamics_[i_motion_dyn]; + + TimeBinSet acquisition_times_in_bin = sptr_motion_dyn->get_time_bin_set_for_state( current_combination[i_motion_dyn] ); + acquisition_time_bins_for_this_state = sirf::intersect_time_bin_sets(acquisition_time_bins_for_this_state, acquisition_times_in_bin); + + } + + TimeAxisType time_in_dynamic_state = get_total_time_in_set(acquisition_time_bins_for_this_state); + cout << "Time spent in this motion state: " << time_in_dynamic_state << endl; + + if( time_in_dynamic_state > 0) + { + std::vector > all_motion_fields; + + for( int i_motion_dyn = 0; i_motion_dynmotion_dynamics_[i_motion_dyn]; + std::vector< SignalBin > signal_bins = sptr_motion_dyn->get_bins(); + + SignalBin bin = signal_bins[ current_combination[i_motion_dyn] ]; + + all_motion_fields.push_back( sptr_motion_dyn->get_interpolated_deformation_field( std::get<1>(bin) ) ); + + } + + this->pet_cont_gen_.map_tissue();//crucial call, as the deformation results in deformed contrast generator data + dsd_.deform_contrast_generator(this->pet_cont_gen_, all_motion_fields); + + this->acquire_raw_data(); + + + float const ms_per_second = 1000.f; + float const result = time_in_dynamic_state/ms_per_second; + float const zero = 0.f; + // sptr_target_acquisitions_->axpby(&result, *sptr_target_acquisitions_, &zero, *sptr_target_acquisitions_ ); + + if( time_in_dynamic_state > total_scan_time) + throw std::runtime_error("The time in the dynamic state is longer than the total scan time. Maybe you confused the units of dynamic time signal and passed acquisition time."); + + float const noise_scaling = PET_GLOBAL_NOISE_SCALING * (float)time_in_dynamic_state/(float)total_scan_time; + + this->add_noise(noise_scaling); + + output_name_stream << ".hs"; + this->write_simulation_results( output_name_stream.str() ); + this->sptr_target_acquisitions_->fill(0.f); + } + } +} + + +void PETDynamicSimulation::set_template_acquisition_data(void) +{ + this->source_acquisitions_ = PETAcquisitionDataInFile( this->filename_rawdata_.c_str() ); +} + +void PETDynamicSimulation::set_template_image_data( const std::string& filename_header_with_ext ) +{ + this->template_image_data_ = STIRImageData(filename_header_with_ext); +} + +void PETDynamicSimulation::set_output_filename_prefix( const std::string& output_filename_prefix) +{ + this->output_filename_prefix_ = output_filename_prefix; +} + +void PETDynamicSimulation::acquire_raw_data( void ) +{ + std::vector< STIRImageData > contrast_filled_volumes = this->pet_cont_gen_.get_contrast_filled_volumes(); + + STIRImageData activity_img = contrast_filled_volumes[0]; + STIRImageData attenuation_map = contrast_filled_volumes[1]; + + std::string outname_img = "/media/sf_SharedFolder/CCPPETMR/imageInMemory.hv"; + activity_img.write( outname_img ); + + + activity_img = this->get_reduced_pet_img_in_template_format( activity_img ); + attenuation_map = this->get_reduced_pet_img_in_template_format( attenuation_map ); + + + STIRImageData template_img(activity_img); + + outname_img = "/media/sf_SharedFolder/CCPPETMR/imageInMemory_templatedims.hv"; + activity_img.write( outname_img ); + + stir::shared_ptr sptr_ray_matrix (new RayTracingMatrix() ); + this->acq_model_.set_matrix( sptr_ray_matrix ); + + PETAttenuationModel att_mod(attenuation_map, this->acq_model_); + this->acq_model_.set_asm( std::make_shared(att_mod)); + + this->acq_model_.set_up( stir::shared_ptr(new PETAcquisitionDataInFile(source_acquisitions_)), + stir::shared_ptr(new STIRImageData(template_img) ) ); + + std::cout << "Application of forward model." << std::endl; + this->sptr_target_acquisitions_ = this->acq_model_.forward(activity_img); +} + +STIRImageData PETDynamicSimulation::get_reduced_pet_img_in_template_format( const STIRImageData& full_size_img) +{ + std::vector< int > input_dims; + input_dims.resize(3,0); + full_size_img.get_dimensions(&input_dims[0]); + + size_t Nz = input_dims[2]; + size_t Ny = input_dims[1]; + size_t Nx = input_dims[0]; + + size_t const num_voxels = Nx*Ny*Nz; + + std::vector < float > vol_data; + vol_data.resize(num_voxels, 0); + full_size_img.get_data(&vol_data[0]); + + + std::vector< int > template_dims; + template_dims.resize(3,0); + + auto works = this->template_image_data_.get_dimensions(&template_dims[0]); + + if(works == -1) + throw std::runtime_error("Irregular range of dimensions in PET image data."); + + std::reverse( input_dims.begin(), input_dims.end() ); + std::reverse( template_dims.begin(), template_dims.end() ); + + std::vector< float > reduced_data; + reduced_data.resize(template_dims[0]*template_dims[1]*template_dims[2],0); + + std::vector< size_t > offsets; + for(int i = 0; i<3; i++) + { + if(input_dims[i] >= template_dims[i]) + offsets.push_back( size_t(float(input_dims[i]- template_dims[i])/2.f)); + + else + throw std::runtime_error("Please give only data which has equal or larger data dimensions than the template image."); + } + + offsets[2] = input_dims[2] - template_dims[2]; + + for(size_t nz = 0; nz template_image_data_ ); + out.set_data(&reduced_data[0]); + + return out; + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/dynsim_deformer.cpp b/src/xDynamicSimulation/cDynamicSimulation/dynsim_deformer.cpp new file mode 100644 index 000000000..c7bc4d47f --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/dynsim_deformer.cpp @@ -0,0 +1,193 @@ +/* +SyneRBI Synergistic Image Reconstruction Framework (SIRF) +Copyright 2018 - 2022 Physikalisch-Technische Bundesanstalt (PTB) + +This is software developed for the Collaborative Computational +Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +(http://www.ccpsynerbi.ac.uk/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +/*! +\file +\ingroup Simulation +\brief Classes and utilities for deforming MR and PET images during simulation. + +\author Johannes Mayer +\author SyneRBI +*/ + +#include "sirf/cDynamicSimulation/dynsim_deformer.h" + +#include "sirf/Reg/NiftyResample.h" +#include "sirf/common/GeometricalInfo.h" + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +using namespace sirf; + +void DynamicSimulationDeformer::deform_contrast_generator(MRContrastGenerator& mr_cont_gen, std::vector > &vec_displacement_fields) +{ + + NiftyResampler resampler; + resampler.set_interpolation_type_to_cubic_spline(); + + // deform the images in the contrast generator with the supplied displacement fields + GadgetronImagesVector& img_data = mr_cont_gen.get_contrast_filled_volumes(); + + if(vec_displacement_fields.size() > 0) + img_data.reorient(*(vec_displacement_fields[0].get_geom_info_sptr())); + + std::shared_ptr< sirf::GadgetronImageData > sptr_img_to_deform = std::move(img_data.clone()); + + // both floating and reference image must be in the same coordinate system + resampler.set_reference_image(sptr_img_to_deform); + resampler.set_floating_image (sptr_img_to_deform); + + for( size_t i_trafo=0; i_trafo >( vec_displacement_fields[i_trafo] ); + resampler.add_transformation(disp_trafo); + } + + resampler.add_transformation(std::make_shared >(offset_)); + resampler.process(); + + // now clear the transformations, and put the deformed image as new floating + resampler.clear_transformations(); + resampler.set_floating_image(resampler.get_output_sptr()); + + // then resample into the template image coordinates + if(mr_template_available_) + resampler.set_reference_image(sptr_mr_template_img_); + + resampler.process(); + + const std::shared_ptr sptr_deformed_img = resampler.get_output_sptr(); + + if(mr_template_available_) + { + img_data = GadgetronImagesVector(*sptr_mr_template_img_); + } + + sptr_deformed_img->copy(sptr_deformed_img->begin(), + img_data.begin(), + img_data.end()); + + mr_cont_gen.set_contrast_filled_volumes(img_data); + + std::vector< NiftiImageData3DDeformation > empty_vec_to_free_memory; + vec_displacement_fields.swap( empty_vec_to_free_memory ); +} + +NiftiImageData3D DynamicSimulationDeformer::resample_to_template(NiftiImageData3D img, bool const use_nearest_neighbor) const +{ + if(!mr_template_available_) + return img; + else{ + + NiftiImageData3D real_valued_template(*sptr_mr_template_img_); + NiftyResampler resampler; + if(use_nearest_neighbor) + resampler.set_interpolation_type_to_nearest_neighbour(); + else + resampler.set_interpolation_type_to_cubic_spline(); + + resampler.set_floating_image( std::make_shared > (img)); + resampler.set_reference_image(std::make_shared > (img)); + + for( size_t i_trafo=0; i_trafo >( displacement_offset_[i_trafo] ); + resampler.add_transformation(disp_trafo); + } + + resampler.add_transformation(std::make_shared >(offset_)); + resampler.process(); + + // now clear the transformations, and put the deformed image as new floating + resampler.clear_transformations(); + resampler.set_floating_image(resampler.get_output_sptr()); + resampler.set_reference_image(std::make_shared >(real_valued_template)); + resampler.process(); + + NiftiImageData3D deformed_img(*resampler.get_output_sptr()); + return deformed_img; + } +} + + +void DynamicSimulationDeformer::deform_contrast_generator(PETContrastGenerator& pet_cont_gen, std::vector >& vec_displacement_fields) +{ + std::vector< STIRImageData >& vect_img_data = pet_cont_gen.get_contrast_filled_volumes(); + + bool equal_geometry = false; + equal_geometry = ( *(vect_img_data[0].get_geom_info_sptr()) == *(vec_displacement_fields[0].get_geom_info_sptr())); + + if( !equal_geometry ) + throw std::runtime_error("You don't have the same geometry between STIR image data and vector fields"); + + if( vect_img_data.size() != 2) + throw std::runtime_error(" Please call map_tissue before the deformation of the contrast generator. You need both activity and attenaution in the correct motion state."); + + for(size_t i_cont=0; i_cont >& vec_deformation_fields) +{ + std::cout << "printing vox geo inf for stir image" << std::endl; + print_io::print_voxelized_geometrical_info( img ); + + std::shared_ptr > sptr_img_to_deform = + std::make_shared >(img); + std::cout << "#####################################################################" << std::endl; + + std::cout << "printing vox geo inf for niftiimage from stir image" << std::endl; + print_io::print_voxelized_geometrical_info( *sptr_img_to_deform ); + + std::cout << " \n printing vox geo inf for deformation field" << std::endl; + print_io::print_voxelized_geometrical_info( vec_deformation_fields[0] ); + + auto geom_stir_img = img.get_geom_info_sptr(); + + std::vector > deformation_fields_with_stir_geometry; + for(size_t i=0; i((float*)sptr_nifti->data, *geom_stir_img)); + } + + NiftyResampler resampler; + + resampler.set_interpolation_type_to_cubic_spline(); + + resampler.set_reference_image(sptr_img_to_deform); + resampler.set_floating_image(sptr_img_to_deform); + + for( size_t i_disp=0; i_disp > disp_trafo = + std::make_shared >( deformation_fields_with_stir_geometry[i_disp] ); + resampler.add_transformation(disp_trafo); + } + + resampler.process(); + const std::shared_ptr sptr_deformed_img = resampler.get_output_sptr(); + + sptr_deformed_img->copy(sptr_deformed_img->begin(), + img.begin(), + img.end()); + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/dynsim_noisegenerator.cpp b/src/xDynamicSimulation/cDynamicSimulation/dynsim_noisegenerator.cpp new file mode 100644 index 000000000..ff452854c --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/dynsim_noisegenerator.cpp @@ -0,0 +1,95 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.08.09 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#include +#include +#include +#include +#include + +#include "sirf/cDynamicSimulation/dynsim_noisegenerator.h" + + +using namespace sirf; + +unsigned int aNoiseGenerator::generate_pseudo_seed( void ) +{ + srand( time(NULL) ); + unsigned int seed = 0; + while(seed == 0) + seed = rand(); + + return seed; +} + +void PoissonNoiseGenerator::add_noise( PETAcquisitionData& noisy_acq, PETAcquisitionData& noise_free_acq) +{ + try + { + auto sptr_noise_free_data = noise_free_acq.data(); + auto sptr_noisy_data = noisy_acq.data(); + + this->stir_noise_gen_.generate_random( *(sptr_noisy_data), *(sptr_noise_free_data) ); + noisy_acq.set_data( sptr_noisy_data ); + } + catch(...) + { + std::cout << "Something went wrong in adding poisson noise" << std::endl; + } +} + + +void GaussianNoiseGenerator::add_noise( MRAcquisitionData& ad ) +{ + if( this->SNR_ > 0) + { + this->noise_width_kspace_ = noise_width_from_snr( ad ); + this->add_noise_to_data(ad); + } + else + { + std::cout << " --- The SNR you supplied was 0, so no noise is added" <add_noise_to_data( ad ); + } + +} + +float GaussianNoiseGenerator::noise_width_from_snr( MRAcquisitionData& ad ) +{ + this->noise_width_img_ = this->signal_img_ / this->SNR_; + + size_t num_acquistions = ad.number(); + + if( num_acquistions <= 0) + return 0.f; + + float const suggested_noise_width = this->noise_width_img_; + return suggested_noise_width; +} + +void GaussianNoiseGenerator::add_noise_to_data( MRAcquisitionData& ad ) +{ + std::cout << "Adding gaussian noise of width " << this->noise_width_kspace_ <generate_pseudo_seed()); + + + std::normal_distribution< float > gaussian_distribution( this->mean_noise_ , this->noise_width_kspace_ ); + + for( size_t i_acq=0; i_acqsequence_specific_scaling_; + + ad.set_acquisition( i_acq, acq); + } +} diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/auxiliary_input_output.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/auxiliary_input_output.h new file mode 100644 index 000000000..4e6d0f602 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/auxiliary_input_output.h @@ -0,0 +1,229 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.04.05 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + + +#ifdef _SIRFIMAGEDATA_H_ + std::cout << "###############################################################" < +#include +#include +#include + +#include +// #include + +#include "sirf/STIR/stir_data_containers.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" + + +#include "sirf/common/GeometricalInfo.h" +#include "sirf/common/ImageData.h" + +#include "sirf/Reg/NiftiImageData3D.h" + +#define epiph(x) #x << " = " << x + +#define PIX_SIZE_X 3.20f +#define PIX_SIZE_Y 3.20f +#define PIX_SIZE_Z 1.92f + +// #define PIX_SIZE_X 2.0f +// #define PIX_SIZE_Y 2.0f +// #define PIX_SIZE_Z 2.0f + + +typedef float TimeAxisType; +typedef float SignalAxisType; +typedef std::pair SignalPoint; +typedef std::vector< SignalPoint > SignalContainer; + + +namespace print_io{ + + void print_voxelized_geometrical_info( const sirf::ImageData& im ); +} + + +namespace data_io{ + + template + void write_raw(std::string const output_name_without_ext, T* ptr_to_data, size_t const num_elements) + { + std::cout<< "Writing file " << output_name_without_ext << std::endl; + std::stringstream name_stream; + name_stream << output_name_without_ext << ".bin"; + + std::vector buffer; + buffer.resize(num_elements); + + for( size_t i=0; i (buffer.data()), buffer.size()*sizeof(T)); + + out.close(); + + std::cout<< "Finished writing file " << name_stream.str() << std::endl; + + }; + + + + template + void write_ISMRMRD_Image_to_nii(std::string const output_name_without_ext, ISMRMRD::Image &img) + { + std::cout << "Started writing " << output_name_without_ext << std::endl; + std::cout << "Only first channel will be written to file. " << output_name_without_ext << std::endl; + + // determine data size. only take first channel + sirf::VoxelisedGeometricalInfo3D::Size data_size{ (unsigned int)img.getMatrixSizeX() , (unsigned int)img.getMatrixSizeY() , (unsigned int)img.getMatrixSizeZ() }; + + // set spacing + float const pix_size_X = (float)img.getFieldOfViewX() / (float)data_size[0]; + float const pix_size_Y = (float)img.getFieldOfViewY() / (float)data_size[1]; + float const pix_size_Z = (float)img.getFieldOfViewZ() / (float)data_size[2]; + + sirf::VoxelisedGeometricalInfo3D::Spacing spacing{pix_size_X, pix_size_Y, pix_size_Z}; + + + sirf::VoxelisedGeometricalInfo3D::Offset offset; + for( int i=0; i<3; i++) + offset[i] = (-0.5f * data_size[i] + 0.5f) * spacing[i]; + + + sirf::VoxelisedGeometricalInfo3D::DirectionMatrix dir_mat; + + dir_mat[0] = {1,0,0}; + dir_mat[1] = {0,1,0}; + dir_mat[2] = {0,0,1}; + + sirf::VoxelisedGeometricalInfo3D geometry_info( offset, spacing, data_size, dir_mat); + + + size_t const num_relevant_elements = data_size[0]*data_size[1]*data_size[2] ; + + + std::vector< float > data( num_relevant_elements ); + + + for( size_t i=0; i nifti_img(&data[0], geometry_info); + nifti_img.write( output_name_without_ext + ".nii"); + + std::cout << "Finished writing " << output_name_without_ext << std::endl; + }; + + template + void write_MVF_from_ISMRMRD_Image_to_Analyze(std::string const output_name_without_ext, ISMRMRD::Image mvf) + { + + if(mvf.getNumberOfChannels() != 3) + throw std::runtime_error("Please pass a 3d vector field to write out."); + + size_t const Nx = mvf.getMatrixSizeX(); + size_t const Ny = mvf.getMatrixSizeY(); + size_t const Nz = mvf.getMatrixSizeZ(); + + std::cout << "Rescaling motion fields to milimeters." < (output_name_without_ext, mvf); + + } + + template + void write_MVF_from_ISMRMRD_Image_to_Analyze_In_PET_Geometry(std::string const output_name_without_ext, ISMRMRD::Image mvf) + { + + if(mvf.getNumberOfChannels() != 3) + throw std::runtime_error("Please pass a 3d vector field to write out."); + + size_t const Nx = mvf.getMatrixSizeX(); + size_t const Ny = mvf.getMatrixSizeY(); + size_t const Nz = mvf.getMatrixSizeZ(); + + std::cout << "Rescaling motion fields to milimeters." < (output_name_without_ext, mvf); + + } + + + + + void write_PET_image_to_hv( const std::string& filename_without_ext,const sirf::STIRImageData& img); + + + template + std::vector< T > read_single_column_txt( const std::string& filename_txt_without_ext ) + { + std::string const filename_with_ext = filename_txt_without_ext + ".txt"; + + std::vector output; + std::string line; + std::ifstream myfile (filename_with_ext); + + if (myfile.is_open()) + { + while ( getline (myfile,line) ) + { + T val; + while(myfile >> val) + output.push_back(val); + } + + myfile.close(); + } + + else + throw std::runtime_error( "Unable to open file "+filename_with_ext); + + return output; + } + + SignalContainer read_surrogate_signal( const std::string& filename_time_axis, const std::string& filename_signal ); + +} + +namespace mr_io{ + + ISMRMRD::IsmrmrdHeader read_ismrmrd_header( std::string path_ismrmrd_h5_file_with_ext); + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/cdynamicsimulation.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/cdynamicsimulation.h new file mode 100644 index 000000000..704185add --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/cdynamicsimulation.h @@ -0,0 +1,90 @@ +/* +SyneRBI Synergistic Image Reconstruction Framework (SIRF) +Copyright 2021 Physikalisch-Technische Bundesanstalt (PTB) + +This is software developed for the Collaborative Computational +Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +(http://www.ccpsynerbi.ac.uk/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef SIMULATION_C_INTERFACE +#define SIMULATION_C_INTERFACE + +#include + +#ifndef CSIMULATION_FOR_MATLAB +#define PTR_INT size_t +#define PTR_FLOAT size_t +#define PTR_DOUBLE size_t +extern "C" { +#else +#define PTR_INT int* +#define PTR_FLOAT float* +#define PTR_DOUBLE double* +#endif + + +// MR Simulation +void* cDS_MRDynamicSimulation(const void* ptr_labels, const char* fname_xml); +void* cDS_simulateData(void* ptr_sim); +void* cDS_writeSimulationResults(const void* ptr_sim, const char* fname_with_ext); +void* cDS_saveParameterMapsGroundTruth(const void* ptr_sim, const char* fname_with_ext); +void* cDS_saveMotionGroundTruth(const void* ptr_sim); + +void* cDS_setAcquisitionTemplateData(void* ptr_sim, const void* ptr_acqs); +void* cDS_setContrastTemplateData(void* ptr_sim, const void* ptr_acqs); +void* cDS_setCoilmaps(void* ptr_sim, const void* ptr_csm); + +void* cDS_setSNR(void* ptr_sim, PTR_FLOAT SNR); +void* cDS_setNoiseLabel(void* ptr_sim, int const label); + +void* cDS_setOffsetTransformation(void* ptr_sim, const void* ptr_trafo); + +void* cDS_addMRMotionDynamic(void* ptr_sim, void* ptr_dyn); +void* cDS_addMRContrastDynamic(void* ptr_sim, void* ptr_dyn); +void* cDS_addExternalContrastDynamic(void* ptr_sim, void* ptr_dyn); +// Dynamics +void* cDS_setDynamicSignal(void* ptr_dyn, PTR_FLOAT ptr_time, PTR_FLOAT ptr_signal, int const num_points); +void* cDS_addMRDisplacementField(void* ptr_dyn, const void* ptr_dvf); +void* cDS_setMRGroundTruthFolderName(void* ptr_dyn, const char* fpath_output_prefix); +void* cDS_setCyclicality(void* ptr_dyn, bool const cyc); + + +void* cDS_setMRAcquisitions(void* ptr_dyn, void* ptr_ad); + +void* cDS_getIdxCorrSizes(void* ptr_dyn, void* ptr_ad, PTR_INT ptr_sizes); +void* cDS_getIdxCorr(void* ptr_dyn, int const bin_num, PTR_INT ptr_idx_corr); + + +// MR Dynamics +void* cDS_MRMotionDynamic( int const num_states ); + +void* cDS_MRContrastDynamic( int const num_states ); +void* cDS_addDynamicLabel(void* ptr_contrast_dyn, int const label); +void* cDS_setMRParameterExtremes(void* ptr_contrast_dyn, void* ptr_tissueparameter_0, void* ptr_tissueparameter_1); + +void* cDS_ExternalMRContrastDynamic( void ); +void* cDS_appendExternalTissueSignal(void* ptr_dyn, int const num_points, PTR_INT ptr_labels, PTR_FLOAT ptr_sig); + + +// Tissue Parameters +void* cDS_getTissueParameter(const void* ptr_sim, const unsigned int label); +void* cDS_setT1Value(void* ptr_tissue_parameter, float const T1_ms); +void* cDS_setSpinDensity(void* ptr_tissue_parameter, float const spin_density); + + +#ifndef CSIMULATION_FOR_MATLAB +} +#endif + +#endif diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/contrastgenerator.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/contrastgenerator.h new file mode 100644 index 000000000..230124bbb --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/contrastgenerator.h @@ -0,0 +1,145 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.28 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + +#include +#include +#include + +#include +#include + +#include "sirf/Reg/NiftiImageData3D.h" + +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/cDynamicSimulation/tissuelabelmapper.h" + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/STIR/stir_data_containers.h" + +#include "sirf/STIR/stir_types.h" + +// base class for contrast generators. inherit for different modalities. +// Reading the header is the same for each modality (hopefully!!!). +// otherwise the header reading can be just inherited and implemented +// as a virutal method + + +#define CASE_MAP_PET_CONTRAST 0 +#define CASE_MAP_PET_ATTENUATION 1 + + +typedef std::vector SeqParamType; + +class AbstractContrastGenerator { + +public: + AbstractContrastGenerator(){}; + AbstractContrastGenerator(const LabelVolume& tissue_labels, const std::string& filename_tissue_parameter_xml); + + + // pure virtual since formats are very diff for pet and mri and ct + virtual void map_contrast()=0; + void replace_petmr_tissue_parameters(LabelType label, TissueParameter tiss_param); + TissueParameter get_petmr_tissue_parameter( LabelType label ); +protected: + virtual void resample_to_template_image( void )=0; + TissueLabelMapper tlm_; + +}; + +typedef std::tuple< LabelType, std::uint32_t, complex_float_t> ExternalTissueSignal; // label-#, acquisition time-stamp, MR signal + + +class MRContrastGenerator : public AbstractContrastGenerator { + +public: + + MRContrastGenerator (const LabelVolume& tissue_labels, const std::string& filename_tissue_parameter_xml); + + void set_template_rawdata(const sirf::MRAcquisitionData& ad); + void set_rawdata_header(const ISMRMRD::IsmrmrdHeader& hdr); + + + void map_contrast(); + void map_contrast(const std::vector& ext_sig); + void map_parameters(); + + + complex_float_t get_signal_for_tissuelabel( size_t const label ); + + sirf::GadgetronImagesVector& get_contrast_filled_volumes(bool const resample_output=false); + + const std::vector >& get_parameter_filled_volumes() const { + return this->parameter_filled_volumes_; + } + + void set_contrast_filled_volumes(const sirf::GadgetronImagesVector& img) + { + contrast_filled_volumes_ = img; + } + +private: + + + sirf::NiftiImageData3D get_parameter_map(const int which_parameter); + float get_parameter_from_tissue(const TissueParameter tp, const int which_parameter) const; + + std::vector build_label_signal_map(std::vector ext_sig) const; + + void resample_to_template_image( void ); + sirf::GadgetronImagesVector contrast_filled_volumes_; + std::vector > parameter_filled_volumes_; + ISMRMRD::IsmrmrdHeader hdr_; + + std::shared_ptr sptr_acqu_; + +}; + + +std::vector < complex_float_t > map_flash_contrast( std::shared_ptr const ptr_to_tiss_par, + const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr); + + +std::vector map_bssfp_contrast( std::shared_ptr const ptr_to_tiss_par, + const ISMRMRD::IsmrmrdHeader& ismrmrd_hdr); + + +class PETContrastGenerator : public AbstractContrastGenerator { + +public: + + PETContrastGenerator():AbstractContrastGenerator() {}; + + PETContrastGenerator(const LabelVolume& tissue_labels, const std::string& filename_tissue_parameter_xml); + + + void set_template_image_from_file( const std::string& filename_header_with_ext ); + + std::vector< int > get_dimensions( void ); + std::vector< float > get_voxel_sizes( void ); + + std::vector< sirf::STIRImageData >& get_contrast_filled_volumes(bool const resample_output=false); + + void map_tissue(); + void map_contrast(); + void map_attenuation(); + +private: + + void resample_to_template_image( void ); + + bool template_img_is_set_ = false; + + std::vector < sirf::STIRImageData > contrast_filled_volumes_; + void map_tissueparams_member(int const case_map); + + sirf::STIRImageData template_pet_image_data_; + +}; diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamics.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamics.h new file mode 100644 index 000000000..376a879a8 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamics.h @@ -0,0 +1,802 @@ +/* +SyneRBI Synergistic Image Reconstruction Framework (SIRF) +Copyright 2018 - 2022 Physikalisch-Technische Bundesanstalt (PTB) + +This is software developed for the Collaborative Computational +Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +(http://www.ccpsynerbi.ac.uk/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +/*! +\file +\ingroup Simulation +\brief Classes and utilities for handling dynamic processes in the simulation + +\author Johannes Mayer +\author SyneRBI +*/ + + +#pragma once + +#include +#include +#include +#include + +#include + +#include "sirf/cDynamicSimulation/phantom_input.h" +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/cDynamicSimulation/tissuelabelmapper.h" +#include "sirf/cDynamicSimulation/contrastgenerator.h" + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/Reg/ImageWeightedMean.h" +#include "sirf/Reg/NiftiImageData3DDeformation.h" + + + +/*! +\brief TIC units used in ISMRMRD Encoding.Idx.TimeMr in miliseconds +*/ +#define SIRF_SCANNER_MS_PER_TIC 2.5 + +namespace sirf{ + +/*! +\brief Datatype used for the time axis of surrogate signals +*/ +typedef float TimeAxisType; + +/*! +\brief Datatype used for the signal axis of surrogate signals +*/ +typedef float SignalAxisType; + +/*! +\brief A signal bin consists of the triplet (lower border, centre, upper border) +*/ +typedef std::tuple SignalBin; + +/*! +\brief Signal points of surrogate signals consist of a pair of (time,signal) +*/ +typedef std::pair SignalPoint; + +/*! +\brief Container class used to store surrogate signals +*/ +typedef std::vector< SignalPoint > SignalContainer; + +/*! +\brief A signal bin consists of the triplet (lower border, centre, upper border) +*/ +typedef sirf::NiftiImageData3DDisplacement MotionFieldType; + +/*! +\brief Method to check if a signal point is inside a signal bin +*/ +bool is_in_bin( SignalAxisType const signal, SignalBin const bin); + +/*! +\brief The SIRF container class used to store MR acquisition data +*/ +typedef sirf::AcquisitionsVector MRDataType; + + +/*! +\brief Method to intersect two MRAcquisitionData containers by compaing the acquisition scan_coutners. + +Data are taken from one_dat, just scan_counters are compared. Acquisitions from other_dat are not returned! +*/ +MRDataType intersect_mr_acquisition_data( const sirf::MRAcquisitionData& one_dat, const sirf::MRAcquisitionData& other_dat ); + + +/*! +\brief Class to handle surrogate signals that are sets of points. +*/ +class SurrogateProcessor{ + +public: + SurrogateProcessor(){}; + + void set_signal(const SignalContainer& sig){ + this->signal_ = sig; + } + + SignalContainer get_signal(void) const{ + return signal_; + } + + bool is_empty(void) const{ + return signal_.size() < 1; + } + + SignalAxisType get_average_signal(const std::vector timepoints) const{ + SignalAxisType avg_signal = 0.f; + for(int i=0; i(bin) = 0.0; + std::get<1>(bin) = 0.5; + std::get<2>(bin) = 1.0; + + signal_bins_.push_back(bin); + } + + BinProcessor(unsigned int const num_bins, bool const cyclic=false){ + + if(num_bins < 1) + throw std::runtime_error("Please a number of bins >= 1."); + + num_bins_ = num_bins; + cyclic_ = cyclic; + + this->set_bins(); + } + + int get_num_bins( void ) const { + return num_bins_; + } + + std::vector get_bins( void ) const { + return signal_bins_; + } + + std::vector get_bin_centers(void) const { + + std::vector centres; + for(int i=0; i(signal_bins_.at(i))); + + return centres; + } + + void set_cyclicality(const bool cyclic){ + this->cyclic_ = cyclic; + set_bins(); + } + bool is_cyclic(void) const{ + return cyclic_; + } + + /*! + \brief Overlays the interval [0,1] with num_bins_ non-overlapping bins + */ + void set_bins( void ){ + + this->signal_bins_.clear(); + + this->cyclic_ ? set_cyclic_bins(num_bins_) + : set_non_cyclic_bins(num_bins_); + } + +private: + /*! + \brief Applies bins where the 0th bin is centered around 0 and assumes a cyclic interval [0,1]. + */ + void set_cyclic_bins( int const num_bins); + /*! + \brief Applies bins where the 0th bin is centered around 0.5*1/num_bins_. + */ + void set_non_cyclic_bins( int const num_bins); + + int num_bins_; + bool cyclic_; + std::vector< SignalBin > signal_bins_; + +}; + +/*! +\brief Interface for simulated dynamic processes. + +Dynamics are generally composed of a SurrogateProcessor and a BinProcessor and a SurrogateProcessor and expose their methods. +*/ +class Dynamic{ + +public: + + Dynamic() : bp_(){} + Dynamic(const unsigned int num_states) : bp_(num_states){ + } + + int get_num_simul_states(void) const { + return bp_.get_num_bins(); + } + + virtual void set_dynamic_signal(const SignalContainer& sig) + { + sp_.set_signal(sig); + } + + std::vector< SignalBin > get_bins(void) const { + return bp_.get_bins(); + } + + virtual void set_cyclicality(const bool cyclic){ + this->bp_.set_cyclicality(cyclic); + } + + SignalAxisType interpolate_signal(const TimeAxisType time_point) const + { + return this->sp_.linear_interpolate_signal(time_point); + } + + /*! + \brief Function to compute average surrogate signal over the timepoints at which MR rawdata was acquired + + The surrogate signal is interpolated linearly onto the timepoints at which the acquisitions were acquired. + This subtracts the minimum time for the acquisition data assuming the first acquisition was acquires at t=0 ms. + */ + virtual SignalAxisType get_average_surrogate_signal(const sirf::MRAcquisitionData& ad) const + { + std::vector timepts; + for(int ia=0; iasp_.get_average_signal(timepts); + } + +protected: + SurrogateProcessor sp_; + BinProcessor bp_; +}; + +/*! +\brief Class to handle the temporal change of tissue parameters to a surrogate signal. + +Two tissue parameters can be passes as extreme points that correspond to a surrogate signal of 0 and 1. +E.g. two tissue parameters with different T1 due to inflow of a T1 contrast agent where 0 corresponds +to the native T1 and 1 corresponds to the maximum observed concentration. + +*/ +class ContrastProcessor { + +public: + + ContrastProcessor(){}; + + TissueParameterList get_interpolated_tissue_params(SignalAxisType signal) const; + + void set_parameter_extremes(TissueParameter tiss_at_0, TissueParameter tiss_at_1); + + /*! + /brief Add labels of a segmentation which behave identically (e.g. all labels containing blood) + */ + void add_dynamic_label(LabelType l) { + this->list_cont_var_labels_.push_back(l); + } + + std::vector< TimeAxisType > get_sampled_time_points(void) const { + return time_points_sampled_; + } + + void add_timepoint(const TimeAxisType time){ + time_points_sampled_.push_back(time); + } + + void empty_timepoints(void){ + this->time_points_sampled_.clear(); + } + +private: + + static std::vector< TimeAxisType > time_points_sampled_; + + std::vector< LabelType > list_cont_var_labels_; + std::pair< TissueParameter, TissueParameter > tissue_parameter_extremes_; + +}; + + +/*! +\brief Utility class to process displacement fields. +*/ +class MotionProcessor { + +public: + MotionProcessor(); + ~MotionProcessor(); + + std::string get_temp_folder_name(void) const + { + return this->temp_folder_name_; + } + void set_ground_truth_folder_name(const std::string prefix_output_dir) + { + this->ground_truth_folder_name_ = prefix_output_dir; + this->make_ground_truth_folder(); + } + + void add_displacement_field(const MotionFieldType& dvf) + { + this->displacement_fields_.push_back( dvf ); + } + + int get_which_motion_processor_am_i(void) const + { + return this->which_motion_processor_am_i_; + } + + int get_num_total_motion_dynamics(void) const + { + return this->num_total_motion_processors_; + } + + sirf::NiftiImageData3DDeformation get_interpolated_deformation_field(const SignalAxisType signal, const bool cyclic) const; + + void set_displacement_fields( const std::vector< MotionFieldType > &input_vectors); + + /*! + /brief Function to store motion fields in a temporary folder instead of keeping them in memory. + */ + void prep_displacement_fields( void ); + + + void save_ground_truth_displacements( const std::vector< SignalAxisType >& gt_signal_points, const bool cyclic) const; + + bool delete_temp_folder() const; + +protected: + + bool const destroy_upon_deletion_ = true; + bool const keep_motion_fields_in_memory_ = true; + + std::string setup_tmp_folder_name( void ); + std::string setup_gt_folder_name( void ); + bool make_temp_folder() const; + bool make_ground_truth_folder() const; + + sirf::NiftiImageData3DDeformation calc_inverse_offset_deformation( sirf::NiftiImageData3DDeformation offset_deformation ) const; + sirf::NiftiImageData3DDisplacement scale_displacementfields_to_mm( const sirf::NiftiImageData3DDisplacement &dvf) const; + + std::string const temp_folder_prefix_ = "/media/sf_CCPPETMR/TestData/Input/xDynamicSimulation/cDynamicSimulation/Temp/"; + std::string const temp_mvf_prefix_ = "/motion_field_"; + + std::string temp_folder_name_ ; + std::string ground_truth_folder_name_ ; + + std::vector< std::string > temp_mvf_filenames_; + + static int num_total_motion_processors_; + int which_motion_processor_am_i_; + + std::vector displacement_fields_; +}; + +/*! +\brief Interface class to define dynamic processes for the simulation of MR acquisition. +*/ + +class MRDynamic : public Dynamic{ + +public: + MRDynamic(): Dynamic(){} + MRDynamic(unsigned int const num_simul_states): Dynamic(num_simul_states){} + + std::vector get_binned_mr_acquisitions(void) const + { + return this->binned_mr_acquisitions_; + } + + MRDataType get_binned_mr_acquisitions(unsigned int const bin_num) const + { + if(bin_num >= binned_mr_acquisitions_.size()) + throw std::runtime_error("Please access only bin numbers in the range of 0 and num_simul_states_-1."); + + return binned_mr_acquisitions_.at(bin_num); + } + + void clear_binning_data() + { + std::vector empty_acquis_vec; + binned_mr_acquisitions_.swap( empty_acquis_vec ); + + std::vector > empty_idx_corr; + idx_corr_.swap(empty_idx_corr); + + } + + virtual std::deque get_idx_corr(int const bin_num) const + { + if(bin_num >= binned_mr_acquisitions_.size()) + throw std::runtime_error("Please access only bin numbers in the range of 0 and num_simul_states_-1."); + + return this->idx_corr_.at(bin_num); + } + + virtual std::vector get_idx_corr_sizes() const + { + std::vector idx_corr_sizes; + for(int i=0; i binned_mr_acquisitions_; + std::vector > idx_corr_; +}; + +/*! +\brief Implementation of MR dynamic executing motion during simulation. +*/ + +class MRMotionDynamic : public MRDynamic{ + +public: + + MRMotionDynamic(unsigned int const num_simul_states): MRDynamic(num_simul_states){} + virtual void bin_mr_acquisitions( sirf::MRAcquisitionData& all_acquisitions ); + + void set_ground_truth_folder_name(const std::string prefix_output_dir) + { + mp_.set_ground_truth_folder_name(prefix_output_dir); + } + + void add_displacement_field(const MotionFieldType& dvf) + { + mp_.add_displacement_field(dvf); + } + + int get_which_motion_dynamic_am_i(void) const + { + return mp_.get_which_motion_processor_am_i(); + } + + int get_num_total_motion_dynamics(void) const + { + return mp_.get_num_total_motion_dynamics(); + } + + sirf::NiftiImageData3DDeformation get_interpolated_deformation_field_at_timepoint(const TimeAxisType time_seconds) const + { + SignalAxisType sig = sp_.linear_interpolate_signal(time_seconds); + return this->get_interpolated_deformation_field(sig); + } + + sirf::NiftiImageData3DDeformation get_interpolated_deformation_field(const SignalAxisType signal) const + { + return mp_.get_interpolated_deformation_field(signal, bp_.is_cyclic()); + } + + /*! + \brief Computes the average motion state over the time of acuqisition. + Subtracts the temporal offset i.e. assumes the first acquisition to be at t=0 ms + */ + sirf::NiftiImageData3DDeformation get_average_deformation_field(const sirf::MRAcquisitionData& ad) + { + SignalAxisType const avg_sig = get_average_surrogate_signal(ad); + return get_interpolated_deformation_field(avg_sig); + } + + void set_displacement_fields( const std::vector< MotionFieldType > &input_vectors, bool const motion_fields_are_cyclic = false) + { + bp_.set_cyclicality(motion_fields_are_cyclic); + mp_.set_displacement_fields(input_vectors); + } + void prep_displacement_fields(void) + { + mp_.prep_displacement_fields(); + } + + void save_ground_truth_displacements( const std::vector< SignalAxisType >& gt_signal_points) const + { + mp_.save_ground_truth_displacements(gt_signal_points, bp_.is_cyclic()); + } + + void save_ground_truth_displacements(void) const + { + this->save_ground_truth_displacements(bp_.get_bin_centers()); + } + + bool delete_temp_folder() const + { + mp_.delete_temp_folder(); + } + + +private: + MotionProcessor mp_; +}; + +/*! +\brief Implementation of MR dynamic executing tissue parameter changes during simulation. +*/ + +class MRContrastDynamic: public MRDynamic { + +public: + MRContrastDynamic(unsigned int const num_simul_states): MRDynamic(num_simul_states){}; + void bin_mr_acquisitions( sirf::MRAcquisitionData& all_acquisitions ); + + TissueParameterList get_interpolated_tissue_params(SignalAxisType signal) const + { + return cp_.get_interpolated_tissue_params(signal); + } + + void set_parameter_extremes(TissueParameter tiss_at_0, TissueParameter tiss_at_1) + { + cp_.set_parameter_extremes(tiss_at_0, tiss_at_1); + } + + void add_dynamic_label(LabelType l) + { + cp_.add_dynamic_label(l); + } + + std::vector< TimeAxisType > get_sampled_time_points(void) const + { + return cp_.get_sampled_time_points(); + } + +private: + ContrastProcessor cp_; + +}; + + +/*! +\brief Implementation of MR dynamic changing the signal according to a pre-computed magnetisation. +*/ + +class ExternalMRContrastDynamic: public MRDynamic { + +public: + ExternalMRContrastDynamic(): MRDynamic(){}; + + virtual void bin_mr_acquisitions( sirf::MRAcquisitionData& all_acquisitions ) + { + if(external_signals_.size() != all_acquisitions.number()) + { + std::stringstream message; + message << "Please supply the same number (" << external_signals_.size() << ") of tissue signal sets in temporal order" + "as number of readouts (" << all_acquisitions.number() << ") using set_tissue_signals() prior to calling this function.\n"; + throw std::runtime_error(message.str()); + } + + all_acquisitions.sort_by_time(); + + vector().swap(binned_mr_acquisitions_); + + ISMRMRD::Acquisition acq; + for(int i=0; i& ext_signals) + { + external_signals_.push_back(ext_signals); + } + + void set_tissue_signals(std::vector > signals) + { + external_signals_ = signals; + } + + std::vector get_tissue_signals(const int i) const + { + return external_signals_[i]; + } + + int get_num_simul_states( void ) const + { + return binned_mr_acquisitions_.size(); + } + +private: + std::vector > external_signals_; +}; + + +// PET Dynamics and auxiliary methods + +template +struct Interval{ + + Interval (): min_(0), max_(0) {}; + Interval (T min, T max): min_(min), max_(max) + { + if( min > max ) + throw std::runtime_error("Please give an interval with smaller minimum than maximum."); + }; + + bool is_empty() + { + return std::abs(min_- max_) < 1e-8; + } + + T min_; + T max_; +}; + + +template< typename T> +Interval intersect_intervals(const Interval& one_interval, const Interval& other_interval) +{ + Interval intersection; + + if( one_interval.min_ > other_interval.max_ || other_interval.min_ > one_interval.max_ ) + return intersection; + else + { + intersection.min_ = std::max(one_interval.min_, other_interval.min_); + intersection.max_ = std::min(one_interval.max_, other_interval.max_); + + return intersection; + } +} + +typedef Interval TimeBin; +typedef std::vector< TimeBin > TimeBinSet; + + +TimeBin intersect_time_intervals( const TimeBin& one_interval, const TimeBin& other_interval); +TimeBinSet intersect_time_bin_sets( const TimeBinSet& one_set, const TimeBinSet& other_set); +TimeAxisType get_total_time_in_set( TimeBinSet& set_of_bins ); +TimeAxisType get_time_from_between_two_signal_points(SignalAxisType signal, SignalPoint left_point, SignalPoint right_point); + + +class PETDynamic : public Dynamic{ + +public: + + PETDynamic(unsigned int const num_simul_states): Dynamic(num_simul_states){} + + void bin_total_time_interval(TimeBin time_interval_total_dynamic_process); + + TimeBinSet get_time_bin_set_for_state(unsigned int const which_state); + TimeAxisType get_time_spent_in_bin(unsigned int const which_state ); + +protected: + std::vector< TimeBinSet > binned_time_intervals_; + +}; + + +class PETMotionDynamic: public PETDynamic { + +public: + PETMotionDynamic(unsigned int const num_simul_states): PETDynamic(num_simul_states) {}; + + void save_ground_truth_displacements(void) const + { + mp_.save_ground_truth_displacements(bp_.get_bin_centers(),bp_.is_cyclic()); + } + + void save_ground_truth_displacements( const std::vector< SignalAxisType >& gt_signal_points) const + { + mp_.save_ground_truth_displacements(gt_signal_points, bp_.is_cyclic()); + } + + void set_ground_truth_folder_name(const std::string prefix_output_dir) + { + mp_.set_ground_truth_folder_name(prefix_output_dir); + } + + void add_displacement_field(const MotionFieldType& dvf) + { + mp_.add_displacement_field(dvf); + } + + int get_which_motion_dynamic_am_i(void) const + { + return mp_.get_which_motion_processor_am_i(); + } + + int get_num_total_motion_dynamics(void) const + { + return mp_.get_num_total_motion_dynamics(); + } + + sirf::NiftiImageData3DDeformation get_interpolated_deformation_field(const SignalAxisType signal) const + { + return mp_.get_interpolated_deformation_field(signal, bp_.is_cyclic()); + } + + void set_displacement_fields( const std::vector< MotionFieldType > &input_vectors, bool const motion_fields_are_cyclic = false) + { + bp_.set_cyclicality(motion_fields_are_cyclic); + mp_.set_displacement_fields(input_vectors); + } + + void prep_displacement_fields(void) + { + mp_.prep_displacement_fields(); + } + + bool delete_temp_folder() const + { + mp_.delete_temp_folder(); + } + +private: + MotionProcessor mp_; + bool const keep_motion_fields_in_memory_ = true; +}; + +class PETContrastDynamic: public PETDynamic { + +public: + PETContrastDynamic(unsigned int const num_simul_states): PETDynamic(num_simul_states){}; + + TissueParameterList get_interpolated_tissue_params(SignalAxisType signal) const + { + return cp_.get_interpolated_tissue_params(signal); + } + + void set_parameter_extremes(TissueParameter tiss_at_0, TissueParameter tiss_at_1) + { + cp_.set_parameter_extremes(tiss_at_0, tiss_at_1); + } + + void add_dynamic_label(LabelType l) + { + cp_.add_dynamic_label(l); + } + + std::vector< TimeAxisType > get_sampled_time_points(void) const + { + return cp_.get_sampled_time_points(); + } + +private: + ContrastProcessor cp_; +}; + +} // namespace sirf \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamicsimulation_x.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamicsimulation_x.h new file mode 100755 index 000000000..02f156ac1 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynamicsimulation_x.h @@ -0,0 +1,273 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.07.20 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ +#pragma once + +#include + +#include +#include + +#include "sirf/Reg/AffineTransformation.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/Gadgetron/gadgetron_x.h" +#include "sirf/STIR/stir_x.h" + +#include "sirf/cDynamicSimulation/tissuelabelmapper.h" +#include "sirf/cDynamicSimulation/contrastgenerator.h" +#include "sirf/cDynamicSimulation/dynamics.h" +#include "sirf/cDynamicSimulation/dynsim_noisegenerator.h" +#include "sirf/cDynamicSimulation/dynsim_deformer.h" + + +#define IMG_DATA_TYPE 7 // from ismrmrd enum ISMRMRD_CXFLOAT = 7 + + + +typedef std::vector DimensionsType; + +/*! + \brief A class to generate combinations of integers in a vector. + This is used to compute combinations of different motion states if multiple motion dynamics are present during simulation. +*/ +class LinearCombiGenerator{ + +public: + LinearCombiGenerator(DimensionsType const dims): dims_(dims){}; + + void set_dims(DimensionsType const dims){ this->dims_ = dims;}; + DimensionsType get_dims( void ){ return this->dims_;}; + + + std::vector< DimensionsType > get_all_combinations( void ) + { + this->compute_all_combinations(); + return this->all_combinations_; + }; + + size_t get_num_total_combinations() + { + size_t linear_range = 1; + for( int direction=0; direction all_combinations_; + + void compute_all_combinations( void ) + { + size_t const linear_range = this->get_num_total_combinations(); + + for( size_t i=0; idims_.size(); j++) + { + size_t curr_idx = i; + + for(int k=0;krecurse(curr_idx, k, curr_combination); + + curr_idx = curr_idx % this->dims_[j]; + curr_combination.push_back(curr_idx); + } + this->all_combinations_.push_back( curr_combination ); + } + }; + + + int recurse(size_t const idx, int const num_iter, DimensionsType const curr_combination) + { + int l = (idx - curr_combination[num_iter])/this->dims_[num_iter]; + return l; + }; +}; + +/*! + \brief Interface for a dynamic simulation + +*/ +class aDynamicSimulation { + +public: + + aDynamicSimulation(){}; + ~aDynamicSimulation(){}; + + virtual void simulate_data( void ) = 0; + virtual void write_simulation_results( const std::string& filename_output_with_extension ) = 0; + + virtual void save_ground_truth_displacements(void) const = 0; + virtual void acquire_raw_data( void ) = 0; + +protected: + sirf::DynamicSimulationDeformer dsd_; + +}; + + +class MRDynamicSimulation : public aDynamicSimulation { + +public: + + MRDynamicSimulation(MRContrastGenerator mr_cont_gen) : mr_cont_gen_( mr_cont_gen ) + {}; + + void write_simulation_results( const std::string& filename_output_with_extension ); + + + void set_acquisition_template_rawdata(const sirf::MRAcquisitionData& acquisitions); + void set_contrast_template_rawdata(const sirf::MRAcquisitionData& acquisitions); + + void set_SNR(float const SNR); + void set_noise_label(int const label); + + void add_dynamic( std::shared_ptr sptr_motion_dyn){ + this->motion_dynamics_.push_back(sptr_motion_dyn); + } + void add_dynamic( std::shared_ptr sptr_contrast_dyn){ + this->contrast_dynamics_.push_back(sptr_contrast_dyn); + } + void add_dynamic( std::shared_ptr sptr_ext_contrast_dyn){ + this->external_contrast_.push_back(sptr_ext_contrast_dyn); + } + + void simulate_data( void ); + + void set_coilmaps(const std::shared_ptr sptr_csm) + { + this->acq_model_.set_csm(sptr_csm); + }; + + virtual void acquire_raw_data( void ); + virtual void save_ground_truth_displacements() const; + + virtual void save_groud_truth_parameter_maps( const std::string prefix_output ); + + virtual void set_offset_transformation(const sirf::AffineTransformation& trafo) + { + dsd_.set_offset_transformation(trafo); + } + + TissueParameter get_petmr_tissue_parameter(LabelType label){ + return mr_cont_gen_.get_petmr_tissue_parameter(label); + } + +private: + + std::vector< std::shared_ptr > motion_dynamics_; + std::vector< std::shared_ptr > contrast_dynamics_; + std::vector< std::shared_ptr > external_contrast_; + + GaussianNoiseGenerator noise_generator_; + + std::shared_ptr sptr_source_acquisitions_; + std::shared_ptr sptr_template_data_; + std::shared_ptr sptr_simul_data_; + + MRContrastGenerator mr_cont_gen_; + sirf::MRAcquisitionModel acq_model_; + + sirf::AcquisitionsVector + get_acquisitions_for_motionstate(DimensionsType current_combination) const; + + std::vector > + get_motionfields_for_motionstate(DimensionsType current_combination) const; + + LinearCombiGenerator prepare_motion_information(void); + + void update_tissue_parameters(TimeAxisType current_time_point); + + void shift_time_start_to_zero( void ); + void simulate_motion_dynamics( void ); + void simulate_contrast_dynamics( void ); + void simulate_simultaneous_motion_contrast_dynamics( void ); + void simulate_external_contrast_motion_dynamics( void ); + + void set_noise_scaling(); +}; + + +class PETDynamicSimulation : public aDynamicSimulation{ + +public: + PETDynamicSimulation( PETContrastGenerator pet_cont_gen ):aDynamicSimulation(), pet_cont_gen_(pet_cont_gen){}; + + void simulate_statics( void ); + + void simulate_data( void ); + void simulate_data( size_t const total_scan_time ); + + std::string get_filename_rawdata( void ) + { + return this-> filename_rawdata_; + } + + virtual void set_filename_rawdata( std::string const filename_template_rawdata ) + { + this->filename_rawdata_ = filename_template_rawdata; + } + + void set_template_acquisition_data( void ); + void set_template_image_data( const std::string& filename_header_with_ext ); + + void set_output_filename_prefix( const std::string& output_filename_prefix_); + + virtual void acquire_raw_data( void ); + + void add_dynamic( std::shared_ptr sptr_motion_dyn){ + this->motion_dynamics_.push_back(sptr_motion_dyn); + } + void add_dynamic( std::shared_ptr sptr_contrast_dyn){ + this->contrast_dynamics_.push_back(sptr_contrast_dyn); + } + + void add_noise( void ); + void add_noise( float const scaling_factor ); + + void write_simulation_results( const std::string& filename_output_with_extension ); + virtual void save_ground_truth_displacements() const; + virtual void save_groud_truth_parameter_maps() const + { + throw std::runtime_error("Not implemented yet"); + } + +private: + + std::string filename_rawdata_; + std::string output_filename_prefix_; + + void simulate_motion_dynamics(size_t const total_scan_time ); + + std::vector< std::shared_ptr > motion_dynamics_; + std::vector< std::shared_ptr > contrast_dynamics_; + + std::shared_ptr sptr_noise_generator_; + + sirf::STIRImageData get_reduced_pet_img_in_template_format( const sirf::STIRImageData& full_size_img ); + + PETContrastGenerator pet_cont_gen_; + sirf::STIRImageData template_image_data_; + + sirf::PETAcquisitionModelUsingMatrix acq_model_; + + sirf::PETAcquisitionDataInFile source_acquisitions_; + std::shared_ptr sptr_target_acquisitions_; + +}; + + + + + + diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_deformer.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_deformer.h new file mode 100644 index 000000000..c9e7a4776 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_deformer.h @@ -0,0 +1,117 @@ +/* +SyneRBI Synergistic Image Reconstruction Framework (SIRF) +Copyright 2018 - 2022 Physikalisch-Technische Bundesanstalt (PTB) + +This is software developed for the Collaborative Computational +Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR) +(http://www.ccpsynerbi.ac.uk/). + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +/*! +\file +\ingroup Simulation +\brief Classes and utilities for deforming MR and PET images during simulation. + +\author Johannes Mayer +\author SyneRBI +*/ + +#pragma once + +#include +#include + +#include + +#include "sirf/cDynamicSimulation/contrastgenerator.h" + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" // this header (rather the Gadgetron Base IO including Nifti) must not be included after the SIRFImageData.h headers. DONT put it into the cpp! + +#include "sirf/Reg/AffineTransformation.h" +#include "sirf/Reg/NiftiImageData3D.h" +#include "sirf/Reg/NiftiImageData3DDeformation.h" +#include "sirf/Reg/NiftiImageData3DDisplacement.h" + +#include "sirf/STIR/stir_data_containers.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" + + +namespace sirf{ + + + +/*! + \brief Utility class to deform image data with motion fields during a simulation. + This class contains an affine offset transformation and can deform contrast generators. +*/ +class DynamicSimulationDeformer +{ + +public: + + DynamicSimulationDeformer(){ + + std::array id_translation{0,0,0}; + std::array id_euler{0,0,0}; + + offset_ = sirf::AffineTransformation(id_translation, id_euler); + + } + + void set_offset_transformation(const sirf::AffineTransformation& trafo) + { + offset_ = trafo; + } + void add_offset_deformation(const std::vector > &vec) + { + displacement_offset_ = vec; + } + + /*! + \brief Function to deform the image in reference motion state held by a contrast generator into the motion states defined by the displacement fields. + */ + void deform_contrast_generator(MRContrastGenerator& mr_cont_gen, std::vector >& vec_displacement_fields); + void deform_contrast_generator(PETContrastGenerator& pet_cont_gen, std::vector >& vec_displacement_fields); + + /*! + \brief Function to resample an image to a template geometry. + This function is used to resample the segmentation volume to the acquisition template geometry. + During the resampling the first the displacement offset, then the affine offset_ transformation is applied, and then the volume is resampled into the template geometry. + */ + sirf::NiftiImageData3D resample_to_template(sirf::NiftiImageData3D img, bool const use_nearest_neighbor=false) const; + + void set_template_rawdata(const sirf::MRAcquisitionData& ad) + { + sptr_mr_template_img_ = std::shared_ptr(new sirf::GadgetronImagesVector(ad)); + mr_template_available_ = true; + } + + + +protected: + + + sirf::AffineTransformation offset_; + std::vector > displacement_offset_; + + std::shared_ptr sptr_mr_template_img_; + bool mr_template_available_ = false; + + static const std::string temp_folder_name_; + + static void deform_pet_image( sirf::STIRImageData& img, std::vector > &vec_displacement_fields); + +}; + +} //namespace sirf \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_noisegenerator.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_noisegenerator.h new file mode 100644 index 000000000..7f939d658 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/dynsim_noisegenerator.h @@ -0,0 +1,99 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.08.09 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#pragma once + +#include + +#include + +#include "sirf/STIR/stir_types.h" + +#include "sirf/STIR/stir_data_containers.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" + +// #define RPE_NOISE_SCALING 1.71558f +#define RPE_NOISE_SCALING 1.f + +typedef unsigned int SeedType; + + +class aNoiseGenerator{ + +public: + + aNoiseGenerator(){}; + aNoiseGenerator( SeedType const random_seed ): random_seed_(random_seed){} + virtual void set_random_seed( SeedType const seed) {this->random_seed_ = seed;}; + SeedType const get_random_seed( void ) {return this->random_seed_;}; + +protected: + SeedType random_seed_ = 1; + unsigned int generate_pseudo_seed(); + + +}; + + + +class PoissonNoiseGenerator: public aNoiseGenerator{ + +public: + + PoissonNoiseGenerator():aNoiseGenerator(), stir_noise_gen_(1.0f,false) + { + this->stir_noise_gen_.seed(this->generate_pseudo_seed()); + } + + PoissonNoiseGenerator(float const noise_scale):aNoiseGenerator(), stir_noise_gen_(noise_scale,false) + { + this->stir_noise_gen_.seed(this->generate_pseudo_seed()); + } + + virtual void set_random_seed( SeedType const seed) + { + this->random_seed_ = seed; + this->stir_noise_gen_.seed(this->random_seed_); + } + + void add_noise( sirf::PETAcquisitionData& noisy_acq, sirf::PETAcquisitionData& noise_free_acq); + +private: + stir::GeneralisedPoissonNoiseGenerator stir_noise_gen_; + +}; + +class GaussianNoiseGenerator: public aNoiseGenerator{ + +public: + + GaussianNoiseGenerator():aNoiseGenerator(), noise_width_img_(0.f){}; + GaussianNoiseGenerator(float const SNR): aNoiseGenerator(), SNR_(SNR){}; + + void set_signal_img(float const signal){ this->signal_img_ = signal; }; + void set_SNR(float const SNR){ this->SNR_ = SNR; }; + void set_sampling_specific_scaling( float const scaling) { this->sequence_specific_scaling_ = scaling;}; + + void add_noise( sirf::MRAcquisitionData& acquisition_vector ); + + +private: + static float constexpr mean_noise_ = 0.f; + + float SNR_ = -1; + float signal_img_ = 0.f; + float noise_width_img_ =0.f; + float noise_width_kspace_ =0.f; + + float sequence_specific_scaling_ = 1.f; + + void add_noise_to_data( sirf::MRAcquisitionData& acquisition_vector ); + float noise_width_from_snr( sirf::MRAcquisitionData& acquisition_vector ); + +}; \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/phantom_input.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/phantom_input.h new file mode 100644 index 000000000..029c4e528 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/phantom_input.h @@ -0,0 +1,95 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.26 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + + +#include +#include + +#include + +#include +#include +#include +#include + + +#include "sirf/common/GeometricalInfo.h" +#include "sirf/Reg/NiftiImageData3D.h" +#include "sirf/Reg/NiftiImageData3DDisplacement.h" + +#include "H5Cpp.h" + + + + +typedef unsigned int DataTypeSegmentation; +typedef float DataTypeMotionFields; + + +template < typename T > +std::vector< T > read_1D_dataset_from_h5( const std::string& h5_filename_with_suffix, const std::string& name_dataset, H5T_class_t data_type_dataset, H5::PredType data_type_reader ) +{ + using namespace H5; + const H5std_string dataset_name_h5 = name_dataset; + + std::cout << "Opening dataset " << name_dataset < output( num_elements ); + dataset.read( &output[0], data_type_reader, dataspace, dataspace); + + return output; + } + else + { + throw std::runtime_error("Please give read only from datasets with type passed to data_type."); + } + +} + +sirf::VoxelisedGeometricalInfo3D read_voxelised_geometry_info_from_h5_dataset( const std::string& h5_filename_with_suffix, const std::string& name_group ); + +template< typename inputType > +sirf::NiftiImageData3D read_nifti_from_h5( const std::string& h5_filename_with_suffix, const std::string& name_dataset, H5T_class_t data_type_dataset, H5::PredType data_type_reader ) +{ + std::stringstream sstream_dataset; + sstream_dataset << name_dataset << "/data"; + std::vector< inputType > dat = read_1D_dataset_from_h5 ( h5_filename_with_suffix, sstream_dataset.str(), data_type_dataset, data_type_reader ); + sirf::VoxelisedGeometricalInfo3D geo_info = read_voxelised_geometry_info_from_h5_dataset( h5_filename_with_suffix, name_dataset ); + + sirf::NiftiImageData3D< float > nifti_img( &dat[0], geo_info); + + return nifti_img; +} + +sirf::NiftiImageData3D read_segmentation_to_nifti_from_h5(const std::string& h5_filename_with_suffix); +std::vector< sirf::NiftiImageData3DDisplacement > read_motionfields_to_nifti_from_h5(const std::string& h5_filename_with_suffix, const std::string& motionfield_type); + +void scale_vector_data_to_geometry( sirf::NiftiImageData3DDisplacement &dvf ); + +std::vector< sirf::NiftiImageData3DDisplacement > read_cardiac_motionfields_to_nifti_from_h5( const std::string& h5_filename_with_suffix ); +std::vector< sirf::NiftiImageData3DDisplacement > read_respiratory_motionfields_to_nifti_from_h5( const std::string& h5_filename_with_suffix ); diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissuelabelmapper.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissuelabelmapper.h new file mode 100644 index 000000000..025600e29 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissuelabelmapper.h @@ -0,0 +1,73 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.27 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/Reg/NiftiImageData3D.h" + +typedef std::vector< std::shared_ptr > TissueVector; + +typedef sirf::NiftiImageData3D< float > LabelVolume; + + +class TissueLabelMapper{ + +public: + TissueLabelMapper(); + TissueLabelMapper(const LabelVolume& label_array, const std::string& xml_path); + + inline TissueVector get_segmentation_tissues (void) const + { + return this->segmentation_tissues_; + }; + + std::string get_filepath_tissue_parameter_xml( void ); + const int* get_segmentation_dimensions( void ); + + std::shared_ptr get_sptr_geometry(void) const{ + return segmentation_labels_.get_geom_info_sptr(); + } + LabelVolume get_segmentation_labels( void ); + TissueParameterList get_tissue_parameter_list( void ) const; + + void map_labels_to_tissue_from_xml( void ); + + void replace_petmr_tissue_parameters( const LabelType& label, const TissueParameter& tiss); + + // NEEDS TO STAY PUBLIC! + void assign_tissues_to_labels( void ); + + +// private: + + std::string filepath_tissue_parameter_xml_; + + TissueParameterList tissue_parameter_list_; + + LabelVolume segmentation_labels_; + TissueVector segmentation_tissues_; + +}; + + +// public methods for the class +TissueVector assign_tissue_parameters_to_labels( const TissueParameterList& tiss_list, const LabelVolume& label_volume ); diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissueparameters.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissueparameters.h new file mode 100644 index 000000000..d4bb8c46c --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/tissueparameters.h @@ -0,0 +1,108 @@ +/* file describing tissue parameter structs. +these data containers should be filled by an xml parser. + +author johannes mayer +date 20180321 +institute PTB Berlin + +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + + +typedef unsigned int LabelType; + +struct MRTissueParameter { + + float spin_density_percentH2O_; + float t1_miliseconds_; + float t2_miliseconds_; + float cs_ppm_; + ~MRTissueParameter(){}; + + MRTissueParameter operator+ (const MRTissueParameter& mr_to_add) const + { + MRTissueParameter mr_tiss; + mr_tiss.spin_density_percentH2O_ = this->spin_density_percentH2O_ + mr_to_add.spin_density_percentH2O_; + mr_tiss.t1_miliseconds_ = this->t1_miliseconds_ + mr_to_add.t1_miliseconds_; + mr_tiss.t2_miliseconds_ = this->t2_miliseconds_ + mr_to_add.t2_miliseconds_; + mr_tiss.cs_ppm_ = this->cs_ppm_ + mr_to_add.cs_ppm_; + + return mr_tiss; + }; + +}; + + + +struct PETTissueParameter { + + float attenuation_1_by_cm_; + float activity_kBq_ml_; + ~PETTissueParameter(){}; + + PETTissueParameter operator+ (const PETTissueParameter& pet_to_add) const + { + PETTissueParameter pet_tiss; + pet_tiss.attenuation_1_by_cm_ = this->attenuation_1_by_cm_ + pet_to_add.attenuation_1_by_cm_; + pet_tiss.activity_kBq_ml_ = this->activity_kBq_ml_ + pet_to_add.activity_kBq_ml_; + return pet_tiss; + } + +}; + + + + +struct TissueParameter { + + LabelType label_; + std::string name_; + + MRTissueParameter mr_tissue_; + PETTissueParameter pet_tissue_; + + ~TissueParameter(){}; + + TissueParameter operator+ (const TissueParameter& tiss_to_add) const + { + TissueParameter tiss; + tiss.label_ = this->label_; + tiss.name_ = this->name_; + + tiss.mr_tissue_ = this->mr_tissue_ + tiss_to_add.mr_tissue_; + tiss.pet_tissue_ = this->pet_tissue_ + tiss_to_add.pet_tissue_; + + return tiss; + }; +}; + + +MRTissueParameter operator* (float const x, const MRTissueParameter& a_mr); +PETTissueParameter operator* (float const x, const PETTissueParameter& a_pet); +TissueParameter operator* (float const x, const TissueParameter& a_tiss); + + + +typedef std::vector< TissueParameter > TissueParameterList; + + +TissueParameterList read_TissueParameters_from_xml(const std::string& xml_filepath); + +MRTissueParameter get_mrtissueparameter_from_ptree(boost::property_tree::ptree const pt); +PETTissueParameter get_pettissueparameter_from_ptree(boost::property_tree::ptree const pt); + + +bool check_label_uniqueness( const TissueParameterList& tiss_list); \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/volume_orientator.h b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/volume_orientator.h new file mode 100644 index 000000000..25193de9c --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/include/sirf/cDynamicSimulation/volume_orientator.h @@ -0,0 +1,101 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.09.18 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/iUtilities/LocalisedException.h" + + + +namespace sirf{ + +enum ReadoutDir {ro_dir_x, ro_dir_y, ro_dir_z}; + +class aVolumeOrientator{ + + +public: + + aVolumeOrientator(){}; + aVolumeOrientator(ReadoutDir dir):readout_direction_wrt_input_(dir){}; + + void set_readout_direction( ReadoutDir direction){ this->readout_direction_wrt_input_ = direction;}; + ReadoutDir get_readout_direction( ReadoutDir direction){ return this->readout_direction_wrt_input_;}; + + template + ISMRMRD::Image reorient_image( ISMRMRD::Image img) + { + + uint16_t Nx = img.getMatrixSizeX(); + uint16_t Ny = img.getMatrixSizeY(); + uint16_t Nz = img.getMatrixSizeZ(); + uint16_t Nc = img.getNumberOfChannels(); + + std::vector< uint16_t > vol_sizes{Nx, Ny, Nz}; + vol_sizes = this->reshuffle_indices( vol_sizes ); + + ISMRMRD::Image reoriented_volume( img ); + reoriented_volume.resize(vol_sizes[0], vol_sizes[1], vol_sizes[2], Nc); + + std::vector lin_index{0,1,2}; + auto shuffled_index = this->reshuffle_indices( lin_index ); + + std::vector< size_t > offsets_for_inversion{0,0,0}; + std::vector< int > sign_for_inversion{1,1,1}; + + // offsets_for_inversion[ (readout_direction_wrt_input_ - 1 + 3)%3 ] = vol_sizes[(readout_direction_wrt_input_ - 1 + 3)%3]-1; + // offsets_for_inversion[ (readout_direction_wrt_input_ - 2 + 3)%3 ] = vol_sizes[(readout_direction_wrt_input_ - 1 + 3)%3]-1; + // sign_for_inversion[ (readout_direction_wrt_input_ - 1 + 3)%3 ] = -1; + // sign_for_inversion[ (readout_direction_wrt_input_ - 2 + 3)%3 ] = -1; + + for( size_t nc=0; nc vol_access{nx,ny,nz}; + + size_t const access_x = offsets_for_inversion[0] + sign_for_inversion[0]*vol_access[ shuffled_index[0] ]; + size_t const access_y = offsets_for_inversion[1] + sign_for_inversion[1]*vol_access[ shuffled_index[1] ]; + size_t const access_z = offsets_for_inversion[2] + sign_for_inversion[2]*vol_access[ shuffled_index[2] ]; + + reoriented_volume( access_x, access_y, access_z ,nc) = img(nx, ny, nz, nc); + } + + return reoriented_volume; + } + + +protected: + + ReadoutDir readout_direction_wrt_input_; + + int sign_permutation; + + template< typename T > + std::vector reshuffle_indices( std::vector &indices) + { + if(indices.size()> 3) + throw LocalisedException("Only give 3 indices to shuffle volume orientation.", __FILE__, __LINE__); + + if(readout_direction_wrt_input_ > 2) + throw LocalisedException("Please give only readout directions 0 for x, 1 for y or 2 for z..", __FILE__, __LINE__); + + std::vector shuffled_idxs; + shuffled_idxs.resize(3,0); + for(int i=0; i<3; i++) + { + shuffled_idxs[(i+1)%3] = indices[i]; + } + + return shuffled_idxs; + } +}; + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/phantom_input.cpp b/src/xDynamicSimulation/cDynamicSimulation/phantom_input.cpp new file mode 100644 index 000000000..9bf5f6414 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/phantom_input.cpp @@ -0,0 +1,161 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.26 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#include "sirf/cDynamicSimulation/phantom_input.h" + +using namespace std; +using namespace H5; +using namespace sirf; + + +VoxelisedGeometricalInfo3D read_voxelised_geometry_info_from_h5_dataset( const std::string& h5_filename_with_suffix, const std::string& name_group ) +{ + + // --------------------------- Extend to directions if necessary --------------------------- + + std::stringstream group_prefix; + group_prefix << name_group << "/voxelised_geometry"; + + std::stringstream namestream_size; + namestream_size << group_prefix.str() << "/size" ; + + std::stringstream namestream_offset; + namestream_offset << group_prefix.str() << "/offset" ; + + std::stringstream namestream_spacing; + namestream_spacing << group_prefix.str() << "/spacing"; + + // ------------------------------------------------------------------------------------------ + + H5T_class_t type_input_uint = H5T_INTEGER; + PredType type_reader_uint = PredType::NATIVE_UINT32; + + std::vector< unsigned int > data_size = read_1D_dataset_from_h5< unsigned int >(h5_filename_with_suffix, namestream_size.str(), type_input_uint, type_reader_uint); + + H5T_class_t type_input_float = H5T_FLOAT; + PredType type_reader_float = PredType::NATIVE_FLOAT; + + std::vector< float > data_offset = read_1D_dataset_from_h5 (h5_filename_with_suffix, namestream_offset.str(), type_input_float, type_reader_float ); + std::vector< float > data_spacing = read_1D_dataset_from_h5 (h5_filename_with_suffix, namestream_spacing.str(), type_input_float, type_reader_float ); + + + if( data_size.size() !=3 || data_spacing.size() !=3 || data_offset.size() !=3) + throw std::runtime_error( "The input is not three-dimensional geometry."); + + VoxelisedGeometricalInfo3D::Offset geo_offset; + VoxelisedGeometricalInfo3D::Spacing geo_spacing; + VoxelisedGeometricalInfo3D::Size geo_size; + + for(int i=0;i<3;i++) + { + geo_offset [i] = data_offset[i]; + geo_spacing[i] = data_spacing[i]; + geo_size [i] = data_size[i]; + } + + // THIS ORIENTATION NEEDS TO BE FIXED DUE TO STIRs ++- ORIENTATION + VoxelisedGeometricalInfo3D::Coordinate l_dir, p_dir, s_dir; + + l_dir[0]=1; l_dir[1]= 0; l_dir[2]=0; + p_dir[0]=0; p_dir[1]= 1; p_dir[2]=0; + s_dir[0]=0; s_dir[1]= 0; s_dir[2]=-1; + + + VoxelisedGeometricalInfo3D::DirectionMatrix geo_dir; + geo_dir[0] = l_dir; geo_dir[1] = p_dir; geo_dir[2] = s_dir; + + VoxelisedGeometricalInfo3D geo_info(geo_offset, geo_spacing, geo_size, geo_dir); + + return geo_info; +} + +sirf::NiftiImageData3D read_segmentation_to_nifti_from_h5(const std::string& h5_filename_with_suffix) +{ + std::cout << "Reading segmentation from file: " << h5_filename_with_suffix < segmentation_nifti = read_nifti_from_h5( h5_filename_with_suffix, dataset_name, type_input, type_reader ); + + return segmentation_nifti; +} + +std::vector< sirf::NiftiImageData3DDisplacement > read_motionfields_to_nifti_from_h5(const std::string& h5_filename_with_suffix, const std::string& motionfield_type) +{ + + std::cout << "Reading motionfield from file: " << h5_filename_with_suffix << std::endl; + + VoxelisedGeometricalInfo3D geo_info = read_voxelised_geometry_info_from_h5_dataset(h5_filename_with_suffix, "/segmentation"); + + VoxelisedGeometricalInfo3D::Size const data_size = geo_info.get_size(); + size_t const num_voxels = data_size[0] * data_size[1] * data_size[2]; + + std::stringstream sstream_name_dataset; + sstream_name_dataset << "/motionfields/" << motionfield_type << "/data"; + + H5T_class_t type_input_float = H5T_FLOAT; + PredType type_reader_float = PredType::NATIVE_FLOAT; + + std::vector< float > dvf_data = read_1D_dataset_from_h5 ( h5_filename_with_suffix, sstream_name_dataset.str(), type_input_float, type_reader_float ); + + size_t const num_dimensions = 3; + size_t const num_phases = dvf_data.size() / (num_voxels * num_dimensions); + + + std::vector< sirf::NiftiImageData3DDisplacement > out; + + for( int i=0; i dvf_i( &dvf_data[i * num_dimensions*num_voxels], geo_info); + scale_vector_data_to_geometry( dvf_i ); + + out.push_back(dvf_i); + } + + return out; +} + +void scale_vector_data_to_geometry( sirf::NiftiImageData3DDisplacement &dvf ) +{ + const VoxelisedGeometricalInfo3D::Spacing input_spacing = dvf.get_geom_info_sptr()->get_spacing(); + const VoxelisedGeometricalInfo3D::Size input_size = dvf.get_geom_info_sptr()->get_size(); + + size_t const Nz = input_size[2]; + size_t const Ny = input_size[1]; + size_t const Nx = input_size[0]; + + size_t const num_voxels = Nx*Ny*Nz; + + size_t const num_dimensions = 3; + + std::array direction_sign{ 1, 1, 1 }; + + for( size_t nz=0; nz > read_cardiac_motionfields_to_nifti_from_h5( const std::string& h5_filename_with_suffix ) +{ + return read_motionfields_to_nifti_from_h5(h5_filename_with_suffix, "cardiac"); + +} +std::vector< sirf::NiftiImageData3DDisplacement > read_respiratory_motionfields_to_nifti_from_h5( const std::string& h5_filename_with_suffix ) +{ + return read_motionfields_to_nifti_from_h5(h5_filename_with_suffix, "respiratory"); +} diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/CMakeLists.txt b/src/xDynamicSimulation/cDynamicSimulation/tests/CMakeLists.txt new file mode 100644 index 000000000..4a56901c4 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/CMakeLists.txt @@ -0,0 +1,47 @@ +#======================================================================== +# Author: Johannes Mayer +# Copyright 2016 - 2020 University College London +# Copyright 2021 Physikalisch-Technische Bundesanstalt Berlin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +#========================================================================= + +set( SIMULATION_TEST_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/tests_dynamicsimulation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_tissueparameters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_contrastgenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_phantom_input.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_dynamics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_noisegenerator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_volume_orientator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_dynsim_deformer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_auxiliary_testing_functions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_auxiliary_input_output.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/tests_c_interface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/auxiliary_testing_functions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/app_mracquisitiondata.cpp +) + +add_library(SIMULATION_TESTS_LIB ${SIMULATION_TEST_FILES}) +target_link_libraries(SIMULATION_TESTS_LIB PUBLIC csirf cgadgetron cdynamicsimulation MR_TESTS_CPP_AUXILIARY) +target_include_directories(SIMULATION_TESTS_LIB PUBLIC "$$") + + +add_executable(SIMULATION_TESTS_CPLUSPLUS ${CMAKE_CURRENT_SOURCE_DIR}/all_simulation_tests.cpp ${STIR_REGISTRIES}) +target_link_libraries(SIMULATION_TESTS_CPLUSPLUS PUBLIC SIMULATION_TESTS_LIB ${STIR_LIBRARIES}) +INSTALL(TARGETS SIMULATION_TESTS_CPLUSPLUS DESTINATION bin) + +ADD_TEST(NAME SIMULATION_TESTS_CPLUSPLUS + COMMAND SIMULATION_TESTS_CPLUSPLUS + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/all_simulation_tests.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/all_simulation_tests.cpp new file mode 100644 index 000000000..2b6f5c848 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/all_simulation_tests.cpp @@ -0,0 +1,539 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.21 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +// Files collecting all tests for one specific module. + + +#include +#include +#include +#include +#include +#include + +#include "test_input_filenames.h" + +#include "app_mracquisitiondata.h" +#include "tests_auxiliary_testing_functions.h" +#include "tests_auxiliary_input_output.h" +#include "tests_tissueparameters.h" +#include "tests_contrastgenerator.h" +#include "tests_phantom_input.h" +#include "tests_dynamics.h" +#include "tests_dynamicsimulation.h" +#include "tests_noisegenerator.h" +#include "tests_dynsim_deformer.h" +#include "tests_c_interface.h" + + +bool run_apps(void) +{ + // std::string const fname_ismrmrd = std::string(SHARED_FOLDER_PATH) + "/PublicationData/FatWaterQuantification/Output/5DMotion/output_grpe_mri_simulation_motion_type_cardiorespiratory__num_motion_states_10_x_10.h5"; + std::string const fname_ismrmrd = std::string(SHARED_FOLDER_PATH) + "/PublicationData/FatWaterQuantification/Output/4DMotion/Cardiac/output_grpe_mri_simulation_motion_type_cardiac_num_motion_states_10.h5"; + apps_johannesmayer::omitt_first_acquisition(fname_ismrmrd); +} + + +bool run_tests_auxiliary_testing_functions(void ) +{ + + std::cout<< "Running " << __FUNCTION__ << std::endl; + + try + { + bool tests_successful = true; + int i=0; + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_serialized_ismrmrd_header(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_mock_acquisition_vector(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_mock_ismrmrd_image_with_cube(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_mock_pet_contrast_generator(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_mock_sawtooth_signal(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + tests_successful *= test_aux_test_funs::test_get_mock_gaussian_csm(); + std::cout << "#:" << ++i << "-------------------------------------" << std::endl; + + return tests_successful; + + } + catch(std::runtime_error const &e){ + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < dyn_tests; + std::cout << "start ----------------------------------------------------" < mr_dynsim_tests; + + // mr_dynsim_tests.push_back(test_lin_combi_gen::test_get_all_combinations()); + // mr_dynsim_tests.push_back(tests_datageneration::read_write_h5_filecontent()); + mr_dynsim_tests.push_back(tests_mr_dynsim::test_write_ground_truth_parametermaps()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_constructor()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_simulate_statics()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_simulate_dynamics()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_simulate_5d_motion_dynamics()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_external_contrast_acquisition()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_simulate_rpe_acquisition()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_dce_acquisition()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_4d_mri_acquisition()); + // mr_dynsim_tests.push_back(tests_mr_dynsim::test_5d_mri_acquisition()); + + std::cout << "mr dynamic simulation test results = "; + for( size_t i=0; i pet_dynsim_tests; + + // pet_dynsim_tests.push_back(test_pet_dynsim::test_constructor()); + // pet_dynsim_tests.push_back(test_pet_dynsim::set_template_acquisition_data()); + // pet_dynsim_tests.push_back(test_pet_dynsim::test_simulate_statics()); + // pet_dynsim_tests.push_back(test_pet_dynsim::test_simulate_motion_dynamics()); + // pet_dynsim_tests.push_back(test_pet_dynsim::test_4d_pet_acquisition()); + // pet_dynsim_tests.push_back(test_pet_dynsim::test_5d_pet_acquisition()); + + std::cout << "pet dynamic simulation test results = "; + for( size_t i=0; i noise_tests; + + noise_tests.push_back(test_noisegen::test_add_poisson_noise()); + noise_tests.push_back(test_noisegen::test_add_gaussian_noise()); + + std::cout << "Results " << __FUNCTION__ << " = "; + + for( size_t i=0; i tlm_tests, abstract_contgen_tests, mr_contgen_tests, pet_contgen_tests; + + // tlm tests + + tlm_tests.push_back( test_tlm::test_get_filepath_tissue_parameter_xml() ); + tlm_tests.push_back( test_tlm::test_get_labels_array() ); + tlm_tests.push_back( test_tlm::test_get_segmentation_dimensions() ); + tlm_tests.push_back( test_tlm::test_assign_tissue_parameters_label_found() ); + tlm_tests.push_back( test_tlm::test_assign_tissue_parameters_label_not_found() ); + tlm_tests.push_back( test_tlm::test_map_labels_to_tissue_from_xml() ); + tlm_tests.push_back( test_tlm::test_replace_petmr_tissue_parameters() ); + + std::cout << "#### #### #### Tissue-Label-Mapper test results = "; + for( size_t i=0; i test_results{}; + + test_results.push_back(test_read_1D_dataset_from_h5(H5_XCAT_PHANTOM_PATH)); + test_results.push_back(test_read_geometrical_info_from_h5( H5_XCAT_PHANTOM_PATH )); + test_results.push_back(test_read_segmentation_to_nifti( H5_XCAT_PHANTOM_PATH )); + test_results.push_back(test_read_motionfield_to_nifti( H5_XCAT_PHANTOM_PATH )); + + std::cout << "#### #### #### " << __FUNCTION__ << " test results = "; + for( size_t i=0; i test_results{}; + + test_results.push_back(DynSimDeformerTester::test_mr_geometry()); + test_results.push_back(DynSimDeformerTester::test_nifti_data_deformation()); + test_results.push_back(DynSimDeformerTester::test_deform_mr_contrast_generator()); + test_results.push_back(DynSimDeformerTester::test_deform_pet_contrast_generator()); + test_results.push_back(DynSimDeformerTester::test_motion_of_MotionDynamics()); + + + std::cout << "#### #### #### " << __FUNCTION__ << " test results = "; + for( size_t i=0; i test_results{}; + + test_results.push_back(test_simulation_interface::test_bin_data_from_handle()); + + std::cout << "#### #### #### " << __FUNCTION__ << " test results = "; + for( size_t i=0; i 1) + fprintf(stdout, "Please do not pass any arguments. This just runs test code."); + + // ok *= run_tests_auxiliary_testing_functions(); + // ok *= run_tests_auxiliary_input_output(); + // ok *= run_tests_tissueparameters(); + // ok *= run_tests_contrastgenerator(); + // ok *= run_tests_phantom_input(); + // ok *= run_tests_noise_generator(); + // ok *= run_tests_dynamics(); + // ok *= run_tests_c_interface(); + // ok *= run_tests_dynsim_deformer(); + ok *= run_tests_dynamic_simulation(); + + if(ok) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; + } + catch(const std::exception &error) { + std::cerr << "\nHere's the error:\n\t" << error.what() << "\n\n"; + return EXIT_FAILURE; + } + +} + + diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/app_mracquisitiondata.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/app_mracquisitiondata.cpp new file mode 100644 index 000000000..61b60d63d --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/app_mracquisitiondata.cpp @@ -0,0 +1,42 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2019.10.07 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#include "app_mracquisitiondata.h" + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include + + +void apps_johannesmayer::omitt_first_acquisition(std::string const fname_ismrmrd) +{ + + size_t const first_acq = 1; //start with this acquisition + + sirf::AcquisitionsVector acq_vec; + acq_vec.read( fname_ismrmrd ); + + sirf::AcquisitionsVector coherent_acq_vec; + coherent_acq_vec.copy_acquisitions_info(acq_vec); + + for(size_t i_acq=first_acq; i_acq + + +namespace apps_johannesmayer{ + + void omitt_first_acquisition(std::string const fname_ismrmrd); + + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/auxiliary_testing_functions.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/auxiliary_testing_functions.cpp new file mode 100644 index 000000000..9c76e4d13 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/auxiliary_testing_functions.cpp @@ -0,0 +1,929 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.28 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#include "auxiliary_testing_functions.h" + + +//TODO Johannes #include +#include +#include +#include +#include + +#include +#include + +#include + + +using namespace sirf; + + +TissueParameterList aux_test::get_mock_tissue_param_list( void ) +{ + TissueParameter par1, par2, par3, par4; + par1.name_ = "fake_one"; + par1.label_ = 1; + + par2.name_ = "fake_two"; + par2.label_ = 2; + + par3.name_ = "fake_three"; + par3.label_ = 3; + + par4.name_ = "fake_four"; + par4.label_ = 4; + + TissueParameterList tiss_list; + + tiss_list.push_back(par1); + tiss_list.push_back(par2); + tiss_list.push_back(par3); + tiss_list.push_back(par4); + + return tiss_list; +} + +sirf::VoxelisedGeometricalInfo3D aux_test::get_mock_geometrical_info( void ) +{ + + sirf::VoxelisedGeometricalInfo3D::Offset offset{0.f,0.f,0.f}; + sirf::VoxelisedGeometricalInfo3D::Spacing spacing{1.f,1.f,1.f}; + sirf::VoxelisedGeometricalInfo3D::Size data_size{MOCK_DATA_MATRIX_SIZE, + MOCK_DATA_MATRIX_SIZE, + MOCK_DATA_MATRIX_SIZE}; + + sirf::VoxelisedGeometricalInfo3D::DirectionMatrix dir_mat; + + dir_mat[0] = {1,0,0}; + dir_mat[1] = {0,1,0}; + dir_mat[2] = {0,0,1}; + + sirf::VoxelisedGeometricalInfo3D mock_geo_info( offset, spacing, data_size, dir_mat); + + return mock_geo_info; +} + + + +LabelVolume aux_test::get_mock_label_volume( void ) +{ + + sirf::VoxelisedGeometricalInfo3D mock_geo_info = aux_test::get_mock_geometrical_info(); + auto data_size = mock_geo_info.get_size(); + + size_t const num_vox = data_size[0] * data_size[1] * data_size[2]; + + std::vector mock_data; + float const default_value = 0.f; + mock_data.assign( num_vox, default_value); + + LabelVolume label_vol(&mock_data[0], mock_geo_info); + + for( int i=0; i< label_vol.get_num_voxels(); i++) + { + if( i< label_vol.get_num_voxels()/2 ) + label_vol(i) = 1; + else + label_vol(i) = 2; + } + + return label_vol; +} + +TissueParameter aux_test::get_mock_tissue_parameter( void ) +{ + + TissueParameter tiss_par; + tiss_par.name_ = "mocktissue"; + tiss_par.label_ = 0; + + tiss_par.mr_tissue_ = get_mock_MR_tissue_parameter(); + tiss_par.pet_tissue_ = get_mock_PET_tissue_parameter(); + return tiss_par; +} + +MRTissueParameter aux_test::get_mock_MR_tissue_parameter(void) +{ + MRTissueParameter mr_tissue_pars; + mr_tissue_pars.spin_density_percentH2O_ = 100; + mr_tissue_pars.t1_miliseconds_ = 1; + mr_tissue_pars.t2_miliseconds_ = 2; + mr_tissue_pars.cs_ppm_ = 1; + + return mr_tissue_pars; +} + +PETTissueParameter aux_test::get_mock_PET_tissue_parameter(void) +{ + PETTissueParameter pet_tissue_pars; + pet_tissue_pars.attenuation_1_by_cm_ = 0.01; + pet_tissue_pars.activity_kBq_ml_ = 15; + + + return pet_tissue_pars; +} + + +std::pair< TissueParameter, TissueParameter> aux_test::get_mock_contrast_signal_extremes( void ) +{ + std::pair< TissueParameter, TissueParameter> output; + + TissueParameter tiss_at_0, tiss_at_1; + + tiss_at_0.name_ = "dynamic_par"; + tiss_at_0.label_ = 10000; + + tiss_at_0.mr_tissue_.spin_density_percentH2O_ = 80; + tiss_at_0.mr_tissue_.t1_miliseconds_ = 1157; + tiss_at_0.mr_tissue_.t2_miliseconds_ = 44; + tiss_at_0.mr_tissue_.cs_ppm_ = 0; + + tiss_at_0.pet_tissue_.attenuation_1_by_cm_= 0; + tiss_at_0.pet_tissue_.activity_kBq_ml_= 0; + + output.first = tiss_at_0; + + tiss_at_1.name_ = "dynamic_par"; + tiss_at_1.label_ = 10000; + + tiss_at_1.mr_tissue_.spin_density_percentH2O_ = 80; + tiss_at_1.mr_tissue_.t1_miliseconds_ = 600; + tiss_at_1.mr_tissue_.t2_miliseconds_ = 44; + tiss_at_1.mr_tissue_.cs_ppm_ = 0; + + tiss_at_1.pet_tissue_.attenuation_1_by_cm_= 0; + tiss_at_1.pet_tissue_.activity_kBq_ml_= 0; + + output.second = tiss_at_1; + + return output; +} + +MRContrastGenerator aux_test::get_mock_mr_contrast_generator( void ) +{ + LabelVolume label_list = get_mock_label_volume(); + + MRContrastGenerator mr_cont(label_list, XML_TEST_PATH); + + ISMRMRD::IsmrmrdHeader hdr = get_mock_ismrmrd_header(); + + mr_cont.set_rawdata_header( hdr); + + return mr_cont; + +} + +PETContrastGenerator aux_test::get_mock_pet_contrast_generator( void ) +{ + LabelVolume segmentation_labels = read_segmentation_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + PETContrastGenerator pet_cont_gen( segmentation_labels, XML_XCAT_PATH); + + pet_cont_gen.set_template_image_from_file( PET_TEMPLATE_CONTRAST_IMAGE_DATA_PATH ); + + return pet_cont_gen; +} + + + + +ISMRMRD::IsmrmrdHeader aux_test::get_mock_ismrmrd_header( void ) +{ + using namespace ISMRMRD; + + IsmrmrdHeader hdr; + + SequenceParameters seq_pars = get_mock_sequence_parameters(); + AcquisitionSystemInformation asi = get_mock_acquisition_system_information(); + + // necessary + hdr.experimentalConditions = get_mock_experimental_conditions(); + hdr.encoding = aux_test::get_mock_encoding_vector(); + + // optional + hdr.sequenceParameters = Optional(seq_pars); + hdr.acquisitionSystemInformation = Optional(asi); + + return hdr; + +} + +std::string aux_test::get_serialized_mock_ismrmrd_header( void ) +{ + ISMRMRD::IsmrmrdHeader hdr = get_mock_ismrmrd_header(); + std::ostringstream out; + ISMRMRD::serialize(hdr, out); + + return out.str(); +} + + +ISMRMRD::AcquisitionSystemInformation aux_test::get_mock_acquisition_system_information( void ) +{ + ISMRMRD::AcquisitionSystemInformation asi; + + asi.receiverChannels = ISMRMRD::Optional ( MOCK_DATA_NUM_CHANNELS ); + asi.systemFieldStrength_T = ISMRMRD::Optional( MOCK_FIELD_STRENGTH ); + return asi; + +} + + +ISMRMRD::SequenceParameters aux_test::get_mock_sequence_parameters( void ) +{ + + + using namespace ISMRMRD; + + typedef std::vector ParType; + + SequenceParameters seq_pars; + + ParType TR = {2}; + ParType TE = {1}; + ParType TI = {1}; + ParType flipAngle_deg = {90}; + std::string sequ_type = {"Flash"}; + ParType dE = {2}; + + seq_pars.TR = Optional< ParType >(TR); + seq_pars.TE = Optional< ParType >(TE); + seq_pars.TI = Optional< ParType >(TI); + seq_pars.flipAngle_deg = Optional< ParType >(flipAngle_deg); + seq_pars.sequence_type = Optional< std::string >(sequ_type); + seq_pars.echo_spacing = Optional< ParType >(dE); + + return seq_pars; + +} + +ISMRMRD::ExperimentalConditions aux_test::get_mock_experimental_conditions( void ) +{ + ISMRMRD::ExperimentalConditions e_con; + e_con.H1resonanceFrequency_Hz = 42580000; + return e_con; +} + +std::vector< ISMRMRD::Encoding > aux_test::get_mock_encoding_vector( void ) +{ + ISMRMRD::Encoding enc; + + enc.trajectory = ISMRMRD::TrajectoryType::CARTESIAN; + + enc.encodedSpace = get_mock_encoded_space(); + enc.reconSpace = get_mock_recon_space(); + enc.encodingLimits = get_mock_encoding_limits(); + + + std::vector< ISMRMRD::Encoding > enc_vec; + enc_vec.push_back( enc ); + return enc_vec; +} + +ISMRMRD::EncodingSpace aux_test::get_mock_encoded_space( void ) +{ + + ISMRMRD::MatrixSize mat_size( MOCK_DATA_RO_OVERSAMPLING * MOCK_DATA_MATRIX_SIZE,MOCK_DATA_MATRIX_SIZE,MOCK_DATA_MATRIX_SIZE); + ISMRMRD::FieldOfView_mm fov; + + float const resolution_mm = MOCK_FOV/MOCK_DATA_MATRIX_SIZE; + + fov.x = 1/resolution_mm; + fov.y = 1/resolution_mm; + fov.z = 1/resolution_mm; + + + ISMRMRD::EncodingSpace enc_spac; + enc_spac.matrixSize = mat_size; + enc_spac.fieldOfView_mm = fov; + + return enc_spac; +} + +ISMRMRD::EncodingSpace aux_test::get_mock_recon_space( void ) +{ + + ISMRMRD::MatrixSize mat_size(MOCK_DATA_MATRIX_SIZE,MOCK_DATA_MATRIX_SIZE,MOCK_DATA_MATRIX_SIZE); + ISMRMRD::FieldOfView_mm fov; + + fov.x = MOCK_FOV; + fov.y = MOCK_FOV; + fov.z = MOCK_FOV; + + ISMRMRD::EncodingSpace enc_spac; + enc_spac.matrixSize = mat_size; + enc_spac.fieldOfView_mm = fov; + + return enc_spac; + +} + +ISMRMRD::EncodingLimits aux_test::get_mock_encoding_limits( void ) +{ + unsigned short const max_PE1 = MOCK_DATA_MATRIX_SIZE; + unsigned short const max_PE2 = MOCK_DATA_MATRIX_SIZE; + + unsigned short const center_PE1 = MOCK_DATA_MATRIX_SIZE/2 - 1; + unsigned short const center_PE2 = MOCK_DATA_MATRIX_SIZE/2 - 1; + + ISMRMRD::Limit limit_PE1(0, max_PE1, center_PE1); + ISMRMRD::Limit limit_PE2(0, max_PE2, center_PE2); + + ISMRMRD::EncodingLimits enc_lim; + enc_lim.kspace_encoding_step_1 = ISMRMRD::Optional( limit_PE1); + enc_lim.kspace_encoding_step_2 = ISMRMRD::Optional( limit_PE2); + + return enc_lim; +} + +ISMRMRD::NDArray aux_test::get_mock_ndarray_with_cube( void ) +{ + + + + size_t const Nx = MOCK_DATA_MATRIX_SIZE; + size_t const Ny = MOCK_DATA_MATRIX_SIZE; + size_t const Nz = MOCK_DATA_MATRIX_SIZE; + + std::vector< size_t > mock_dims; + mock_dims.push_back(Nx); + mock_dims.push_back(Ny); + mock_dims.push_back(Nz); + + ISMRMRD::NDArray mock_arr; + mock_arr.resize(mock_dims); + + + float const val = 1; + complex_float_t const cube_value = std::complex( val, val); + + float const cube_radius = Nx/4; + std::vector cube_center = {Nx/2, Ny/2, Nz/2}; + //#pragma omp parallel + for( size_t nz=0; nz aux_test::get_mock_ismrmrd_image_with_cube( void ) +{ + + ISMRMRD::NDArray mock_arr = get_mock_ndarray_with_cube(); + size_t const * img_dims = mock_arr.getDims(); + + ISMRMRD::Image< complex_float_t > mock_img(img_dims[0], img_dims[1], img_dims[2], 1); + + size_t num_elements = mock_img.getNumberOfDataElements(); + + for( size_t i=0; i aux_test::get_mock_ismrmrd_image_with_gradients( void ) +{ + size_t const Nx = MOCK_DATA_MATRIX_SIZE/2; + size_t const Ny = MOCK_DATA_MATRIX_SIZE/2; + size_t const Nz = MOCK_DATA_MATRIX_SIZE; + size_t const Nc = MOCK_DATA_NUM_CHANNELS; + + + ISMRMRD::Image< float > mock_img(Nx, Ny, Nz, Nc); + for(size_t c=0; c vol_dims, int const num_coils ) +{ + + int const num_non_zero_coils = pow( (double)2, std::floor( log2( num_coils ) ) ); + + std::vector sensitivity_widths {vol_dims[0] /2.f, vol_dims[1] /2.f, vol_dims[2] /2.f }; + + + std::vector x_range, y_range, z_range; + + for( size_t i=0; i x_grid(vol_dims), y_grid(vol_dims), z_grid(vol_dims); + + + for( size_t nz=0; nz csm( vol_dims[0], vol_dims[1], vol_dims[2], num_coils ); + + for( size_t i=0; i (0,0); + + + std::vector center_container_size{(size_t)3, (size_t)num_coils}; + ISMRMRD::NDArray< float > coil_centers( center_container_size ); + + if( num_non_zero_coils == 1) + { + std::cout << "Simulating body coil with perfect coil profile." << std::endl; + for(size_t i=0; i(1.f, 0); + + sirf::CoilSensitivitiesVector coilmaps; + coilmaps.append(csm); + + return coilmaps; + + } + else if (num_non_zero_coils == 2) + { + + coil_centers(0, 0) = x_range[ std::floor( x_range.size()/2) ]; + coil_centers(1, 0) = y_range[0]; + coil_centers(2, 0) = 0; + + + coil_centers(0, 1) = x_range[ std::floor( x_range.size()/2) ];; + coil_centers(1, 1) = y_range.back(); + coil_centers(2, 1) = 0; + + } + else + { + size_t const coils_per_side = num_non_zero_coils / 4; + float const side_distances = 1.f/( coils_per_side + 1.f); + + for(size_t j=0; j( exp( - dist_square_to_coil_center ), 0); + } + } + + sirf::CoilSensitivitiesVector coilmaps; + coilmaps.append(csm); + + return coilmaps; +} + +ISMRMRD::AcquisitionHeader aux_test::get_mock_acquisition_header( void ) +{ + ISMRMRD::AcquisitionHeader acq_hdr; + acq_hdr.acquisition_time_stamp = 0; + acq_hdr.number_of_samples = MOCK_DATA_RO_OVERSAMPLING * MOCK_DATA_MATRIX_SIZE; + acq_hdr.available_channels = MOCK_DATA_NUM_CHANNELS; + acq_hdr.center_sample = MOCK_DATA_MATRIX_SIZE/2 - 1; + + return acq_hdr; + +} + +AcquisitionsVector aux_test::get_mock_acquisition_vector ( ISMRMRD::IsmrmrdHeader hdr ) +{ + + using namespace ISMRMRD; + typedef unsigned short unshort; + + std::ostringstream out; + serialize(hdr, out); + + AcquisitionsInfo serialized_hdr(out.str()); + AcquisitionsVector acq_vec(serialized_hdr); + + std::vector< Encoding > encodings = hdr.encoding; + + size_t const num_scans = encodings.size(); + + for( size_t iacq=0; iacq TE = seq_par.TE(); + std::vector TR = seq_par.TR(); + + Encoding enc = encodings[iacq]; + EncodingSpace enc_spac = enc.encodedSpace; + + MatrixSize size_enc_space = enc_spac.matrixSize; + + unshort const NContrasts = TE.size(); + unshort const NSamples = size_enc_space.x; + unshort const NPhases = size_enc_space.y; + unshort const NSlices = size_enc_space.z; + + for( unshort iSlice=0; iSlice signal_point; + + for( size_t i_time=0; i_time aux_test::get_mock_external_signal(const std::vector& label_list, const float weight) +{ + + std::uint32_t dummy_timestamp= 0; + + std::vector sigvec; + float const angle = 30/360*2*M_PI; + const complex_float_t phase = complex_float_t( std::cos(angle), std::sin(angle)); + + for(int i=0; i > +aux_test::get_mock_external_signals_for_templatedata(const std::vector& label_list, const sirf::MRAcquisitionData& ad) +{ + std::vector > external_signals; + for(int i=0; i all_time_points; + + ISMRMRD::Acquisition acq; + for(size_t ia=0; ia signal_point; + + signal_point.first = all_time_points[i]-t0; + signal_point.second = (1 - cos(2*M_PI / period_duration_ms * (all_time_points[i]-t0)))/2; + signal.push_back(signal_point); + } + + return signal; +} + +SignalContainer aux_test::get_mock_sawtooth_signal( AcquisitionsVector acq_vec, TimeAxisType const period_duration_ms) +{ + acq_vec.sort_by_time(); + ISMRMRD::Acquisition acq; + acq_vec.get_acquisition(0, acq); + TimeAxisType t_0 = acq.getHead().acquisition_time_stamp; + + + acq_vec.get_acquisition(acq_vec.items()-1, acq); + TimeAxisType t_fin = acq.getHead().acquisition_time_stamp; + + + unsigned const num_sampling_points = acq_vec.number(); + + TimeAxisType dt = float(t_fin - t_0)/ float(num_sampling_points); + + SignalContainer signal; + + for( unsigned i=0; i signal_point; + + signal_point.first = i * dt; + + signal_point.second = fmod(signal_point.first, period_duration_ms) / period_duration_ms; + signal.push_back(signal_point); + } + + return signal; +} + +SignalContainer aux_test::get_generic_contrast_inflow_signal( sirf::AcquisitionsVector &acq_vec) +{ + ISMRMRD::Acquisition acq; + acq_vec.get_acquisition(0, acq); + TimeAxisType t_0 = acq.getHead().acquisition_time_stamp; + + + acq_vec.get_acquisition(acq_vec.items()-1, acq); + TimeAxisType t_fin = acq.getHead().acquisition_time_stamp; + + SignalContainer signal; + + std::pair zero_signal_point, one_signal_point; + + zero_signal_point.first = t_0; + zero_signal_point.second = SignalAxisType(0); + + signal.push_back(zero_signal_point); + + one_signal_point.first = t_fin; + one_signal_point.second = SignalAxisType(1); + + signal.push_back(one_signal_point); + + return signal; +} + +SignalContainer aux_test::get_generic_contrast_in_and_outflow_signal( sirf::AcquisitionsVector &acq_vec ) +{ + ISMRMRD::Acquisition acq; + + acq_vec.get_acquisition(0, acq); + TimeAxisType t_0 = acq.getHead().acquisition_time_stamp; + + acq_vec.get_acquisition(acq_vec.items()-1, acq); + TimeAxisType t_fin = acq.getHead().acquisition_time_stamp; + + TimeAxisType t_half = (t_fin + t_0)/ (TimeAxisType)2; + + SignalContainer signal; + + std::pair zero_signal_point, one_signal_point, final_signal_point; + + zero_signal_point.first = t_0; + zero_signal_point.second = SignalAxisType(0); + + signal.push_back(zero_signal_point); + + one_signal_point.first = t_half; + one_signal_point.second = SignalAxisType(1); + + signal.push_back(one_signal_point); + + final_signal_point.first = t_fin; + final_signal_point.second = SignalAxisType(0); + + signal.push_back(final_signal_point); + + return signal; +} + + +SignalContainer aux_test::get_generic_respiratory_signal( sirf::AcquisitionsVector &acq_vec) +{ + TimeAxisType const period_duartion_ms = 3300; + return aux_test::get_mock_sinus_signal(acq_vec, period_duartion_ms ); +} + + +SignalContainer aux_test::get_generic_cardiac_signal( sirf::AcquisitionsVector &acq_vec) +{ + TimeAxisType const period_duartion_ms = 900; + return aux_test::get_mock_sawtooth_signal(acq_vec, period_duartion_ms ); +} + + +MRContrastDynamic aux_test::get_constant_contrast( LabelType const which_tissue_label, TissueParameter const template_param, float const T1_ms) +{ + // constant contrast -> i.e. only one state + int const num_sim_states = 1; + MRContrastDynamic cont_dyn(num_sim_states); + + cont_dyn.add_dynamic_label(which_tissue_label); + + // mock signal out of two constant points -> will generate constant T1 as set in the dynamic + SignalPoint sig_pt_0(0,1); + SignalPoint sig_pt_1(1,1); + SignalContainer signal{sig_pt_0, sig_pt_1}; + + cont_dyn.set_dynamic_signal(signal); + + // fix two parameters that correspond to the 0 and 1. + TissueParameter param_at_1 = template_param; + param_at_1.mr_tissue_.t1_miliseconds_ = T1_ms; + + cont_dyn.set_parameter_extremes( template_param, param_at_1); + + return cont_dyn; +} + + + +void aux_test::store_roi( LabelVolume& label_vol, std::vector const labels, std::string const output_prefix) +{ + for(size_t lab=0; lab + +#include +#include + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/Gadgetron/gadgetron_image_wrap.h" + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/cDynamicSimulation/phantom_input.h" +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/cDynamicSimulation/tissuelabelmapper.h" +#include "sirf/cDynamicSimulation/contrastgenerator.h" +#include "sirf/cDynamicSimulation/dynamics.h" + +#include "sirf/common/GeometricalInfo.h" + +#include "test_input_filenames.h" + +// volume sizes + +#define MOCK_FOV 256 +#define MOCK_DATA_MATRIX_SIZE 128 +#define MOCK_DATA_NUM_CHANNELS 4 +#define MOCK_DATA_RO_OVERSAMPLING 1 +#define MOCK_IMAGE_TYPE 5 // from ismrmrd enum ISMRMRD_IMTYPE_COMPLEX = 5 +#define MOCK_DATA_TYPE 7 // from ismrmrd enum ISMRMRD_CXFLOAT = 7 +#define MOCK_FIELD_STRENGTH 1 + +// mock signal +#define MOCK_NUM_SIG_PTS 10 + + +namespace aux_test +{ + + TissueParameterList get_mock_tissue_param_list( void ); + + LabelVolume get_mock_label_volume( void ); + sirf::VoxelisedGeometricalInfo3D get_mock_geometrical_info( void ); + + TissueParameter get_mock_tissue_parameter( void ); + PETTissueParameter get_mock_PET_tissue_parameter( void ); + MRTissueParameter get_mock_MR_tissue_parameter( void ); + + std::pair< TissueParameter, TissueParameter> get_mock_contrast_signal_extremes( void ); + + MRContrastGenerator get_mock_mr_contrast_generator( void ); + PETContrastGenerator get_mock_pet_contrast_generator( void ); + + ISMRMRD::IsmrmrdHeader get_mock_ismrmrd_header( void ); + std::string get_serialized_mock_ismrmrd_header( void ); + + + ISMRMRD::AcquisitionSystemInformation get_mock_acquisition_system_information( void ); + ISMRMRD::SequenceParameters get_mock_sequence_parameters( void ); + ISMRMRD::ExperimentalConditions get_mock_experimental_conditions( void ); + std::vector< ISMRMRD::Encoding > get_mock_encoding_vector( void ); + + ISMRMRD::ExperimentalConditions get_mock_experimental_conditions( void ); + ISMRMRD::EncodingSpace get_mock_encoded_space( void ); + ISMRMRD::EncodingSpace get_mock_recon_space( void ); + ISMRMRD::EncodingLimits get_mock_encoding_limits( void ); + + + + ISMRMRD::NDArray get_mock_ndarray_with_cube( void ); + ISMRMRD::Image< complex_float_t > get_mock_ismrmrd_image_with_cube( void ); + ISMRMRD::Image< float > get_mock_ismrmrd_image_with_gradients( void ); + + sirf::CoilSensitivitiesVector aux_test_get_mock_coilmaps( void ); + sirf::CoilSensitivitiesVector get_mock_gaussian_csm( std::vector vol_dims, int const num_coils ); + + ISMRMRD::AcquisitionHeader get_mock_acquisition_header( void ); + sirf::AcquisitionsVector get_mock_acquisition_vector ( ISMRMRD::IsmrmrdHeader ); + + SignalContainer get_generic_respiratory_signal( sirf::AcquisitionsVector &acq_vec); + SignalContainer get_generic_cardiac_signal( sirf::AcquisitionsVector &acq_vec); + SignalContainer get_generic_contrast_inflow_signal( sirf::AcquisitionsVector &acq_vec); + SignalContainer get_generic_contrast_in_and_outflow_signal( sirf::AcquisitionsVector &acq_vec); + + + SignalContainer get_mock_motion_signal( void ); + std::vector get_mock_external_signal(const std::vector& label_list, const float weight=1.f); + + std::vector > + get_mock_external_signals_for_templatedata(const std::vector& label_list, const sirf::MRAcquisitionData& ad); + + SignalContainer get_mock_sinus_signal( sirf::AcquisitionsVector &acq_vec, TimeAxisType const period_duration_ms); + SignalContainer get_mock_sawtooth_signal( sirf::AcquisitionsVector acq_vec, TimeAxisType const period_duration_ms); + + sirf::MRContrastDynamic get_constant_contrast( LabelType const which_tissue_label, TissueParameter template_param, float const T1_ms); + + void store_roi( LabelVolume& label_vol, std::vector const labels, std::string const output_prefix); + float prep_pet_motion_dyn(sirf::PETMotionDynamic& motion_dyn, SignalContainer const motion_signal); + +}// namespace aux_test diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/test_input_filenames.h b/src/xDynamicSimulation/cDynamicSimulation/tests/test_input_filenames.h new file mode 100644 index 000000000..cdff03346 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/test_input_filenames.h @@ -0,0 +1,57 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.11.13 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + + +// for easier logging +#define epiph(x) #x << " = " << x + + +#define SHARED_FOLDER_PATH "/media/sf_CCPPETMR/" +#define ANALYZE_OUTPUT_TESTPATH SHARED_FOLDER_PATH "analyze_test_output" + +#define TESTDATA_PREFIX "TestData/Input/xDynamicSimulation/cDynamicSimulation/" +#define TESTDATA_OUT_PREFIX "TestData/Output/xDynamicSimulation/cDynamicSimulation/" + +#define H5_XCAT_PHANTOM_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "Segmentations/xcat_phantom_incl_geomertry_128.h5" +#define ISMRMRD_H5_TEST_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "TemplateData/MR/CV_nav_cart_128Cube_FLASH_T1.h5" +#define PATH_2D_ACQ_TEMPLATE SHARED_FOLDER_PATH TESTDATA_PREFIX "TemplateData/MR/meas_MID29_cart_ref_image_FID78804_ismrmrd.h5" +// #define ISMRMRD_H5_TEST_PATH SHARED_FOLDER_PATH "h5_source_files/CV_nav_128_rpe_sfl_gc_usos8.h5" + +#define DISPLACEMENT_FIELD_PATH SHARED_FOLDER_PATH "" + +#define PET_TEMPLATE_CONTRAST_IMAGE_DATA_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "TemplateData/PET/template_image_input_contgen.hv" +#define PET_TEMPLATE_ACQUISITION_IMAGE_DATA_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "TemplateData/PET/template_image_input_acquisition.hv" +#define PET_TEMPLATE_ACQUISITION_DATA_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "TemplateData/PET/template_span11.hs" + + +#define TIME_POINTS_CARDIAC_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "SurrogateSignals/card_time" +#define CARDIAC_SIGNAL_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "SurrogateSignals/card_signal" + +#define TIME_POINTS_RESP_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "SurrogateSignals/resp_time" +#define RESP_SIGNAL_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "SurrogateSignals/resp_signal" + +#define XML_TEST_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "Segmentations/test_TissueParameters_XML.xml" +#define XML_XCAT_PATH SHARED_FOLDER_PATH TESTDATA_PREFIX "Segmentations/XCAT_TissueParameters_XML.xml" + +// #define H5_PHANTOM_TEST_PATH SHARED_FOLDER_PATH "h5_testfile_cube_size3.h5" +#define H5_PHANTOM_TEST_PATH SHARED_FOLDER_PATH "testdata_inputoutput/xcat_phantom_incl_geomertry_64.h5" + +#define ACQU_FILE_NAME SHARED_FOLDER_PATH "acquisitions_file_fwd_test.h5" + +#define FILENAME_MR_RPE_SIM SHARED_FOLDER_PATH "testoutput_mr_rpe_simulation.h5" +#define FILENAME_MR_CONTRAST_DYNSIM SHARED_FOLDER_PATH "testoutput_mr_dynamic_contrast_simulation.h5" +#define FILENAME_MR_MOTION_DYNSIM SHARED_FOLDER_PATH "testoutput_mr_dynamic_motion_simulation.h5" +#define FILENAME_MR_MOTION_CONTRAST_DYNSIM SHARED_FOLDER_PATH "testoutput_mr_dynamic_motion_contrast_simulation.h5" + +#define FILENAME_STATICSIM_PET SHARED_FOLDER_PATH "testoutput_pet_static_simulation.hs" +#define FILENAME_MR_DEFORM_TEST SHARED_FOLDER_PATH "output_deforming_tests/deformed_img" + + + diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/tests_auxiliary_input_output.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_auxiliary_input_output.cpp new file mode 100644 index 000000000..2d3717767 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_auxiliary_input_output.cpp @@ -0,0 +1,113 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.04.03 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#include +#include + +#include + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" +#include "auxiliary_testing_functions.h" + +#include "tests_auxiliary_input_output.h" + +using namespace sirf; + +void test_aux_io::test_write_ndarray_to_raw( void ) +{ + + size_t Nx = 192; + size_t Ny = 192; + size_t Nz = 192; + size_t Ne = 3; + + std::vector< size_t > data_size = {Nx, Ny, Nz, Ne}; + + + ISMRMRD::NDArray< complex_float_t > dummy_data; + dummy_data.resize(data_size); + + for( int nz=0; nz(nx*ne, nx*ne); + } + + + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << "test_binary_writer_" << Nx << "x" << Ny << "x" << Nz; + + data_io::write_raw(name_stream.str(), dummy_data.begin(), dummy_data.getNumberOfElements()); + +} + + + +bool test_aux_io::test_read_acquisitions_vector_number_consistency( void ) +{ + + size_t const expected_num_acquisitions = 128*128; + AcquisitionsVector acqu_vec(ISMRMRD_H5_TEST_PATH); + size_t const read_num_acquisitions = acqu_vec.items(); + + std::cout << epiph(expected_num_acquisitions) << std::endl; + std::cout << epiph(read_num_acquisitions) << std::endl; + + + return read_num_acquisitions == expected_num_acquisitions; + +} + + + + +void test_aux_io::test_write_ismrmrd_image_to_analyze( void ) +{ + typedef complex_float_t input_type_mock_object; + auto img = aux_test::get_mock_ismrmrd_image_with_cube( ); + + data_io::write_ISMRMRD_Image_to_nii< input_type_mock_object > (ANALYZE_OUTPUT_TESTPATH, img); + +} + + + +bool test_aux_io::test_read_single_column_txt( void ) +{ + try + { + bool test_successful = true; + + std::string const filename_input = std::string(SHARED_FOLDER_PATH) + "/TestData/Input/xDynamicSimulation/cDynamicSimulation/SurrogateSignals/card_time"; + + std::vector< float > input = data_io::read_single_column_txt(filename_input); + std::cout << epiph( input.size() ) << std::endl; + + for( size_t i=0; i + +#include +#include "sirf/Gadgetron/gadgetron_data_containers.h" + +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/cDynamicSimulation/dynamics.h" + +using namespace sirf; + + + +bool test_aux_test_funs::test_get_serialized_ismrmrd_header( void ) +{ + try + { + std::string serialized_hdr = aux_test::get_serialized_mock_ismrmrd_header(); + std::cout << serialized_hdr << std::endl; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught in " <<__FUNCTION__ <<" .!" < vol_size {192,192,192}; + + int const num_coils = 9; + sirf::CoilSensitivitiesVector mock_csm = aux_test::get_mock_gaussian_csm(vol_size, num_coils); + + std::stringstream name_stream; + name_stream << "/media/sf_CCPPETMR/test_mock_gaussian_csm_"; + + data_io::write_ISMRMRD_Image_to_nii< complex_float_t > (name_stream.str(), + *((CFImage*) mock_csm.image_wrap(0).ptr_image())); + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught in " <<__FUNCTION__ <<" .!" < img = aux_test::get_mock_ismrmrd_image_with_cube(); + + std::vector< size_t > dim; + dim.push_back(img.getMatrixSizeX ()); + dim.push_back(img.getMatrixSizeY ()); + dim.push_back(img.getMatrixSizeZ ()); + + ISMRMRD::NDArray dat( dim ); + for( int i=0; i(name_stream.str(), dat.begin(), dat.getNumberOfElements()); + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught in " <<__FUNCTION__ <<" .!" < +#include +#include + +#include "auxiliary_testing_functions.h" + +namespace test_aux_test_funs +{ + +bool test_get_serialized_ismrmrd_header( void ); +bool test_get_mock_acquisition_vector( void ); +bool test_get_mock_gaussian_csm( void ); +bool test_get_mock_ismrmrd_image_with_cube( void ); +bool test_get_mock_contrast_generator( void ); +bool test_get_mock_pet_contrast_generator( void ); +bool test_get_mock_sawtooth_signal( void ); + +} \ No newline at end of file diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/tests_c_interface.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_c_interface.cpp new file mode 100644 index 000000000..cf9b6111d --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_c_interface.cpp @@ -0,0 +1,51 @@ +#include "tests_c_interface.h" + +#include "test_input_filenames.h" +#include "auxiliary_testing_functions.h" + +#include "sirf/iUtilities/DataHandle.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/cDynamicSimulation/cdynamicsimulation.h" + +using namespace sirf; + +bool test_simulation_interface::test_bin_data_from_handle( void ) +{ + + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + bool test_succesful = true; + + AcquisitionsVector acq_vec; + std::string fpath_testdata("/media/sf_CCPPETMR/TestData/Input/xDynamicSimulation/cDynamicSimulation/TemplateData/MR/CV_nav_cart_64Cube_1Echo.h5"); + + acq_vec.read(fpath_testdata); + + std::shared_ptr + sptr_ad(new AcquisitionsVector(acq_vec)); + + void* ptr_ad = newObjectHandle(sptr_ad); + + int const num_bins = 4; + MRMotionDynamic motion_dyn(num_bins); + SignalContainer mock_signal = aux_test::get_generic_cardiac_signal(acq_vec); + motion_dyn.set_dynamic_signal(mock_signal); + + std::shared_ptr + sptr_motiondyn(new MRMotionDynamic(motion_dyn)); + + void* ptr_dyn = newObjectHandle(sptr_motiondyn); + + cDS_setMRAcquisitions(ptr_dyn, ptr_ad); + + return true; + } + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < +#include +#include +#include +#include + +#include + +#include "auxiliary_testing_functions.h" +#include "test_input_filenames.h" + +#include "sirf/cDynamicSimulation/tissuelabelmapper.h" +#include "sirf/cDynamicSimulation/tissueparameters.h" +#include "sirf/cDynamicSimulation/contrastgenerator.h" +#include "sirf/cDynamicSimulation/phantom_input.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" +#include "sirf/Gadgetron/gadgetron_data_containers.h" + + +using namespace std; +using namespace ISMRMRD; +using namespace stir; +using namespace sirf; + +// contrast generator + + +bool test_contgen::test_get_tissue_parameter( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + MRContrastGenerator cont_gen = aux_test::get_mock_mr_contrast_generator(); + + LabelType label = 52; + TissueParameter tiss_par = cont_gen.get_petmr_tissue_parameter( label ); + + std::cout << "Label " << label << " was associated with the parameter " << tiss_par.name_ < contrast_dims; + + sirf::Dimensions dims = contrasts.dimensions(); + + contrast_dims.push_back( 3 ); + contrast_dims.push_back( dims["x"] ); + contrast_dims.push_back( dims["y"] ); + contrast_dims.push_back( dims["z"] ); + contrast_dims.push_back( dims["n"] ); + + bool dims_are_correct = true; + for( int i=0; i< 4; i++) + dims_are_correct *= (contrast_dims[i] == input_dims[i]); + + return dims_are_correct; +} + +void test_contgen::test_mr_contgen_map_tissue(void) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + std::cout << "Reading segmentation ... " < > mr_parameter_maps = mr_contgen.get_parameter_filled_volumes(); + std::cout << "There are "<< mr_parameter_maps.size() << " parameter maps in the XCAT output." << std::endl; + + std::stringstream fpath_output_prefix; + fpath_output_prefix << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + + for(int i=0; i label_list_too_small(num_labels); + std::iota(label_list_too_small.begin(), label_list_too_small.end(), 0); + std::vector ext_sig_small = aux_test::get_mock_external_signal(label_list_too_small); + mr_contgen.map_contrast(ext_sig_small); + } + catch(const std::runtime_error& err) + { + std::cout << "The problem should be that the list is too small. The actual error is "<< err.what() << std::endl; + } + try{ + int num_labels = 100; + std::vector label_list_incomplete(num_labels, 1); + std::vector ext_sig_incomplete = aux_test::get_mock_external_signal(label_list_incomplete); + + mr_contgen.map_contrast(ext_sig_incomplete); + } + catch(const std::runtime_error& err) + { + std::cout << "The problem should be that the list is incomplete. The actual error is "<< err.what() << std::endl; + } + + int num_labels = 100; + std::vector label_list_correct(num_labels); + std::iota(label_list_correct.begin(), label_list_correct.end(), 0); + std::vector ext_sig_correct = aux_test::get_mock_external_signal(label_list_correct); + + mr_contgen.map_contrast(ext_sig_correct); + + GadgetronImagesVector& mr_contrasts = mr_contgen.get_contrast_filled_volumes(); + + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(name_stream.str(), mr_contrasts); + +} + + +void test_contgen::test_get_signal_for_tissuelabel_in_xcat() +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + LabelVolume segmentation_labels = read_segmentation_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + MRContrastGenerator mr_contgen( segmentation_labels, XML_XCAT_PATH); + + sirf::AcquisitionsVector av(ISMRMRD_H5_TEST_PATH); + mr_contgen.set_template_rawdata(av); + + size_t const test_label = 3; + + auto signal = mr_contgen.get_signal_for_tissuelabel( test_label ); + + std::cout <<"Signal in label " << test_label << " amounts to: " << signal << std::endl; + +} + +void test_contgen::test_replace_petmr_tissue_parameters_in_xcat() +{ + + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + LabelVolume segmentation_labels = read_segmentation_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + MRContrastGenerator mr_contgen( segmentation_labels, XML_XCAT_PATH); + + sirf::AcquisitionsVector av(ISMRMRD_H5_TEST_PATH); + mr_contgen.set_template_rawdata(av); + + mr_contgen.map_contrast(); + + GadgetronImagesVector& mr_contrasts = mr_contgen.get_contrast_filled_volumes(); + + std::stringstream ss_outname; + ss_outname << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__ << "_contrast_gen_pre_contrast_"; + + sirf::write_imagevector_to_raw(ss_outname.str(), mr_contrasts); + + // now replace one label and see what happens in the image + LabelType label_to_replace = 1; + auto tissue_param_pair = aux_test::get_mock_contrast_signal_extremes(); + + mr_contgen.replace_petmr_tissue_parameters(label_to_replace, tissue_param_pair.second); + mr_contgen.map_contrast(); + + mr_contrasts = mr_contgen.get_contrast_filled_volumes(); + ss_outname.str(std::string());; + ss_outname << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__ << "_contrast_gen_post_contrast_"; + + sirf::write_imagevector_to_raw(ss_outname.str(), mr_contrasts); +} + + + +bool test_contgen::test_map_flash_contrast( void ) +{ + + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + TissueParameter tiss_par = aux_test::get_mock_tissue_parameter(); + auto ptr_to_mock_tiss = std::make_shared(tiss_par); + + ISMRMRD::IsmrmrdHeader hdr = aux_test::get_mock_ismrmrd_header(); + + std::vector flash_contrast = map_flash_contrast(ptr_to_mock_tiss, hdr); + + float const t1 = 1; + float const t2 = 2; + float const dens = 100; + float const angle = M_PI/2; + float const cs = 1; + float const field_strength_t = 1.0; + + float const TR = 2; + float const TE = 1; + + + complex_float_t IMAG_UNIT(0,1); + float const gyro = (float)42.58 * 2*M_PI;; + + complex_float_t input_contrast_echo1 = exp( IMAG_UNIT * gyro/1000.f * TE * cs * field_strength_t)*dens * (float)sin(angle) * + (float)(1-exp(-TR/t1)) / (float)(1- exp(-TR/t1)*cos(angle)) * (float)exp(-TE/t2); + complex_float_t mock_contrast = flash_contrast[0]; + + float const epsilon = 0.00001; + + bool equal_contrast = (input_contrast_echo1.real() - mock_contrast.real() ) < epsilon ; + equal_contrast *= (input_contrast_echo1.imag() - mock_contrast.imag() < epsilon ); + + return equal_contrast; + +} + +bool test_contgen::test_pet_constructor( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + LabelVolume segmentation_labels = aux_test::get_mock_label_volume(); + PETContrastGenerator pet_contgen (segmentation_labels, XML_XCAT_PATH); + + return true; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < resampled_nifti( resampled_images[i] ); + + resampled_nifti.write( outname.str() ); + } + + return true; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < current_tissue_param = tissue_volume[i]; + unsigned int associated_label = current_tissue_param->label_; + + all_labels_correct *= (labels_list(i) == associated_label); + + } + + return all_labels_correct; +} + +bool test_tlm::test_assign_tissue_parameters_label_not_found( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + TissueParameterList tiss_list = aux_test::get_mock_tissue_param_list(); + LabelVolume labels_list = aux_test::get_mock_label_volume(); + labels_list(0) = 23; + try + { + TissueVector tissue_volume = assign_tissue_parameters_to_labels( tiss_list, labels_list); + } + catch( std::runtime_error const &e) + { + + std::cout << "Test output: " << e.what() << std::endl; + return true; + } +} + +bool test_tlm::test_map_labels_to_tissue_from_xml( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + LabelVolume lab_arr = aux_test::get_mock_label_volume(); + + TissueLabelMapper tlm(lab_arr, XML_TEST_PATH); + + tlm.map_labels_to_tissue_from_xml(); + + TissueVector tiss_vec = tlm.get_segmentation_tissues(); + + bool all_labels_correct = true; + for (int i = 0; ilabel_; + + all_labels_correct *= (tissue_label == lab_arr(i)); + } + + return all_labels_correct; +} + + +bool test_tlm::test_replace_petmr_tissue_parameters( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + + LabelVolume lab_arr = aux_test::get_mock_label_volume(); + TissueLabelMapper tlm(lab_arr, XML_TEST_PATH); + + tlm.map_labels_to_tissue_from_xml(); + + TissueParameterList tpl_before_replacement = tlm.get_tissue_parameter_list(); + + + LabelType label_to_replace = 2; + + TissueParameter tiss_par_to_substitute; + tiss_par_to_substitute.name_ = "lala"; + tiss_par_to_substitute.label_ = 19; + tiss_par_to_substitute.mr_tissue_.t1_miliseconds_ = 1.01; + tiss_par_to_substitute.mr_tissue_.t2_miliseconds_ = 1.02; + tiss_par_to_substitute.mr_tissue_.cs_ppm_ = 1.03; + tiss_par_to_substitute.mr_tissue_.spin_density_percentH2O_ = 1.04; + + tiss_par_to_substitute.pet_tissue_.attenuation_1_by_cm_= 1.05; + tiss_par_to_substitute.pet_tissue_.activity_kBq_ml_= 1.06; + + tlm.replace_petmr_tissue_parameters(label_to_replace, tiss_par_to_substitute); + + TissueParameterList tpl_after_replacement = tlm.get_tissue_parameter_list(); + + for(size_t i=0; i + +#include "tests_dynamics.h" +#include "auxiliary_testing_functions.h" + +#include "sirf/common/GeometricalInfo.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/Reg/NiftiImageData3DDeformation.h" +#include "sirf/Reg/NiftiImageData3DDisplacement.h" +#include "sirf/Reg/NiftiImageData3D.h" +#include "sirf/Reg/NiftyResample.h" + +using namespace sirf; + +using std::cout; +using std::endl; + + +bool test_dynamic::test_is_in_bin( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + bool test_succesful; + SignalAxisType test_signal = 0.15; + + SignalAxisType bin_min = 0.0; + SignalAxisType bin_ctr = 0.1; + SignalAxisType bin_max = 0.2; + + SignalBin bin{bin_min, bin_ctr, bin_max}; + + test_succesful = is_in_bin( test_signal , bin ); + + + std::get<0>(bin) = 0.8; + std::get<1>(bin) = 0.0; + std::get<2>(bin) = 0.2; + + test_succesful *= is_in_bin(test_signal, bin); + + + std::get<0>(bin) = 0.0; + std::get<1>(bin) = 0.0; + std::get<2>(bin) = 0.0; + + test_succesful *= !(is_in_bin(test_signal, bin)); + + return test_succesful; +} + + +bool test_surrogateprocessor::test_linear_interpolate_signal( ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + + try + { + SignalContainer mock_signal = aux_test::get_mock_motion_signal(); + + SurrogateProcessor sp; + sp.set_signal(mock_signal); + + size_t num_repetitions_for_speed_estimate = 256*256*3; + + for( size_t rep=0; rep timpts; + for(auto& elem: mock_signal) + timpts.push_back(elem.first); + + SurrogateProcessor sp; + sp.set_signal(mock_signal); + + SignalAxisType avg_signal = sp.get_average_signal(timpts); + cout << epiph ( avg_signal ) <(end_timept - begin_timept).count() << "[s]" << std::endl; + std::cout << "That is :" << std::chrono::duration_cast(end_timept - begin_timept).count() / (float)num_intersections << "[s]/intersection" << std::endl; + + int32_t num_overlap = one_end_counter - other_start_counter; + num_overlap = (num_overlap>=0) ? num_overlap+1 : 0; + + bool test_succesful = (num_overlap == intersec_vec.items()); + + cout << epiph(num_overlap) << endl; + cout << epiph(intersec_vec.items()) << endl; + + + return test_succesful; +} + + + +bool test_dynamic::get_average_deformation_field( void ) +{ + std::cout << "--- Running "<< __FUNCTION__ << std::endl; + try{ + AcquisitionsVector av_template; + av_template.read( PATH_2D_ACQ_TEMPLATE ); + + int const num_motion_states = 10; + MRMotionDynamic motion_dyn( num_motion_states ); + + auto resp_mvfs = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + motion_dyn.set_displacement_fields(resp_mvfs); + + auto avg_vf = motion_dyn.get_average_deformation_field(av_template); + return true; + } + + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < all_bins = bp.get_bins(); + + std::cout << "#### NONCYCLIC DYNAMIC ####" << std::endl; + for( int i=0; i (all_bins[i] )) << endl; + cout << epiph(std::get<1> (all_bins[i] )) << endl; + cout << epiph(std::get<2> (all_bins[i] )) << endl; + } + + test_succesful = ( all_bins.size() == num_bins ); + + cyclic = true; + bp.set_cyclicality(cyclic); + all_bins = bp.get_bins(); + + std::cout << "#### CYCLIC DYNAMIC ####" << std::endl; + for( int i=0; i (all_bins[i] )) << endl; + cout << epiph(std::get<1> (all_bins[i] )) << endl; + cout << epiph(std::get<2> (all_bins[i] )) << endl; + } + + test_succesful = ( all_bins.size() == num_bins+1 ); + + return test_succesful; + } + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < gt_points{0.0, 0.1, 0.2, 0.3, 0.4}; + motion_dyn.save_ground_truth_displacements(gt_points); + + return test_succesful; + } + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < mvfs = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + MotionProcessor mp; + mp.set_displacement_fields(mvfs); + + return test_succesful; + } + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < pet_sirf_img( pet_img ); +// auto pet_img_data = pet_sirf_img.get_raw_nifti_sptr(); + +// float const img_off_x = pet_img_data->qoffset_x; +// float const img_off_y = pet_img_data->qoffset_y; +// float const img_off_z = pet_img_data->qoffset_z; + + +// float const img_quart_b = pet_img_data->quatern_b; +// float const img_quart_c = pet_img_data->quatern_c; +// float const img_quart_d = pet_img_data->quatern_d; +// float const img_quart_ac = pet_img_data->qfac; + +// float const img_slope = pet_img_data->scl_slope; +// float const img_inter = pet_img_data->scl_inter; + +// float const img_dx = pet_img_data->dx; +// float const img_dy = pet_img_data->dy; +// float const img_dz = pet_img_data->dz; +// float const img_dt = pet_img_data->dt; +// float const img_du = pet_img_data->du; +// float const img_dv = pet_img_data->dv; +// float const img_dw = pet_img_data->dw; + +// cout << epiph( img_off_x )<< endl; +// cout << epiph( img_off_y )<< endl; +// cout << epiph( img_off_z )<< endl; + +// cout << epiph( img_quart_b ) << endl; +// cout << epiph( img_quart_c ) << endl; +// cout << epiph( img_quart_d ) << endl; +// cout << epiph( img_quart_ac) << endl; +// cout << epiph( img_slope ) << endl; +// cout << epiph( img_inter ) << endl; + + +// cout << epiph( img_dx ) << endl; +// cout << epiph( img_dy ) << endl; +// cout << epiph( img_dz ) << endl; +// cout << epiph( img_dt ) << endl; +// cout << epiph( img_du ) << endl; +// cout << epiph( img_dv ) << endl; +// cout << epiph( img_dw ) << endl; + +// auto resp_mvfs = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + +// int const num_simul_bins = 1; +// PETMotionDynamic motion_dyn(num_simul_bins); +// motion_dyn.set_displacement_fields(resp_mvfs); +// motion_dyn.prep_displacement_fields(); +// motion_dyn.align_motion_fields_with_image(pet_img); + +// auto some_mvf = motion_dyn.get_interpolated_deformation_field( 0.f ); +// auto mvf_img_data = some_mvf.get_raw_nifti_sptr(); + +// float const mvf_off_x = mvf_img_data->qoffset_x; +// float const mvf_off_y = mvf_img_data->qoffset_y; +// float const mvf_off_z = mvf_img_data->qoffset_z; + +// float const mvf_quart_b = mvf_img_data->quatern_b; +// float const mvf_quart_c = mvf_img_data->quatern_c; +// float const mvf_quart_d = mvf_img_data->quatern_d; +// float const mvf_quart_ac = mvf_img_data->qfac; + +// float const mvf_slope = mvf_img_data->scl_slope; +// float const mvf_inter = mvf_img_data->scl_inter; + +// float const mvf_dx = mvf_img_data->dx; +// float const mvf_dy = mvf_img_data->dy; +// float const mvf_dz = mvf_img_data->dz; +// float const mvf_dt = mvf_img_data->dt; +// float const mvf_du = mvf_img_data->du; +// float const mvf_dv = mvf_img_data->dv; +// float const mvf_dw = mvf_img_data->dw; + +// cout << epiph( mvf_off_x)<< endl; +// cout << epiph( mvf_off_y)<< endl; +// cout << epiph( mvf_off_z)<< endl; + +// cout << epiph(mvf_quart_b ) << endl; +// cout << epiph(mvf_quart_c ) << endl; +// cout << epiph(mvf_quart_d ) << endl; +// cout << epiph(mvf_quart_ac) << endl; + +// cout << epiph( mvf_slope ) << endl; +// cout << epiph( mvf_inter ) << endl; + +// cout << epiph( mvf_dx ) << endl; +// cout << epiph( mvf_dy ) << endl; +// cout << epiph( mvf_dz ) << endl; +// cout << epiph( mvf_dt ) << endl; +// cout << epiph( mvf_du ) << endl; +// cout << epiph( mvf_dv ) << endl; +// cout << epiph( mvf_dw ) << endl; + +// return test_succesful; +// } +// catch( std::runtime_error const &e) +// { +// cout << "Exception caught " <<__FUNCTION__ <<" .!" < ref_mvf = resp_motion_fields[ resp_motion_fields.size()-1 ]; + + + ref_mvf.write( output_path + "mvf_read_in_iso" ); + + VoxelisedGeometricalInfo3D ref_geo = *( ref_mvf.get_geom_info_sptr() ); + + + VoxelisedGeometricalInfo3D::Offset ref_offset = ref_geo.get_offset() ; + VoxelisedGeometricalInfo3D::Size ref_size = ref_geo.get_size() ; + VoxelisedGeometricalInfo3D::DirectionMatrix ref_dir = ref_geo.get_direction() ; + + VoxelisedGeometricalInfo3D::Spacing ref_spacing = ref_geo.get_spacing(); + VoxelisedGeometricalInfo3D::Spacing dst_spacing = ref_spacing; + + std::vector spacing_scaling {2,3,4} ; + + dst_spacing[0] = spacing_scaling[0] * dst_spacing[0]; + dst_spacing[1] = spacing_scaling[1] * dst_spacing[1]; + dst_spacing[2] = spacing_scaling[2] * dst_spacing[2]; + + VoxelisedGeometricalInfo3D dst_geo(ref_offset, dst_spacing, ref_size, ref_dir ); + + NiftiImageData3DDisplacement dst_mvf( (float*) ref_mvf.get_raw_nifti_sptr()->data, dst_geo); + dst_mvf.write( output_path + "mvf_non_iso_dstgeo" ); + + NiftyResampler resampler; + + resampler.set_interpolation_type_to_cubic_spline(); + + resampler.set_floating_image(std::make_shared< NiftiImageData3DDisplacement >(dst_mvf)); + resampler.set_reference_image (std::make_shared< NiftiImageData3DDisplacement >(ref_mvf)); + + + resampler.process(); + + resampler.get_output_sptr()->write( output_path + "mvf_non_iso_resampled" ); + + return test_succesful; + } + catch( std::runtime_error const &e) + { + cout << "Exception caught " <<__FUNCTION__ <<" .!" < +#include +#include + +#include +#include + +#include "sirf/Gadgetron/gadgetron_data_containers.h" +#include "sirf/Gadgetron/gadgetron_x.h" +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" + +#include "sirf/cDynamicSimulation/dynamicsimulation_x.h" +#include "sirf/cDynamicSimulation/phantom_input.h" + + +#include "auxiliary_testing_functions.h" +#include "tests_dynamicsimulation.h" + + +using namespace std; +using namespace sirf; +using namespace ISMRMRD; + + +bool test_lin_combi_gen::test_get_all_combinations( void ) +{ + std::cout << " --- Running " << __FUNCTION__ << std::endl; + + try + { + int const N = 1; + int const M = 2; + int const L = 3; + + DimensionsType dims; + dims.push_back(N); + dims.push_back(M); + dims.push_back(L); + + LinearCombiGenerator lcg( dims ); + + auto all_perm = lcg.get_all_combinations(); + + for(size_t i=0;i > card_motion_fields = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + for(int i=0; i > resp_motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + for(int i=0; i(motion_dyn)); + + std::stringstream ss_output_prefix; + ss_output_prefix << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + + mr_dyn_sim.save_groud_truth_parameter_maps(ss_output_prefix.str()); + + return true; + +} +catch( std::runtime_error const &e) +{ + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps(std::make_shared(csm)); + + float const test_SNR = 15; + size_t const noise_label = 13; + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + float const test_SNR = 15; + size_t const noise_label = 13; + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + // generate mock respiratory motion dynamic + float const respiratory_period_ms = 6000; + SignalContainer resp_signal = aux_test::get_mock_sinus_signal( all_acquis, respiratory_period_ms); + + auto resp_motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + size_t const num_resp_states = 8; + MRMotionDynamic resp_dyn(num_resp_states); + + resp_dyn.set_displacement_fields( resp_motion_fields, false ); + resp_dyn.set_dynamic_signal(resp_signal); + + mr_dyn_sim.add_dynamic( std::make_shared ( resp_dyn )); + + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + float const test_SNR = 15; + size_t const noise_label = 13; + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + // generate mock respiratory motion dynamic + float const respiratory_period_ms = 6000; + SignalContainer resp_signal = aux_test::get_mock_sinus_signal( contrast_template, respiratory_period_ms); + + auto resp_motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + size_t const num_sim_stats_per_dyn = 4; + size_t const num_resp_states = num_sim_stats_per_dyn; + MRMotionDynamic resp_dyn(num_resp_states); + + resp_dyn.set_displacement_fields( resp_motion_fields, false ); + resp_dyn.set_dynamic_signal(resp_signal); + + mr_dyn_sim.add_dynamic( std::make_shared ( resp_dyn )); + + // + // generate mock cardiac motion dynamic + float const cardiac_period_ms = 1000; + SignalContainer card_signal = aux_test::get_mock_sawtooth_signal( contrast_template, respiratory_period_ms); + + auto card_motion_fields = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + size_t const num_card_states = num_sim_stats_per_dyn; + MRMotionDynamic card_dyn(num_card_states); + + card_dyn.set_displacement_fields( card_motion_fields, true ); + card_dyn.set_dynamic_signal(card_signal); + + mr_dyn_sim.add_dynamic( std::make_shared ( card_dyn )); + + // run actual + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + std::cout << epiph( data_dims[0] ) <(csm)); + + float const test_SNR = 15; + size_t const noise_label = 13; + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + AcquisitionsVector all_acquis(ISMRMRD_H5_TEST_PATH); + mr_dyn_sim.set_acquisition_template_rawdata(all_acquis); + + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + + + + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + // SETTING UP MOTION DYNAMICS ######################################################################## + + if( num_simul_motion_states > 1) + { + MRMotionDynamic card_dyn(num_simul_motion_states), resp_dyn(num_simul_motion_states); + + std::string const signal_path = input_path + "/SurrogateSignals/"; + + std::string fname_timepts, fname_signalpts; + + // add card motion + fname_timepts = signal_path + "card_time"; + fname_signalpts = signal_path + "card_signal"; + SignalContainer card_signal = data_io::read_surrogate_signal(fname_timepts, fname_signalpts); + card_dyn.set_dynamic_signal( card_signal ); + + // add resp motion + fname_timepts = signal_path + "resp_time"; + fname_signalpts = signal_path + "resp_signal"; + SignalContainer resp_signal = data_io::read_surrogate_signal(fname_timepts, fname_signalpts); + resp_dyn.set_dynamic_signal( resp_signal ); + + card_dyn.set_ground_truth_folder_name( output_path + "ground_truth_motionfields_card"); + resp_dyn.set_ground_truth_folder_name( output_path + "ground_truth_motionfields_resp"); + + auto binned_resp_acq = resp_dyn.get_binned_mr_acquisitions(); + auto binned_card_acq = card_dyn.get_binned_mr_acquisitions(); + for( int i=0; i ( card_dyn )); + mr_dyn_sim.add_dynamic( std::make_shared ( resp_dyn )); + } + // #################################################################################################### + + + + if( simulate_data ) + { + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR 5D MRI SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < roi_labels{1,2,3,4,50,72,73}; + std::string const output_prefix_roi = output_path; + + auto data_dims = segmentation_labels.get_dimensions(); + + std::vector< size_t > vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + + + + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + // SETTING UP MOTION DYNAMICS ######################################################################## + + if( num_simul_motion_dyn > 1) + { + MRMotionDynamic motion_dyn( num_simul_motion_dyn ); + + + std::string const signal_path = input_path + "/SurrogateSignals/"; + + std::string fname_timepts, fname_signalpts; + + if( do_cardiac_sim ) + { + fname_timepts = signal_path + "card_time"; + fname_signalpts = signal_path + "card_signal"; + } + else + { + fname_timepts = signal_path + "resp_time"; + fname_signalpts = signal_path + "resp_signal"; + } + + std::string motion_type_suffix = do_cardiac_sim? "card" : "resp"; + motion_dyn.set_ground_truth_folder_name( output_path + "ground_truth_motionfields_" + motion_type_suffix); + + + SignalContainer motion_signal = data_io::read_surrogate_signal(fname_timepts, fname_signalpts); + + // std::cout << "WARNING: CONSTANT SIGNAL: " << std::endl; + // for( size_t j=0; j< motion_signal.size(); ++j) + // motion_signal[j].second = 0.99; + + motion_dyn.set_dynamic_signal( motion_signal ); + + auto binned_acq = motion_dyn.get_binned_mr_acquisitions(); + for( int i=0; i ( motion_dyn )); + } + // #################################################################################################### + + + aux_test::store_roi( segmentation_labels, roi_labels, output_prefix_roi); + + + if( simulate_data ) + { + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR 4D MRI SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + float const test_SNR = 19; + size_t const noise_label = 13; + + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + int const num_simul_motion_dyn = 16; + + // SETTING UP MOTION DYNAMICS ######################################################################## + if( num_simul_motion_dyn > 0) + { + MRMotionDynamic respiratory_motion_dyn( num_simul_motion_dyn ); + + std::string const filename_resp_timepoints = input_path + "/timepoints_dce_resp_signal"; + std::string const filename_resp_signal = input_path + "/dce_resp_signal"; + + SignalContainer respiratory_signal = data_io::read_surrogate_signal(filename_resp_timepoints, filename_resp_signal); + + respiratory_motion_dyn.set_dynamic_signal( respiratory_signal ); + + respiratory_motion_dyn.set_ground_truth_folder_name( output_path + "ground_truth_motionfields"); + + + auto resp_motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + respiratory_motion_dyn.set_displacement_fields( resp_motion_fields, false ); + + mr_dyn_sim.add_dynamic( std::make_shared ( respiratory_motion_dyn )); + } + + // SETTING UP CONRAST DYNAMICS ######################################################################## + + int const num_contrast_states = 48; + + int const t1_aif_0 = 235; + int const t1_lesion_0 = 527; + int const t1_healthy_tissue_0 = 488; + + + std::string const filename_contrast_timepoints = input_path + "/timepoints_dce_contrast_signal"; + + std::string const filename_aif_t1_ =input_path + "/aif_signal_T1_0_0.23482_T1_1_2.0853"; + std::string const filename_healthy_tissue_t1 = input_path + "/liver_signal_T1_0_0.48778_T1_1_0.90637"; + std::string const filename_lesion_t1_ = input_path + "/lesion_signal_T1_0_0.52744_T1_1_1.2473"; + + // #################################################################################################### + + MRContrastDynamic aif_contrast(num_contrast_states), healthy_tissue_contrast(num_contrast_states), lesion_contrast(num_contrast_states); + + std::vector aif_dynamic_labels = {5, 6, 7, 8, 36, 37}; + for(int i=0; i healthy_tissue_dynamic_labels = {13}; + for(int i=0; i lesion_dynamic_labels = {74}; + for(int i=0; i 0) + { + mr_dyn_sim.add_dynamic( std::make_shared (aif_contrast) ); + mr_dyn_sim.add_dynamic( std::make_shared (healthy_tissue_contrast) ); + mr_dyn_sim.add_dynamic( std::make_shared (lesion_contrast) ); + } + + // #################################################################################################### + + if( simulate_data ) + { + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < 0 ) + { + std::cout << "Storing ground truth motion information" << std::endl; + mr_dyn_sim.save_ground_truth_displacements(); + } + + return true; + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < vol_dims{(size_t)data_dims[1], (size_t)data_dims[2], (size_t)data_dims[3]}; + + size_t num_coils = 4; + auto csm = aux_test::get_mock_gaussian_csm(vol_dims, num_coils); + mr_dyn_sim.set_coilmaps( std::make_shared(csm)); + + float const test_SNR = 15; + size_t const noise_label = 13; + mr_dyn_sim.set_SNR(test_SNR); + mr_dyn_sim.set_noise_label( noise_label ); + + // generate mock respiratory motion dynamic + float const respiratory_period_ms = 6000; + SignalContainer resp_signal = aux_test::get_mock_sinus_signal( contrast_template, respiratory_period_ms); + + auto resp_motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + size_t const num_sim_stats_per_dyn = 4; + size_t const num_resp_states = num_sim_stats_per_dyn; + MRMotionDynamic resp_dyn(num_resp_states); + + resp_dyn.set_displacement_fields( resp_motion_fields, false ); + resp_dyn.set_dynamic_signal(resp_signal); + + mr_dyn_sim.add_dynamic( std::make_shared ( resp_dyn )); + + + const int minimum_num_labels = 100; + std::vector label_list(minimum_num_labels); + std::iota(label_list.begin(), label_list.end(), 0); + + ExternalMRContrastDynamic edyn; + auto ext_signals = aux_test::get_mock_external_signals_for_templatedata(label_list, acquisition_template); + edyn.set_tissue_signals(ext_signals); + + mr_dyn_sim.add_dynamic( std::make_shared(edyn)); + + // generate mock cardiac motion dynamic + // float const cardiac_period_ms = 1000; + // SignalContainer card_signal = aux_test::get_mock_sawtooth_signal( contrast_template, respiratory_period_ms); + + // auto card_motion_fields = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + // size_t const num_card_states = num_sim_stats_per_dyn; + // MRMotionDynamic card_dyn(num_card_states); + + // card_dyn.set_displacement_fields( card_motion_fields, true ); + // card_dyn.set_dynamic_signal(card_signal); + + // mr_dyn_sim.add_dynamic( std::make_shared ( card_dyn )); + + // run actual + clock_t t; + t = clock(); + mr_dyn_sim.simulate_data(); + t = clock() - t; + + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < (resp_dyn) ); + // resp_dyn.set_displacement_fields( resp_motion_fields, false ); + + card_dyn.bin_total_time_interval( total_time ); + auto card_motion_fields = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + card_dyn.set_displacement_fields( card_motion_fields, true ); + pet_dyn_sim.add_dynamic( std::make_shared (card_dyn) ); + + pet_dyn_sim.simulate_data( tot_time_card_ms ); + + return true; + + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < 1) + { + PETMotionDynamic motion_dyn( num_sim_motion_states ); + + stringstream path_time_pts, path_sig_pts; + + path_time_pts << input_path << "/SurrogateSignals/"; + path_sig_pts << input_path << "/SurrogateSignals/"; + + if( do_cardiac_sim ) + { + path_time_pts << "card_time"; + path_sig_pts << "card_signal"; + } + else + { + path_time_pts << "resp_time"; + path_sig_pts << "resp_signal"; + } + + SignalContainer motion_signal = data_io::read_surrogate_signal( path_time_pts.str(), path_sig_pts.str()); + + // have constant signal + // std::cout << "WARNING: CONSTANT SIGNAL ASSUMED" << std::endl; + // for(int i=0; i (motion_dyn) ); + + } + if( simulate_data ) + { + clock_t t; + t = clock(); + + std::cout << "Simulating Data" << std::endl; + pet_dyn_sim.simulate_data( tot_time_ms ); + std::cout << "Finished Simulating Data" << std::endl; + + t = clock() - t; + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < 0 ) + { + PETMotionDynamic card_dyn( num_sim_card_states ), resp_dyn( num_sim_resp_states ); + + std::string fname_timepts, fname_signalpts; + + // read cardiac signal + fname_timepts = signal_path + "card_time"; + fname_signalpts = signal_path + "card_signal"; + SignalContainer card_signal = data_io::read_surrogate_signal(fname_timepts, fname_signalpts); + card_dyn.set_dynamic_signal( card_signal ); + + float tot_time_card_ms = aux_test::prep_pet_motion_dyn(card_dyn, card_signal); + + // read cardiac signal + fname_timepts = signal_path + "resp_time"; + fname_signalpts = signal_path + "resp_signal"; + SignalContainer resp_signal = data_io::read_surrogate_signal(fname_timepts, fname_signalpts); + + float tot_time_resp_ms = aux_test::prep_pet_motion_dyn(resp_dyn, resp_signal); + + std::cout << epiph(tot_time_card_ms) << std::endl; + std::cout << epiph(tot_time_resp_ms) << std::endl; + + tot_time_ms = 0.5*(tot_time_card_ms+tot_time_resp_ms); // replace total time + + + // read motion fields + auto card_mvfs = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + card_dyn.set_displacement_fields( card_mvfs, true ); + + auto resp_mvfs = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + resp_dyn.set_displacement_fields( resp_mvfs, false ); + + // add the dynamics + pet_dyn_sim.add_dynamic( std::make_shared (card_dyn) ); + pet_dyn_sim.add_dynamic( std::make_shared (resp_dyn) ); + + } + if( simulate_data ) + { + + clock_t t; + t = clock(); + + std::cout << "Simulating Data" << std::endl; + pet_dyn_sim.simulate_data( tot_time_ms ); + std::cout << "Finished Simulating Data" << std::endl; + + t = clock() - t; + std::cout << " TIME FOR SIMULATION: " << (float)t/CLOCKS_PER_SEC/60.f << " MINUTES." < + +#include "tests_dynsim_deformer.h" + +#include "auxiliary_testing_functions.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + +#include "sirf/Reg/NiftyResample.h" +#include "sirf/Reg/NiftiImageData3DDeformation.h" + +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" + + +using namespace sirf; + + +bool DynSimDeformerTester::test_nifti_data_deformation( void ) +{ + std::cout << " --- Running " << __FUNCTION__ << std::endl; + try + { + LabelVolume segmentation_labels = read_segmentation_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + auto sptr_img_to_deform = std::make_shared< NiftiImageData3D >( segmentation_labels ); + + // auto motion_fields = read_cardiac_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + auto motion_fields = read_respiratory_motionfields_to_nifti_from_h5( H5_XCAT_PHANTOM_PATH ); + + for(size_t i=0; i resampler; + + resampler.set_interpolation_type_to_cubic_spline(); + resampler.set_reference_image(sptr_img_to_deform); + resampler.set_floating_image (sptr_img_to_deform); + + auto disp_trafo = std::make_shared >( ( motion_fields[i] ).get_as_deformation_field(motion_fields[i]) ); + resampler.add_transformation(disp_trafo); + + resampler.process(); + + auto output_img = resampler.get_output_sptr(); + + output_img->write( sstream_output.str() ); + + + } + + return true; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" <(fname_segmentation); + + segmentation.get_geom_info_sptr()->print_info(); + + AcquisitionsVector contrast_rawdata(fname_ct); + ISMRMRD::MatrixSize matsize = contrast_rawdata.acquisitions_info().get_IsmrmrdHeader().encoding[0].reconSpace.matrixSize; + + int const Nx = matsize.x; + int const Ny = matsize.y; + int const Nz = matsize.z; + + GadgetronImagesVector contrast_template(contrast_rawdata); + + for(int i=0; iptr_image(); + ptr_img->resize(Nx, Ny, Nz, 1); + + // #pragma omp parallel + for( size_t nz=0; nzoperator()(nx, ny, nz, 0) = segmentation(nx,ny,nz); + } + + } + contrast_template.get_geom_info_sptr()->print_info(); + contrast_template.reorient(*(segmentation.get_geom_info_sptr())); + + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_contrast" << __FUNCTION__; + + sirf::write_imagevector_to_raw(name_stream.str(), contrast_template); + + NiftyResampler resampler; + resampler.set_interpolation_type_to_cubic_spline(); + + // both floating and reference image must be in the same coordinate system + resampler.set_floating_image (std::make_shared< GadgetronImagesVector> (contrast_template)); + + AcquisitionsVector target_rawdata(fname_at); + GadgetronImagesVector acquisition_template(target_rawdata); + resampler.set_reference_image(std::make_shared< GadgetronImagesVector> (acquisition_template)); + + acquisition_template.get_geom_info_sptr()->print_info(); + contrast_template.get_geom_info_sptr()->print_info(); + + + resampler.process(); + + const std::shared_ptr sptr_deformed_img = resampler.get_output_sptr(); + + GadgetronImagesVector img_data(acquisition_template); + + sptr_deformed_img->copy(sptr_deformed_img->begin(), + img_data.begin(), + img_data.end()); + + + name_stream.str(""); + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_resampled" << __FUNCTION__; + sirf::write_imagevector_to_raw(name_stream.str(), img_data); + + return true; + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < curr_mvf = (motion_fields[i]).get_as_deformation_field( motion_fields[i] ); + + std::vector< NiftiImageData3DDeformation > vec_mvfs; + vec_mvfs.push_back( curr_mvf ); + + mr_cont_gen.map_contrast(); + DynamicSimulationDeformer dsd; + dsd.deform_contrast_generator(mr_cont_gen, vec_mvfs); + + GadgetronImagesVector curr_motion_state = mr_cont_gen.get_contrast_filled_volumes(); + + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__ << "_state_" << i; + + sirf::write_imagevector_to_raw(name_stream.str(), curr_motion_state); + + } + + return true; + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < static_state = pet_cont_gen.get_contrast_filled_volumes(); + + + for( size_t i_motion=0; i_motion curr_motion_state_nii( curr_motion_state[0] ); + curr_motion_state_nii.write(filename_stream.str() ); + // data_io::write_PET_image_to_hv(filename_stream.str(), curr_motion_state[0]); + } + + return true; + } + + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < motion_bins = motion_dyn.get_bins(); + + for( size_t i=0; i(motion_bins[i]); + + std::cout << "Getting mvf for motion state " << motion_state << std::endl; + + sirf::NiftiImageData3DDeformation curr_mvf = motion_dyn.get_interpolated_deformation_field( motion_state ); + + std::vector< NiftiImageData3DDeformation > vec_mvfs; + vec_mvfs.push_back( curr_mvf ); + + mr_cont_gen.map_contrast(); + + DynamicSimulationDeformer dsd; + dsd.deform_contrast_generator(mr_cont_gen, vec_mvfs); + + GadgetronImagesVector curr_motion_state = mr_cont_gen.get_contrast_filled_volumes(); + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__ << "_dyn_" << i; + sirf::write_imagevector_to_raw(name_stream.str(), curr_motion_state); + + } + + return test_succesful; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < + + +#include "tests_noisegenerator.h" + +using namespace sirf; + + +bool test_noisegen::test_add_poisson_noise( void ) +{ + std::cout << "--- Running " << __FUNCTION__ << std::endl; + try + { + std::stringstream fpath_pet_exampledata; + fpath_pet_exampledata << SHARED_FOLDER_PATH << TESTDATA_PREFIX << "TemplateData/PET/simulated_data.hs"; + + PETAcquisitionDataInMemory noise_free_acq(fpath_pet_exampledata.str().c_str()); + PETAcquisitionDataInMemory noisy_acq(noise_free_acq); + + PoissonNoiseGenerator png; + png.add_noise( noisy_acq, noise_free_acq); + + std::stringstream name_stream; + name_stream << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + + std::cout << "Writing " << name_stream.str() << std::endl; + noisy_acq.write(name_stream.str()); + + return true; + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" < + +#include "auxiliary_testing_functions.h" +#include "sirf/common/GeometricalInfo.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" +#include "sirf/cDynamicSimulation/phantom_input.h" +#include "tests_phantom_input.h" + + +using namespace sirf; +using namespace std; + + +bool test_read_1D_dataset_from_h5( std::string h5_filename_with_suffix) +{ + std::cout << "--- Running " << __FUNCTION__ < input = read_1D_dataset_from_h5(h5_filename_with_suffix, name_dataset, type_input, type_reader); + + stringstream ss_outname; + ss_outname << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + + data_io::write_raw (ss_outname.str(), &input[0], input.size()); + + return true; +} + +bool test_read_geometrical_info_from_h5( std::string h5_filename_with_suffix ) +{ + std::cout << "--- Running " << __FUNCTION__ < segmentation_nifti = read_nifti_from_h5( h5_filename_with_suffix, dataset_name, type_input, type_reader ); + + std::cout <<"Maximum val. in segmentation: " << epiph( segmentation_nifti.get_max() ) < > all_dvfs = read_motionfields_to_nifti_from_h5(h5_filename_with_suffix, type_motionfield); + + std::cout << "Number of motion fields " << all_dvfs.size() << std::endl; + stringstream ss_outname; + ss_outname << SHARED_FOLDER_PATH << TESTDATA_OUT_PREFIX << "output_" << __FUNCTION__; + + + all_dvfs[all_dvfs.size()-1].write( ss_outname.str() ); + + return true; +} + + + diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/tests_phantom_input.h b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_phantom_input.h new file mode 100644 index 000000000..02ab70d5b --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_phantom_input.h @@ -0,0 +1,19 @@ + /* ================================================ + +Author: Johannes Mayer +Date: 2018.03.26 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + +#pragma once + +#include +#include +#include + + +bool test_read_1D_dataset_from_h5( std::string h5_filename_with_suffix ); +bool test_read_geometrical_info_from_h5( std::string h5_filename_with_suffix ); +bool test_read_segmentation_to_nifti( std::string h5_filename_with_suffix ); +bool test_read_motionfield_to_nifti( std::string h5_filename_with_suffix ); diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/tests_tissueparameters.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_tissueparameters.cpp new file mode 100644 index 000000000..276e718ec --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_tissueparameters.cpp @@ -0,0 +1,299 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.03.21 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#include "tests_tissueparameters.h" + +#include "auxiliary_testing_functions.h" + +using boost::property_tree::ptree; + + +bool test_allocate_MRTissueParameter_successful(void) +{ + std::cout << __FUNCTION__ << std::endl; + + MRTissueParameter mr_tissue_pars; + mr_tissue_pars.t1_miliseconds_ = 1000; + mr_tissue_pars.t2_miliseconds_ = 200; + mr_tissue_pars.cs_ppm_ = 4.23; + + return true; +} + +bool test_allocate_PETTissueParameter_successful(void) +{ + std::cout << __FUNCTION__ << std::endl; + + PETTissueParameter pet_tissue_pars; + pet_tissue_pars.attenuation_1_by_cm_ = 0.01; + pet_tissue_pars.activity_kBq_ml_ = 15; + + return true; +} + +bool test_allocate_TissueParameter_successful(void) +{ + std::cout << __FUNCTION__ << std::endl; + + TissueParameter tissue_pars; + tissue_pars.label_ = 1; + tissue_pars.name_ = "Liver"; + + MRTissueParameter mr_tiss; + PETTissueParameter pet_tiss; + + tissue_pars.mr_tissue_ = mr_tiss; + tissue_pars.pet_tissue_ = pet_tiss; + + return true; +} + +bool test_TissueParameter_algebra( void ) +{ + std::cout << __FUNCTION__ << std::endl; + + TissueParameter first_param, second_param; + + first_param.label_ = 0; + first_param.name_ = "name_0"; + + first_param.mr_tissue_.spin_density_percentH2O_ = 0.f; + first_param.mr_tissue_.t1_miliseconds_ = 0.f; + first_param.mr_tissue_.t2_miliseconds_ = 0.f; + first_param.mr_tissue_.cs_ppm_ = 0.f; + + + first_param.pet_tissue_.attenuation_1_by_cm_ = 0.f; + first_param.pet_tissue_.activity_kBq_ml_ = 0.f; + + + second_param.label_ = 1; + second_param.name_ = "name_1"; + + second_param.mr_tissue_.spin_density_percentH2O_ = 1.f; + second_param.mr_tissue_.t1_miliseconds_ = 1.f; + second_param.mr_tissue_.t2_miliseconds_ = 1.f; + second_param.mr_tissue_.cs_ppm_ = 1.f; + + + second_param.pet_tissue_.attenuation_1_by_cm_ = 1.f; + second_param.pet_tissue_.activity_kBq_ml_ = 1.f; + + float interpol_weight = 0.3; + + TissueParameter interpol_param = (1.f-interpol_weight) * second_param + interpol_weight*first_param; + + std::cout< +#include +#include + + +#include "sirf/cDynamicSimulation/tissueparameters.h" + + + +bool test_allocate_MRTissueParameter_successful(void); +bool test_allocate_PETTissueParameter_successful(void); +bool test_allocate_TissueParameter_successful(void); + +bool test_TissueParameter_algebra( void ); + +bool test_get_MRTissueParameter_from_ptree(void); +bool test_get_PETTissueParameter_from_ptree(void); + +void test_exception_throw_if_node_not_exists(void); + +bool test_read_TissueParameter_label_from_xml( std::string const xml_filepath ); + +//TissueParameterList get_test_tissue_parameter_list( void ); + + +bool test_check_label_uniqueness_fails(); +bool test_check_label_uniqueness_true(); + diff --git a/src/xDynamicSimulation/cDynamicSimulation/tests/tests_volume_orientator.cpp b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_volume_orientator.cpp new file mode 100644 index 000000000..245f21224 --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tests/tests_volume_orientator.cpp @@ -0,0 +1,50 @@ +/* ================================================ + +Author: Johannes Mayer +Date: 2018.09.18 +Institution: Physikalisch-Technische Bundesanstalt Berlin + +================================================ */ + + +#include "tests_volume_orientator.h" + + +#include "auxiliary_testing_functions.h" +#include "sirf/cDynamicSimulation/auxiliary_input_output.h" + + + +#define OUTPUTNAME_INPUT_IMG SHARED_FOLDER_PATH "volume_orientation_input" +#define OUTPUTNAME_REORIENTED_IMG SHARED_FOLDER_PATH "volume_orientation_output" + + +using namespace sirf; + +bool aVolumeOrientatorTester::test_reorient_image( ) +{ + + try + { + bool test_succesful = true; + sirf::ReadoutDir ro_dir = ro_dir_z; + aVolumeOrientator vol_or(ro_dir); + + auto img = aux_test::get_mock_ismrmrd_image_with_gradients(); + data_io::write_ISMRMRD_Image_to_nii(OUTPUTNAME_INPUT_IMG, img); + + + img = vol_or.reorient_image< float > ( img ); + data_io::write_ISMRMRD_Image_to_nii(OUTPUTNAME_REORIENTED_IMG, img); + + return test_succesful; + + } + catch( std::runtime_error const &e) + { + std::cout << "Exception caught " <<__FUNCTION__ <<" .!" <filepath_tissue_parameter_xml_ = filepath_tissue_parameter_xml; + this->segmentation_labels_ = segmentation_labels; + +} + + +std::string TissueLabelMapper::get_filepath_tissue_parameter_xml() +{ + return this->filepath_tissue_parameter_xml_; +} + + +LabelVolume TissueLabelMapper::get_segmentation_labels( void ) +{ + return this->segmentation_labels_; +} + +TissueParameterList TissueLabelMapper::get_tissue_parameter_list( void ) const +{ + return this->tissue_parameter_list_; +} + +void TissueLabelMapper::map_labels_to_tissue_from_xml( void ) +{ + this->tissue_parameter_list_ = read_TissueParameters_from_xml(filepath_tissue_parameter_xml_); + this-> assign_tissues_to_labels(); +} + + +const int* TissueLabelMapper::get_segmentation_dimensions( void ) +{ + return this->segmentation_labels_.get_dimensions(); +} + + +void TissueLabelMapper::assign_tissues_to_labels( void ) +{ + this->segmentation_tissues_ = assign_tissue_parameters_to_labels( this->tissue_parameter_list_, this->segmentation_labels_); +} + + +TissueVector assign_tissue_parameters_to_labels( const TissueParameterList& tiss_list, const LabelVolume& label_volume ) +{ + + size_t const num_tissue_params = tiss_list.size(); + + std::map > lut; + + for(size_t i =0; i(tiss_list[i]) )); //map label to pointer + } + + size_t const num_voxels = label_volume.get_num_voxels(); + + TissueVector tissue_vect(num_voxels); + + for( size_t i_vox =0; i_voxsecond; + } + else + { + std::stringstream msg; + msg << "The label " << (LabelType)label_volume(i_vox) << " in your label volume does not appear in the label list."; + throw std::runtime_error(msg.str()); + } + + } + + return tissue_vect; +} + + + +void TissueLabelMapper::replace_petmr_tissue_parameters( const LabelType& label, const TissueParameter& replacement_tiss) +{ + size_t const num_tissues = tissue_parameter_list_.size(); + + bool label_found = false; + + for( size_t i_tiss=0; i_tisstissue_parameter_list_[i_tiss]; + + if(curr_tiss.label_ == label) + { + curr_tiss.mr_tissue_ = replacement_tiss.mr_tissue_; + curr_tiss.pet_tissue_ = replacement_tiss.pet_tissue_; + + this->tissue_parameter_list_[i_tiss] = curr_tiss; + label_found = true; + + break; + } + } + if( !label_found ) + throw std::runtime_error("The label you tried to replace did not exist in the segmentation."); + // else + // { + // this->segmentation_tissues_ = assign_tissue_parameters_to_labels( this->tissue_parameter_list_, segmentation_labels_); + // } + + +} + + diff --git a/src/xDynamicSimulation/cDynamicSimulation/tissueparameters.cpp b/src/xDynamicSimulation/cDynamicSimulation/tissueparameters.cpp new file mode 100644 index 000000000..f30c9c18a --- /dev/null +++ b/src/xDynamicSimulation/cDynamicSimulation/tissueparameters.cpp @@ -0,0 +1,199 @@ +/* Implementation of TissueParameter + +author johannes mayer +date 20180321 +institute PTB Berlin + +*/ + +#include +#include +#include "sirf/cDynamicSimulation/tissueparameters.h" + +using boost::property_tree::ptree; + + + +MRTissueParameter operator* (float const x, const MRTissueParameter& a_mr) +{ + MRTissueParameter mr_tiss; + mr_tiss.spin_density_percentH2O_ = x * a_mr.spin_density_percentH2O_; + mr_tiss.t1_miliseconds_ = x * a_mr.t1_miliseconds_; + mr_tiss.t2_miliseconds_ = x * a_mr.t2_miliseconds_; + mr_tiss.cs_ppm_ = x * a_mr.cs_ppm_; + + return mr_tiss; +} + +PETTissueParameter operator* (float const x, const PETTissueParameter& a_pet) +{ + PETTissueParameter pet_tiss; + pet_tiss.attenuation_1_by_cm_ = x * a_pet.attenuation_1_by_cm_; + pet_tiss.activity_kBq_ml_ = x * a_pet.activity_kBq_ml_; + + return pet_tiss; +} + +TissueParameter operator* (float const x, const TissueParameter& a_tiss) +{ + TissueParameter tiss; + tiss.label_ = a_tiss.label_; + tiss.name_ = a_tiss.name_; + + tiss.mr_tissue_ = x * a_tiss.mr_tissue_; + tiss.pet_tissue_ = x * a_tiss.pet_tissue_; + + return tiss; +} + + + + + +TissueParameterList read_TissueParameters_from_xml(const std::string& xml_filepath) +{ + + std::cout <<"Reading xml file: " << xml_filepath << "." << std::endl; + + try + { + std::ifstream in(xml_filepath); + in.exceptions( in.failbit ); + + + ptree pt; + + read_xml( in, pt); + + TissueParameterList tiss_list; + + BOOST_FOREACH( ptree::value_type const& v, pt.get_child("TissueParameterList") ) + { + + + if( v.first == "TissueParameter") + { + TissueParameter tiss_par; + tiss_par.label_ = v.second.get< unsigned >( "label"); + tiss_par.name_ = v.second.get< std::string > ("name"); + + + tiss_par.mr_tissue_ = get_mrtissueparameter_from_ptree( v.second ); + tiss_par.pet_tissue_ = get_pettissueparameter_from_ptree( v.second ); + tiss_list.push_back(tiss_par); + } + } + + if(tiss_list.size() == 0) + { + throw std::range_error( "The TissueParameterList of your xml file does not contain a single TissueParameter node." ); + } + if( !check_label_uniqueness(tiss_list) ) + { + throw std::runtime_error( "The TissueParameterList of your xml file contains multiple parameters with the same label. Please only assign unique labels." ); + } + + + std::cout <<"Reading of: " << xml_filepath << " finished without exception." << std::endl; + + return tiss_list; + } + + // error handling + + catch ( const std::ios_base::failure &e) + { + + std::cout << "Failed to open " << xml_filepath << "\n" + << "Caught an ios_base::failure" << "\n" + << "Explanatory string: " << e.what() << "\n" + << "Error code: " << e.code() << std::endl; + throw; + } + catch ( const boost::property_tree::ptree_bad_path &e) + { + std::cout << "Caught bad_path exception " < ("spin_density_percentH2O"); + mr_tiss.t1_miliseconds_ = mr_tissue_tree.get ("t1_miliseconds"); + mr_tiss.t2_miliseconds_ = mr_tissue_tree.get ("t2_miliseconds"); + mr_tiss.cs_ppm_ = mr_tissue_tree.get ("cs_ppm"); + } + catch( const boost::property_tree::ptree_bad_path &e) + { + std::cout << "Caught bad_path exception " < ("attenuation_1_by_cm"); + pet_tiss.activity_kBq_ml_ = pet_tissue_tree.get ("activity_kBq_ml"); + } + catch( const boost::property_tree::ptree_bad_path &e) + { + std::cout << "Caught bad_path exception." < all_labels; + all_labels.resize(num_tissue_params); + + for (size_t i=0; i::iterator it; + + for (size_t i=0; i= 3 and sys.version_info[1] >= 4: + ABC = abc.ABC +else: + ABC = abc.ABCMeta('ABC', (), {}) + +from deprecation import deprecated + +from sirf.Utilities import check_status, assert_validity, try_calling, error + +import sirf +from sirf import SIRF +from sirf.SIRF import DataContainer + +import sirf.pysimulation as pysim +import sirf.pyiutilities as pyiutil +# import sirf.pygadgetron as pygadgetron +# import sirf.pysirf as pysirf + +import sirf.Reg as pReg +import sirf.Gadgetron as pMR + + + +def format_arrays_for_setters(data): + + if not isinstance(data, np.ndarray): + raise error('Wrong input format.' + \ + ' Should be numpy.ndarray. Got {}'.format(type(data))) + + if data.dtype != np.float32: + the_data = data.astype(np.float32) + + convert = not the_data.flags['C_CONTIGUOUS'] + if convert: + the_data = np.ascontiguousarray(the_data) + + return the_data + + +class MRDynamicSimulation(object): + ''' + Class to perform dynamic MRI simulations + ''' + def __init__(self, tissue_labels, fname_xml): + + self.handle = None + assert_validity(tissue_labels, pReg.NiftiImageData3D) + + self.handle = pysim.cDS_MRDynamicSimulation(tissue_labels.handle, fname_xml) + check_status(self.handle) + + def set_template_data(self, ad): + ''' + Method to set both contrast and acquisition template data, cf. methods set_acquisition_template_data and set_contrast_template_data. + ''' + self.set_acquisition_template_data(ad) + self.set_contrast_template_data(ad) + + def set_acquisition_template_data(self, ad): + ''' + Setting MR rawdata to define + - a destination geometry into which the signal-filled segmentation is resampled. + - defining the encoding that is performed, i.e. trajectory and # of readout and phase-encoding points etc. + ''' + assert_validity(ad, pMR.AcquisitionData) + pysim.cDS_setAcquisitionTemplateData(self.handle, ad.handle) + + + def set_contrast_template_data(self, ad): + ''' + Setter to define MR rawdata to fix sequence parameters used in signal computation for steady state sequences. + ''' + assert_validity(ad, pMR.AcquisitionData) + pysim.cDS_setContrastTemplateData(self.handle, ad.handle) + + + def simulate_data(self): + ''' + Method to start simulation after it is set up. + ''' + try_calling( pysim.cDS_simulateData(self.handle) ) + + + def set_csm(self, csm): + ''' + Setter for the employed coil sensitiviy profiles. They have to match the geometry of the acquisition template data. + ''' + assert_validity(csm, pMR.CoilSensitivityData) + try_calling( pysim.cDS_setCoilmaps(self.handle, csm.handle)) + + def set_snr(self, SNR): + ''' + Setting the signal to noise ratio which is added to the MR rawdata as complex Gaussian noise + ''' + SNR = np.array(SNR).astype(np.float32) + pysim.cDS_setSNR(self.handle, SNR.ctypes.data) + + + def set_snr_label(self, SNR_label): + ''' + Method to define a label in the segmentation corresponding to a tissue type used to compute the signal from on which the SNR is based. + ''' + pysim.cDS_setNoiseLabel(self.handle, SNR_label) + + + def set_offset_trafo(self, trafo): + ''' + Method to add an affine transform which is applied to the 3D segmentation prior to resampling into template geometry + ''' + assert_validity(trafo, pReg.AffineTransformation) + try_calling(pysim.cDS_setOffsetTransformation(self.handle, trafo.handle)) + + def write_simulation_results(self, fpath_output): + pysim.cDS_writeSimulationResults(self.handle, fpath_output) + + def save_motion_ground_truth(self): + pysim.cDS_saveMotionGroundTruth(self.handle) + + + def save_parametermap_ground_truth(self, filename_prefix): + ''' + Method storing the parameters T1, T2, spin density, chemical shift and the segmentation resampled into the destination geometry as nifti images. + ''' + pysim.cDS_saveParameterMapsGroundTruth(self.handle, filename_prefix) + return (filename_prefix + "_T1_ms.nii", filename_prefix + "_T2_ms.nii", filename_prefix + "_spindensity.nii", filename_prefix + "_labels.nii") + + ''' + Method to include an independent motion mode in the simulation. + ''' + def add_motion_dynamic(self, motiondyn): + try_calling(pysim.cDS_addMRMotionDynamic(self.handle, motiondyn.handle)) + + ''' + Method to include contrast changes during the data acquisition. + ''' + def add_contrast_dynamic(self, contdyn): + try_calling(pysim.cDS_addMRContrastDynamic(self.handle, contdyn.handle)) + + ''' + Method to activate the simulation of a pre-computed magnetisation. This will overwrite other contrast dynamics. + ''' + def add_external_contrast_dynamic(self, contrastdyn): + try_calling(pysim.cDS_addExternalContrastDynamic(self.handle, contrastdyn.handle)) + + +class Dynamic(object): + ''' + Class to define properties shared by dynamic processes during data acquisition simulation that can be added to the simulation class. + ''' + def __init__(self, num_states): + self.num_states = num_states + + + def set_dynamic_signal(self, time_points_seconds, signal_points): + ''' + Method to set the temporal evolution of the simulation + ''' + num_signal_points = time_points_seconds.size + if num_signal_points != signal_points.size: + AssertionError("The signal and time point arrays do not have identical size.") + + the_time = format_arrays_for_setters(time_points_seconds) + the_signal = format_arrays_for_setters(signal_points) + + pysim.cDS_setDynamicSignal(self.handle, + the_time.ctypes.data,\ + the_signal.ctypes.data,\ + num_signal_points) + + def add_displacement_field(self, dvf): + assert_validity(dvf, pReg.NiftiImageData3DDisplacement) + pysim.cDS_addMRDisplacementField(self.handle, dvf.handle) + + def set_cyclicality(self, is_cyclic): + pysim.cDS_setCyclicality(self.handle, is_cyclic) + + def get_idx_corr_sizes(self, ad): + + assert_validity(ad, pMR.AcquisitionData) + sizes = np.zeros(shape=(self.num_states,), dtype=np.int32) + sizes = np.ascontiguousarray(sizes) + pysim.cDS_getIdxCorrSizes(self.handle, ad.handle, sizes.ctypes.data) + + return sizes + + def get_idx_corr(self, ad): + ''' + Method to get the index of the phase encoding lines that were simulated in the same dynamic state. + ''' + idx_sizes = self.get_idx_corr_sizes(ad) + idx_corr = [] + for ibin in range(len(idx_sizes)): + num_entries=idx_sizes[ibin] + idx = np.zeros(shape=(num_entries,), dtype=np.int32) + idx = np.ascontiguousarray(idx) + pysim.cDS_getIdxCorr(self.handle, ibin, idx.ctypes.data) + idx_corr.append(idx) + + return idx_corr + + +class MRMotionDynamic(Dynamic): + ''' + Class to define motion that is executed during the simulted data acquisition. + ''' + def __init__(self, num_states): + ''' + num_states: # of motion states that are simulated. This is NOT constrained to be equal to the # of motion vector fields + that are supplied to the motion dynamic. + ''' + self.num_states = num_states + self.handle = None + self.handle = pysim.cDS_MRMotionDynamic(num_states) + check_status(self.handle) + + + def add_displacement_field(self, dvf): + ''' + Method to add a displacement field, each added displacement vector field defines a motion state. The order in which the + motion fields are supplied is important where the first corresponds to surrogate signal = 0 and the last to surrogate signal = 1. + ''' + assert_validity(dvf, pReg.NiftiImageData3DDisplacement) + pysim.cDS_addMRDisplacementField(self.handle, dvf.handle) + + + def set_cyclicality(self, is_cyclic): + ''' + Method defining whether surrogate signal = 0 corresponds to the same motion state as surrogate signal = 1. + ''' + pysim.cDS_setCyclicality(self.handle, is_cyclic) + + + + def set_groundtruth_folder_prefix(self, prefix_existing_path): + ''' + Method defining the path to which the motion dynamic stores its ground truth motion fields. + ''' + pysim.cDS_setMRGroundTruthFolderName(self.handle, prefix_existing_path) + + + +class TissueParameter(): + ''' + Class carrying a tissue parameter associated with a simulation. + The information is filled based on the XML file detailing the . + + This is primarily used to define tissue parameters corresponding to surrogate signal = 0 and surrogate signal = 1 + in an MRContrastDynamic. + ''' + def __init__(self, sim, label): + ''' + sim: MRDynamicSimulation object + label: label in segmentation corresponding to the tissue. + ''' + self.handle = None + + assert_validity(sim, MRDynamicSimulation) + + self.handle = pysim.cDS_getTissueParameter(sim.handle, label) + check_status(self.handle) + + def set_T1_value(self, T1_ms): + ''' + Method to modify the T1 value of the tissue parameters. + ''' + pysim.cDS_setT1Value(self.handle,T1_ms) + + def set_spin_density(self, spin_density): + ''' + Method to modify the spin density of the tissue parameters. + ''' + pysim.cDS_setSpinDensity(self.handle, spin_density) + + + +class MRContrastDynamic(Dynamic): + ''' + Class to modify the tissue parameters over time during the simulation of MR data acquisition. + This is primarily used to modify T1 contrast agent inflow. + ''' + def __init__(self, num_states): + ''' + num_states: # of simualted contrast states across the time of data acquisition. + ''' + self.num_states = num_states + self.handle = None + self.handle = pysim.cDS_MRContrastDynamic(num_states) + check_status(self.handle) + + def add_dynamic_label(self, label): + ''' + Method to add segmentation labels that will behave identially during the simulation of contrast variation. + ''' + pysim.cDS_addDynamicLabel(self.handle, label) + + def set_parameter_extremes(self, tissue_0, tissue_1): + ''' + tissue_0: tissue parameter corresponding to surrogate signal = 0. + tissue_1: tissue parameter corresponding to surrogate signal = 1. + ''' + assert_validity(tissue_0, TissueParameter) + assert_validity(tissue_1, TissueParameter) + + pysim.cDS_setMRParameterExtremes(self.handle, tissue_0.handle, tissue_1.handle) + +class ExternalMRContrastDynamic(Dynamic): + ''' + Class to carry information on pre-computed magnetisation that is simulated. + ''' + def __init__(self): + self.handle = None + self.handle = pysim.cDS_ExternalMRContrastDynamic() + check_status(self.handle) + + def add_external_signal(self, external_signal): + + num_sig_pts = external_signal.get_num_signal_points() + num_labels = external_signal.get_num_labels() + print("Adding external MR signal for {} labels at {} time points".format(num_labels, num_sig_pts)) + + for i in range(num_sig_pts): + ptr_labels, ptr_sig = external_signal.get_signal_pointers(i) + pysim.cDS_appendExternalTissueSignal(self.handle, num_labels, ptr_labels, ptr_sig) + +# helper class to set up external MR signal +class ExternalMRSignal(): + ''' + Auxiliary class for storing information on pre-computed magnetisation. Each label in a segmentation must have an + associated magnetisation signal that is available for each simulated time point. + ''' + def __init__(self, labels, signals): + ''' + labels: comprehensive set of labels to which the magnetisation in signals + signals: shape (labels.size, #time points) where # time points = acquisition_template.number() + ''' + assert type(signals) is np.ndarray, "Please pass a numpy ndarray of signals. You provided {}".format(type(signals)) + assert np.iscomplexobj(signals), "Please pass a 64-bit complex numpy array" + + (num_tissue_signals, num_time_pts) = signals.shape + self.number_of_labels = labels.size + + assert self.number_of_labels == num_tissue_signals, "Please pass a signal array of shape (#labels, #time points)" + + self.tissue_signals = np.array([self.TissueSignal() for _ in range(num_time_pts)]) + self.set_up_signal(labels, signals) + + def get_num_signal_points(self): + return self.tissue_signals.size + + def get_num_labels(self): + return self.number_of_labels + + def set_up_signal(self, labels, signals): + + if signals.dtype is not np.complex64: + the_signals = signals.astype(np.complex64) + else: + the_signals = signals + + if labels.dtype is not np.int32: + the_labels = labels.astype(np.int32) + else: + the_labels = labels + + num_time_pts = the_signals.shape[1] + + for t in range(num_time_pts): + signal_at_time = the_signals[:,t] + self.tissue_signals[t] = self.TissueSignal(the_labels, signal_at_time) + ''' + Auxiliary method to access arrays in C-level. + ''' + def get_signal_pointers(self, time_index): + + return self.tissue_signals[time_index].get_signal_pointers() + ''' + Auxiliary class to access arrays in C-level. + ''' + class TissueSignal(): + + def __init__(self, labels=None, signals=None): + + self.time_ms = 0.0 + self.labels = np.ascontiguousarray(labels) + self.signals = np.ascontiguousarray(signals) + + def get_signal_pointers(self): + return( self.labels.ctypes.data, self.signals.ctypes.data) \ No newline at end of file diff --git a/src/xDynamicSimulation/pDynamicSimulation/TissueParameterList.py b/src/xDynamicSimulation/pDynamicSimulation/TissueParameterList.py new file mode 100644 index 000000000..84021c3d1 --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/TissueParameterList.py @@ -0,0 +1,94 @@ +import xmltodict +import numpy as np + + +class TissueParameterList: + + def __init__(self): + + self.tissue_parameters = np.array([], dtype=object) + + def print_contents(self): + for tp in self.tissue_parameters: + msg = "We have extracted {} for label {}.".format(tp.name, tp.label) + print(msg) + + def read_xml_to_dict(xml_path): + + with open(xml_path, 'r') as file: + xml_data = file.read() + xml_dict = xmltodict.parse(xml_data) + + return xml_dict + + def parse_xml(self, xml_path): + + xml_dict = TissueParameterList.read_xml_to_dict(xml_path) + self.extract_dictionary(xml_dict) + + + def extract_dictionary(self, xml_dict): + + parameter_list = xml_dict['TissueParameterList']['TissueParameter'] + + for param in parameter_list: + label = int(param['label']) + name = param['name'] + + mr_param = param['MRTissueParameter'] + + rho = float(mr_param['spin_density_percentH2O']) + t1 = float(mr_param['t1_miliseconds']) + t2 = float(mr_param['t2_miliseconds']) + cs = float(mr_param['cs_ppm']) + + pet_param = param['PETTissueParameter'] + mu = float(pet_param['attenuation_1_by_cm']) + act = float(pet_param['activity_kBq_ml']) + + tp = self.TissueParameter(label, name, rho, t1, t2, cs, mu, act) + self.tissue_parameters = np.append(self.tissue_parameters, tp) + + def get_labels_and_names(self): + + label_dict = {} + + for tp in self.tissue_parameters: + label_dict[tp.name] = tp.label + + return label_dict + + def mr_as_array(self): + + arr = np.empty( shape=(self.tissue_parameters.size, 5)) + + for i in range(self.tissue_parameters.size): + arr[i,:] = self.tissue_parameters[i].mr_as_array() + + return arr + + class TissueParameter: + + def __init__(self, label, name, spin_density, T1_ms, T2_ms, cs_ppm, activity_kBq_ml, attenuation_1_by_cm): + + self.label = label + self.name = name + self.spin_density = spin_density + self.T1_ms = T1_ms + self.T2_ms = T2_ms + self.cs_ppm = cs_ppm + self.activity_kBq_ml = activity_kBq_ml + self.attenuation_1_by_cm = attenuation_1_by_cm + + def as_array(self): + return np.array([self.label, self.spin_density, self.T1_ms, self.T2_ms, self.cs_ppm, self.attenuation_1_by_cm, self.activity_kBq_ml]) + + def mr_as_array(self): + arr = self.as_array() + extracted_idx = [0, 1, 2, 3, 4] + return arr[extracted_idx] + + def pet_as_array(self): + arr = self.as_array() + extracted_idx = [0, 5, 6] + return arr[extracted_idx] diff --git a/src/xDynamicSimulation/pDynamicSimulation/pysimulation.i b/src/xDynamicSimulation/pDynamicSimulation/pysimulation.i new file mode 100644 index 000000000..b53d51a57 --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/pysimulation.i @@ -0,0 +1,5 @@ +%module pysimulation +%{ +#include "sirf/cDynamicSimulation/cdynamicsimulation.h" +%} +%include "sirf/cDynamicSimulation/cdynamicsimulation.h" diff --git a/src/xDynamicSimulation/pDynamicSimulation/tests/CMakeLists.txt b/src/xDynamicSimulation/pDynamicSimulation/tests/CMakeLists.txt new file mode 100644 index 000000000..4d8dcf5e8 --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +#======================================================================== +# Copyright 2017 - 2021 Science Technology Facilities Council +# Copyright 2017 - 2021 University College London +# Copyright 2021 Physikalisch-Technische Bundesanstalt (PTB) +# This file is part of the SyneRBI Synergistic Image Reconstruction Framework (SIRF) SuperBuild. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +#========================================================================= +add_test(NAME SIMULATION_TESTS_PYTHON + COMMAND ${Python_EXECUTABLE} -m test_all + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/xDynamicSimulation/pDynamicSimulation/tests") + # COMMAND ${Python_EXECUTABLE} -m nose --with-coverage --cover-package=sirf.Gadgetron src/xGadgetron/pGadgetron/ ${exclude_tests} + # WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) +# add_test(NAME MR_DEMOS_PYTHON +# COMMAND ${Python_EXECUTABLE} run_all.py --non-interactive +# WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/examples/Python/MR") +# add_test(NAME MR_GADGETRON_DEMOS_PYTHON +# COMMAND ${Python_EXECUTABLE} run_all.py --non-interactive +# WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/examples/Python/MR/Gadgetron") +# add_test(NAME MR_PYTHON_ALGEBRA +# COMMAND ${Python_EXECUTABLE} -m unittest test_algebra +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/src/xDynamicSimulation/pDynamicSimulation/tests/test_all.py b/src/xDynamicSimulation/pDynamicSimulation/tests/test_all.py new file mode 100644 index 000000000..784f5d3cb --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/tests/test_all.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +"""test sets +v{version} + +Usage: + test_all [--help | options] + +Options: + -r, --record record the measurements rather than check them + -v, --verbose report each test status + +{author} + +{licence} +""" +from sirf.Utilities import __licence__, RE_PYEXT +from glob import glob +from os import path +__version__ = "0.2.0" +__author__ = "Casper da Costa-Luis" + + +if __name__ == "__main__": + from docopt import docopt + args = docopt(__doc__.format( + version=__version__, author=__author__, licence=__licence__), + version=__version__) + + record = args['--record'] + verbose = args['--verbose'] + + failed = 0 + ntests = 0 + + for script in sorted(glob('*.py')): + if path.abspath(__file__) == path.abspath(script): + continue + print('\n\n--- running %s' % script) + test = RE_PYEXT.sub("", script) + main = RE_PYEXT.sub(".test_main(record, verbose, throw=False)", script) + exec('import ' + test) + f, n = eval(main) + if f: + print(' %d of %d tests failed' % (f, n)) + failed += f + ntests += n + + if failed: + import sys + print('%d of %d tests failed' % (failed, ntests)) + sys.exit(failed) + print('all %d tests passed' % ntests) diff --git a/src/xDynamicSimulation/pDynamicSimulation/tests/test_simulate_MR.py b/src/xDynamicSimulation/pDynamicSimulation/tests/test_simulate_MR.py new file mode 100644 index 000000000..b71225281 --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/tests/test_simulate_MR.py @@ -0,0 +1,407 @@ +# -*- coding: utf-8 -*- +"""sirf.Gadgetron Test set 1. +v{version} + +Fully sampled data tests + +Usage: + test1 [--help | options] + +Options: + -r, --record record the measurements rather than check them + -v, --verbose report each test status + +{author} + +{licence} +""" +import numpy as np +from pathlib import Path + +import sirf.DynamicSimulation as pDS +import sirf.Gadgetron as pMR +import sirf.Reg as pReg + +from sirf.Utilities import __license__, runner + +__version__ = "3.1.0" +__author__ = "Johannes Mayer" + +def prepare_test_simulation(fname_mr_rawdata, fpath_xml): + + rawdata = pMR.AcquisitionData(fname_mr_rawdata) + rawdata = pMR.preprocess_acquisition_data(rawdata) + + empty_img = pMR.ImageData() + empty_img.from_acquisition_data (rawdata) + + labels = pReg.NiftiImageData3D(empty_img) + + label_array = labels.as_array() + Nx, Ny, Nz = label_array.shape + + tissue_label = 13 + + label_array[:] = 0 + label_array[Nx//4:3*Nx//4, Ny//4:3*Ny//4, Nz//4:3*Nz//4] = tissue_label + + labels.fill(label_array) + + mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + return mrsim, rawdata, labels + + +def prep_displacement_field(nifti_3D_volume): + + dvf_x = nifti_3D_volume.deep_copy() + dvf_y = nifti_3D_volume.deep_copy() + dvf_z = nifti_3D_volume.deep_copy() + + Nx, Ny, Nz = nifti_3D_volume.shape + + dvf_x.fill(Nx / 3) + dvf_y.fill(Ny / 3) + dvf_z.fill(Nz / 3) + + return pReg.NiftiImageData3DDisplacement(dvf_x, dvf_y, dvf_z) + + +def test_static_mr_simulation(rec=False, verb=False, throw=True): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/cDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Segmentations/XCAT_TissueParameters_XML.xml' + fpath_template_rawdata = input_fpath_prefix + 'TemplateData/MR/CV_nav_cart_64Cube_1Echo.h5' + + mrsim, rawdata, __ = prepare_test_simulation(fpath_template_rawdata, fpath_xml) + + mrsim.set_contrast_template_data(rawdata) + mrsim.set_acquisition_template_data(rawdata) + + csm = pMR.CoilSensitivityData() + csm.calculate(rawdata) + mrsim.set_csm(csm) + + SNR = 5 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + mrsim.simulate_data() + + input_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + fpath_output = input_fpath_prefix + 'mr_static_simulation.h5' + + output_file = Path(fpath_output) + if not output_file.is_file(): + mrsim.write_simulation_results(fpath_output) + + return 1 + +def test_get_idx_corr(rec=False, verb=False, throw=True): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + + fpath_template_rawdata = input_fpath_prefix + 'General/meas_MID27_CV_11s_TI2153_a6_2x2x8_TR45_FID33312_defaultorientation.h5' + ad = pMR.AcquisitionData(fpath_template_rawdata) + + num_resp_states = 4 + + resp_motion = pDS.MRMotionDynamic(num_resp_states) + resp_motion.set_cyclicality(False) + + # generate artificial motion signal + Nt = 100000 + t0_s = 0 + tmax_s = 1200 + time_points = np.linspace(t0_s, tmax_s, Nt) + + resp_frequency_Hz = 0.2 + resp_curve = 0.5 * ( 1 + np.sin( 2*np.pi*resp_frequency_Hz*time_points)) + + resp_motion.set_dynamic_signal(time_points, resp_curve) + + print("The bins with acquisitions are of size {}".format(resp_motion.get_idx_corr_sizes(ad))) + idx_corr = resp_motion.get_idx_corr(ad) + + print("The idx has size {}".format(len(idx_corr))) + + for idx in idx_corr: + print("We have idx with {} numbers.".format(idx.shape)) + if any(idx > ad.number()): + raise AssertionError("Indices larger than the maximum number appear in the idx_corr.") + + return 1 + +def test_motion_mr_simulation(rec=False, verb=False, throw=True): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/cDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Segmentations/XCAT_TissueParameters_XML.xml' + fpath_template_rawdata = input_fpath_prefix + 'TemplateData/MR/CV_nav_cart_64Cube_1Echo.h5' + + # + mrsim, rawdata, labels = prepare_test_simulation(fpath_template_rawdata, fpath_xml) + + mrsim.set_contrast_template_data(rawdata) + mrsim.set_acquisition_template_data(rawdata) + + csm = pMR.CoilSensitivityData() + csm.calculate(rawdata) + mrsim.set_csm(csm) + + SNR = 5 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + # + num_resp_states = 4 + + resp_motion = pDS.MRMotionDynamic(num_resp_states) + resp_motion.set_cyclicality(False) + prefix_motion_GT = output_fpath_prefix + "gt_" + resp_motion.set_groundtruth_folder_prefix(prefix_motion_GT + "resp") + + # generate artificial motion signal + Nt = 100 + t0_s = 0 + tmax_s = 1200 + time_points = np.linspace(t0_s, tmax_s, Nt) + + resp_frequency_Hz = 0.2 + resp_curve = 0.5 * ( 1 + np.sin( 2*np.pi*resp_frequency_Hz*time_points)) + + resp_motion.set_dynamic_signal(time_points, resp_curve) + + # + inhale_dvf = prep_displacement_field(labels) + identity_trafo = prep_displacement_field(labels) + identity_trafo.fill(0) + + resp_motion.add_displacement_field(identity_trafo) + resp_motion.add_displacement_field(inhale_dvf) + + # + num_card_states = 4 + card_motion = pDS.MRMotionDynamic(num_card_states) + card_motion.set_cyclicality(True) + card_motion.set_groundtruth_folder_prefix(prefix_motion_GT + "card") + + card_motion.set_dynamic_signal(time_points, resp_curve) + + card_motion.add_displacement_field(identity_trafo) + card_motion.add_displacement_field(inhale_dvf) + + # + mrsim.add_motion_dynamic(resp_motion) + mrsim.add_motion_dynamic(card_motion) + mrsim.simulate_data() + mrsim.save_motion_ground_truth() + + # + fpath_output = output_fpath_prefix + 'mr_motion_simulation.h5' + + simulated_file = Path(fpath_output) + if not simulated_file.is_file(): + mrsim.write_simulation_results(str(simulated_file)) + + return 1 + +def test_contrast_mr_simulation(rec=False, verb=False, throw=True): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/cDynamicSimulation/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Segmentations/XCAT_TissueParameters_XML.xml' + fpath_template_rawdata = input_fpath_prefix + 'TemplateData/MR/CV_nav_cart_64Cube_1Echo.h5' + + # + mrsim, rawdata, __ = prepare_test_simulation(fpath_template_rawdata, fpath_xml) + + mrsim.set_contrast_template_data(rawdata) + mrsim.set_acquisition_template_data(rawdata) + + csm = pMR.CoilSensitivityData() + csm.calculate(rawdata) + mrsim.set_csm(csm) + + SNR = 5 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + num_cont_states = 4 + gadovist_contrast = pDS.MRContrastDynamic(num_cont_states) + + # generate artificial contrast signal of linear uptake + Nt = 1000 + t0_s = 0 + tmax_s = 300 + time_points = np.linspace(t0_s, tmax_s, Nt) + + cont_curve = np.linspace(0, 1, Nt) + gadovist_contrast.set_dynamic_signal(time_points, cont_curve) + + # generate list on which labels the dynamic will act + uptake_labels = [5, 6, 7, 8, 36, 37] + for label in uptake_labels: + gadovist_contrast.add_dynamic_label(label) + + # fix what the meaning of signal=0 and signal=1 is + tissue_template = pDS.TissueParameter(mrsim, uptake_labels[0]) + T1_1_ms = 600 + tp0 = tissue_template + + tissue_template.set_T1_value(T1_1_ms) + tp1 = tissue_template + + gadovist_contrast.set_parameter_extremes(tp0, tp1) + + mrsim.add_contrast_dynamic(gadovist_contrast) + mrsim.simulate_data() + + fpath_output = output_fpath_prefix + 'mr_contrast_simulation.h5' + + simulated_file = Path(fpath_output) + if not simulated_file.is_file(): + mrsim.write_simulation_results(str(simulated_file)) + + ad_output = pMR.AcquisitionData(file=fpath_output) + template_img = pMR.ImageData() + template_img.from_acquisition_data (ad_output) + am = pMR.AcquisitionModel(ad_output, csm) + am.set_coil_sensitivity_maps(csm) + recon = am.inverse(ad_output) + recon = recon.abs() + recon_nii = pReg.NiftiImageData3D(recon) + + fpath_reconstructed_simulation = output_fpath_prefix + 'reconstructed_mr_contrast_simulation.h5' + recon_nii.write(fpath_reconstructed_simulation) + + + return 1 + +def create_dummy_mrf_signal(num_tissue_types,num_time_points): + # + labels = np.array([i for i in range(num_tissue_types)]) + time_curve = np.sin(np.array([t for t in range(num_time_points)]) * np.pi / num_time_points) \ + +1j * np.cos(np.array([t for t in range(num_time_points)]) * 2 * np.pi / num_time_points) + + mrf_signal = labels[:,np.newaxis] * time_curve[np.newaxis,:] / num_tissue_types / np.sqrt(2) + + return pDS.ExternalMRSignal(labels, mrf_signal) + +def test_simulate_external_contrast(rec=False, verb=False, throw=True): + + fpath_testdata_prefix = '/media/sf_CCPPETMR/TestData/' + output_fpath_prefix = fpath_testdata_prefix + 'Output/xDynamicSimulation/pDynamicSimulation/' + fpath_simulation_output = output_fpath_prefix + 'mrf_simulation_static.h5' + + input_fpath_prefix = fpath_testdata_prefix + 'Input/xDynamicSimulation/pDynamicSimulation/' + + fpath_xml = input_fpath_prefix + 'Cube128/XCAT_TissueParameters_XML.xml' + fpath_template_contrast_rawdata = input_fpath_prefix + 'Cube128/CV_nav_cart_128Cube_FLASH_T1.h5' + fpath_template_acquisition_rawdata = input_fpath_prefix + 'General/meas_MID33_rad_2d_gc_FID78808_ismrmrd.h5' + fpath_segmentation = input_fpath_prefix + 'Cube128/label_volume.nii' + + # generate a simulation object + labels = pReg.NiftiImageData3D(fpath_segmentation) + mrsim = pDS.MRDynamicSimulation(labels, fpath_xml) + + # read template data + contrast_template = pMR.AcquisitionData(fpath_template_contrast_rawdata) + contrast_template = pMR.preprocess_acquisition_data(contrast_template) + + acquisition_template = pMR.AcquisitionData(fpath_template_acquisition_rawdata) + acquisition_template = pMR.preprocess_acquisition_data(acquisition_template) + acquisition_template = pMR.set_goldenangle2D_trajectory(acquisition_template) + acquisition_template.sort_by_time() + # + csm = pMR.CoilSensitivityData() + csm.calculate(acquisition_template) + mrsim.set_csm(csm) + + use_subset = True + if use_subset: + subset_reduction = 10 + subset_idx = np.arange(0,acquisition_template.number() // subset_reduction, dtype=np.int32) + # subset_idx = np.arange(0,3, dtype=np.int32) + acquisition_template = acquisition_template.get_subset(subset_idx) + + # + mrsim.set_contrast_template_data(contrast_template) + mrsim.set_acquisition_template_data(acquisition_template) + + # Set trafo between 3D coordintes and 2D slice + offset_z_mm = -128 + translation = np.array([0, 0, offset_z_mm]) + euler_angles_deg = np.array([15,15,0]) + + offset_trafo = pReg.AffineTransformation(translation, euler_angles_deg) + mrsim.set_offset_trafo(offset_trafo) + + # fix image quality parameters + SNR = 5 + SNR_label = 13 + + mrsim.set_snr(SNR) + mrsim.set_snr_label(SNR_label) + + # create the external MR signal + num_max_labels = 100 + external_signal = create_dummy_mrf_signal(num_max_labels, acquisition_template.number()) + + external_contrast = pDS.ExternalMRContrastDynamic() + external_contrast.add_external_signal(external_signal) + + mrsim.add_external_contrast_dynamic(external_contrast) + + # run + mrsim.simulate_data() + mrsim.write_simulation_results(fpath_simulation_output) + + + # reconstruct the simulated output + simulated_ad = pMR.AcquisitionData(fpath_simulation_output) # -> don't preprocess, it'll crash gadgetron! + + template_img = pMR.ImageData() + template_img.from_acquisition_data(simulated_ad) + + am = pMR.AcquisitionModel(simulated_ad, template_img) + + # csm = pMR.CoilSensitivityData() + # csm.calculate(simulated_ad) + am.set_coil_sensitivity_maps(csm) + + recon = am.inverse(simulated_ad) + recon = recon.abs() + recon_nii = pReg.NiftiImageData3D(recon) + + fpath_reconstructed_simulation = output_fpath_prefix + 'reconstructed_mrf_simulation_static.h5' + recon_nii.write(fpath_reconstructed_simulation) + + return 1 + +def test_main(rec=False, verb=False, throw=True): + + num_tests = 0 + num_tests += test_get_idx_corr(rec, verb, throw) + # num_tests += test_static_mr_simulation(rec, verb, throw) + # num_tests += test_motion_mr_simulation(rec, verb, throw) + # num_tests += test_simulate_external_contrast(rec,verb,throw) + # num_tests += test_contrast_mr_simulation(rec, verb, throw) + + return False, num_tests + +if __name__ == "__main__": + runner(test_main, __doc__, __version__, __author__) diff --git a/src/xDynamicSimulation/pDynamicSimulation/tests/z_test_recon_simulation_output.py b/src/xDynamicSimulation/pDynamicSimulation/tests/z_test_recon_simulation_output.py new file mode 100644 index 000000000..a14d4eeb8 --- /dev/null +++ b/src/xDynamicSimulation/pDynamicSimulation/tests/z_test_recon_simulation_output.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +"""sirf.Gadgetron test. +v{version} + +Constructor of MR image data from MR acquisition data + +Usage: + test_imagedata_constructor [--help | options] + +Options: + -v, --verbose report each test status +{author} + +{licence} +""" + +import numpy as np +import nibabel as nib + +from sirf.Gadgetron import * +from sirf.Utilities import runner, __license__ +__version__ = "0.2.3" +__author__ = "Johannes Mayer" + + +def save_to_nii(sirf_img, fname_with_ext): + img = nib.Nifti1Image(np.squeeze(np.abs(sirf_img.as_array())), np.eye(4)) + nib.save(img, fname_with_ext) + +def recon_cartesian_motion_avg(rawdata): + + rawdata.sort() + + imgdata = ImageData() + imgdata.from_acquisition_data(rawdata) + + acq_model = AcquisitionModel() + acq_model.set_up(rawdata, imgdata) + + csms = CoilSensitivityData() + csms.calculate(rawdata) + acq_model.set_coil_sensitivity_maps(csms) + + recon = acq_model.inverse(rawdata) + + dicom_value_range = (2**16-1) + img_content = recon.as_array() + img_content = dicom_value_range * (img_content - np.amin(img_content[:]) ) / ( np.amax(img_content[:]) - np.amin(img_content[:]) ) + + recon.fill(img_content) + recon.abs() + + return recon + +def test_recon_output_simulate_statics(record=False, verb=False, throw=True): + + print("Running a reconstruction of simulated MR data") + + prefix_data_path = "/media/sf_CCPPETMR/TestData/Output/xDynamicSimulation/" + input_data_path = prefix_data_path + "cDynamicSimulation/" + + rawdata = AcquisitionData(input_data_path + '/output_test_test_simulate_statics.h5') + + recon = recon_cartesian_motion_avg(rawdata) + output_data_path = prefix_data_path + "pDynamicSimulation/" + save_to_nii(recon, output_data_path + "output_recon_simulate_statics.nii") + + +def test_recon_output_simulate_dynamics(record=False, verb=False, throw=True): + + print("Running a reconstruction of simulated MR data") + + prefix_data_path = "/media/sf_CCPPETMR/TestData/Output/xDynamicSimulation/" + input_data_path = prefix_data_path + "cDynamicSimulation/" + + rawdata = AcquisitionData(input_data_path + '/output_test_test_simulate_dynamics.h5') + + recon = recon_cartesian_motion_avg(rawdata) + + output_data_path = prefix_data_path + "pDynamicSimulation/" + save_to_nii(recon, output_data_path + "output_recon_simulate_dynamics.nii") + + + test_failed = False + return test_failed, 1 + +def test_recon_output_simulate_5d_dynamics(record=False, verb=False, throw=True): + + print("Running a reconstruction of simulated MR data") + + prefix_data_path = "/media/sf_CCPPETMR/TestData/Output/xDynamicSimulation/" + input_data_path = prefix_data_path + "cDynamicSimulation/" + + rawdata = AcquisitionData(input_data_path + '/output_test_test_simulate_5d_motion_dynamics.h5') + + recon = recon_cartesian_motion_avg(rawdata) + output_data_path = prefix_data_path + "pDynamicSimulation/" + save_to_nii(recon, output_data_path + "output_recon_simulate_dynamics.nii") + + + test_failed = False + return test_failed, 1 + + +def test_recon_output_python_simulate_statics(record=False, verb=False, throw=True): + + print("Running a reconstruction of simulated MR data") + + prefix_data_path = "/media/sf_CCPPETMR/TestData/Output/xDynamicSimulation/" + input_data_path = prefix_data_path + "pDynamicSimulation/" + + rawdata = AcquisitionData(input_data_path + '/mr_static_simulation.h5') + + recon = recon_cartesian_motion_avg(rawdata) + + output_data_path = prefix_data_path + "pDynamicSimulation/" + save_to_nii(recon, output_data_path + "recon_mr_static_python_simulation.nii") + + test_failed = False + return test_failed, 1 + +def test_recon_output_python_simulate_motion(record=False, verb=False, throw=True): + + print("Running a reconstruction of simulated motoin MR data") + + prefix_data_path = "/media/sf_CCPPETMR/TestData/Output/xDynamicSimulation/" + input_data_path = prefix_data_path + "pDynamicSimulation/" + + rawdata = AcquisitionData(input_data_path + '/mr_motion_simulation.h5') + + recon = recon_cartesian_motion_avg(rawdata) + output_data_path = prefix_data_path + "pDynamicSimulation/" + save_to_nii(recon, output_data_path + "recon_mr_motion_python_simulation.nii") + + test_failed = False + return test_failed, 1 + + + +def test_main(record=False, verb=False, throw=True): + + # return False, 0 + + all_tests_failed = False + number_executed_tests = 0 + + # test_failure, num_tests = test_recon_output_simulate_statics(record, verb, throw) + + # all_tests_failed = all_tests_failed and test_failure + # number_executed_tests += num_tests + + # test_failure, num_tests = test_recon_output_simulate_dynamics(record, verb, throw) + # all_tests_failed = all_tests_failed and test_failure + # # number_executed_tests += num_tests + + # test_failure, num_tests = test_recon_output_simulate_5d_dynamics(record, verb, throw) + # all_tests_failed = all_tests_failed and test_failure + # number_executed_tests += num_tests + + # test_failure, num_tests = test_recon_output_python_simulate_statics(record, verb, throw) + # all_tests_failed = all_tests_failed and test_failure + # number_executed_tests += num_tests + + test_failure, num_tests = test_recon_output_python_simulate_motion(record, verb, throw) + all_tests_failed = all_tests_failed and test_failure + number_executed_tests += num_tests + + return all_tests_failed, number_executed_tests + + +if __name__ == "__main__": + runner(test_main, __doc__, __version__, __author__) diff --git a/src/xGadgetron/cGadgetron/cgadgetron.cpp b/src/xGadgetron/cGadgetron/cgadgetron.cpp index 3e1e710e5..781acaebd 100644 --- a/src/xGadgetron/cGadgetron/cgadgetron.cpp +++ b/src/xGadgetron/cGadgetron/cgadgetron.cpp @@ -207,6 +207,68 @@ cGT_parameter(void* ptr, const char* obj, const char* name) CATCH; } + +extern "C" +void* +cGT_setAcquisitionParameter(void* ptr, const char* param_name, const void* val) +{ + CAST_PTR(DataHandle, h_acq, ptr); + ISMRMRD::Acquisition& acq = + objectFromHandle(h_acq); + + if (sirf::iequals(param_name, "measurement_uid")) + acq.measurement_uid() = dataFromHandle(val); + else if (sirf::iequals(param_name, "scan_counter")) + acq.scan_counter() = dataFromHandle(val); + else if (sirf::iequals(param_name, "acquisition_time_stamp")) + acq.acquisition_time_stamp() = dataFromHandle(val); + else if (sirf::iequals(param_name, "available_channels")) + acq.available_channels() = dataFromHandle(val); + else if (sirf::iequals(param_name, "discard_pre")) + acq.discard_pre() = dataFromHandle(val); + else if (sirf::iequals(param_name, "discard_post")) + acq.discard_post() = dataFromHandle(val); + else if (sirf::iequals(param_name, "center_sample")) + acq.center_sample() = dataFromHandle(val); + else if (sirf::iequals(param_name, "encoding_space_ref")) + acq.encoding_space_ref() = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_kspace_encode_step_1")) + acq.idx().kspace_encode_step_1 = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_kspace_encode_step_2")) + acq.idx().kspace_encode_step_2 = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_average")) + acq.idx().average = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_slice")) + acq.idx().slice = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_contrast")) + acq.idx().contrast = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_phase")) + acq.idx().phase = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_repetition")) + acq.idx().repetition = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_set")) + acq.idx().set = dataFromHandle(val); + else if (sirf::iequals(param_name, "idx_segment")) + acq.idx().segment = dataFromHandle(val); + else if (sirf::iequals(param_name, "sample_time_us")) + acq.sample_time_us() = dataFromHandle(val); + + // else if (sirf::iequals(param_name, "position")) + // acq.position() = dataFromHandle(val); + // else if (sirf::iequals(param_name, "read_dir")) + // acq.read_dir() = dataFromHandle(val); + // else if (sirf::iequals(param_name, "phase_dir")) + // acq.phase_dir() = dataFromHandle(val); + // else if (sirf::iequals(param_name, "slice_dir")) + // acq.slice_dir() = dataFromHandle(val); + // else if (sirf::iequals(param_name, "patient_table_position")) + // acq.patient_table_position() = dataFromHandle(val); + else + return unknownObject("parameter", param_name, __FILE__, __LINE__); + + return new DataHandle; +} + extern "C" void* cGT_setParameter(void* ptr, const char* obj, const char* par, const void* val) @@ -214,6 +276,8 @@ cGT_setParameter(void* ptr, const char* obj, const char* par, const void* val) try { if (sirf::iequals(obj, "coil_sensitivity")) return cGT_setCSParameter(ptr, par, val); + if (sirf::iequals(obj, "acquisition")) + return cGT_setAcquisitionParameter(ptr,par,val); return unknownObject("object", obj, __FILE__, __LINE__); } CATCH; @@ -253,6 +317,7 @@ cGT_setCSParameter(void* ptr, const char* par, const void* val) return new DataHandle; } + extern "C" void* cGT_computeCoilSensitivities(void* ptr_csms, void* ptr_acqs) @@ -565,6 +630,22 @@ cGT_getAcquisitionsSubset(void* ptr_acqs, size_t const ptr_idx, size_t const num CATCH; } +extern "C" +void* +cGT_discardAcquisitionData(void* ptr_acqs) +{ + try { + CAST_PTR(DataHandle, h_acqs, ptr_acqs); + + MRAcquisitionData& ad = + objectFromHandle(h_acqs); + + ad.discard_data(); + return new DataHandle; + } + CATCH; +} + extern "C" void* cGT_cloneAcquisitions(void* ptr_input) diff --git a/src/xGadgetron/cGadgetron/gadgetron_data_containers.cpp b/src/xGadgetron/cGadgetron/gadgetron_data_containers.cpp index 292834d3b..9d1f0ad4e 100644 --- a/src/xGadgetron/cGadgetron/gadgetron_data_containers.cpp +++ b/src/xGadgetron/cGadgetron/gadgetron_data_containers.cpp @@ -1941,8 +1941,8 @@ CFImage CoilSensitivitiesVector::get_csm_as_cfimage(const KSpaceSubset::TagType void CoilSensitivitiesVector::forward(GadgetronImageData& img, GadgetronImageData& combined_img)const { - if(combined_img.items() != this->items() ) - throw LocalisedException("The number of coilmaps does not equal the number of images to which they should be applied to.", __FILE__, __LINE__); + if(combined_img.items() > this->items() ) + throw LocalisedException("The number of coilmaps is less than the number of images to which they should be applied to.", __FILE__, __LINE__); if(!combined_img.check_dimension_consistency()) throw LocalisedException("The image dimensions in the source image container are not consistent.", __FILE__, __LINE__); @@ -1991,8 +1991,8 @@ void CoilSensitivitiesVector::coilchannels_from_combined_image(GadgetronImageDat void CoilSensitivitiesVector::backward(GadgetronImageData& combined_img, const GadgetronImageData& img)const { - if(img.items() != this->items() ) - throw LocalisedException("The number of coilmaps does not equal the number of images to be combined.", __FILE__, __LINE__); + if(img.items() > this->items() ) + throw LocalisedException("The number of coilmaps is less than the number of images to be combined.", __FILE__, __LINE__); // check for matching dimensions if(!img.check_dimension_consistency()) diff --git a/src/xGadgetron/cGadgetron/gadgetron_x.cpp b/src/xGadgetron/cGadgetron/gadgetron_x.cpp index f4439db56..0826dc4b1 100644 --- a/src/xGadgetron/cGadgetron/gadgetron_x.cpp +++ b/src/xGadgetron/cGadgetron/gadgetron_x.cpp @@ -426,8 +426,8 @@ MRAcquisitionModel::fwd(GadgetronImageData& ic, CoilSensitivitiesVector& cc, auto tag_img = KSpaceSubset::get_tag_from_img(*ptr_img); - sirf::AcquisitionsVector subset; - KSpaceSubset::SetType idx_set; + sirf::AcquisitionsVector subset(ac.acquisitions_info()); + KSpaceSubset::SetType idx_set; for(int j=0; jsptr_enc_->forward(subset, *ptr_img); ac.set_subset(subset, idx_set); //assume forward does not reorder the acquisitions } diff --git a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/cgadgetron.h b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/cgadgetron.h index 8546843fe..e4528243c 100644 --- a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/cgadgetron.h +++ b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/cgadgetron.h @@ -58,6 +58,8 @@ extern "C" { void* cGT_setAcquisitionModelParameter (void* ptr_am, const char* name, const void* ptr); void* cGT_AcquisitionModelParameter(void* ptr_am, const char* name); + void* cGT_setCSParameter(void* ptr, const char* par, const void* val); + void* cGT_setAcquisitionParameter(void* ptr, const char* param_name, const void* val); void* cGT_setCSMs(void* ptr_am, const void* ptr_csms); void* cGT_acquisitionModelNorm(void* ptr_am, int num_iter, int verb); void* cGT_AcquisitionModelForward(void* ptr_am, const void* ptr_imgs); @@ -70,6 +72,7 @@ extern "C" { void* cGT_acquisitionFromContainer(void* ptr_acqs, unsigned int acq_num); void* cGT_appendAcquisition(void* ptr_acqs, void* ptr_acq); void* cGT_createEmptyAcquisitionData(void* ptr_ad); + void* cGT_discardAcquisitionData(void* ptr_acqs); void* cGT_getAcquisitionsSubset(void* ptr_acqs, PTR_INT const ptr_idx, PTR_INT const num_elem_subset); void* cGT_cloneAcquisitions(void* ptr_input); diff --git a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_data_containers.h b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_data_containers.h index 5890333ba..3b55a1b45 100644 --- a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_data_containers.h +++ b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_data_containers.h @@ -64,7 +64,9 @@ Some acquisitions do not participate directly in the reconstruction process !(acq).isFlagSet(ISMRMRD::ISMRMRD_ACQ_IS_PARALLEL_CALIBRATION_AND_IMAGING) && \ !(acq).isFlagSet(ISMRMRD::ISMRMRD_ACQ_LAST_IN_MEASUREMENT) && \ !(acq).isFlagSet(ISMRMRD::ISMRMRD_ACQ_IS_REVERSE) && \ + !(acq).isFlagSet(ISMRMRD::ISMRMRD_ACQ_IS_SURFACECOILCORRECTIONSCAN_DATA) && \ (acq).flags() >= (1 << (ISMRMRD::ISMRMRD_ACQ_IS_NOISE_MEASUREMENT - 1))) + /*! \ingroup MR @@ -396,6 +398,22 @@ namespace sirf { virtual void set_data(const complex_float_t* z, int all = 1) = 0; virtual void get_data(complex_float_t* z, int all = 1); + + virtual void discard_data() + { + std::cout << "Warning: we are discarding all data keeping only the header." << std::endl; + ISMRMRD::Acquisition acq; + for(int i=0; inumber(); ++i) + { + this->get_acquisition(i, acq); + int num_traj = acq.getHead().number_of_samples * acq.getHead().trajectory_dimensions; + std::vector< float > tmp_traj(num_traj); + memcpy(&tmp_traj[0], acq.getTrajPtr(), num_traj * sizeof(acq.getTrajPtr())); + acq.resize(1,1,num_traj); + memcpy(acq.getTrajPtr(), &tmp_traj[0], num_traj * sizeof(acq.getTrajPtr())); + this->set_acquisition(i, acq); + } + } virtual void set_user_floats(float const * const z, int const idx); diff --git a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_x.h b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_x.h index 5d65dac13..5226a8e0b 100644 --- a/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_x.h +++ b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/gadgetron_x.h @@ -506,7 +506,6 @@ namespace sirf { gadgetron::shared_ptr sptr_csms_; gadgetron::shared_ptr sptr_enc_; }; - } #endif diff --git a/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.h b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/mrtest_auxiliary_funs.h similarity index 100% rename from src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.h rename to src/xGadgetron/cGadgetron/include/sirf/Gadgetron/mrtest_auxiliary_funs.h index 6c8e1d326..cd3a1d954 100644 --- a/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.h +++ b/src/xGadgetron/cGadgetron/include/sirf/Gadgetron/mrtest_auxiliary_funs.h @@ -36,8 +36,8 @@ limitations under the License. namespace sirf{ - void preprocess_acquisition_data(MRAcquisitionData& ad); + void set_unit_dcf(MRAcquisitionData& ad); void set_acq_default_orientation(std::string path_in, std::string path_out); diff --git a/src/xGadgetron/cGadgetron/tests/CMakeLists.txt b/src/xGadgetron/cGadgetron/tests/CMakeLists.txt index 50f14a0e3..829661fd0 100644 --- a/src/xGadgetron/cGadgetron/tests/CMakeLists.txt +++ b/src/xGadgetron/cGadgetron/tests/CMakeLists.txt @@ -168,6 +168,10 @@ add_executable(MR_TESTS_CPLUSPLUS ${CMAKE_CURRENT_SOURCE_DIR}/mrtests.cpp) add_dependencies(MR_TESTS_CPLUSPLUS GENERATE_SIMULATED_TESTDATA) target_link_libraries(MR_TESTS_CPLUSPLUS PUBLIC MR_TESTS_CPP_AUXILIARY csirf cgadgetron) + +INSTALL(TARGETS MR_TESTS_CPP_AUXILIARY + PUBLIC_HEADER DESTINATION "$$") + INSTALL(TARGETS MR_TESTS_CPLUSPLUS DESTINATION bin) diff --git a/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.cpp b/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.cpp index 4ecdb5310..734489ef8 100644 --- a/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.cpp +++ b/src/xGadgetron/cGadgetron/tests/mrtest_auxiliary_funs.cpp @@ -27,7 +27,7 @@ limitations under the License. \author CCP PETMR */ -#include "mrtest_auxiliary_funs.h" +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" #include @@ -111,7 +111,7 @@ void sirf::write_imagevector_to_raw(const std::string& fname_prefix, const sirf: for(int i=0; i sptr_ad(new AcquisitionsVector); AcquisitionsVector& av = (AcquisitionsVector&)*sptr_ad; av.read(path_in); + + ISMRMRD::MatrixSize matsize = av.acquisitions_info().get_IsmrmrdHeader().encoding[0].reconSpace.matrixSize; + ISMRMRD::FieldOfView_mm fov_mm = av.acquisitions_info().get_IsmrmrdHeader().encoding[0].reconSpace.fieldOfView_mm; + int na = av.number(); int acq_dim[10]; av.get_acquisitions_dimensions((size_t)acq_dim); @@ -130,19 +134,26 @@ void sirf::set_acq_default_orientation(std::string path_in, std::string path_out ISMRMRD::Acquisition acq; for (int i = 0; i < na; i++) { av.get_acquisition(i, acq); - float* read_dir = acq.read_dir(); - float* phase_dir = acq.phase_dir(); - float* slice_dir = acq.slice_dir(); - - read_dir[0] = 1.0f; - read_dir[1] = 0.0f; - read_dir[2] = 0.0f; - phase_dir[0] = 0.0f; - phase_dir[1] = 1.0f; - phase_dir[2] = 0.0f; - slice_dir[0] = 0.0f; - slice_dir[1] = 0.0f; - slice_dir[2] = 1.0f; + + acq.read_dir()[0] = 1.0f; + acq.read_dir()[1] = 0.0f; + acq.read_dir()[2] = 0.0f; + + acq.phase_dir()[0] = 0.0f; + acq.phase_dir()[1] = 1.0f; + acq.phase_dir()[2] = 0.0f; + + acq.slice_dir()[0] = 0.0f; + acq.slice_dir()[1] = 0.0f; + acq.slice_dir()[2] = 1.0f; + + // acq.position()[0] = + fov_mm.x/2 - 0.5*fov_mm.x/matsize.x; + // acq.position()[1] = + fov_mm.y/2 - 0.5*fov_mm.y/matsize.y; + // acq.position()[2] = + fov_mm.z/2 - 0.5*fov_mm.z/matsize.z; + + acq.position()[0] = 0.f; + acq.position()[1] = 0.f; + acq.position()[2] = 0.f; av.set_acquisition(i, acq); } diff --git a/src/xGadgetron/cGadgetron/tests/mrtests.cpp b/src/xGadgetron/cGadgetron/tests/mrtests.cpp index 8b2cd2c8e..95034c0df 100644 --- a/src/xGadgetron/cGadgetron/tests/mrtests.cpp +++ b/src/xGadgetron/cGadgetron/tests/mrtests.cpp @@ -46,7 +46,8 @@ limitations under the License. #include "sirf/Gadgetron/FourierEncoding.h" #include "sirf/Gadgetron/TrajectoryPreparation.h" -#include "mrtest_auxiliary_funs.h" +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" + using namespace gadgetron; using namespace sirf; @@ -221,8 +222,11 @@ bool test_CoilSensitivitiesVector_calculate( MRAcquisitionData& av) std::cout << "We have " << csv.items() << " coilmaps" << std::endl; if(mr_cpp_tests_writefiles) - sirf::write_imagevector_to_raw(__FUNCTION__, csv); - + { + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), csv); + } return true; } @@ -396,9 +400,13 @@ bool test_bwd(MRAcquisitionData& av) acquis_model.bwd(img_vec, csm, av); + if(mr_cpp_tests_writefiles) - sirf::write_imagevector_to_raw(__FUNCTION__, img_vec); - + { + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), img_vec); + } return true; } @@ -474,8 +482,13 @@ bool test_rpe_csm(MRAcquisitionData& av) csm.set_csm_smoothness(50); csm.calculate(av); - if(mr_cpp_tests_writefiles) - sirf::write_imagevector_to_raw(__FUNCTION__, csm); + + if(mr_cpp_tests_writefiles) + { + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), csm); + } return true; } @@ -524,10 +537,12 @@ bool test_rpe_bwd(MRAcquisitionData& av) img_vec.append(iw); } - if(mr_cpp_tests_writefiles) - sirf::write_imagevector_to_raw(__FUNCTION__, img_vec); - + { + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), img_vec); + } return true; @@ -584,8 +599,10 @@ bool test_rpe_fwd(MRAcquisitionData& av) if(mr_cpp_tests_writefiles) { - sirf::write_imagevector_to_raw(__FUNCTION__, img_vec); - + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), img_vec); + std::stringstream fname_output_raw; fname_output_raw << "output_" << __FUNCTION__ << "_rawdata.h5"; av.write(fname_output_raw.str()); @@ -624,8 +641,11 @@ bool test_mracquisition_model_rpe_bwd(MRAcquisitionData& av) acquis_model.bwd(img_vec, csm, av); if(mr_cpp_tests_writefiles) - sirf::write_imagevector_to_raw(__FUNCTION__, img_vec); - + { + std::stringstream fname_output; + fname_output << "output_" << __FUNCTION__; + sirf::write_imagevector_to_raw(fname_output.str(), img_vec); + } return true; } diff --git a/src/xGadgetron/cGadgetron/tests/mrtests_prep_testdata.cpp b/src/xGadgetron/cGadgetron/tests/mrtests_prep_testdata.cpp index 536bbf6ae..a7c375647 100644 --- a/src/xGadgetron/cGadgetron/tests/mrtests_prep_testdata.cpp +++ b/src/xGadgetron/cGadgetron/tests/mrtests_prep_testdata.cpp @@ -27,7 +27,7 @@ limitations under the License. */ #include -#include "mrtest_auxiliary_funs.h" +#include "sirf/Gadgetron/mrtest_auxiliary_funs.h" int main ( int argc, char* argv[]) { diff --git a/src/xGadgetron/pGadgetron/Gadgetron.py b/src/xGadgetron/pGadgetron/Gadgetron.py index 8e28f94b2..61f85c8b9 100644 --- a/src/xGadgetron/pGadgetron/Gadgetron.py +++ b/src/xGadgetron/pGadgetron/Gadgetron.py @@ -607,38 +607,6 @@ def calculate(self, data, method=None): raise error('Cannot calculate coil sensitivities from %s' % \ repr(type(data))) - def __calc_from_acquisitions(self, data, method_name): - - if data.handle is None: - raise AssertionError("The handle for data is None. Please pass valid acquisition data.") - - dcw = compute_kspace_density(data) - - data = data * dcw - if method_name == 'Inati': - try: - from ismrmrdtools import coils - except: - raise error('Inati method requires ismrmrd-python-tools') - - cis = CoilImagesData() - try_calling(pygadgetron.cGT_computeCoilImages(cis.handle, data.handle)) - cis_array = cis.as_array() - csm, _ = coils.calculate_csm_inati_iter(cis_array) - - if self.handle is not None: - pyiutil.deleteDataHandle(self.handle) - self.handle = pysirf.cSIRF_clone(cis.handle) - nc, nz, ny, nx = self.dimensions() - ns = self.number() # number of total dynamics (slices, contrasts, etc.) - nz = nz//ns # z-dimension of a slice - csm = numpy.reshape(csm, (nc, ns, nz, ny, nx)) - csm = numpy.swapaxes(csm, 0, 1) - self.fill(csm.astype(numpy.complex64)) - - elif method_name == 'SRSS': - try_calling(pygadgetron.cGT_computeCoilSensitivities(self.handle, data.handle)) - def __calc_from_images(self, data, method_name): if data.handle is None: @@ -797,6 +765,35 @@ def patient_table_position(self): def info(self, method): return eval('self.' + method + '()') + + def set_kspace_encode_step_1(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_kspace_encode_step_1', int(val)) + def set_kspace_encode_step_2(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_kspace_encode_step_2', int(val)) + def set_average(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_average', int(val)) + def set_slice(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_slice', int(val)) + def set_contrast(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_contrast', int(val)) + def set_phase(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_phase', int(val)) + def set_repetition(self, val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_repetition', val) + def set_set(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_set', int(val)) + def set_segment(self,val): + assert self.handle is not None + return parms.set_int_par(self.handle, 'acquisition', 'idx_segment', int(val)) + class AcquisitionData(DataContainer): ''' Class for an MR acquisitions container. @@ -931,6 +928,15 @@ def get_subset(self, idx): return subset + def discard_data(self): + ''' + Resizes every acquisition to size 1 to avoid keeping data in memory for simulation. + ''' + assert self.handle is not None + pygadgetron.cGT_discardAcquisitionData(self.handle) + + return + def set_user_floats(self, data, idx): ''' Writes the data into the user_float[idx] data field of the acquisition diff --git a/src/xSTIR/cSTIR/stir_data_containers.cpp b/src/xSTIR/cSTIR/stir_data_containers.cpp index b930cf54c..3f7528e2c 100644 --- a/src/xSTIR/cSTIR/stir_data_containers.cpp +++ b/src/xSTIR/cSTIR/stir_data_containers.cpp @@ -248,6 +248,8 @@ PETAcquisitionData::binary_op_( STIRImageData::STIRImageData(const ImageData& id) { throw std::runtime_error("TODO - create STIRImageData from general SIRFImageData."); + + /* The following is incorrect. Dimensions dim = id.dimensions(); int nx = dim["x"]; diff --git a/src/xSTIR/cSTIR/tests/test3.cpp b/src/xSTIR/cSTIR/tests/test3.cpp index 3ef6bbf6e..e108c830c 100644 --- a/src/xSTIR/cSTIR/tests/test3.cpp +++ b/src/xSTIR/cSTIR/tests/test3.cpp @@ -36,10 +36,10 @@ limitations under the License. #include "cstir.h" #include "object.h" -//#include "stir_types.h" -//#include "stir_data_containers.h" -#include "stir_x.h" -//#include "SIRF/common/envar.h" +//#include "sirf/cSTIR/stir_types.h" +//#include "sirf/cSTIR/stir_data_containers.h" +#include "sirf/cSTIR/stir_x.h" +//#include "sirf/common/envar.h" using namespace stir; using namespace ecat;