This is an automated email from the git hooks/post-receive script. ghisvail-guest pushed a commit to branch master in repository ismrmrd.
commit 1e83341414e47a8c96db86a97f53a0f9da1a9ec7 Author: Ghislain Antony Vaillant <[email protected]> Date: Fri Sep 18 09:34:54 2015 +0100 Imported Upstream version 1.3.1 --- .travis.yml | 17 +++ CMakeLists.txt | 86 ++++++------ README.md | 9 +- cmake/FindJava.cmake | 212 ------------------------------ cmake/FindNumPy.cmake | 101 -------------- cmake/cpack_options.cmake.in | 12 +- cmake/ismrmrd_cpack.cmake | 40 +++--- examples/c/CMakeLists.txt | 26 +++- examples/c/README.md | 13 ++ examples/c/README.txt | 11 -- examples/c/main.c | 20 ++- examples/data/.gitignore | 1 - examples/data/README.txt | 17 --- examples/matlab/todo.txt | 1 - examples/python/ismrmrd_create_dataset.py | 174 ------------------------ examples/python/ismrmrd_recon_dataset.py | 132 ------------------- include/ismrmrd/ismrmrd.h | 4 +- include/ismrmrd/xml.h | 10 +- include/version.in | 1 + libsrc/dataset.c | 41 +++--- libsrc/dataset.cpp | 20 +++ libsrc/ismrmrd.c | 9 +- libsrc/ismrmrd.cpp | 29 +++- libsrc/xml.cpp | 68 +++++++--- matlab/+ismrmrd/+xml/deserialize.m | 5 +- matlab/+ismrmrd/+xml/serialize.m | 2 + schema/ismrmrd.xsd | 6 +- tests/CMakeLists.txt | 21 +++ tests/test_acquisitions.cpp | 172 ++++++++++++++++++++++++ tests/test_channels.cpp | 76 +++++++++++ tests/test_flags.cpp | 65 +++++++++ tests/test_images.cpp | 169 ++++++++++++++++++++++++ tests/test_ismrmrd.h | 4 + tests/test_main.cpp | 26 ++++ tests/test_ndarray.cpp | 71 ++++++++++ tests/test_quaternions.cpp | 60 +++++++++ utilities/CMakeLists.txt | 106 +++++++-------- utilities/ismrmrd_info.cpp | 6 +- utilities/recon_cartesian_2d.cpp | 95 +++++++------ 39 files changed, 1056 insertions(+), 882 deletions(-) diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c5219cc --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: cpp + +compiler: + - gcc + - clang + +before_install: + - sudo apt-get update +install: + - sudo apt-get install libboost-all-dev libhdf5-serial-dev h5utils doxygen + +before_script: + - mkdir build + - cd build + - cmake .. + +script: make && make check diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c27f2..0616fe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,10 +50,10 @@ endif (WIN32) #the flexible header. The micro number changes when there are small changes #in the utility libraries, that don't affect the data format itself. set(ISMRMRD_VERSION_MAJOR 1) -set(ISMRMRD_VERSION_MINOR 2) -set(ISMRMRD_VERSION_PATCH 3) +set(ISMRMRD_VERSION_MINOR 3) +set(ISMRMRD_VERSION_PATCH 1) -set(ISMRMRD_XML_SCHEMA_SHA1 "99a63f4e8cf08ffc268f5ee8b8c8b2a1edf69412") +set(ISMRMRD_XML_SCHEMA_SHA1 "9b899c6ad806bc2388c70461d6e5affe5cc6d750") #Remove line breaks and white space that does not change the meaning of the schema file(STRINGS ${CMAKE_SOURCE_DIR}/schema/ismrmrd.xsd SCHEMA_STRINGS) #Read all strings from file @@ -91,6 +91,19 @@ endif() set(ISMRMRD_VERSION_STRING ${ISMRMRD_VERSION_MAJOR}.${ISMRMRD_VERSION_MINOR}.${ISMRMRD_VERSION_PATCH}) set(ISMRMRD_SOVERSION ${ISMRMRD_VERSION_MAJOR}.${ISMRMRD_VERSION_MINOR}) +# Find HDF5 for dataset support +find_package(HDF5 1.8 COMPONENTS C) + +if (HDF5_FOUND) + set (ISMRMRD_DATASET_SUPPORT true) + set (ISMRMRD_DATASET_SOURCES libsrc/dataset.c libsrc/dataset.cpp) + set (ISMRMRD_DATASET_INCLUDE_DIR ${HDF5_C_INCLUDE_DIR}) + set (ISMRMRD_DATASET_LIBRARIES ${HDF5_LIBRARIES}) +else (HDF5_FOUND) + set (ISMRMRD_DATASET_SUPPORT false) + message (WARNING "HDF5 not found. Dataset and file support unavailable!") +endif (HDF5_FOUND) + # Generate the version.h header file find_package(Git) if (GIT_FOUND) @@ -111,43 +124,35 @@ install(FILES ${CMAKE_BINARY_DIR}/include/ismrmrd/version.h DESTINATION include/ # remember to add ${CMAKE_BINARY_DIR}/include to the include path # --- VERSIONING (end) ---- - -# --- Main Library (begin) ---- -# required packages for main library -find_package(HDF5 1.8 COMPONENTS C REQUIRED) +# --- Main Library (begin) ---- # in windows, install the HDF5 dependencies -if (WIN32) - if (ISMRMRD_INSTALL_DEPENDENCIES) - if ( HDF5_FOUND ) - if(DEFINED ENV{HDF5_ROOT}) - set(HDF5_BIN_DIR $ENV{HDF5_ROOT}/bin) - else (DEFINED ENV{HDF5_ROOT}) - set(HDF5_BIN_DIR ${HDF5_C_INCLUDE_DIR}/../bin) - endif (DEFINED ENV{HDF5_ROOT}) - message("Install hdf5 libraries from ${HDF5_BIN_DIR} ") - install( DIRECTORY ${HDF5_BIN_DIR} DESTINATION bin/.. FILES_MATCHING PATTERN "*.dll" ) - endif (HDF5_FOUND) - endif (ISMRMRD_INSTALL_DEPENDENCIES) -endif (WIN32) +if (HDF5_FOUND AND WIN32 AND ISMRMRD_INSTALL_DEPENDENCIES) + if(DEFINED ENV{HDF5_ROOT}) + set(HDF5_BIN_DIR $ENV{HDF5_ROOT}/bin) + else (DEFINED ENV{HDF5_ROOT}) + set(HDF5_BIN_DIR ${HDF5_C_INCLUDE_DIR}/../bin) + endif (DEFINED ENV{HDF5_ROOT}) + message("Install hdf5 libraries from ${HDF5_BIN_DIR} ") + install( DIRECTORY ${HDF5_BIN_DIR} DESTINATION bin/.. FILES_MATCHING PATTERN "*.dll" ) +endif (HDF5_FOUND AND WIN32 AND ISMRMRD_INSTALL_DEPENDENCIES) # include directories for main library set(ISMRMRD_TARGET_INCLUDE_DIRS include ${CMAKE_BINARY_DIR}/include - ${HDF5_C_INCLUDE_DIR} + ${ISMRMRD_DATASET_INCLUDE_DIR} ) set(ISMRMRD_TARGET_SOURCES libsrc/ismrmrd.c libsrc/ismrmrd.cpp - libsrc/dataset.c - libsrc/dataset.cpp libsrc/xml.cpp libsrc/meta.cpp + ${ISMRMRD_DATASET_SOURCES} ) -set(ISMRMRD_TARGET_LINK_LIBS ${HDF5_LIBRARIES}) +set(ISMRMRD_TARGET_LINK_LIBS ${ISMRMRD_DATASET_LIBRARIES}) # optional handling of system-installed pugixml if(USE_SYSTEM_PUGIXML) @@ -183,40 +188,35 @@ install(TARGETS ismrmrd DESTINATION lib) install(DIRECTORY include/ismrmrd DESTINATION include) # install the schema file -install(FILES schema/ismrmrd.xsd - DESTINATION share/ismrmrd/schema) +install(FILES schema/ismrmrd.xsd DESTINATION share/ismrmrd/schema) # install the cmake modules -install(FILES - cmake/FindIsmrmrd.cmake - cmake/FindFFTW3.cmake - cmake/FindNumPy.cmake -DESTINATION - share/ismrmrd/cmake -) +install(FILES cmake/FindIsmrmrd.cmake cmake/FindFFTW3.cmake DESTINATION share/ismrmrd/cmake) # --- Main Library (end) ---- -# turn on testing -enable_testing() - # process subdirectories add_subdirectory(doc) -add_subdirectory(examples/c) + add_subdirectory(utilities) +if (HDF5_FOUND) + add_subdirectory(examples/c) +endif (HDF5_FOUND) + +# TODO: make this work on Windows +if (NOT WIN32) + add_subdirectory(tests) +endif (NOT WIN32) # install the matlab api install(DIRECTORY matlab DESTINATION share/ismrmrd) -# TODO build and install the python bindings - # Create package string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER) include(${ISMRMRD_CMAKE_DIR}/ismrmrd_cpack.cmake) if(CPACK_GENERATOR) message(STATUS "Found CPack generators: ${CPACK_GENERATOR}") - configure_file("${ISMRMRD_CMAKE_DIR}/cpack_options.cmake.in" - ${ISMRMRD_CPACK_CFG_FILE} @ONLY) + configure_file("${ISMRMRD_CMAKE_DIR}/cpack_options.cmake.in" ${ISMRMRD_CPACK_CFG_FILE} @ONLY) set(CPACK_PROJECT_CONFIG_FILE ${ISMRMRD_CPACK_CFG_FILE}) - include (CPack) -endif() + include(CPack) +endif(CPACK_GENERATOR) diff --git a/README.md b/README.md index 1f2be2d..2e1a516 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[](https://travis-ci.org/ismrmrd/ismrmrd) + ISMRM Raw Data Format (ISMRMRD) =============================== @@ -12,7 +14,7 @@ To download the source code, clone the git archive: API Documentation can be found at https://ismrmrd.github.io/api/. -You will need CMake, HDF5, Boost and optionally Doxygen and FFTW to build the C/C++ code. Please see the ISMRMRD documentation for specific installation instructions for [Linux](https://ismrmrd.github.io/index.html#linux-installation), [Mac OS X](https://ismrmrd.github.io/index.html#mac-osx-installation), and [Windows](https://ismrmrd.github.io/index.html#windows-installation). +You will need CMake, HDF5, and optionally Boost and FFTW to build the C/C++ code. To generate the API documentation you will need Doxygen. Please see the ISMRMRD documentation for specific installation instructions for [Linux](https://ismrmrd.github.io/index.html#linux-installation), [Mac OS X](https://ismrmrd.github.io/index.html#mac-osx-installation), and [Windows](https://ismrmrd.github.io/index.html#windows-installation). Overview --------- @@ -26,3 +28,8 @@ A raw data set consist mainly of 2 sections: 1. Raw data section. This section contains all the acquired data in the experiment. Each data item is preceded by a C-struct with encoding numbers, etc. Following this data header is a channel header and data for each acquired channel. The raw data headers are defined in a C/C++ header file (ismrmrd.h). Please see the [C header](https://github.com/ismrmrd/ismrmrd/blob/master/include/ismrmrd/ismrmrd.h) and the [documentation](https://ismrmrd.github.io/index.html#fixed-data-structures) f [...] In addition to these sections, the ISMRMRD format also specifies an image header for storing reconstructed images and the accompanying C/C++ library provides a convenient way of writing such images into HDF5 files along with generic arrays for storing less well defined data structures, e.g. coil sensitivity maps or other calibration data. + +Other Resources +--------------- + +- [Python implementation](https://www.github.com/ismrmrd/ismrmrd-python) diff --git a/cmake/FindJava.cmake b/cmake/FindJava.cmake deleted file mode 100644 index 4d6f888..0000000 --- a/cmake/FindJava.cmake +++ /dev/null @@ -1,212 +0,0 @@ -# - Find Java -# This module finds if Java is installed and determines where the -# include files and libraries are. This code sets the following -# variables: -# -# Java_JAVA_EXECUTABLE = the full path to the Java runtime -# Java_JAVAC_EXECUTABLE = the full path to the Java compiler -# Java_JAVAH_EXECUTABLE = the full path to the Java header generator -# Java_JAVADOC_EXECUTABLE = the full path to the Java documention generator -# Java_XJC_EXECUTABLE = the full path to the Java JAXB generator -# Java_JAR_EXECUTABLE = the full path to the Java archiver -# Java_VERSION_STRING = Version of the package found (java version), eg. 1.6.0_12 -# Java_VERSION_MAJOR = The major version of the package found. -# Java_VERSION_MINOR = The minor version of the package found. -# Java_VERSION_PATCH = The patch version of the package found. -# Java_VERSION_TWEAK = The tweak version of the package found (after '_') -# Java_VERSION = This is set to: $major.$minor.$patch(.$tweak) -# -# The minimum required version of Java can be specified using the -# standard CMake syntax, e.g. find_package(Java 1.5) -# -# NOTE: ${Java_VERSION} and ${Java_VERSION_STRING} are not guaranteed to be -# identical. For example some java version may return: -# Java_VERSION_STRING = 1.5.0_17 -# and -# Java_VERSION = 1.5.0.17 -# -# another example is the Java OEM, with: -# Java_VERSION_STRING = 1.6.0-oem -# and -# Java_VERSION = 1.6.0 -# -# For these components the following variables are set: -# -# Java_FOUND - TRUE if all components are found. -# Java_INCLUDE_DIRS - Full paths to all include dirs. -# Java_LIBRARIES - Full paths to all libraries. -# Java_<component>_FOUND - TRUE if <component> is found. -# -# Example Usages: -# find_package(Java) -# find_package(Java COMPONENTS Runtime) -# find_package(Java COMPONENTS Development) -# - -#============================================================================= -# Copyright 2002-2009 Kitware, Inc. -# Copyright 2009-2011 Mathieu Malaterre <[email protected]> -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# The HINTS option should only be used for values computed from the system. -set(_JAVA_HINTS - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\2.0;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.9;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.8;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.7;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.6;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.5;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.4;JavaHome]/bin" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\1.3;JavaHome]/bin" - $ENV{JAVA_HOME}/bin - ) -# Hard-coded guesses should still go in PATHS. This ensures that the user -# environment can always override hard guesses. -set(_JAVA_PATHS - /usr/lib/java/bin - /usr/share/java/bin - /usr/local/java/bin - /usr/local/java/share/bin - /usr/java/j2sdk1.4.2_04 - /usr/lib/j2sdk1.4-sun/bin - /usr/java/j2sdk1.4.2_09/bin - /usr/lib/j2sdk1.5-sun/bin - /opt/sun-jdk-1.5.0.04/bin - ) -find_program(Java_JAVA_EXECUTABLE - NAMES java - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -if(Java_JAVA_EXECUTABLE) - execute_process(COMMAND ${Java_JAVA_EXECUTABLE} -version - RESULT_VARIABLE res - OUTPUT_VARIABLE var - ERROR_VARIABLE var # sun-java output to stderr - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE) - if( res ) - if(${Java_FIND_REQUIRED}) - message( FATAL_ERROR "Error executing java -version" ) - else() - message( STATUS "Warning, could not run java --version") - endif() - else() - # extract major/minor version and patch level from "java -version" output - # Tested on linux using - # 1. Sun / Sun OEM - # 2. OpenJDK 1.6 - # 3. GCJ 1.5 - # 4. Kaffe 1.4.2 - if(var MATCHES "java version \"[0-9]+\\.[0-9]+\\.[0-9_.]+.*\".*") - # This is most likely Sun / OpenJDK, or maybe GCJ-java compat layer - string( REGEX REPLACE ".* version \"([0-9]+\\.[0-9]+\\.[0-9_.]+.*)\".*" - "\\1" Java_VERSION_STRING "${var}" ) - elseif(var MATCHES "java full version \"kaffe-[0-9]+\\.[0-9]+\\.[0-9_]+\".*") - # Kaffe style - string( REGEX REPLACE "java full version \"kaffe-([0-9]+\\.[0-9]+\\.[0-9_]+).*" - "\\1" Java_VERSION_STRING "${var}" ) - else() - if(NOT Java_FIND_QUIETLY) - message(WARNING "regex not supported: ${var}. Please report") - endif() - endif() - string( REGEX REPLACE "([0-9]+).*" "\\1" Java_VERSION_MAJOR "${Java_VERSION_STRING}" ) - string( REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" Java_VERSION_MINOR "${Java_VERSION_STRING}" ) - string( REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" Java_VERSION_PATCH "${Java_VERSION_STRING}" ) - # warning tweak version can be empty: - string( REGEX REPLACE "[0-9]+\\.[0-9]+\\.[0-9]+[_\\.]?([0-9]*).*$" "\\1" Java_VERSION_TWEAK "${Java_VERSION_STRING}" ) - if( Java_VERSION_TWEAK STREQUAL "" ) # check case where tweak is not defined - set(Java_VERSION ${Java_VERSION_MAJOR}.${Java_VERSION_MINOR}.${Java_VERSION_PATCH}) - else() - set(Java_VERSION ${Java_VERSION_MAJOR}.${Java_VERSION_MINOR}.${Java_VERSION_PATCH}.${Java_VERSION_TWEAK}) - endif() - endif() - -endif() - - -find_program(Java_JAR_EXECUTABLE - NAMES jar - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -find_program(Java_JAVAC_EXECUTABLE - NAMES javac - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -find_program(Java_XJC_EXECUTABLE - NAMES xjc - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -find_program(Java_JAVAH_EXECUTABLE - NAMES javah - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -find_program(Java_JAVADOC_EXECUTABLE - NAMES javadoc - HINTS ${_JAVA_HINTS} - PATHS ${_JAVA_PATHS} -) - -include(FindPackageHandleStandardArgs) -if(Java_FIND_COMPONENTS) - foreach(component ${Java_FIND_COMPONENTS}) - # User just want to execute some Java byte-compiled - if(component STREQUAL "Runtime") - find_package_handle_standard_args(Java - REQUIRED_VARS Java_JAVA_EXECUTABLE - VERSION_VAR Java_VERSION - ) - elseif(component STREQUAL "Development") - find_package_handle_standard_args(Java - REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE Java_JAVAC_EXECUTABLE - Java_XJC_EXECUTABLE Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE - VERSION_VAR Java_VERSION - ) - else() - message(FATAL_ERROR "Comp: ${component} is not handled") - endif() - set(Java_${component}_FOUND TRUE) - endforeach() -else() - # Check for everything - find_package_handle_standard_args(Java - REQUIRED_VARS Java_JAVA_EXECUTABLE Java_JAR_EXECUTABLE Java_JAVAC_EXECUTABLE - Java_XJC_EXECUTABLE Java_JAVAH_EXECUTABLE Java_JAVADOC_EXECUTABLE - VERSION_VAR Java_VERSION - ) -endif() - - -mark_as_advanced( - Java_JAVA_EXECUTABLE - Java_JAR_EXECUTABLE - Java_JAVAC_EXECUTABLE - Java_XJC_EXECUTABLE - Java_JAVAH_EXECUTABLE - Java_JAVADOC_EXECUTABLE - ) - -# LEGACY -set(JAVA_RUNTIME ${Java_JAVA_EXECUTABLE}) -set(JAVA_ARCHIVE ${Java_JAR_EXECUTABLE}) -set(JAVA_COMPILE ${Java_JAVAC_EXECUTABLE}) - diff --git a/cmake/FindNumPy.cmake b/cmake/FindNumPy.cmake deleted file mode 100644 index 6feeb73..0000000 --- a/cmake/FindNumPy.cmake +++ /dev/null @@ -1,101 +0,0 @@ -# - Find the NumPy libraries -# This module finds if NumPy is installed, and sets the following variables -# indicating where it is. -# -# TODO: Update to provide the libraries and paths for linking npymath lib. -# -# NUMPY_FOUND - was NumPy found -# NUMPY_VERSION - the version of NumPy found as a string -# NUMPY_VERSION_MAJOR - the major version number of NumPy -# NUMPY_VERSION_MINOR - the minor version number of NumPy -# NUMPY_VERSION_PATCH - the patch version number of NumPy -# NUMPY_VERSION_DECIMAL - e.g. version 1.6.1 is 10601 -# NUMPY_INCLUDE_DIRS - path to the NumPy include files - -#============================================================================ -# Copyright 2012 Continuum Analytics, Inc. -# -# MIT License -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR -# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. -# -#============================================================================ - -# Finding NumPy involves calling the Python interpreter -if(NumPy_FIND_REQUIRED) - find_package(PythonInterp REQUIRED) -else() - find_package(PythonInterp) -endif() - -if(NOT PYTHONINTERP_FOUND) - set(NUMPY_FOUND FALSE) - return() -endif() - -execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" - "import numpy as n; print(n.__version__); print(n.get_include());" - RESULT_VARIABLE _NUMPY_SEARCH_SUCCESS - OUTPUT_VARIABLE _NUMPY_VALUES_OUTPUT - ERROR_VARIABLE _NUMPY_ERROR_VALUE - OUTPUT_STRIP_TRAILING_WHITESPACE) - -if(NOT _NUMPY_SEARCH_SUCCESS MATCHES 0) - if(NumPy_FIND_REQUIRED) - message(FATAL_ERROR - "NumPy import failure:\n${_NUMPY_ERROR_VALUE}") - endif() - set(NUMPY_FOUND FALSE) - return() -endif() - -# Convert the process output into a list -string(REGEX REPLACE ";" "\\\\;" _NUMPY_VALUES ${_NUMPY_VALUES_OUTPUT}) -string(REGEX REPLACE "\n" ";" _NUMPY_VALUES ${_NUMPY_VALUES}) -list(GET _NUMPY_VALUES 0 NUMPY_VERSION) -list(GET _NUMPY_VALUES 1 NUMPY_INCLUDE_DIRS) - -string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" _VER_CHECK "${NUMPY_VERSION}") -if("${_VER_CHECK}" STREQUAL "") - # The output from Python was unexpected. Raise an error always - # here, because we found NumPy, but it appears to be corrupted somehow. - message(FATAL_ERROR - "Requested version and include path from NumPy, got instead:\n${_NUMPY_VALUES_OUTPUT}\n") - return() -endif() - -# Make sure all directory separators are '/' -string(REGEX REPLACE "\\\\" "/" NUMPY_INCLUDE_DIRS ${NUMPY_INCLUDE_DIRS}) - -# Get the major and minor version numbers -string(REGEX REPLACE "\\." ";" _NUMPY_VERSION_LIST ${NUMPY_VERSION}) -list(GET _NUMPY_VERSION_LIST 0 NUMPY_VERSION_MAJOR) -list(GET _NUMPY_VERSION_LIST 1 NUMPY_VERSION_MINOR) -list(GET _NUMPY_VERSION_LIST 2 NUMPY_VERSION_PATCH) -string(REGEX MATCH "[0-9]*" NUMPY_VERSION_PATCH ${NUMPY_VERSION_PATCH}) -math(EXPR NUMPY_VERSION_DECIMAL - "(${NUMPY_VERSION_MAJOR} * 10000) + (${NUMPY_VERSION_MINOR} * 100) + ${NUMPY_VERSION_PATCH}") - -find_package_message(NUMPY - "Found NumPy: version \"${NUMPY_VERSION}\" ${NUMPY_INCLUDE_DIRS}" - "${NUMPY_INCLUDE_DIRS}${NUMPY_VERSION}") - -set(NUMPY_FOUND TRUE) - diff --git a/cmake/cpack_options.cmake.in b/cmake/cpack_options.cmake.in index cd82556..6a8f5ef 100644 --- a/cmake/cpack_options.cmake.in +++ b/cmake/cpack_options.cmake.in @@ -8,7 +8,7 @@ set(CPACK_PACKAGE_VERSION_MAJOR "@ISMRMRD_VERSION_MAJOR@") set(CPACK_PACKAGE_VERSION_MINOR "@ISMRMRD_VERSION_MINOR@") set(CPACK_PACKAGE_VERSION_PATCH "@ISMRMRD_VERSION_PATCH@") set(CPACK_PACKAGE_NAME "@PROJECT_NAME@") -set(CPACK_PACKAGE_VENDOR "https://sourceforge.net/p/ismrmrd") +set(CPACK_PACKAGE_VENDOR "http://ismrmrd.github.io/") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "ISMRM Raw Data Format (ISMRMRD)") set(CPACK_PACKAGE_INSTALL_DIRECTORY "@PROJECT_NAME_LOWER@") set(CPACK_RESOURCE_FILE_LICENSE "@CMAKE_SOURCE_DIR@/LICENSE") @@ -18,19 +18,13 @@ set(CPACK_PACKAGE_MAINTAINER "Michael S. Hansen <[email protected]>") set(CPACK_PACKAGE_CONTACT "Michael S. Hansen <[email protected]>") # DEB specific -set(CPACK_DEBIAN_PACKAGE_DEPENDS "@DEBIAN_PACKAGE_DEPENDS@") set(CPACK_DEBIAN_PACKAGE_SECTION "devel") set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Implementation of the ISMRMRD format.") -# RPM specific -# TODO: set(CPACK_RPM_PACKAGE_REQUIRES "@RPM_PACKAGE_DEPENDS@") -# TODO: set(CPACK_RPM_PACKAGE_DESCRIPTION "Implementation of the ISMRMRD format.") - # NSIS specific -set(CPACK_NSIS_HELP_LINK "http:\\\\\\\\ismrmrd.sourceforge.net") -set(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\ismrmrd.sourceforge.net") +set(CPACK_NSIS_HELP_LINK "http://ismrmrd.github.io") +set(CPACK_NSIS_URL_INFO_ABOUT "http://ismrmrd.github.io") set(CPACK_NSIS_MODIFY_PATH ON) set(CPACK_NSIS_DISPLAY_NAME "ismrmrd") diff --git a/cmake/ismrmrd_cpack.cmake b/cmake/ismrmrd_cpack.cmake index eee956c..94ad624 100644 --- a/cmake/ismrmrd_cpack.cmake +++ b/cmake/ismrmrd_cpack.cmake @@ -8,31 +8,33 @@ if(UNIX) if(EXISTS ${DPKG_PROGRAM}) list(APPEND CPACK_GENERATOR "DEB") endif(EXISTS ${DPKG_PROGRAM}) - # TODO: RPM - #find_program(RPMBUILD_PROGRAM rpmbuild) - #if(EXISTS ${RPMBUILD_PROGRAM}) - # list(APPEND CPACK_GENERATOR "RPM") - #endif(EXISTS ${RPMBUILD_PROGRAM}) endif(UNIX) -if(WIN32) - # NSLS - list(APPEND CPACK_GENERATOR "NSIS") -endif(WIN32) +# Enable/Disable automatic search for dependencies: +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) -# TODO: list(APPEND CPACK_SOURCE_GENERATOR "TGZ") -# TODO: list(APPEND CPACK_SOURCE_GENERATOR "ZIP") -list(APPEND CPACK_SOURCE_IGNORE_FILES ";.git;.gitignore;todo.txt;_clang-format;build/") +# Enable/Disable component install for CPack generator DEB +set(CPACK_DEB_COMPONENT_INSTALL OFF) +set(CPACK_DEB_PACKAGE_COMPONENT OFF) -# set dependencies explicitly -include(InstallRequiredSystemLibraries) -set(DEBIAN_PACKAGE_DEPENDS "libhdf5-7, libfftw3-3, libboost-program-options-dev") -# TODO: set(RPM_PACKAGE_DEPENDS "hdf5-devel") +# Set dependencies explicitly +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libhdf5-7, libfftw3-3, libboost-program-options-dev") -# where the package metadata are +# Where the package metadata are set(ISMRMRD_CPACK_CFG_FILE "${PROJECT_BINARY_DIR}/cpack_options.cmake") -# Where the package to be installed -#set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CMAKE_INSTALL_PREFIX}) +# Where the package to be installed set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) message("CPACK_PACKAGING_INSTALL_PREFIX: " ${CPACK_PACKAGING_INSTALL_PREFIX}) + +if(WIN32) + # NSLS + list(APPEND CPACK_GENERATOR "NSIS") +endif(WIN32) + +list(APPEND CPACK_SOURCE_GENERATOR "TGZ") +list(APPEND CPACK_SOURCE_GENERATOR "ZIP") +list(APPEND CPACK_SOURCE_IGNORE_FILES ";.git;.gitignore;todo.txt;_clang-format;build/") + +# set dependencies explicitly +include(InstallRequiredSystemLibraries) diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 3ab0884..8988f17 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -1,5 +1,23 @@ -include_directories(${CMAKE_SOURCE_DIR}/include) +cmake_minimum_required(VERSION 2.8) +project(ISMRMRD-C-EXAMPLE) -add_executable(ismrmrd_c_demo main.c) -target_link_libraries(ismrmrd_c_demo ismrmrd) -install(TARGETS ismrmrd_c_demo DESTINATION bin) +# if building this example as a standalone project +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + if(NOT DEFINED ENV{ISMRMRD_HOME}) + message(FATAL_ERROR "ISMRMRD_HOME environment variable must be defined") + endif(NOT DEFINED ENV{ISMRMRD_HOME}) + + list(APPEND CMAKE_MODULE_PATH "$ENV{ISMRMRD_HOME}/share/ismrmrd/cmake") + + find_package(Ismrmrd REQUIRED) + +# otherwise, building it as part of ISMRMRD itself +else(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(ISMRMRD_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include") + set(ISMRMRD_LIBRARIES ismrmrd) +endif(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + +include_directories(${ISMRMRD_INCLUDE_DIR}) +add_executable(ismrmrd_c_example main.c) +target_link_libraries(ismrmrd_c_example ${ISMRMRD_LIBRARIES}) +install(TARGETS ismrmrd_c_example DESTINATION bin) diff --git a/examples/c/README.md b/examples/c/README.md new file mode 100644 index 0000000..a20d781 --- /dev/null +++ b/examples/c/README.md @@ -0,0 +1,13 @@ +This is an example of a simple C project that is built on ISMRMRD. + +Instructions for building: + +1. Install ISMRMRD and CMake +2. Define the environment variable ISMRMRD_HOME, + e.g. `export ISMRMRD_HOME=/usr/local/ismrmrd` +3. Compile the example: + + mkdir build + cmake .. + make + ./ismrmrd_c_example diff --git a/examples/c/README.txt b/examples/c/README.txt deleted file mode 100644 index e430630..0000000 --- a/examples/c/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -This is an example of a simple C project that is built on ISMRMRD. - -Instructions for building: -1. Install ISMRMRD -2. Define the environment variable ISMRMRD_HOME, - e.g. ISMRMRD_HOME=/usr/local/ismrmrd -3. In a directory of your choosing: - mkdir buildcdemo - cmake ${ISMRMRD_HOME}/examples/c - make - ./ismrmrd_c_demo \ No newline at end of file diff --git a/examples/c/main.c b/examples/c/main.c index 6bb5d07..a2f265e 100644 --- a/examples/c/main.c +++ b/examples/c/main.c @@ -45,12 +45,9 @@ int main(void) ismrmrd_write_header(&dataset1, xmlhdr); /* Append some acquisitions */ - /* must initialize an acquisition before you can use it */ - ismrmrd_init_acquisition(&acq); nacq_write = 5; for (n=0; n < nacq_write; n++) { - /* must free an acquisition before you can reinitialize it */ - ismrmrd_init_acquisition(&acq); + /* must initialize an acquisition before you can use it */ ismrmrd_init_acquisition(&acq); acq.head.number_of_samples = 128; acq.head.active_channels = 4; @@ -76,6 +73,7 @@ int main(void) ismrmrd_set_flag(&(acq.head.flags), ISMRMRD_ACQ_LAST_IN_SLICE); } ismrmrd_append_acquisition(&dataset1, &acq); + ismrmrd_cleanup_acquisition(&acq); } /* Close the dataset */ @@ -91,6 +89,7 @@ int main(void) /* Read the header */ xmlstring = ismrmrd_read_header(&dataset2); printf("Header: %s\n", xmlstring); + free(xmlstring); /* Get the number of acquisitions */ nacq_read = ismrmrd_get_number_of_acquisitions(&dataset2); @@ -131,6 +130,8 @@ int main(void) #else printf("Data 3: %f\t 2: %f\n", creal(acq3.data[4]), creal(acq2.data[4])); #endif + ismrmrd_cleanup_acquisition(&acq2); + ismrmrd_cleanup_acquisition(&acq3); /* Create and store an image */ ismrmrd_init_image(&im); @@ -152,6 +153,7 @@ int main(void) ((float*)im.data)[loc] = 2.0; } ismrmrd_append_image(&dataset2, "testimages", &im); + ismrmrd_cleanup_image(&im); numim = ismrmrd_get_number_of_images(&dataset2, "testimages"); printf("Number of images stored = %d\n", numim); @@ -160,6 +162,7 @@ int main(void) ismrmrd_read_image(&dataset2, "testimages", 1, &im2); printf("Image 1 attribute string = %s\n", im2.attribute_string); printf("Image 1 at position 10 has value = %f\n", ((float*)im2.data)[10]); + ismrmrd_cleanup_image(&im2); /* Create and store an array */ ismrmrd_init_ndarray(&arr); @@ -174,19 +177,14 @@ int main(void) } ismrmrd_append_array(&dataset2, "testarray", &arr); printf("Number of arrays stored = %d\n", ismrmrd_get_number_of_arrays(&dataset2, "testarray")); + ismrmrd_cleanup_ndarray(&arr); /* Read it back in */ ismrmrd_init_ndarray(&arr2); ismrmrd_read_array(&dataset2, "testarray", 0, &arr2); printf("Array 2 at position 10 has value = %f\n", ((float*)arr2.data)[10]); + ismrmrd_cleanup_ndarray(&arr2); - /* Clean up */ - /* This frees the internal memory of the acquisitions */ - ismrmrd_cleanup_acquisition(&acq); - ismrmrd_cleanup_acquisition(&acq2); - ismrmrd_cleanup_acquisition(&acq3); - free(xmlstring); - /* Close the dataset */ ismrmrd_close_dataset(&dataset2); diff --git a/examples/data/.gitignore b/examples/data/.gitignore deleted file mode 100644 index 424e4bc..0000000 --- a/examples/data/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.h5 \ No newline at end of file diff --git a/examples/data/README.txt b/examples/data/README.txt deleted file mode 100644 index 92962b7..0000000 --- a/examples/data/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -ISMRM Raw Data Example files ------------------------------ - - - -Please download the example data files from: - -https://sourceforge.net/projects/ismrmrd/files/data/ - -On Linux command line, simply type: - -wget https://sourceforge.net/projects/ismrmrd/files/data/3D_partial_fourier.h5 -wget https://sourceforge.net/projects/ismrmrd/files/data/simple_gre.h5 -wget https://sourceforge.net/projects/ismrmrd/files/data/simple_spiral.h5 - - - diff --git a/examples/matlab/todo.txt b/examples/matlab/todo.txt deleted file mode 100644 index 50b1078..0000000 --- a/examples/matlab/todo.txt +++ /dev/null @@ -1 +0,0 @@ -Put Michaels spiral and gridder example back. diff --git a/examples/python/ismrmrd_create_dataset.py b/examples/python/ismrmrd_create_dataset.py deleted file mode 100644 index 0ac2939..0000000 --- a/examples/python/ismrmrd_create_dataset.py +++ /dev/null @@ -1,174 +0,0 @@ -# coding: utf-8 -import os -import ismrmrd -import numpy as np -import matplotlib.pyplot as plt - - -filename = 'testdata.h5' -if os.path.isfile(filename): - os.remove(filename) -# Create an empty ISMRMRD dataset -dset = ismrmrd.Dataset(filename, "dataset") - -# Synthesize the object -nX, nY = 256, 256 -rho = np.zeros((nX, nY)) -x0, x1 = nX / 4, 3 * nX / 4 -y0, y1 = nY / 4, 3 * nY / 4 -rho[x0:x1, y0:y1] = 1 - -plt.imshow(rho) - -# Synthesize some coil sensitivities -X, Y = np.meshgrid(np.arange(nX) / nX / 2.0, np.arange(nY) / nY / 2.0) -C = np.zeros((nX, nY, 4), dtype=np.complex64) -C[:,:,0] = np.exp(-((X - 0.5) ** 2 + Y ** 2) + 1j * (X - 0.5)) -C[:,:,1] = np.exp(-((X + 0.5) ** 2 + Y ** 2) - 1j * (X + 0.5)) -C[:,:,2] = np.exp(-(X ** 2 + (Y - 0.5) ** 2) + 1j * (Y - 0.5)) -C[:,:,3] = np.exp(-(X ** 2 + (Y + 0.5) ** 2) - 1j * (Y + 0.5)) -ncoils = np.size(C, 2) - -# Synthesize the k-space data -nreps = 5 -noiselevel = 0.05 -K = np.zeros((nX, nY, ncoils, nreps), dtype=np.complex64) -for rep in range(nreps): - for coil in range(ncoils): - noise = noiselevel * (np.random.randn(nX, nY) + 1j * np.random.randn(nX, nY)) - K[:,:,coil,rep] = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(C[:,:,coil] * rho + noise))) - -rep0 = np.sqrt(np.sum(np.abs(K) ** 2, 2)) -plt.imshow(rep0[:,:,0]) - - -for rep in range(nreps): - for line in range(nY): - # Generate header - head = ismrmrd.AcquisitionHeader() - counter = ismrmrd.EncodingCounters() - head.version = 1 - head.number_of_samples = nX - head.center_sample = nX / 2 - head.active_channels = ncoils - head.read_dir = [1., 0., 0.] - head.phase_dir = [0., 1., 0.] - head.slice_dir = [0., 0., 1.] - head.scan_counter = rep * nY + line - counter.kspace_encode_step_1 = line - counter.repetition = rep - head.idx = counter - # Note: the correct API for setting Acquisition flags looks like this: - # acq.setFlag(ismrmrd.FlagBit(ismrmrd.ACQ_FIRST_IN_ENCODE_STEP1)) - # but using this API would require using only ugly "acq.setXXX" methods - # since the call to "acq.setHead()" below overwrites the Acquisition's header - head.flags = 0 - if line == 0: - head.flags |= 1 << ismrmrd.ACQ_LAST_IN_ENCODE_STEP1 - head.flags |= 1 << ismrmrd.ACQ_FIRST_IN_SLICE - head.flags |= 1 << ismrmrd.ACQ_FIRST_IN_REPETITION - elif line == nY - 1: - head.flags |= 1 << ismrmrd.ACQ_LAST_IN_ENCODE_STEP1 - head.flags |= 1 << ismrmrd.ACQ_LAST_IN_SLICE - head.flags |= 1 << ismrmrd.ACQ_LAST_IN_REPETITION - - # Generate k-space data - data = (np.array([c.real for c in np.array(K[:,line,:,rep])]) + - 1j * np.array([c.imag for c in np.array(K[:,line,:,rep])])) - - # Construct acquisition object from header - acq = ismrmrd.Acquisition(head=head) - - # Fill in the internal data array - acq.data = data - - # Append to HDF5 dataset - dset.append_acquisition(acq) - -# Fill the XML header -try: - import ismrmrd_xsd - HAS_XSD = True -except ImportError: - HAS_XSD = False - -if HAS_XSD: - header = ismrmrd_xsd.ismrmrdHeader() - - # Experimental Conditions - exp = ismrmrd_xsd.experimentalConditionsType() - exp.H1resonanceFrequency_Hz = 128000000 - header.experimentalConditions = exp - - # Acquisition System Information - sys = ismrmrd_xsd.acquisitionSystemInformationType() - sys.receiverChannels = ncoils - header.acquisitionSystemInformation = sys - - # Encoding - encoding = ismrmrd_xsd.encoding() - encoding.trajectory = ismrmrd_xsd.trajectoryType.cartesian - - # Encoded Space - fov = ismrmrd_xsd.fieldOfView_mm() - fov.x = 256 - fov.y = 256 - fov.z = 5 - - matrix = ismrmrd_xsd.matrixSize() - matrix.x = np.size(K, 0) - matrix.y = np.size(K, 1) - matrix.z = 1 - - space = ismrmrd_xsd.encodingSpaceType() - space.matrixSize = matrix - space.fieldOfView_mm = fov - - # Set encoded and recon space (same) - encoding.encodedSpace = space - encoding.reconSpace = space - - # Encoding limits - limits = ismrmrd_xsd.encodingLimitsType() - - limits0 = ismrmrd_xsd.limitType() - limits0.minimum = 0 - limits0.center = np.size(K, 0) / 2 - limits0.maximum = np.size(K, 0) - 1 - limits.kspaceEncodingStep0 = limits0 - - limits1 = ismrmrd_xsd.limitType() - limits1.minimum = 0 - limits1.center = np.size(K, 1) / 2 - limits1.maximum = np.size(K, 1) - 1 - limits.kspaceEncodingStep1 = limits1 - - limits_rep = ismrmrd_xsd.limitType() - limits_rep.minimum = 0 - limits_rep.center = nreps / 2 - limits_rep.maximum = nreps - 1 - limits.repetition = limits_rep - - limits_slice = ismrmrd_xsd.limitType() - limits_slice.minimum = 0 - limits_slice.center = 0 - limits_slice.maximum = 0 - limits.slice = limits_slice - - limits_rest = ismrmrd_xsd.limitType() - limits_rest.minimum = 0 - limits_rest.center = 0 - limits_rest.maximum = 0 - limits.average = limits_rest - limits.contrast = limits_rest - limits.kspaceEncodingStep2 = limits_rest - limits.phase = limits_rest - limits.segment = limits_rest - limits.set = limits_rest - - encoding.encodingLimits = limits - header.encoding.append(encoding) - - dset.write_header(header.toxml('utf-8')) - -dset.close() diff --git a/examples/python/ismrmrd_recon_dataset.py b/examples/python/ismrmrd_recon_dataset.py deleted file mode 100644 index a0ccd49..0000000 --- a/examples/python/ismrmrd_recon_dataset.py +++ /dev/null @@ -1,132 +0,0 @@ -# coding: utf-8 - -import os -import ismrmrd -import ismrmrd_xsd -import numpy as np -from numpy.fft import ifft, fftshift -import matplotlib.pyplot as plt - -# Load file -filename = 'testdata.h5' -if not os.path.isfile(filename): - print("%s is not a valid file" % filename) - raise SystemExit -dset = ismrmrd.Dataset(filename, 'dataset', create_if_needed=False) - -header = ismrmrd_xsd.CreateFromDocument(dset.read_header()) -enc = header.encoding[0] - -# Matrix size -eNx = enc.encodedSpace.matrixSize.x -eNy = enc.encodedSpace.matrixSize.y -eNz = enc.encodedSpace.matrixSize.z -rNx = enc.reconSpace.matrixSize.x -rNy = enc.reconSpace.matrixSize.y -rNz = enc.reconSpace.matrixSize.z - -# Field of View -eFOVx = enc.encodedSpace.fieldOfView_mm.x -eFOVy = enc.encodedSpace.fieldOfView_mm.y -eFOVz = enc.encodedSpace.fieldOfView_mm.z -rFOVx = enc.reconSpace.fieldOfView_mm.x -rFOVy = enc.reconSpace.fieldOfView_mm.y -rFOVz = enc.reconSpace.fieldOfView_mm.z - -# Number of Slices, Reps, Contrasts, etc. -ncoils = header.acquisitionSystemInformation.receiverChannels -if enc.encodingLimits.slice != None: - nslices = enc.encodingLimits.slice.maximum + 1 -else: - nslices = 1 - -if enc.encodingLimits.repetition != None: - nreps = enc.encodingLimits.repetition.maximum + 1 -else: - nreps = 1 - -if enc.encodingLimits.contrast != None: - ncontrasts = enc.encodingLimits.contrast.maximum + 1 -else: - ncontrasts = 1 - - -all_data = np.zeros((nreps, ncontrasts, nslices, ncoils, eNz, eNy, eNx), dtype=np.complex64) -for acqnum in range(dset.number_of_acquisitions): - acq = dset.read_acquisition(acqnum) - - # TODO: Currently ignoring noise scans - if acq.head.flags & ismrmrd.ACQ_IS_NOISE_MEASUREMENT: - continue - - rep = acq.head.idx.repetition - contrast = acq.head.idx.contrast - slice = acq.head.idx.slice - y = acq.head.idx.kspace_encode_step_1 - z = acq.head.idx.kspace_encode_step_2 - all_data[rep, contrast, slice, :, z, y, :] = acq.data - -#fig = plt.figure() -#h, w = nreps * ncontrasts, eNz * nslices -#i = 0 -#for rep in range(nreps): -# for contrast in range(ncontrasts): -# for slice in range(nslices): -# for z in range(eNz): -# K = all_data[rep,contrast,slice,:,z,:,:] -# comb = np.sqrt(np.squeeze(np.sum(np.abs(K) ** 2, 2))) -# a = fig.add_subplot(h, w, i) -# plt.imshow(comb) -# i += 1 -#fig.set_size_inches(16, 16) - -images = [] -for rep in range(nreps): - for contrast in range(ncontrasts): - for slice in range(nslices): - K = all_data[rep,contrast,slice,:,:,:,:] - K = fftshift(ifft(fftshift(K, axes=3), axis=3), axes=3) - - # chop if needed - if eNx != rNx: - i0 = (eNx - rNx) / 2 - i1 = (eNx - rNx) / 2 + rNx - im = K[:,:,:,i0:i1] - else: - im = K - - im = fftshift(ifft(fftshift(im, axes=2), axis=2), axes=2) - if np.size(im, 1) > 1: - im = fftshift(ifft(fftshift(im, axes=1), axis=1), axes=1) - - im = np.squeeze(np.sqrt(np.sum(np.abs(im) ** 2, 0))) - images.append(im) - -l = len(images) -fig = plt.figure() -for n, im in enumerate(images): - a = fig.add_subplot(1, 5, n) - plt.imshow(im) -fig.set_size_inches(16, 4) - -## # grab the first acquisition for extra info -## acqh = dset.read_acquisition(0).head - -## for n, img in enumerate(images): -## hdr = ismrmrd.ImageHeader() -## hdr.acquisition_time_stamp = acqh.acquisition_time_stamp -## hdr.flags = 0 -## hdr.measurement_uid = acqh.measurement_uid -## hdr.phase_dir = acqh.phase_dir -## hdr.physiology_time_stamp = acqh.physiology_time_stamp -## hdr.position = acqh.position -## hdr.read_dir = acqh.read_dir -## hdr.slice_dir = acqh.slice_dir -## hdr.channels = 1 -## hdr.image_data_type = ismrmrd.DATA_FLOAT -## hdr.image_type = ismrmrd.TYPE_MAGNITUDE -## hdr.image_index = n -## hdr.slice = n - -## dset.appendImageHeader(hdr, "image_%d.hdr" % n) -## dset.appendArray(img, "image_%d.img" % n) diff --git a/include/ismrmrd/ismrmrd.h b/include/ismrmrd/ismrmrd.h index 24bfeab..a4539b0 100644 --- a/include/ismrmrd/ismrmrd.h +++ b/include/ismrmrd/ismrmrd.h @@ -755,8 +755,10 @@ public: void setHead(const ImageHeader& head); // Attribute string - void getAttributeString(std::string &atrr) const; + void getAttributeString(std::string &attr) const; + const char *getAttributeString() const; void setAttributeString(const std::string &attr); + void setAttributeString(const char *attr); size_t getAttributeStringLength() const; // Data diff --git a/include/ismrmrd/xml.h b/include/ismrmrd/xml.h index 5695532..1e4b782 100644 --- a/include/ismrmrd/xml.h +++ b/include/ismrmrd/xml.h @@ -302,10 +302,12 @@ namespace ISMRMRD struct SequenceParameters { - std::vector<float> TR; - std::vector<float> TE; - std::vector<float> TI; - std::vector<float> flipAngle_deg; + Optional<std::vector<float> > TR; + Optional<std::vector<float> > TE; + Optional<std::vector<float> > TI; + Optional<std::vector<float> > flipAngle_deg; + Optional<std::string> sequence_type; + Optional<std::vector<float> > echo_spacing; }; struct IsmrmrdHeader diff --git a/include/version.in b/include/version.in index 5239d4f..43378ae 100644 --- a/include/version.in +++ b/include/version.in @@ -6,5 +6,6 @@ #define ISMRMRD_VERSION_PATCH @ISMRMRD_VERSION_PATCH@ #define ISMRMRD_XMLHDR_VERSION @ISMRMRD_VERSION_MINOR@ #define ISMRMRD_GIT_SHA1_HASH "@ISMRMRD_GIT_SHA1@" +#define ISMRMRD_DATASET_SUPPORT @ISMRMRD_DATASET_SUPPORT@ #endif /* ISMRMRD_VERSION_H */ diff --git a/libsrc/dataset.c b/libsrc/dataset.c index b1ee935..1379e4d 100644 --- a/libsrc/dataset.c +++ b/libsrc/dataset.c @@ -419,6 +419,7 @@ static hid_t get_hdf5type_image_attribute_string(void) { hid_t datatype = H5Tcopy(H5T_C_S1); herr_t h5status = H5Tset_size(datatype, H5T_VARIABLE); if (h5status < 0) { + H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL); ISMRMRD_PUSH_ERR(ISMRMRD_FILEERROR, "Failed get image attribute string data type"); } return datatype; @@ -738,6 +739,8 @@ static int get_array_properties(const ISMRMRD_Dataset *dset, const char *path, dims[n] = hdfdims[rank-n-1]; } + free(hdfdims); + /* clean up */ h5status = H5Tclose(hdf5type); if (h5status < 0) { @@ -858,7 +861,7 @@ int ismrmrd_init_dataset(ISMRMRD_Dataset *dset, const char *filename, dset->filename = (char *) malloc(strlen(filename) + 1); if (dset->filename == NULL) { - return ISMRMRD_PUSH_ERR(ISMRMRD_MEMORYERROR, "Failed to malloc dataset groupname"); + return ISMRMRD_PUSH_ERR(ISMRMRD_MEMORYERROR, "Failed to malloc dataset filename"); } strcpy(dset->filename, filename); @@ -889,9 +892,16 @@ int ismrmrd_open_dataset(ISMRMRD_Dataset *dset, const bool create_if_needed) { dset->fileid = fileid; } else if (create_if_needed == false) { - H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL); - /* Some sort of error opening the file - Maybe it doesn't exist? */ - return ISMRMRD_PUSH_ERR(ISMRMRD_FILEERROR, "Failed to open file."); + /*Try opening the file as read-only*/ + fileid = H5Fopen(dset->filename, H5F_ACC_RDONLY, H5P_DEFAULT); + if (fileid > 0) { + dset->fileid = fileid; + } + else{ + H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL); + /* Some sort of error opening the file - Maybe it doesn't exist? */ + return ISMRMRD_PUSH_ERR(ISMRMRD_FILEERROR, "Failed to open file."); + } } else { /* Try creating a new file using the default properties. */ @@ -1027,26 +1037,16 @@ char * ismrmrd_read_header(const ISMRMRD_Dataset *dset) { goto cleanup_path; } - void *buff[1] = { NULL }; dataset = H5Dopen2(dset->fileid, path, H5P_DEFAULT); datatype = get_hdf5type_xmlheader(); /* Read it into a 1D buffer*/ - h5status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buff); - if (h5status < 0 || buff[0] == NULL) { + h5status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &xmlstring); + if (h5status < 0 || xmlstring == NULL) { H5Ewalk2(H5E_DEFAULT, H5E_WALK_UPWARD, walk_hdf5_errors, NULL); ISMRMRD_PUSH_ERR(ISMRMRD_FILEERROR, "Failed to read header."); goto cleanup_path; } - /* Unpack */ - xmlstring = (char *) malloc(strlen(buff[0])+1); - if (NULL == xmlstring) { - ISMRMRD_PUSH_ERR(ISMRMRD_MEMORYERROR, "Failed to malloc xmlstring"); - goto cleanup_path; - } else { - memcpy(xmlstring, buff[0], strlen(buff[0])+1); - } - /* Clean up */ h5status = H5Tclose(datatype); if (h5status < 0) { @@ -1157,6 +1157,7 @@ int ismrmrd_read_acquisition(const ISMRMRD_Dataset *dset, uint32_t index, ISMRMR memcpy(acq->data, hdf5acq.data.p, ismrmrd_size_of_acquisition_data(acq)); /* clean up */ + free(path); free(hdf5acq.traj.p); free(hdf5acq.data.p); @@ -1266,7 +1267,7 @@ int ismrmrd_read_image(const ISMRMRD_Dataset *dset, const char *varname, int status; hid_t datatype; - char *path, *headerpath, *attrpath, *datapath; + char *path, *headerpath, *attrpath, *datapath, *attr_string; uint32_t numims; if (dset==NULL) { @@ -1305,13 +1306,17 @@ int ismrmrd_read_image(const ISMRMRD_Dataset *dset, const char *varname, /* Handle the attribute string */ attrpath = append_to_path(dset, path, "attributes"); datatype = get_hdf5type_image_attribute_string(); - status = read_element(dset, attrpath, (void *) &im->attribute_string, datatype, index); + status = read_element(dset, attrpath, (void *) &attr_string, datatype, index); if (status != ISMRMRD_NOERROR) { return ISMRMRD_PUSH_ERR(ISMRMRD_FILEERROR, "Failed to read image attribute string."); } free(attrpath); H5Tclose(datatype); + /* copy the attribute string read from the file into the Image */ + memcpy(im->attribute_string, attr_string, ismrmrd_size_of_image_attribute_string(im)); + free(attr_string); + /* Handle the data */ datapath = append_to_path(dset, path, "data"); datatype = get_hdf5type_ndarray(im->head.data_type); diff --git a/libsrc/dataset.cpp b/libsrc/dataset.cpp index e679f99..a87bdfc 100644 --- a/libsrc/dataset.cpp +++ b/libsrc/dataset.cpp @@ -111,6 +111,16 @@ template <typename T> void Dataset::readImage(const std::string &var, uint32_t i } } +// Specific instantiations +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<uint16_t> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<int16_t> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<uint32_t> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<int32_t> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<float> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<double> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<complex_float_t> &im); +template EXPORTISMRMRD void Dataset::readImage(const std::string &var, uint32_t index, Image<complex_double_t> &im); + uint32_t Dataset::getNumberOfImages(const std::string &var) { uint32_t num = ismrmrd_get_number_of_images(&dset_, var.c_str()); @@ -152,6 +162,16 @@ template <typename T> void Dataset::readNDArray(const std::string &var, uint32_t } } +// Specific instantiations +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<uint16_t> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<int16_t> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<uint32_t> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<int32_t> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<float> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<double> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<complex_float_t> &arr); +template EXPORTISMRMRD void Dataset::readNDArray(const std::string &var, uint32_t index, NDArray<complex_double_t> &arr); + uint32_t Dataset::getNumberOfNDArrays(const std::string &var) { uint32_t num = ismrmrd_get_number_of_arrays(&dset_, var.c_str()); diff --git a/libsrc/ismrmrd.c b/libsrc/ismrmrd.c index 3d687d4..3fba17c 100644 --- a/libsrc/ismrmrd.c +++ b/libsrc/ismrmrd.c @@ -268,10 +268,13 @@ int ismrmrd_make_consistent_image(ISMRMRD_Image *im) { attr_size = ismrmrd_size_of_image_attribute_string(im); if (attr_size > 0) { - im->attribute_string = (char *)realloc(im->attribute_string, attr_size); + // Allocate space plus a null-terminating character + im->attribute_string = (char *)realloc(im->attribute_string, attr_size + sizeof(*im->attribute_string)); if (im->attribute_string == NULL) { return ISMRMRD_PUSH_ERR(ISMRMRD_MEMORYERROR, "Failed to realloc image attribute string"); } + // Set the null terminating character + im->attribute_string[im->head.attribute_string_len] = '\0'; } data_size = ismrmrd_size_of_image_data(im); @@ -528,8 +531,8 @@ int ismrmrd_set_channel_on(uint64_t channel_mask[ISMRMRD_CHANNEL_MASKS], const u if (channel_mask==NULL) { return ISMRMRD_PUSH_ERR(ISMRMRD_RUNTIMEERROR, "Pointer to channel_mask should not be NULL."); } - bitmask = 1 << (chan % ISMRMRD_CHANNEL_MASKS); - offset = chan / ISMRMRD_CHANNEL_MASKS; + bitmask = 1 << (chan % 64); + offset = chan / 64; channel_mask[offset] |= bitmask; return ISMRMRD_NOERROR; } diff --git a/libsrc/ismrmrd.cpp b/libsrc/ismrmrd.cpp index baa6dd1..d33c1b1 100644 --- a/libsrc/ismrmrd.cpp +++ b/libsrc/ismrmrd.cpp @@ -931,19 +931,34 @@ template <typename T> void Image<T>::getAttributeString(std::string &attr) const attr.assign(""); } +template <typename T> const char *Image<T>::getAttributeString() const +{ + return im.attribute_string; +} + template <typename T> void Image<T>::setAttributeString(const std::string &attr) { - size_t length = attr.length(); - im.head.attribute_string_len = static_cast<uint32_t>(length); + this->setAttributeString(attr.c_str()); +} - // Add null terminating character - length++; +template <typename T> void Image<T>::setAttributeString(const char *attr) +{ + // Get the string length + size_t length = strlen(attr); - im.attribute_string = (char *)realloc(im.attribute_string, length); - if (NULL==im.attribute_string) { + // Allocate space plus a null terminator and check for success + char *newPointer = (char *)realloc(im.attribute_string, (length+1) * sizeof(*im.attribute_string)); + if (NULL==newPointer) { throw std::runtime_error(build_exception_string()); } - strncpy(im.attribute_string, attr.c_str(), length); + + // Make changes only if reallocation was successful + im.attribute_string = newPointer; + im.head.attribute_string_len = static_cast<uint32_t>(length); + + // Set the null terminator and copy the string + im.attribute_string[length] = '\0'; + strncpy(im.attribute_string, attr, length); } template <typename T> size_t Image<T>::getAttributeStringLength() const diff --git a/libsrc/xml.cpp b/libsrc/xml.cpp index 5e6a3d7..fde4e2b 100644 --- a/libsrc/xml.cpp +++ b/libsrc/xml.cpp @@ -222,7 +222,7 @@ namespace ISMRMRD throw std::runtime_error("experimentalConditions not defined in ismrmrdHeader"); } else { ExperimentalConditions e; - e.H1resonanceFrequency_Hz = std::atoi(experimentalConditions.child_value("H1resonanceFrequency_Hz")); + e.H1resonanceFrequency_Hz = std::atol(experimentalConditions.child_value("H1resonanceFrequency_Hz")); h.experimentalConditions = e; } @@ -396,10 +396,24 @@ namespace ISMRMRD if (sequenceParameters) { SequenceParameters p; - p.TR = parse_vector_float(sequenceParameters,"TR"); - p.TE = parse_vector_float(sequenceParameters,"TE"); - p.TI = parse_vector_float(sequenceParameters,"TI"); - p.flipAngle_deg = parse_vector_float(sequenceParameters, "flipAngle_deg"); + + std::vector<float> r; + r = parse_vector_float(sequenceParameters, "TR"); + if (!r.empty()) p.TR = r; + + r = parse_vector_float(sequenceParameters, "TE"); + if (!r.empty()) p.TE = r; + + r = parse_vector_float(sequenceParameters, "TI"); + if (!r.empty()) p.TI = r; + + r = parse_vector_float(sequenceParameters, "flipAngle_deg"); + if (!r.empty()) p.flipAngle_deg = r; + + p.sequence_type = parse_optional_string(sequenceParameters, "sequence_type"); + + r = parse_vector_float(sequenceParameters, "echo_spacing"); + if (!r.empty()) p.echo_spacing = r; h.sequenceParameters = p; } @@ -655,24 +669,42 @@ namespace ISMRMRD if (h.sequenceParameters) { n1 = root.append_child("sequenceParameters"); - if (!h.sequenceParameters->TR.size()) { - throw std::runtime_error("TR section of sequenceParameters does not contain any values"); - } - if (!h.sequenceParameters->TE.size()) { - throw std::runtime_error("TE section of sequenceParameters does not contain any values"); + + if (h.sequenceParameters->TR.is_present()) + { + for (size_t i = 0; i < h.sequenceParameters->TR->size(); i++) { + append_node(n1, "TR", h.sequenceParameters->TR->operator[](i)); + } } - for (size_t i = 0; i < h.sequenceParameters->TR.size(); i++) { - append_node(n1,"TR",h.sequenceParameters->TR[i]); + if (h.sequenceParameters->TE.is_present()) + { + for (size_t i = 0; i < h.sequenceParameters->TE->size(); i++) { + append_node(n1, "TE", h.sequenceParameters->TE->operator[](i)); + } } - for (size_t i = 0; i < h.sequenceParameters->TE.size(); i++) { - append_node(n1,"TE",h.sequenceParameters->TE[i]); + + if (h.sequenceParameters->TI.is_present()) + { + for (size_t i = 0; i < h.sequenceParameters->TI->size(); i++) { + append_node(n1, "TI", h.sequenceParameters->TI->operator[](i)); + } } - for (size_t i = 0; i < h.sequenceParameters->TI.size(); i++) { - append_node(n1,"TI",h.sequenceParameters->TI[i]); + + if (h.sequenceParameters->flipAngle_deg.is_present()) + { + for (size_t i = 0; i < h.sequenceParameters->flipAngle_deg->size(); i++) { + append_node(n1, "flipAngle_deg", h.sequenceParameters->flipAngle_deg->operator[](i)); + } } - for (size_t i = 0; i < h.sequenceParameters->flipAngle_deg.size(); i++) { - append_node(n1,"flipAngle_deg",h.sequenceParameters->flipAngle_deg[i]); + + append_optional_node(n2, "sequence_type", h.sequenceParameters->sequence_type); + + if (h.sequenceParameters->echo_spacing.is_present()) + { + for (size_t i = 0; i < h.sequenceParameters->echo_spacing->size(); i++) { + append_node(n1, "echo_spacing", h.sequenceParameters->echo_spacing->operator[](i)); + } } } diff --git a/matlab/+ismrmrd/+xml/deserialize.m b/matlab/+ismrmrd/+xml/deserialize.m index d0b5b96..7aabf82 100644 --- a/matlab/+ismrmrd/+xml/deserialize.m +++ b/matlab/+ismrmrd/+xml/deserialize.m @@ -202,6 +202,8 @@ function status = isNumericalType(name) 'TE', ... 'TI', ... 'flipAngle_deg', ... + 'sequence_type', ... + 'echo_spacing', ... 'x', 'y', 'z', ... 'minimum', 'maximum', 'center'}; @@ -233,7 +235,8 @@ function status = isStringType(name) 'trajectory', ... 'coilName', ... 'calibrationMode',... - 'interleavingDimension'}; + 'interleavingDimension',... + 'sequence_type'}; status = ismember(name, headerStringTypes); end diff --git a/matlab/+ismrmrd/+xml/serialize.m b/matlab/+ismrmrd/+xml/serialize.m index 7eb5740..874752f 100644 --- a/matlab/+ismrmrd/+xml/serialize.m +++ b/matlab/+ismrmrd/+xml/serialize.m @@ -175,6 +175,8 @@ if isfield(header,'sequenceParameters') append_optional(docNode,n1,sequenceParameters,'TE',@num2str); append_optional(docNode,n1,sequenceParameters,'TI',@num2str); append_optional(docNode,n1,sequenceParameters,'flipAngle_deg',@num2str); + append_optional(docNode,n1,sequenceParameters,'sequence_type'); + append_optional(docNode,n1,sequenceParameters,'echo_spacing',@num2str); docRootNode.appendChild(n1); end diff --git a/schema/ismrmrd.xsd b/schema/ismrmrd.xsd index 9553980..02168e4 100644 --- a/schema/ismrmrd.xsd +++ b/schema/ismrmrd.xsd @@ -187,10 +187,12 @@ <xs:complexType name="sequenceParametersType"> <xs:sequence> - <xs:element minOccurs="1" maxOccurs="unbounded" type="xs:float" name="TR"/> - <xs:element minOccurs="1" maxOccurs="unbounded" type="xs:float" name="TE"/> + <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="TR"/> + <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="TE"/> <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="TI"/> <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="flipAngle_deg"/> + <xs:element minOccurs="0" maxOccurs="1" type="xs:string" name="sequence_type"/> + <xs:element minOccurs="0" maxOccurs="unbounded" type="xs:float" name="echo_spacing"/> </xs:sequence> </xs:complexType> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..5d8d2c0 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +find_package(Boost 1.43 COMPONENTS unit_test_framework) + +if (NOT Boost_UNIT_TEST_FRAMEWORK_FOUND) + message("Boost Unit Test Framework not found. Not compiling tests") + return() +endif (NOT Boost_UNIT_TEST_FRAMEWORK_FOUND) + +include_directories(${CMAKE_SOURCE_DIR}/include ${CMAKE_BINARY_DIR}/include ${Boost_INCLUDE_DIR}) + +add_executable(test_ismrmrd + test_main.cpp + test_acquisitions.cpp + test_images.cpp + test_ndarray.cpp + test_flags.cpp + test_channels.cpp + test_quaternions.cpp) + +target_link_libraries(test_ismrmrd ismrmrd ${Boost_LIBRARIES}) + +add_custom_target(check COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_ismrmrd DEPENDS test_ismrmrd) diff --git a/tests/test_acquisitions.cpp b/tests/test_acquisitions.cpp new file mode 100644 index 0000000..7cc5532 --- /dev/null +++ b/tests/test_acquisitions.cpp @@ -0,0 +1,172 @@ +#include "ismrmrd/ismrmrd.h" +#include "ismrmrd/version.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(AcquisitionsTest) + +static void check_header(ISMRMRD_AcquisitionHeader* chead); + +BOOST_AUTO_TEST_CASE(test_acquisition_header) +{ + ISMRMRD_AcquisitionHeader chead; + + // Check that header is of expected size + size_t expected_size = 9 * sizeof(uint16_t) + + (3 + ISMRMRD_PHYS_STAMPS) * sizeof(uint32_t) + + ISMRMRD_USER_INTS * sizeof(int32_t) + + (1 + ISMRMRD_CHANNEL_MASKS) * sizeof(uint64_t) + + ((2 * ISMRMRD_POSITION_LENGTH) + (3 * ISMRMRD_DIRECTION_LENGTH) + + 1 + ISMRMRD_USER_FLOATS) * sizeof(float) + + (9 + ISMRMRD_USER_INTS) * sizeof(uint16_t); + BOOST_CHECK_EQUAL(sizeof(chead), expected_size); + + // Check that header is initialized properly + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition_header(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition_header(&chead), ISMRMRD_NOERROR); + check_header(&chead); +} + +BOOST_AUTO_TEST_CASE(test_acquisition_init_cleanup) +{ + ISMRMRD_Acquisition acq; + + // Check initialization of acquisition + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition(&acq), ISMRMRD_NOERROR); + BOOST_CHECK(!acq.traj); + BOOST_CHECK(!acq.data); + + // Check cleanup of acquisition + BOOST_CHECK_EQUAL(ismrmrd_cleanup_acquisition(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_cleanup_acquisition(&acq), ISMRMRD_NOERROR); + BOOST_CHECK(!acq.traj); + BOOST_CHECK(!acq.data); +} + +BOOST_AUTO_TEST_CASE(test_acquisition_create_free) +{ + ISMRMRD_Acquisition* cacqp = NULL; + + // Check creation of new acquisition + BOOST_CHECK(cacqp = ismrmrd_create_acquisition()); + // Check that it's initialized + check_header(&cacqp->head); + BOOST_CHECK(!cacqp->traj); + BOOST_CHECK(!cacqp->data); + + // Check cleanup + BOOST_CHECK_EQUAL(ismrmrd_free_acquisition(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_free_acquisition(cacqp), ISMRMRD_NOERROR); + BOOST_CHECK(!cacqp->traj); + BOOST_CHECK(!cacqp->data); +} + +BOOST_AUTO_TEST_CASE(test_acquisition_copy) +{ + // Weak check of acquisition copying + ISMRMRD_Acquisition csrc, cdst; + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition(&csrc), ISMRMRD_NOERROR); + // TODO: it is necessary to call init_acquisition on the destination acquisition + // before copying, in case its traj or data are non-NULL! + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition(&cdst), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_acquisition(&cdst, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_acquisition(NULL, &csrc), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_acquisition(NULL, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_acquisition(&cdst, &csrc), ISMRMRD_NOERROR); + check_header(&cdst.head); + BOOST_CHECK_EQUAL(ismrmrd_size_of_acquisition_data(&cdst), 0); + BOOST_CHECK(!cdst.traj); + BOOST_CHECK(!cdst.data); +} + +BOOST_AUTO_TEST_CASE(test_acquisition_make_consistent) +{ + ISMRMRD_Acquisition acq; + BOOST_CHECK_EQUAL(ismrmrd_init_acquisition(&acq), ISMRMRD_NOERROR); + + BOOST_CHECK_EQUAL(ismrmrd_make_consistent_acquisition(NULL), ISMRMRD_RUNTIMEERROR); + + uint16_t nsamples = 512; + uint16_t nchannels = 8; + uint16_t ntrajd = 2; + acq.head.number_of_samples = nsamples; + acq.head.active_channels = nchannels; + acq.head.trajectory_dimensions = ntrajd; + + BOOST_CHECK_EQUAL(ismrmrd_size_of_acquisition_traj(&acq), nsamples * ntrajd * sizeof(*acq.traj)); + BOOST_CHECK_EQUAL(ismrmrd_size_of_acquisition_data(&acq), nsamples * nchannels * sizeof(*acq.data)); + BOOST_CHECK_EQUAL(ismrmrd_make_consistent_acquisition(&acq), ISMRMRD_NOERROR); + + BOOST_CHECK_EQUAL(acq.head.available_channels, nchannels); + // check that traj and data were allocated + BOOST_CHECK(acq.traj); + BOOST_CHECK(acq.data); + + ismrmrd_cleanup_acquisition(&acq); +} + +static void check_header(ISMRMRD_AcquisitionHeader* chead) +{ + BOOST_CHECK_EQUAL(chead->version, ISMRMRD_VERSION_MAJOR); + BOOST_CHECK_EQUAL(chead->number_of_samples, 0); + BOOST_CHECK_EQUAL(chead->available_channels, 1); + BOOST_CHECK_EQUAL(chead->active_channels, 1); + BOOST_CHECK_EQUAL(chead->flags, 0); + BOOST_CHECK_EQUAL(chead->measurement_uid, 0); + BOOST_CHECK_EQUAL(chead->scan_counter, 0); + BOOST_CHECK_EQUAL(chead->acquisition_time_stamp, 0); + for (int idx = 0; idx < ISMRMRD_PHYS_STAMPS; idx++) { + BOOST_CHECK_EQUAL(chead->physiology_time_stamp[idx], 0); + } + + for (int idx = 0; idx < ISMRMRD_CHANNEL_MASKS; idx++) { + BOOST_CHECK_EQUAL(chead->channel_mask[idx], 0); + } + BOOST_CHECK_EQUAL(chead->discard_pre, 0); + BOOST_CHECK_EQUAL(chead->discard_post, 0); + BOOST_CHECK_EQUAL(chead->center_sample, 0); + BOOST_CHECK_EQUAL(chead->encoding_space_ref, 0); + BOOST_CHECK_EQUAL(chead->trajectory_dimensions, 0); + BOOST_CHECK_EQUAL(chead->sample_time_us, 0); + for (int idx = 0; idx < ISMRMRD_POSITION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->position[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->read_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->phase_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->slice_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_POSITION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->patient_table_position[idx], 0); + } + + // EncodingCounters + BOOST_CHECK_EQUAL(chead->idx.kspace_encode_step_1, 0); + BOOST_CHECK_EQUAL(chead->idx.kspace_encode_step_2, 0); + BOOST_CHECK_EQUAL(chead->idx.average, 0); + BOOST_CHECK_EQUAL(chead->idx.slice, 0); + BOOST_CHECK_EQUAL(chead->idx.contrast, 0); + BOOST_CHECK_EQUAL(chead->idx.phase, 0); + BOOST_CHECK_EQUAL(chead->idx.repetition, 0); + BOOST_CHECK_EQUAL(chead->idx.set, 0); + BOOST_CHECK_EQUAL(chead->idx.segment, 0); + + for (int idx = 0; idx < ISMRMRD_USER_INTS; idx++) { + BOOST_CHECK_EQUAL(chead->idx.user[idx], 0); + } + + for (int idx = 0; idx < ISMRMRD_USER_INTS; idx++) { + BOOST_CHECK_EQUAL(chead->user_int[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_USER_FLOATS; idx++) { + BOOST_CHECK_EQUAL(chead->user_float[idx], 0); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_channels.cpp b/tests/test_channels.cpp new file mode 100644 index 0000000..54a1c8a --- /dev/null +++ b/tests/test_channels.cpp @@ -0,0 +1,76 @@ +#include "ismrmrd/ismrmrd.h" +#include "ismrmrd/version.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(ChannelTest) + +void fill_channels(uint64_t mask[ISMRMRD_CHANNEL_MASKS]) +{ + for (int i = 0; i < ISMRMRD_CHANNEL_MASKS; i++) { + mask[i] = 0xFFFFFFFFFFFFFFFF; + } +} + +BOOST_AUTO_TEST_CASE(test_is_channel_on) +{ + uint64_t channel_mask[ISMRMRD_CHANNEL_MASKS] = {0}; + + // TODO: this returns and ISMRMRD_RUNTIMEERROR, which casts to "true" + /* BOOST_CHECK_EQUAL(ismrmrd_is_channel_on(NULL, 0), false); */ + + for (int chan = 0; chan < 64 * ISMRMRD_CHANNEL_MASKS; chan++) { + BOOST_CHECK_EQUAL(ismrmrd_is_channel_on(channel_mask, chan), false); + } + + fill_channels(channel_mask); + for (int chan = 0; chan < 64 * ISMRMRD_CHANNEL_MASKS; chan++) { + BOOST_CHECK_EQUAL(ismrmrd_is_channel_on(channel_mask, chan), true); + } +} + +BOOST_AUTO_TEST_CASE(test_set_channel_on) +{ + uint64_t channel_mask[ISMRMRD_CHANNEL_MASKS] = {0}; + + BOOST_CHECK_EQUAL(ismrmrd_set_channel_on(NULL, 0), ISMRMRD_RUNTIMEERROR); + + for (int chan = 0; chan < 64 * ISMRMRD_CHANNEL_MASKS; chan++) { + BOOST_CHECK_EQUAL(ismrmrd_set_channel_on(channel_mask, chan), ISMRMRD_NOERROR); + uint64_t bitmask = 1 << (chan % 64); + size_t offset = chan / 64; + BOOST_REQUIRE((channel_mask[offset] & bitmask) != 0); + } +} + +BOOST_AUTO_TEST_CASE(test_set_channel_off) +{ + uint64_t channel_mask[ISMRMRD_CHANNEL_MASKS] = {0}; + fill_channels(channel_mask); + + BOOST_CHECK_EQUAL(ismrmrd_set_channel_off(NULL, 0), ISMRMRD_RUNTIMEERROR); + + for (int chan = 0; chan < 64 * ISMRMRD_CHANNEL_MASKS; chan++) { + BOOST_CHECK_EQUAL(ismrmrd_set_channel_off(channel_mask, chan), ISMRMRD_NOERROR); + + uint64_t bitmask = 1 << (chan % 64); + size_t offset = chan / 64; + BOOST_REQUIRE((channel_mask[offset] & bitmask) == 0); + } +} + +BOOST_AUTO_TEST_CASE(test_set_all_channels_off) +{ + uint64_t channel_mask[ISMRMRD_CHANNEL_MASKS] = {0}; + fill_channels(channel_mask); + + BOOST_CHECK_EQUAL(ismrmrd_set_all_channels_off(NULL), ISMRMRD_RUNTIMEERROR); + + BOOST_CHECK_EQUAL(ismrmrd_set_all_channels_off(channel_mask), ISMRMRD_NOERROR); + for (int idx = 0; idx < ISMRMRD_CHANNEL_MASKS; idx++) { + BOOST_REQUIRE(channel_mask[idx] == 0); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_flags.cpp b/tests/test_flags.cpp new file mode 100644 index 0000000..7402f0a --- /dev/null +++ b/tests/test_flags.cpp @@ -0,0 +1,65 @@ +#include "ismrmrd/ismrmrd.h" +#include "ismrmrd/version.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(FlagTest) + +BOOST_AUTO_TEST_CASE(test_is_flag_set) +{ + uint64_t flags = 0; + + for (int f = 1; f <= 64; f++) { + BOOST_CHECK_EQUAL(ismrmrd_is_flag_set(flags, f), false); + } + + for (int f = 1; f <= 64; f++) { + flags |= (1 << (f - 1)); + BOOST_CHECK_EQUAL(ismrmrd_is_flag_set(flags, f), true); + } +} + +BOOST_AUTO_TEST_CASE(test_set_flag) +{ + uint64_t flags = 0; + + BOOST_CHECK_EQUAL(ismrmrd_set_flag(NULL, ISMRMRD_ACQ_USER8), ISMRMRD_RUNTIMEERROR); + + for (int f = 1; f <= 64; f++) { + BOOST_CHECK_EQUAL(ismrmrd_set_flag(&flags, f), ISMRMRD_NOERROR); + BOOST_REQUIRE((flags & (1 << (f - 1))) != 0); + } +} + +BOOST_AUTO_TEST_CASE(test_set_flags) +{ + uint64_t flags = 0; + + BOOST_CHECK_EQUAL(ismrmrd_set_flags(NULL, 0xFFFFFFFFFFFFFFFF), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_set_flags(&flags, 0xFFFFFFFFFFFFFFFF), ISMRMRD_NOERROR); + + BOOST_CHECK_EQUAL(flags, 0xFFFFFFFFFFFFFFFF); +} + +BOOST_AUTO_TEST_CASE(test_clear_flag) +{ + uint64_t flags = 0xFFFFFFFFFFFFFFFF; + + BOOST_CHECK_EQUAL(ismrmrd_clear_flag(NULL, ISMRMRD_IMAGE_USER8), ISMRMRD_RUNTIMEERROR); + for (int f = 1; f <= 64; f++) { + BOOST_CHECK_EQUAL(ismrmrd_clear_flag(&flags, f), ISMRMRD_NOERROR); + BOOST_REQUIRE((flags & (1 << (f - 1))) == 0); + } +} + +BOOST_AUTO_TEST_CASE(test_clear_all_flags) +{ + uint64_t flags = 0xFFFFFFFFFFFFFFFF; + + BOOST_CHECK_EQUAL(ismrmrd_clear_all_flags(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_clear_all_flags(&flags), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(flags, 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_images.cpp b/tests/test_images.cpp new file mode 100644 index 0000000..374c603 --- /dev/null +++ b/tests/test_images.cpp @@ -0,0 +1,169 @@ +#include "ismrmrd/ismrmrd.h" +#include "ismrmrd/version.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(ImagesTest) + +static void check_header(ISMRMRD_ImageHeader* chead); + +BOOST_AUTO_TEST_CASE(test_image_header) +{ + ISMRMRD_ImageHeader chead; + + // Check that header is of expected size + size_t expected_size = 15 * sizeof(uint16_t) + + (3 + ISMRMRD_PHYS_STAMPS) * sizeof(uint32_t) + + ISMRMRD_USER_INTS * sizeof(int32_t) + + 1 * sizeof(uint64_t) + + ((2 * ISMRMRD_POSITION_LENGTH) + (3 * ISMRMRD_DIRECTION_LENGTH) + + 3 + ISMRMRD_USER_FLOATS) * sizeof(float); + BOOST_CHECK_EQUAL(sizeof(chead), expected_size); + + // Check that header is initialized properly + BOOST_CHECK_EQUAL(ismrmrd_init_image_header(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_init_image_header(&chead), ISMRMRD_NOERROR); + check_header(&chead); +} + +BOOST_AUTO_TEST_CASE(test_image_init_cleanup) +{ + ISMRMRD_Image cimg; + + // Check initialization of image + BOOST_CHECK_EQUAL(ismrmrd_init_image(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_init_image(&cimg), ISMRMRD_NOERROR); + check_header(&cimg.head); + BOOST_CHECK(!cimg.attribute_string); + BOOST_CHECK(!cimg.data); + + // Check cleanup of image + BOOST_CHECK_EQUAL(ismrmrd_cleanup_image(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_cleanup_image(&cimg), ISMRMRD_NOERROR); + BOOST_CHECK(!cimg.attribute_string); + BOOST_CHECK(!cimg.data); +} + +BOOST_AUTO_TEST_CASE(test_image_create_free) +{ + ISMRMRD_Image* cimgp = NULL; + + // Check creation of new image + BOOST_CHECK(cimgp = ismrmrd_create_image()); + // Check that it's initialized + check_header(&cimgp->head); + BOOST_CHECK(!cimgp->attribute_string); + BOOST_CHECK(!cimgp->data); + + // Check cleanup + BOOST_CHECK_EQUAL(ismrmrd_free_image(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_free_image(cimgp), ISMRMRD_NOERROR); + BOOST_CHECK(!cimgp->attribute_string); + BOOST_CHECK(!cimgp->data); +} + +BOOST_AUTO_TEST_CASE(test_image_copy) +{ + // Weak check of image copying + ISMRMRD_Image csrc, cdst; + BOOST_CHECK_EQUAL(ismrmrd_init_image(&csrc), ISMRMRD_NOERROR); + // TODO: it is necessary to call init_image on the destination image + // before copying, in case its attribute_string or data are non-NULL! + BOOST_CHECK_EQUAL(ismrmrd_init_image(&cdst), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_image(&cdst, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_image(NULL, &csrc), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_image(NULL, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_image(&cdst, &csrc), ISMRMRD_NOERROR); + check_header(&cdst.head); + BOOST_CHECK_EQUAL(ismrmrd_size_of_image_attribute_string(&cdst), 0); + BOOST_CHECK_EQUAL(ismrmrd_size_of_image_data(&cdst), 0); + BOOST_CHECK(!cdst.attribute_string); + BOOST_CHECK(!cdst.data); +} + +BOOST_AUTO_TEST_CASE(test_image_make_consistent) +{ + ISMRMRD_Image img; + BOOST_CHECK_EQUAL(ismrmrd_init_image(&img), ISMRMRD_NOERROR); + + BOOST_CHECK_EQUAL(ismrmrd_make_consistent_image(NULL), ISMRMRD_RUNTIMEERROR); + + uint16_t matrix_size[] = {128, 128, 1}; + uint16_t nchannels = 8; + uint16_t dtype = ISMRMRD_FLOAT; + uint16_t attrlen = 65; + img.head.matrix_size[0] = matrix_size[0]; + img.head.matrix_size[1] = matrix_size[1]; + img.head.matrix_size[2] = matrix_size[2]; + img.head.channels = nchannels; + img.head.data_type = dtype; + img.head.attribute_string_len = attrlen; + + BOOST_CHECK_EQUAL(ismrmrd_size_of_image_data(&img), + matrix_size[0] * matrix_size[1] * matrix_size[2] * + nchannels * ismrmrd_sizeof_data_type(dtype)); + BOOST_CHECK_EQUAL(ismrmrd_size_of_image_attribute_string(&img), + attrlen * sizeof(*img.attribute_string)); + BOOST_CHECK_EQUAL(ismrmrd_make_consistent_image(&img), ISMRMRD_NOERROR); + + // check that data and attribute_string were allocated + BOOST_CHECK(img.data); + BOOST_CHECK(img.attribute_string); + + ismrmrd_cleanup_image(&img); +} + +static void check_header(ISMRMRD_ImageHeader* chead) +{ + BOOST_CHECK_EQUAL(chead->version, ISMRMRD_VERSION_MAJOR); + BOOST_CHECK_EQUAL(chead->matrix_size[0], 0); + BOOST_CHECK_EQUAL(chead->matrix_size[1], 1); + BOOST_CHECK_EQUAL(chead->matrix_size[2], 1); + BOOST_CHECK_EQUAL(chead->channels, 1); + + BOOST_CHECK_EQUAL(chead->data_type, 0); + BOOST_CHECK_EQUAL(chead->flags, 0); + BOOST_CHECK_EQUAL(chead->measurement_uid, 0); + for (int idx = 0; idx < 3; idx++) { + BOOST_CHECK_EQUAL(chead->field_of_view[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_POSITION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->position[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->read_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->phase_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_DIRECTION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->slice_dir[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_POSITION_LENGTH; idx++) { + BOOST_CHECK_EQUAL(chead->patient_table_position[idx], 0); + } + BOOST_CHECK_EQUAL(chead->average, 0); + BOOST_CHECK_EQUAL(chead->slice, 0); + BOOST_CHECK_EQUAL(chead->contrast, 0); + BOOST_CHECK_EQUAL(chead->phase, 0); + BOOST_CHECK_EQUAL(chead->repetition, 0); + BOOST_CHECK_EQUAL(chead->set, 0); + BOOST_CHECK_EQUAL(chead->acquisition_time_stamp, 0); + for (int idx = 0; idx < ISMRMRD_PHYS_STAMPS; idx++) { + BOOST_CHECK_EQUAL(chead->physiology_time_stamp[idx], 0); + } + BOOST_CHECK_EQUAL(chead->image_type, 0); + BOOST_CHECK_EQUAL(chead->image_index, 0); + BOOST_CHECK_EQUAL(chead->image_series_index, 0); + + for (int idx = 0; idx < ISMRMRD_USER_INTS; idx++) { + BOOST_CHECK_EQUAL(chead->user_int[idx], 0); + } + for (int idx = 0; idx < ISMRMRD_USER_FLOATS; idx++) { + BOOST_CHECK_EQUAL(chead->user_float[idx], 0); + } + BOOST_CHECK_EQUAL(chead->attribute_string_len, 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_ismrmrd.h b/tests/test_ismrmrd.h new file mode 100644 index 0000000..ece311f --- /dev/null +++ b/tests/test_ismrmrd.h @@ -0,0 +1,4 @@ +#pragma once + +void silent_error_handler(const char *file, int line, + const char *function, int code, const char *msg); diff --git a/tests/test_main.cpp b/tests/test_main.cpp new file mode 100644 index 0000000..4b1809d --- /dev/null +++ b/tests/test_main.cpp @@ -0,0 +1,26 @@ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "ISMRMRD Unit Tests" +#include <boost/test/unit_test.hpp> + +#include "ismrmrd/ismrmrd.h" +using namespace ISMRMRD; + +void silent_error_handler(const char *file, int line, + const char *function, int code, const char *msg) +{ +} + +struct GlobalConfig { + // global setup + GlobalConfig() + { + // enable more verbose testing output + boost::unit_test::unit_test_log.set_threshold_level(boost::unit_test::log_test_units); + // silence ISMRMRD errors on stdout + ismrmrd_set_error_handler(silent_error_handler); + } + // global teardown + ~GlobalConfig() { } +}; + +BOOST_GLOBAL_FIXTURE(GlobalConfig); diff --git a/tests/test_ndarray.cpp b/tests/test_ndarray.cpp new file mode 100644 index 0000000..abea4ab --- /dev/null +++ b/tests/test_ndarray.cpp @@ -0,0 +1,71 @@ +#include "ismrmrd/ismrmrd.h" +#include "ismrmrd/version.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(NDArrayTest) + +BOOST_AUTO_TEST_CASE(test_ndarray_init_cleanup) +{ + ISMRMRD_NDArray carr; + + // Check initialization of ndarray + BOOST_CHECK_EQUAL(ismrmrd_init_ndarray(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_init_ndarray(&carr), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(carr.version, ISMRMRD_VERSION_MAJOR); + BOOST_CHECK_EQUAL(carr.data_type, 0); // TODO: enumerate ISMRMRD_NO_DATATYPE + BOOST_CHECK_EQUAL(carr.ndim, 0); + for (int idx = 0; idx < ISMRMRD_NDARRAY_MAXDIM; idx++) { + BOOST_CHECK_EQUAL(carr.dims[idx], 0); + } + BOOST_CHECK(!carr.data); + + // Check cleanup of ndarray + BOOST_CHECK_EQUAL(ismrmrd_cleanup_ndarray(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_cleanup_ndarray(&carr), ISMRMRD_NOERROR); + BOOST_CHECK(!carr.data); +} + +BOOST_AUTO_TEST_CASE(test_ndarray_create_free) +{ + ISMRMRD_NDArray* carrp = NULL; + + // Check creation of new ndarray + BOOST_CHECK(carrp = ismrmrd_create_ndarray()); + BOOST_CHECK_EQUAL(carrp->version, ISMRMRD_VERSION_MAJOR); + BOOST_CHECK_EQUAL(carrp->data_type, 0); // TODO: enumerate ISMRMRD_NO_DATATYPE + BOOST_CHECK_EQUAL(carrp->ndim, 0); + for (int idx = 0; idx < ISMRMRD_NDARRAY_MAXDIM; idx++) { + BOOST_CHECK_EQUAL(carrp->dims[idx], 0); + } + BOOST_CHECK(!carrp->data); + + // Check cleanup + BOOST_CHECK_EQUAL(ismrmrd_free_ndarray(NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_free_ndarray(carrp), ISMRMRD_NOERROR); + BOOST_CHECK(!carrp->data); +} + +BOOST_AUTO_TEST_CASE(test_ndarray_copy) +{ + // Weak check of ndarray copying + ISMRMRD_NDArray csrc, cdst; + BOOST_CHECK_EQUAL(ismrmrd_init_ndarray(&csrc), ISMRMRD_NOERROR); + // NOTE: it is necessary to call init_ndarray on the destination ndarray + // before copying, in case its data is non-NULL! + BOOST_CHECK_EQUAL(ismrmrd_init_ndarray(&cdst), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_ndarray(&cdst, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_ndarray(NULL, &csrc), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_ndarray(NULL, NULL), ISMRMRD_RUNTIMEERROR); + BOOST_CHECK_EQUAL(ismrmrd_copy_ndarray(&cdst, &csrc), ISMRMRD_NOERROR); + BOOST_CHECK_EQUAL(cdst.version, ISMRMRD_VERSION_MAJOR); + BOOST_CHECK_EQUAL(cdst.data_type, 0); // TODO: enumerate ISMRMRD_NO_DATATYPE + BOOST_CHECK_EQUAL(cdst.ndim, 0); + for (int idx = 0; idx < ISMRMRD_NDARRAY_MAXDIM; idx++) { + BOOST_CHECK_EQUAL(cdst.dims[idx], 0); + } + BOOST_CHECK(!cdst.data); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/test_quaternions.cpp b/tests/test_quaternions.cpp new file mode 100644 index 0000000..f73ef36 --- /dev/null +++ b/tests/test_quaternions.cpp @@ -0,0 +1,60 @@ +#include "ismrmrd/ismrmrd.h" +#include <boost/test/unit_test.hpp> + +using namespace ISMRMRD; + +BOOST_AUTO_TEST_SUITE(QuaternionTest) + +BOOST_AUTO_TEST_CASE(test_directions_to_quaternion) +{ + float read_dir[3] = {1.0, 0, 0}; + float phase_dir[3] = {0, 1.0, 0}; + float slice_dir[3] = {0, 0, 1.0}; + float quaternion[4]; + + /* convert the direction vectors to a quaternion and verify */ + ismrmrd_directions_to_quaternion(read_dir, phase_dir, slice_dir, quaternion); + + BOOST_CHECK_EQUAL(quaternion[0], 0.0); + BOOST_CHECK_EQUAL(quaternion[1], 0.0); + BOOST_CHECK_EQUAL(quaternion[2], 0.0); + BOOST_CHECK_EQUAL(quaternion[3], 1.0); +} + +BOOST_AUTO_TEST_CASE(test_quaternion_to_directions) +{ + float read_dir[3]; + float phase_dir[3]; + float slice_dir[3]; + float quaternion[4] = {0.0, 0.0, 0.0, 1.0}; + + /* convert the quaternion back to direction cosines and verify */ + ismrmrd_quaternion_to_directions(quaternion, read_dir, phase_dir, slice_dir); + BOOST_CHECK_EQUAL(read_dir[0], 1.0); + BOOST_CHECK_EQUAL(read_dir[1], 0.0); + BOOST_CHECK_EQUAL(read_dir[2], 0.0); + BOOST_CHECK_EQUAL(phase_dir[0], 0.0); + BOOST_CHECK_EQUAL(phase_dir[1], 1.0); + BOOST_CHECK_EQUAL(phase_dir[2], 0.0); + BOOST_CHECK_EQUAL(slice_dir[0], 0.0); + BOOST_CHECK_EQUAL(slice_dir[1], 0.0); + BOOST_CHECK_EQUAL(slice_dir[2], 1.0); +} + +BOOST_AUTO_TEST_CASE(test_sign_of_directions) +{ + float read_dir[3] = {1.0, 0, 0}; + float phase_dir[3] = {0, 1.0, 0}; + float slice_dir[3] = {0, 0, 1.0}; + + /* check that determinant is > 0 */ + BOOST_REQUIRE(ismrmrd_sign_of_directions(read_dir, phase_dir, slice_dir) > 0); + + /* flip sign of third column and check that determinant is < 0 */ + slice_dir[0] = -slice_dir[0]; + slice_dir[1] = -slice_dir[1]; + slice_dir[2] = -slice_dir[2]; + BOOST_REQUIRE(ismrmrd_sign_of_directions(read_dir, phase_dir, slice_dir) < 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/utilities/CMakeLists.txt b/utilities/CMakeLists.txt index 2257dec..73dc095 100644 --- a/utilities/CMakeLists.txt +++ b/utilities/CMakeLists.txt @@ -8,66 +8,68 @@ add_executable(ismrmrd_info ismrmrd_info.cpp) target_link_libraries(ismrmrd_info ismrmrd) install(TARGETS ismrmrd_info DESTINATION bin) -add_executable(ismrmrd_read_timing_test read_timing_test.cpp) -target_link_libraries(ismrmrd_read_timing_test ismrmrd) -install(TARGETS ismrmrd_read_timing_test DESTINATION bin) - if (NOT WIN32) - add_executable(ismrmrd_test_xml - ismrmrd_test_xml.cpp + add_executable(ismrmrd_test_xml + ismrmrd_test_xml.cpp ${CMAKE_SOURCE_DIR}/libsrc/pugixml.cpp ) target_link_libraries(ismrmrd_test_xml ismrmrd) install(TARGETS ismrmrd_test_xml DESTINATION bin) endif(NOT WIN32) -find_package(Boost 1.43 COMPONENTS program_options) -find_package(FFTW3 COMPONENTS single) +if (HDF5_FOUND) + add_executable(ismrmrd_read_timing_test read_timing_test.cpp) + target_link_libraries(ismrmrd_read_timing_test ismrmrd) + install(TARGETS ismrmrd_read_timing_test DESTINATION bin) + + find_package(Boost 1.43 COMPONENTS program_options) + find_package(FFTW3 COMPONENTS single) + + if(FFTW3_FOUND AND Boost_FOUND) + message("FFTW3 and Boost Found... building utilities") -if(FFTW3_FOUND AND Boost_FOUND) - message("FFTW3 and Boost Found, building utilities") - - if(WIN32) - link_directories(${Boost_LIBRARY_DIRS}) - endif(WIN32) + if(WIN32) + link_directories(${Boost_LIBRARY_DIRS}) + endif(WIN32) - include_directories( - ${CMAKE_SOURCE_DIR/include} - ${Boost_INCLUDE_DIR} - ${FFTW3_INCLUDE_DIR}) + include_directories( + ${CMAKE_SOURCE_DIR/include} + ${Boost_INCLUDE_DIR} + ${FFTW3_INCLUDE_DIR}) - # Shepp-Logan phantom - add_executable(ismrmrd_generate_cartesian_shepp_logan - generate_cartesian_shepp_logan.cpp - ismrmrd_phantom.cpp) - if(WIN32) - target_link_libraries( ismrmrd_generate_cartesian_shepp_logan - ismrmrd - ${FFTW3_LIBRARIES}) - else(WIN32) - target_link_libraries( ismrmrd_generate_cartesian_shepp_logan - ismrmrd - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${FFTW3_LIBRARIES}) - endif(WIN32) - install(TARGETS ismrmrd_generate_cartesian_shepp_logan - DESTINATION bin) + # Shepp-Logan phantom + add_executable(ismrmrd_generate_cartesian_shepp_logan + generate_cartesian_shepp_logan.cpp + ismrmrd_phantom.cpp) + if(WIN32) + target_link_libraries( ismrmrd_generate_cartesian_shepp_logan + ismrmrd + ${FFTW3_LIBRARIES}) + else(WIN32) + target_link_libraries( ismrmrd_generate_cartesian_shepp_logan + ismrmrd + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${FFTW3_LIBRARIES}) + endif(WIN32) + install(TARGETS ismrmrd_generate_cartesian_shepp_logan DESTINATION bin) - # Shepp-Logan phantom - add_executable(ismrmrd_recon_cartesian_2d - recon_cartesian_2d.cpp) - if(WIN32) - target_link_libraries( ismrmrd_recon_cartesian_2d - ismrmrd - ${FFTW3_LIBRARIES}) - else(WIN32) - target_link_libraries( ismrmrd_recon_cartesian_2d - ismrmrd - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${FFTW3_LIBRARIES}) - endif(WIN32) - install(TARGETS ismrmrd_recon_cartesian_2d - DESTINATION bin) + # Shepp-Logan phantom + add_executable(ismrmrd_recon_cartesian_2d + recon_cartesian_2d.cpp) + if(WIN32) + target_link_libraries( ismrmrd_recon_cartesian_2d + ismrmrd + ${FFTW3_LIBRARIES}) + else(WIN32) + target_link_libraries( ismrmrd_recon_cartesian_2d + ismrmrd + ${Boost_PROGRAM_OPTIONS_LIBRARY} + ${FFTW3_LIBRARIES}) + endif(WIN32) + install(TARGETS ismrmrd_recon_cartesian_2d DESTINATION bin) -else(FFTW3_FOUND AND Boost_FOUND) - message("FFTW3 or Boost NOT Found, cannot build utilities") -endif(FFTW3_FOUND AND Boost_FOUND) + else(FFTW3_FOUND AND Boost_FOUND) + message("FFTW3 or Boost NOT Found, cannot build utilities") + endif(FFTW3_FOUND AND Boost_FOUND) +else (HDF5_FOUND) + message("HDF5 NOT Found, cannot build utilities") +endif (HDF5_FOUND) diff --git a/utilities/ismrmrd_info.cpp b/utilities/ismrmrd_info.cpp index 77a94ef..e869b8c 100644 --- a/utilities/ismrmrd_info.cpp +++ b/utilities/ismrmrd_info.cpp @@ -4,7 +4,9 @@ int main(int argc, char** argv) { std::cout << "ISMRMRD VERSION INFO: " << std::endl; - std::cout << " -- version " << ISMRMRD_VERSION_MAJOR << "." << ISMRMRD_VERSION_MINOR << "." << ISMRMRD_VERSION_PATCH << std::endl; - std::cout << " -- SHA1 " << ISMRMRD_GIT_SHA1_HASH << std::endl; + std::cout << " -- Version: " << ISMRMRD_VERSION_MAJOR << "." << + ISMRMRD_VERSION_MINOR << "." << ISMRMRD_VERSION_PATCH << std::endl; + std::cout << " -- SHA1: " << ISMRMRD_GIT_SHA1_HASH << std::endl; + std::cout << " -- Dataset support: " << (ISMRMRD_DATASET_SUPPORT ? "yes" : "no") << std::endl; return 0; } diff --git a/utilities/recon_cartesian_2d.cpp b/utilities/recon_cartesian_2d.cpp index 3c09b0b..424296f 100644 --- a/utilities/recon_cartesian_2d.cpp +++ b/utilities/recon_cartesian_2d.cpp @@ -70,67 +70,86 @@ int main(int argc, char** argv) ISMRMRD::EncodingSpace e_space = hdr.encoding[0].encodedSpace; ISMRMRD::EncodingSpace r_space = hdr.encoding[0].reconSpace; - std::cout << "Encoding Matrix Size : [" << e_space.matrixSize.x << ", " << e_space.matrixSize.y << ", " << e_space.matrixSize.z << "]" << std::endl; - std::cout << "Reconstruction Matrix Size : [" << r_space.matrixSize.x << ", " << r_space.matrixSize.y << ", " << r_space.matrixSize.z << "]" << std::endl; - std::cout << "Number of acquisitions : " << d.getNumberOfAcquisitions() << std::endl; - if (e_space.matrixSize.z != 1) { std::cout << "This simple reconstruction application only supports 2D encoding spaces" << std::endl; return -1; } + + uint16_t nX = e_space.matrixSize.x; + uint16_t nY = e_space.matrixSize.y; + + // The number of channels is optional, so read the first line + ISMRMRD::Acquisition acq; + d.readAcquisition(0, acq); + uint16_t nCoils = acq.active_channels(); + + std::cout << "Encoding Matrix Size : [" << e_space.matrixSize.x << ", " << e_space.matrixSize.y << ", " << e_space.matrixSize.z << "]" << std::endl; + std::cout << "Reconstruction Matrix Size : [" << r_space.matrixSize.x << ", " << r_space.matrixSize.y << ", " << r_space.matrixSize.z << "]" << std::endl; + std::cout << "Number of Channels : " << nCoils << std::endl; + std::cout << "Number of acquisitions : " << d.getNumberOfAcquisitions() << std::endl; //Allocate a buffer for the data std::vector<size_t> dims; - dims.push_back(e_space.matrixSize.x); - dims.push_back(e_space.matrixSize.y); + dims.push_back(nX); + dims.push_back(nY); + dims.push_back(nCoils); ISMRMRD::NDArray<complex_float_t> buffer(dims); + memset(buffer.getDataPtr(), 0, sizeof(complex_float_t)*nX*nY*nCoils); //Now loop through and copy data unsigned int number_of_acquisitions = d.getNumberOfAcquisitions(); - ISMRMRD::Acquisition acq; for (unsigned int i = 0; i < number_of_acquisitions; i++) { //Read one acquisition at a time d.readAcquisition(i, acq); //Copy data, we should probably be more careful here and do more tests.... - //We are not considering multiple channels here. - unsigned int offset = acq.idx().kspace_encode_step_1*dims[0]; - memcpy(&buffer.getDataPtr()[offset], acq.getDataPtr(),sizeof(complex_float_t)*dims[0]); - } - - //Let's FFT the k-space to image - fftwf_complex* tmp = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*buffer.getNumberOfElements()); - - if (!tmp) { - std::cout << "Error allocating temporary storage for FFTW" << std::endl; - return -1; + for (uint16_t c=0; c<nCoils; c++) { + memcpy(&buffer(0,acq.idx().kspace_encode_step_1,c), &acq.data(0, c), sizeof(complex_float_t)*nX); + } } - //FFTSHIFT - fftshift(reinterpret_cast<complex_float_t*>(tmp), buffer.getDataPtr(), dims[0], dims[1]); - - //Create the FFTW plan - fftwf_plan p = fftwf_plan_dft_2d(dims[1], dims[0], tmp ,tmp, FFTW_BACKWARD, FFTW_ESTIMATE); + // Do the recon one slice at a time + for (uint16_t c=0; c<nCoils; c++) { + + //Let's FFT the k-space to image (in-place) + fftwf_complex* tmp = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex)*(nX*nY)); - //Execute the FFT - fftwf_execute(p); - - //FFTSHIFT - fftshift( buffer.getDataPtr(), reinterpret_cast<std::complex<float>*>(tmp), dims[0], dims[1]); + if (!tmp) { + std::cout << "Error allocating temporary storage for FFTW" << std::endl; + return -1; + } + + //Create the FFTW plan + fftwf_plan p = fftwf_plan_dft_2d(nY, nX, tmp ,tmp, FFTW_BACKWARD, FFTW_ESTIMATE); + + //FFTSHIFT + fftshift(reinterpret_cast<complex_float_t*>(tmp), &buffer(0,0,c), nX, nY); + + //Execute the FFT + fftwf_execute(p); + + //FFTSHIFT + fftshift( &buffer(0,0,c), reinterpret_cast<std::complex<float>*>(tmp), nX, nY); + + //Clean up. + fftwf_destroy_plan(p); + fftwf_free(tmp); - //Clean up. - fftwf_destroy_plan(p); - fftwf_free(tmp); + } //Allocate an image ISMRMRD::Image<float> img_out(r_space.matrixSize.x, r_space.matrixSize.y, 1, 1); - + memset(img_out.getDataPtr(), 0, sizeof(float_t)*r_space.matrixSize.x*r_space.matrixSize.y); + //f there is oversampling in the readout direction remove it - //Take the magnitude - size_t offset = ((e_space.matrixSize.x - r_space.matrixSize.x)>>1); - for (unsigned int y = 0; y < r_space.matrixSize.y; y++) { - for (unsigned int x = 0; x < r_space.matrixSize.x; x++) { - img_out(x,y) = std::abs(buffer(x+offset, y)); + //Take the sqrt of the sum of squares + uint16_t offset = ((e_space.matrixSize.x - r_space.matrixSize.x)>>1); + for (uint16_t y = 0; y < r_space.matrixSize.y; y++) { + for (uint16_t x = 0; x < r_space.matrixSize.x; x++) { + for (uint16_t c=0; c<nCoils; c++) { + img_out(x,y) += (std::abs(buffer(x+offset, y, c)))*(std::abs(buffer(x+offset, y, c))); + } + img_out(x,y) = std::sqrt(img_out(x,y)); } } @@ -141,7 +160,7 @@ int main(int argc, char** argv) //And so on //Let's write the reconstructed image into the same data file - d.appendImage("myimage", img_out); + d.appendImage("cpp", img_out); return 0; } -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/ismrmrd.git _______________________________________________ debian-med-commit mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit
