From c0d559980966fc26f78332f124e1dd86bf34a9b5 Mon Sep 17 00:00:00 2001 From: Marc Modat Date: Thu, 28 Jul 2022 10:10:07 +0100 Subject: [PATCH] Issue #86 (#85): removed matlab dependent tests. Added a new one with Catch2 as a dependency --- niftyreg_build_version.txt | 2 +- reg-lib/AladinContent.cpp | 2 + reg-lib/cl/CMakeLists.txt | 22 +- reg-test/CMakeLists.txt | 317 +- reg-test/matlab_tests/LSaffine.m | 83 - reg-test/matlab_tests/LSrigid.m | 61 - reg-test/matlab_tests/LTS_test.m | 40 - reg-test/matlab_tests/LTSrigid_affine.m | 71 - reg-test/matlab_tests/MINDSSD_test.m | 94 - reg-test/matlab_tests/NIfTI_20140122/FAQ.pdf | Bin 206445 -> 0 bytes .../NIfTI_20140122/NIfTI_tools.pdf | Bin 61950 -> 0 bytes .../NIfTI_20140122/UseANALYZE.pdf | Bin 91343 -> 0 bytes reg-test/matlab_tests/NIfTI_20140122/affine.m | 554 -- .../matlab_tests/NIfTI_20140122/bipolar.m | 94 - .../NIfTI_20140122/bresenham_line3d.m | 189 - .../matlab_tests/NIfTI_20140122/clip_nii.m | 115 - .../NIfTI_20140122/collapse_nii_scan.m | 260 - .../matlab_tests/NIfTI_20140122/examples.txt | 130 - .../NIfTI_20140122/expand_nii_scan.m | 48 - .../NIfTI_20140122/extra_nii_hdr.m | 255 - .../matlab_tests/NIfTI_20140122/flip_lr.m | 84 - .../NIfTI_20140122/get_nii_frame.m | 164 - .../matlab_tests/NIfTI_20140122/license.txt | 24 - .../matlab_tests/NIfTI_20140122/load_nii.m | 198 - .../NIfTI_20140122/load_nii_ext.m | 207 - .../NIfTI_20140122/load_nii_hdr.m | 280 - .../NIfTI_20140122/load_nii_img.m | 392 -- .../NIfTI_20140122/load_untouch0_nii_hdr.m | 200 - .../NIfTI_20140122/load_untouch_header_only.m | 187 - .../NIfTI_20140122/load_untouch_nii.m | 191 - .../NIfTI_20140122/load_untouch_nii_hdr.m | 217 - .../NIfTI_20140122/load_untouch_nii_img.m | 468 -- .../matlab_tests/NIfTI_20140122/make_ana.m | 210 - .../matlab_tests/NIfTI_20140122/make_nii.m | 256 - .../NIfTI_20140122/mat_into_hdr.m | 83 - .../matlab_tests/NIfTI_20140122/pad_nii.m | 142 - .../matlab_tests/NIfTI_20140122/reslice_nii.m | 321 -- .../NIfTI_20140122/rri_file_menu.m | 179 - .../matlab_tests/NIfTI_20140122/rri_orient.m | 106 - .../NIfTI_20140122/rri_orient_ui.m | 251 - .../NIfTI_20140122/rri_select_file.m | 636 --- .../matlab_tests/NIfTI_20140122/rri_xhair.m | 92 - .../NIfTI_20140122/rri_zoom_menu.m | 33 - .../matlab_tests/NIfTI_20140122/save_nii.m | 286 - .../NIfTI_20140122/save_nii_ext.m | 38 - .../NIfTI_20140122/save_nii_hdr.m | 227 - .../NIfTI_20140122/save_untouch0_nii_hdr.m | 219 - .../NIfTI_20140122/save_untouch_header_only.m | 71 - .../NIfTI_20140122/save_untouch_nii.m | 232 - .../NIfTI_20140122/save_untouch_nii_hdr.m | 207 - .../NIfTI_20140122/save_untouch_slice.m | 580 -- .../matlab_tests/NIfTI_20140122/unxform_nii.m | 40 - .../NIfTI_20140122/verify_nii_ext.m | 45 - .../matlab_tests/NIfTI_20140122/view_nii.m | 4873 ----------------- .../NIfTI_20140122/view_nii_menu.m | 480 -- .../matlab_tests/NIfTI_20140122/xform_nii.m | 521 -- reg-test/matlab_tests/SSD_test.m | 55 - .../affineDeformationField_test.m | 134 - reg-test/matlab_tests/blockMatching_test.m | 235 - reg-test/matlab_tests/changeDataType_test.m | 80 - reg-test/matlab_tests/create_test_data.m | 296 - reg-test/matlab_tests/fGaussian3D.m | 32 - reg-test/matlab_tests/getBSplineField_test.m | 180 - reg-test/matlab_tests/getConvolution_test.m | 92 - .../getLinearElasticityGradient_test.m | 484 -- .../getLinearElasticityValue_test.m | 354 -- reg-test/matlab_tests/imageGradient_test.m | 89 - reg-test/matlab_tests/interpolation_test.m | 92 - reg-test/matlab_tests/matrix_operation_test.m | 41 - reg-test/matlab_tests/mindDescriptor_test.m | 172 - .../matlab_tests/mindsscDescriptor_test.m | 209 - reg-test/matlab_tests/svd_test.m | 31 - .../reg_test_affine_deformation_field.cpp | 155 +- reg-test/target_2D.nii.gz | Bin 27076 -> 0 bytes reg-test/target_3D.nii.gz | Bin 74223 -> 0 bytes 75 files changed, 162 insertions(+), 17646 deletions(-) delete mode 100644 reg-test/matlab_tests/LSaffine.m delete mode 100644 reg-test/matlab_tests/LSrigid.m delete mode 100644 reg-test/matlab_tests/LTS_test.m delete mode 100644 reg-test/matlab_tests/LTSrigid_affine.m delete mode 100644 reg-test/matlab_tests/MINDSSD_test.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/FAQ.pdf delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/NIfTI_tools.pdf delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/UseANALYZE.pdf delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/affine.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/bipolar.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/bresenham_line3d.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/clip_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/collapse_nii_scan.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/examples.txt delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/expand_nii_scan.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/extra_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/flip_lr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/get_nii_frame.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/license.txt delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_nii_ext.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_nii_img.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_untouch0_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_untouch_header_only.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_img.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/make_ana.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/make_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/mat_into_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/pad_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/reslice_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_file_menu.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_orient.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_orient_ui.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_select_file.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_xhair.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/rri_zoom_menu.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_nii_ext.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_untouch0_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_untouch_header_only.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii_hdr.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/save_untouch_slice.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/unxform_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/verify_nii_ext.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/view_nii.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/view_nii_menu.m delete mode 100644 reg-test/matlab_tests/NIfTI_20140122/xform_nii.m delete mode 100644 reg-test/matlab_tests/SSD_test.m delete mode 100644 reg-test/matlab_tests/affineDeformationField_test.m delete mode 100644 reg-test/matlab_tests/blockMatching_test.m delete mode 100644 reg-test/matlab_tests/changeDataType_test.m delete mode 100644 reg-test/matlab_tests/create_test_data.m delete mode 100644 reg-test/matlab_tests/fGaussian3D.m delete mode 100644 reg-test/matlab_tests/getBSplineField_test.m delete mode 100644 reg-test/matlab_tests/getConvolution_test.m delete mode 100644 reg-test/matlab_tests/getLinearElasticityGradient_test.m delete mode 100644 reg-test/matlab_tests/getLinearElasticityValue_test.m delete mode 100644 reg-test/matlab_tests/imageGradient_test.m delete mode 100644 reg-test/matlab_tests/interpolation_test.m delete mode 100644 reg-test/matlab_tests/matrix_operation_test.m delete mode 100644 reg-test/matlab_tests/mindDescriptor_test.m delete mode 100644 reg-test/matlab_tests/mindsscDescriptor_test.m delete mode 100644 reg-test/matlab_tests/svd_test.m delete mode 100755 reg-test/target_2D.nii.gz delete mode 100755 reg-test/target_3D.nii.gz diff --git a/niftyreg_build_version.txt b/niftyreg_build_version.txt index d69c74c8..fff0a247 100644 --- a/niftyreg_build_version.txt +++ b/niftyreg_build_version.txt @@ -1 +1 @@ -73 +74 diff --git a/reg-lib/AladinContent.cpp b/reg-lib/AladinContent.cpp index 4d66931a..bab532ef 100755 --- a/reg-lib/AladinContent.cpp +++ b/reg-lib/AladinContent.cpp @@ -170,6 +170,8 @@ void AladinContent::AllocateDeformationField(size_t bytes) this->CurrentDeformationField = nifti_copy_nim_info(this->CurrentReference); this->CurrentDeformationField->dim[0] = this->CurrentDeformationField->ndim = 5; + if (this->CurrentReference->dim[0] == 2) + this->CurrentDeformationField->dim[3] = this->CurrentDeformationField->nz = 1; this->CurrentDeformationField->dim[4] = this->CurrentDeformationField->nt = 1; this->CurrentDeformationField->pixdim[4] = this->CurrentDeformationField->dt = 1.0; if (this->CurrentReference->nz == 1) diff --git a/reg-lib/cl/CMakeLists.txt b/reg-lib/cl/CMakeLists.txt index a5c06673..0f46b947 100755 --- a/reg-lib/cl/CMakeLists.txt +++ b/reg-lib/cl/CMakeLists.txt @@ -1,24 +1,22 @@ #----------------------------------------------------------------------------- -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") -#----------------------------------------------------------------------------- # Find the OpenCL package -find_package(OPENCL REQUIRED) -if(NOT OPENCL_FOUND) - set(USE_OPENCL OFF CACHE BOOL "To use the OpenCL platform" FORCE) - message(SEND_ERROR "OpenCL not found. The USE_OPENCL flag is turned OFF") +find_package(OpenCL REQUIRED) +if(NOT OpenCL_FOUND) + set(USE_OpenCL OFF CACHE BOOL "To use the OpenCL platform" FORCE) + message(SEND_ERROR "OpenCL not found. The USE_OpenCL flag is turned OFF") return() -else(NOT OPENCL_FOUND) +else(NOT OpenCL_FOUND) message(STATUS "Found OpenCL") -endif(NOT OPENCL_FOUND) +endif(NOT OpenCL_FOUND) #----------------------------------------------------------------------------- set(SOURCE_PATH ${CMAKE_BINARY_DIR}) #----------------------------------------------------------------------------- configure_file(config.h.in ${CMAKE_BINARY_DIR}/config.h IMMEDIATE) -mark_as_advanced(_OPENCL_CPP_INCLUDE_DIRS) +mark_as_advanced(_OpenCL_CPP_INCLUDE_DIRS) #----------------------------------------------------------------------------- include_directories(${CMAKE_BINARY_DIR}) include_directories(${CMAKE_SOURCE_DIR}/reg-lib/cl) -include_directories(${OPENCL_INCLUDE_DIRS}) +include_directories(${OpenCL_INCLUDE_DIRS}) #----------------------------------------------------------------------------- # Build the _reg_opencl_kernels library set(NAME _reg_opencl_kernels) @@ -34,7 +32,7 @@ add_library(${NAME} ${NIFTYREG_LIBRARY_TYPE} ../AladinContent.cpp ../Platform.cpp ) -target_link_libraries(${NAME} ${OPENCL_LIBRARIES}) +target_link_libraries(${NAME} ${OpenCL_LIBRARIES}) install(TARGETS ${NAME} RUNTIME DESTINATION lib LIBRARY DESTINATION lib @@ -53,7 +51,7 @@ install(FILES resampleKernel.cl affineDeformationKernel.cl blockMatchingKernel.c #----------------------------------------------------------------------------- set(NAME _reg_openclinfo) add_library(${NAME} ${NIFTYREG_LIBRARY_TYPE} ${NAME}.cpp ${NAME}.h InfoDevice.h CLContextSingletton.cpp) -target_link_libraries(${NAME} ${OPENCL_LIBRARIES}) +target_link_libraries(${NAME} ${OpenCL_LIBRARIES}) install(TARGETS ${NAME} RUNTIME DESTINATION lib LIBRARY DESTINATION lib diff --git a/reg-test/CMakeLists.txt b/reg-test/CMakeLists.txt index adc66d1d..df71281f 100755 --- a/reg-test/CMakeLists.txt +++ b/reg-test/CMakeLists.txt @@ -1,110 +1,11 @@ -#----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- -# Define the folder to use to store all data -set(DFOLDER ${CMAKE_BINARY_DIR}/reg-test/test-data) -#----------------------------------------------------------------------------- -# Set the 2D image input file -if(EXISTS "${DFOLDER}/refImg2D.nii.gz") - set(TESTING_2D_FILE "${DFOLDER}/refImg2D.nii.gz" CACHE FILEPATH "2D nifti file used to generate testing data") -else(EXISTS "${DFOLDER}/refImg2D.nii.gz") - set(TESTING_2D_FILE "" CACHE FILEPATH "2D nifti file used to generate testing data") -endif(EXISTS "${DFOLDER}/refImg2D.nii.gz") -if(NOT EXISTS "${TESTING_2D_FILE}" AND NOT EXISTS "${DFOLDER}/refImg2D.nii.gz") - set(BUILD_TESTING OFF CACHE BOOL "To build the unit tests" FORCE) - message(STATUS "The specified 2D image file does not exists") - message(STATUS "TESTING_2D_FILE=${TESTING_2D_FILE}") - message(SEND_ERROR "The BUILD_TESTING flag is turned OFF") - return() -endif(NOT EXISTS "${TESTING_2D_FILE}" AND NOT EXISTS "${DFOLDER}/refImg2D.nii.gz") -#----------------------------------------------------------------------------- -# Set the 3D image input file -if(EXISTS "${DFOLDER}/refImg3D.nii.gz") - set(TESTING_3D_FILE "${DFOLDER}/refImg3D.nii.gz" CACHE FILEPATH "3D nifti file used to generate testing data") -else(EXISTS "${DFOLDER}/refImg3D.nii.gz") - set(TESTING_3D_FILE "" CACHE FILEPATH "3D nifti file used to generate testing data") -endif(EXISTS "${DFOLDER}/refImg3D.nii.gz") -if(NOT EXISTS "${TESTING_3D_FILE}" AND NOT EXISTS "${DFOLDER}/refImg3D.nii.gz") +find_package(Catch2 3) +if(NOT Catch2_FOUND) set(BUILD_TESTING OFF CACHE BOOL "To build the unit tests" FORCE) - message(STATUS "The specified 3D image file does not exists") - message(STATUS "TESTING_3D_FILE=${TESTING_3D_FILE}") - message(SEND_ERROR "The BUILD_TESTING flag is turned OFF") + message(STATUS "Catch2 not found") + message(SEND_ERROR "Catch2 is required to generate the unit test. + The BUILD_TESTING flag is turned OFF") return() -endif(NOT EXISTS "${TESTING_3D_FILE}" AND NOT EXISTS "${DFOLDER}/refImg3D.nii.gz") -#----------------------------------------------------------------------------- -# Run the matlab command if required -if(NOT EXISTS "${DFOLDER}/refImg2D.nii.gz" OR NOT EXISTS "${DFOLDER}/refImg3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/affine_mat2D.txt" OR NOT EXISTS "${DFOLDER}/affine_mat3D.txt" OR - NOT EXISTS "${DFOLDER}/affine_def2D.nii.gz" OR NOT EXISTS "${DFOLDER}/affine_def3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/bspline_def2D.nii.gz" OR NOT EXISTS "${DFOLDER}/bspline_def3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/bspline_grid2D.nii.gz" OR NOT EXISTS "${DFOLDER}/bspline_grid3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/refImg2D_uchar.nii.gz" OR NOT EXISTS "${DFOLDER}/refImg3D_uchar.nii.gz" OR - NOT EXISTS "${DFOLDER}/refImg2D_float.nii.gz" OR NOT EXISTS "${DFOLDER}/refImg3D_float.nii.gz" OR - NOT EXISTS "${DFOLDER}/refImg2D_double.nii.gz" OR NOT EXISTS "${DFOLDER}/refImg3D_double.nii.gz" OR - NOT EXISTS "${DFOLDER}/warped_nearest2D.nii.gz" OR NOT EXISTS "${DFOLDER}/warped_nearest3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/warped_linear2D.nii.gz" OR NOT EXISTS "${DFOLDER}/warped_linear3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/warped_cubic2D.nii.gz" OR NOT EXISTS "${DFOLDER}/warped_cubic3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/expectedBlockMatching_mat2D.txt" OR NOT EXISTS "${DFOLDER}/expectedBlockMatching_mat3D.txt" OR - NOT EXISTS "${DFOLDER}/warpedBlockMatchingImg2D.nii.gz" OR NOT EXISTS "${DFOLDER}/warpedBlockMatchingImg3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/inputSVDMatrix.txt" OR NOT EXISTS "${DFOLDER}/expectedSMatrix.txt" OR - NOT EXISTS "${DFOLDER}/inputMatrix1.txt" OR NOT EXISTS "${DFOLDER}/inputMatrix2.txt" OR - NOT EXISTS "${DFOLDER}/expectedVMatrix.txt" OR NOT EXISTS "${DFOLDER}/expectedUMatrix.txt" OR - NOT EXISTS "${DFOLDER}/expectedSubMatrix.txt" OR NOT EXISTS "${DFOLDER}/expectedAddMatrix.txt" OR - NOT EXISTS "${DFOLDER}/expectedMulMatrix.txt" OR NOT EXISTS "${DFOLDER}/expectedInvMatrix.txt" OR - NOT EXISTS "${DFOLDER}/expectedLogMatrix.txt" OR NOT EXISTS "${DFOLDER}/expectedExpMatrix.txt" OR - NOT EXISTS "${DFOLDER}/expectedRigidLTS_2D_70.txt" OR NOT EXISTS "${DFOLDER}/expectedRigidLTS_3D_70.txt" OR - NOT EXISTS "${DFOLDER}/expectedRigidLTS_2D_100.txt" OR NOT EXISTS "${DFOLDER}/expectedRigidLTS_3D_100.txt" OR - NOT EXISTS "${DFOLDER}/expectedAffineLTS_2D_70.txt" OR NOT EXISTS "${DFOLDER}/expectedAffineLTS_3D_70.txt" OR - NOT EXISTS "${DFOLDER}/expectedAffineLTS_2D_100.txt" OR NOT EXISTS "${DFOLDER}/expectedAffineLTS_3D_100.txt" OR - NOT EXISTS "${DFOLDER}/expectedMINDDescriptor2D_1.nii.gz" OR NOT EXISTS "${DFOLDER}/expectedMINDDescriptor3D_1.nii.gz" OR - NOT EXISTS "${DFOLDER}/expectedMINDDescriptor2D_2.nii.gz" OR NOT EXISTS "${DFOLDER}/expectedMINDDescriptor3D_2.nii.gz" OR - NOT EXISTS "${DFOLDER}/P1_2D.txt" OR NOT EXISTS "${DFOLDER}/P1_3D.txt" OR - NOT EXISTS "${DFOLDER}/P2_2D.txt" OR NOT EXISTS "${DFOLDER}/P2_3D.txt" OR - NOT EXISTS "${DFOLDER}/expectedSSDValue2D.txt" OR NOT EXISTS "${DFOLDER}/expectedSSDValue3D.txt" OR - NOT EXISTS "${DFOLDER}/expectedMINDSSDValue2D.txt" OR NOT EXISTS "${DFOLDER}/expectedMINDSSDValue3D.txt" OR - NOT EXISTS "${DFOLDER}/expectedMINDSSCDescriptor2D_1.nii.gz" OR NOT EXISTS "${DFOLDER}/expectedMINDSSCDescriptor3D_1.nii.gz" OR - NOT EXISTS "${DFOLDER}/expectedMINDSSCDescriptor2D_2.nii.gz" OR NOT EXISTS "${DFOLDER}/expectedMINDSSCDescriptor3D_2.nii.gz" OR - NOT EXISTS "${DFOLDER}/expectedImageGradient2D.nii.gz" OR NOT EXISTS "${DFOLDER}/expectedImageGradient3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/convolution2D_mea.nii.gz" OR NOT EXISTS "${DFOLDER}/convolution3D_mea.nii.gz" OR - NOT EXISTS "${DFOLDER}/convolution2D_lin.nii.gz" OR NOT EXISTS "${DFOLDER}/convolution3D_lin.nii.gz" OR - NOT EXISTS "${DFOLDER}/convolution2D_gau.nii.gz" OR NOT EXISTS "${DFOLDER}/convolution3D_gau.nii.gz" OR - NOT EXISTS "${DFOLDER}/convolution2D_spl.nii.gz" OR NOT EXISTS "${DFOLDER}/convolution3D_spl.nii.gz" OR - NOT EXISTS "${DFOLDER}/le_spline_approx2D.txt" OR NOT EXISTS "${DFOLDER}/le_spline_approx3D.txt" OR - NOT EXISTS "${DFOLDER}/le_spline_dense2D.txt" OR NOT EXISTS "${DFOLDER}/le_spline_dense3D.txt" OR - NOT EXISTS "${DFOLDER}/le_field_dense2D.txt" OR NOT EXISTS "${DFOLDER}/le_field_dense3D.txt" OR - NOT EXISTS "${DFOLDER}/le_grad_spline_approx2D.nii.gz" OR NOT EXISTS "${DFOLDER}/le_grad_spline_approx3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/le_grad_spline_dense2D.nii.gz" OR NOT EXISTS "${DFOLDER}/le_grad_spline_dense3D.nii.gz" OR - NOT EXISTS "${DFOLDER}/le_grad_field_dense2D.nii.gz" OR NOT EXISTS "${DFOLDER}/le_grad_field_dense3D.nii.gz" - ) - # Generate the test data using matlab - find_package(Matlab) - if(NOT Matlab_FOUND) - set(BUILD_TESTING OFF CACHE BOOL "To build the unit tests" FORCE) - message(STATUS "Matlab not found") - message(SEND_ERROR "Matlab is required to generate the test data. The BUILD_TESTING flag is turned OFF") - return() - else(NOT Matlab_FOUND) - message(STATUS "Found Matlab") - endif(NOT Matlab_FOUND) - message(STATUS "Generating the test data - Takes several minutes") - if(MSVC) - execute_process(COMMAND - ${Matlab_ROOT_DIR}/bin/matlab -noFigureWindows -nosplash -wait -r "create_test_data('${TESTING_2D_FILE}', '${TESTING_3D_FILE}', '${DFOLDER}');exit" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/reg-test/matlab_tests" - OUTPUT_VARIABLE MATLAB_CMD_OUTPUT - ) - else(MSVC) - execute_process(COMMAND - ${Matlab_ROOT_DIR}/bin/matlab "-nodisplay -nosplash -nodesktop -r \";create_test_data(\'${TESTING_2D_FILE}\',\'${TESTING_3D_FILE}\',\'${DFOLDER}\');exit\"" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/reg-test/matlab_tests" - OUTPUT_VARIABLE MATLAB_CMD_OUTPUT - ) - endif(MSVC) - - message(STATUS "Maltab command output:") - message(STATUS "${MATLAB_CMD_OUTPUT}") -else() - message(STATUS "All test data already exists") -endif() +endif(NOT Catch2_FOUND) #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- # Build the coverage test @@ -153,200 +54,20 @@ endif(NOT MSVC) mark_as_advanced(BUILDNAME) #----------------------------------------------------------------------------- #----------------------------------------------------------------------------- -set(PLATFORM_LIST 0) -if(USE_CUDA) - set(PLATFORM_LIST ${PLATFORM_LIST} 1) -endif(USE_CUDA) -if(USE_OPENCL) - set(PLATFORM_LIST ${PLATFORM_LIST} 2) -endif(USE_OPENCL) -#----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- -set(EXEC reg_test_affine_deformation_field) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_aladin) -foreach(CURRENT_PLATFORM ${PLATFORM_LIST}) - add_test(${EXEC}_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_mat2D.txt ${DFOLDER}/affine_def2D.nii.gz ${CURRENT_PLATFORM}) - add_test(${EXEC}_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_mat3D.txt ${DFOLDER}/affine_def3D.nii.gz ${CURRENT_PLATFORM}) -endforeach(CURRENT_PLATFORM) -#----------------------------------------------------------------------------- -set(EXEC reg_test_bspline_deformation_field) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -foreach(CURRENT_PLATFORM "0") - add_test(${EXEC}_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/bspline_def2D.nii.gz 0 ${CURRENT_PLATFORM}) - add_test(${EXEC}_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/bspline_def3D.nii.gz 0 ${CURRENT_PLATFORM}) - add_test(${EXEC}_comp_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/bspline_def2D.nii.gz 1 ${CURRENT_PLATFORM}) - add_test(${EXEC}_comp_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/bspline_def3D.nii.gz 1 ${CURRENT_PLATFORM}) -endforeach(CURRENT_PLATFORM) -#----------------------------------------------------------------------------- -set(EXEC reg_test_blockMatching) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_aladin) -foreach(CURRENT_PLATFORM ${PLATFORM_LIST}) - add_test(${EXEC}_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/warpedBlockMatchingImg2D.nii.gz ${DFOLDER}/expectedBlockMatching_mat2D.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/warpedBlockMatchingImg3D.nii.gz ${DFOLDER}/expectedBlockMatching_mat3D.txt ${CURRENT_PLATFORM}) -endforeach(CURRENT_PLATFORM) -#----------------------------------------------------------------------------- - set(EXEC reg_test_changeDataType) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - add_test(${EXEC}_uchar_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz uchar ${DFOLDER}/refImg2D_uchar.nii.gz) - add_test(${EXEC}_float_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz float ${DFOLDER}/refImg2D_float.nii.gz) - add_test(${EXEC}_double_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz double ${DFOLDER}/refImg2D_double.nii.gz) - add_test(${EXEC}_uchar_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz uchar ${DFOLDER}/refImg3D_uchar.nii.gz) - add_test(${EXEC}_float_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz float ${DFOLDER}/refImg3D_float.nii.gz) - add_test(${EXEC}_double_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz double ${DFOLDER}/refImg3D_double.nii.gz) -#----------------------------------------------------------------------------- - set(EXEC reg_test_interpolation) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - foreach(CURRENT_PLATFORM ${PLATFORM_LIST}) - add_test(${EXEC}_nea_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz ${DFOLDER}/warped_nearest2D.nii.gz 0 ${CURRENT_PLATFORM}) - add_test(${EXEC}_nea_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz ${DFOLDER}/warped_nearest3D.nii.gz 0 ${CURRENT_PLATFORM}) - add_test(${EXEC}_lin_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz ${DFOLDER}/warped_linear2D.nii.gz 1 ${CURRENT_PLATFORM}) - add_test(${EXEC}_lin_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz ${DFOLDER}/warped_linear3D.nii.gz 1 ${CURRENT_PLATFORM}) - add_test(${EXEC}_cub_2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz ${DFOLDER}/warped_cubic2D.nii.gz 3 ${CURRENT_PLATFORM}) - add_test(${EXEC}_cub_3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz ${DFOLDER}/warped_cubic3D.nii.gz 3 ${CURRENT_PLATFORM}) - endforeach(CURRENT_PLATFORM) -#----------------------------------------------------------------------------- - set(EXEC reg_test_leastTrimmedSquares) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - foreach(CURRENT_PLATFORM ${PLATFORM_LIST}) - add_test(${EXEC}_RigidLS2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_2D.txt ${DFOLDER}/P2_2D.txt 100 0 ${DFOLDER}/expectedRigidLTS_2D_100.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_AffineLS2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_2D.txt ${DFOLDER}/P2_2D.txt 100 1 ${DFOLDER}/expectedAffineLTS_2D_100.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_RigidLS3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_3D.txt ${DFOLDER}/P2_3D.txt 100 0 ${DFOLDER}/expectedRigidLTS_3D_100.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_AffineLS3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_3D.txt ${DFOLDER}/P2_3D.txt 100 1 ${DFOLDER}/expectedAffineLTS_3D_100.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_RigidLTS2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_2D.txt ${DFOLDER}/P2_2D.txt 70 0 ${DFOLDER}/expectedRigidLTS_2D_70.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_AffineLTS2D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_2D.txt ${DFOLDER}/P2_2D.txt 70 1 ${DFOLDER}/expectedAffineLTS_2D_70.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_RigidLTS3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_3D.txt ${DFOLDER}/P2_3D.txt 70 0 ${DFOLDER}/expectedRigidLTS_3D_70.txt ${CURRENT_PLATFORM}) - add_test(${EXEC}_AffineLTS3D_${CURRENT_PLATFORM} ${EXEC} ${DFOLDER}/P1_3D.txt ${DFOLDER}/P2_3D.txt 70 1 ${DFOLDER}/expectedAffineLTS_3D_70.txt ${CURRENT_PLATFORM}) - endforeach(CURRENT_PLATFORM) -#----------------------------------------------------------------------------- - set(EXEC reg_test_matrix_operation) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_maths _reg_ReadWriteImage) - add_test(${EXEC} ${EXEC} ${DFOLDER}/inputMatrix1.txt ${DFOLDER}/inputMatrix2.txt ${DFOLDER}/expectedMulMatrix.txt ${DFOLDER}/expectedAddMatrix.txt ${DFOLDER}/expectedSubMatrix.txt ${DFOLDER}/expectedExpMatrix.txt ${DFOLDER}/expectedLogMatrix.txt ${DFOLDER}/expectedInvMatrix.txt) +include(CTest) +include(Catch) #----------------------------------------------------------------------------- -set(EXEC reg_test_svd) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_maths _reg_ReadWriteImage) -add_test(${EXEC}_0 ${EXEC} ${DFOLDER}/inputSVDMatrix.txt ${DFOLDER}/expectedUMatrix.txt ${DFOLDER}/expectedSMatrix.txt ${DFOLDER}/expectedVMatrix.txt) #----------------------------------------------------------------------------- -if(USE_CUDA OR USE_OPENCL) - set(EXEC reg_test_coherence_blockMatching) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - if(USE_CUDA) - add_test(${EXEC}_2D_1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/warped_cubic2D.nii.gz 1) - add_test(${EXEC}_3D_1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/warped_cubic3D.nii.gz 1) - endif(USE_CUDA) - if(USE_OPENCL) - add_test(${EXEC}_2D_2 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/warped_cubic2D.nii.gz 2) - add_test(${EXEC}_3D_2 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/warped_cubic3D.nii.gz 2) - endif(USE_OPENCL) - # - set(EXEC reg_test_coherence_interpolation) - add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - if(USE_CUDA) - add_test(${EXEC}_nea_2D_1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 0 1) - add_test(${EXEC}_lin_2D_1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 1 1) - add_test(${EXEC}_cub_2D_1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 3 1) - add_test(${EXEC}_nea_3D_1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 0 1) - add_test(${EXEC}_lin_3D_1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 1 1) - add_test(${EXEC}_cub_3D_1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 3 1) - endif(USE_CUDA) - if(USE_OPENCL) - add_test(${EXEC}_nea_2D_2 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 0 2) - add_test(${EXEC}_lin_2D_2 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 1 2) - add_test(${EXEC}_cub_2D_2 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_def2D.nii.gz 3 2) - add_test(${EXEC}_nea_3D_2 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 0 2) - add_test(${EXEC}_lin_3D_2 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 1 2) - add_test(${EXEC}_cub_3D_2 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_def3D.nii.gz 3 2) - endif(USE_OPENCL) - # - set(EXEC reg_test_coherence_affine_deformation_field) +set(EXEC_LIST reg_test_affine_deformation_field) +set(EXEC_LIST ${EXEC_LIST}) + + +foreach(EXEC ${EXEC_LIST}) add_executable(${EXEC} ${EXEC}.cpp) - target_link_libraries(${EXEC} _reg_aladin) - if(USE_CUDA) - add_test(${EXEC}_2D_1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_mat2D.txt ${DFOLDER}/affine_def2D.nii.gz 1) - add_test(${EXEC}_3D_1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_mat3D.txt ${DFOLDER}/affine_def3D.nii.gz 1) - endif(USE_CUDA) - if(USE_OPENCL) - add_test(${EXEC}_2D_2 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/affine_mat2D.txt ${DFOLDER}/affine_def2D.nii.gz 2) - add_test(${EXEC}_3D_2 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/affine_mat3D.txt ${DFOLDER}/affine_def3D.nii.gz 2) - endif(USE_OPENCL) -endif(USE_CUDA OR USE_OPENCL) -#----------------------------------------------------------------------------- -set(EXEC reg_test_mindDescriptor) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_MINDDescriptor_2D1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/expectedMINDDescriptor2D_1.nii.gz) -add_test(${EXEC}_MINDDescriptor_3D1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/expectedMINDDescriptor3D_1.nii.gz) -add_test(${EXEC}_MINDDescriptor_2D2 ${EXEC} ${DFOLDER}/warped_linear2D.nii.gz ${DFOLDER}/expectedMINDDescriptor2D_2.nii.gz) -add_test(${EXEC}_MINDDescriptor_3D2 ${EXEC} ${DFOLDER}/warped_linear3D.nii.gz ${DFOLDER}/expectedMINDDescriptor3D_2.nii.gz) -#----------------------------------------------------------------------------- -set(EXEC reg_test_mindsscDescriptor) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_MINDSSCDescriptor_2D1 ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/expectedMINDSSCDescriptor2D_1.nii.gz) -add_test(${EXEC}_MINDSSCDescriptor_3D1 ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/expectedMINDSSCDescriptor3D_1.nii.gz) -add_test(${EXEC}_MINDSSCDescriptor_2D2 ${EXEC} ${DFOLDER}/warped_linear2D.nii.gz ${DFOLDER}/expectedMINDSSCDescriptor2D_2.nii.gz) -add_test(${EXEC}_MINDSSCDescriptor_3D2 ${EXEC} ${DFOLDER}/warped_linear3D.nii.gz ${DFOLDER}/expectedMINDSSCDescriptor3D_2.nii.gz) -#----------------------------------------------------------------------------- -set(EXEC reg_test_measure) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_SSD_2D ${EXEC} ${DFOLDER}/expectedMINDDescriptor2D_1.nii.gz ${DFOLDER}/expectedMINDDescriptor2D_2.nii.gz SSD ${DFOLDER}/expectedSSDValue2D.txt) -add_test(${EXEC}_MINDSSD_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/warped_linear2D.nii.gz MIND ${DFOLDER}/expectedMINDSSDValue2D.txt) -add_test(${EXEC}_SSD_3D ${EXEC} ${DFOLDER}/expectedMINDDescriptor3D_1.nii.gz ${DFOLDER}/expectedMINDDescriptor3D_2.nii.gz SSD ${DFOLDER}/expectedSSDValue3D.txt) -add_test(${EXEC}_MINDSSD_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/warped_linear3D.nii.gz MIND ${DFOLDER}/expectedMINDSSDValue3D.txt) -#----------------------------------------------------------------------------- -set(EXEC reg_test_imageGradient) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_imageGradient_SYD_2D ${EXEC} ${DFOLDER}/expectedMINDDescriptor2D_1.nii.gz ${DFOLDER}/expectedImageGradient2D.nii.gz 0) -add_test(${EXEC}_imageGradient_SYD_3D ${EXEC} ${DFOLDER}/expectedMINDDescriptor3D_1.nii.gz ${DFOLDER}/expectedImageGradient3D.nii.gz 0) -add_test(${EXEC}_imageGradient_SPL_2D ${EXEC} ${DFOLDER}/expectedMINDDescriptor2D_1.nii.gz ${DFOLDER}/expectedImageGradient2D.nii.gz 3) -add_test(${EXEC}_imageGradient_SPL_3D ${EXEC} ${DFOLDER}/expectedMINDDescriptor3D_1.nii.gz ${DFOLDER}/expectedImageGradient3D.nii.gz 3) -#----------------------------------------------------------------------------- -set(EXEC reg_test_convolution) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_convolution_MEA_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/convolution2D_mea.nii.gz 0) -add_test(${EXEC}_convolution_LIN_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/convolution2D_lin.nii.gz 1) -add_test(${EXEC}_convolution_GAU_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/convolution2D_gau.nii.gz 2) -add_test(${EXEC}_convolution_SPL_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/convolution2D_spl.nii.gz 3) -add_test(${EXEC}_convolution_MEA_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/convolution3D_mea.nii.gz 0) -add_test(${EXEC}_convolution_LIN_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/convolution3D_lin.nii.gz 1) -add_test(${EXEC}_convolution_GAU_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/convolution3D_gau.nii.gz 2) -add_test(${EXEC}_convolution_SPL_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/convolution3D_spl.nii.gz 3) -#----------------------------------------------------------------------------- -set(EXEC reg_test_linearElasticity) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_SPL_APP_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/le_spline_approx2D.txt 0) -add_test(${EXEC}_SPL_APP_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/le_spline_approx3D.txt 0) -add_test(${EXEC}_SPL_DEN_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/le_spline_dense2D.txt 1) -add_test(${EXEC}_SPL_DEN_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/le_spline_dense3D.txt 1) -add_test(${EXEC}_DEF_DEN_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_def2D.nii.gz ${DFOLDER}/le_field_dense2D.txt 2) -add_test(${EXEC}_DEF_DEN_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_def3D.nii.gz ${DFOLDER}/le_field_dense3D.txt 2) -#----------------------------------------------------------------------------- -set(EXEC reg_test_linearElasticityGradient) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -add_test(${EXEC}_SPL_APP_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/le_grad_spline_approx2D.nii.gz 0) -add_test(${EXEC}_SPL_APP_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/le_grad_spline_approx3D.nii.gz 0) -add_test(${EXEC}_SPL_DEN_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_grid2D.nii.gz ${DFOLDER}/le_grad_spline_dense2D.nii.gz 1) -add_test(${EXEC}_SPL_DEN_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_grid3D.nii.gz ${DFOLDER}/le_grad_spline_dense3D.nii.gz 1) -add_test(${EXEC}_DEF_DEN_2D ${EXEC} ${DFOLDER}/refImg2D.nii.gz ${DFOLDER}/bspline_def2D.nii.gz ${DFOLDER}/le_grad_field_dense2D.nii.gz 2) -add_test(${EXEC}_DEF_DEN_3D ${EXEC} ${DFOLDER}/refImg3D.nii.gz ${DFOLDER}/bspline_def3D.nii.gz ${DFOLDER}/le_grad_field_dense3D.nii.gz 2) -#----------------------------------------------------------------------------- -#----------------------------------------------------------------------------- -set(EXEC reg_test_computation_time) -add_executable(${EXEC} ${EXEC}.cpp) -target_link_libraries(${EXEC} _reg_f3d) -#----------------------------------------------------------------------------- + target_link_libraries(${EXEC} PRIVATE Catch2::Catch2WithMain) + target_link_libraries(${EXEC} PRIVATE _reg_aladin) + target_link_libraries(${EXEC} PRIVATE _reg_f3d) + catch_discover_tests(${EXEC}) +endforeach(EXEC) #----------------------------------------------------------------------------- +#----------------------------------------------------------------------------- \ No newline at end of file diff --git a/reg-test/matlab_tests/LSaffine.m b/reg-test/matlab_tests/LSaffine.m deleted file mode 100644 index 12d7bd7e..00000000 --- a/reg-test/matlab_tests/LSaffine.m +++ /dev/null @@ -1,83 +0,0 @@ -function M = LSaffine(p1,p2) -%p1=[x1,y1; -% x1',y1'; -% ...]; -%p2=[x2,y2; -% x2',y2']; -%% -%p2 = Affine*p1; -%% -%% 2D or 3D -dim=size(p1,2); -numPoints = size(p1,1); -numEquations = numPoints*dim; -if(dim==2) - %% CREATE THE MATRIX A - A=zeros(numEquations,6,'single'); - compteur = 1; - for ii=1:numPoints - A(compteur,:)=[p1(ii,:) 1 0 0 0]; - A(compteur+1,:)=[0 0 0 p1(ii,:) 1]; - compteur=compteur+2; - end - % -elseif(dim==3) - %% CREATE THE MATRIX A - A=zeros(numEquations,12,'single'); - compteur = 1; - for ii=1:numPoints - A(compteur,:) =[p1(ii,:) 1 0 0 0 0 0 0 0 0]; - A(compteur+1,:)=[0 0 0 0 p1(ii,:) 1 0 0 0 0]; - A(compteur+2,:)=[0 0 0 0 0 0 0 0 p1(ii,:) 1]; - compteur=compteur+3; - end -else - error('LSaffine - dim > 3 not supported') -end -%% -%% Pseudo inverse: -%% A+ = V*inv(Sig)*U'; -%% -%SVD de A : A=U*Sig*V' -[U,Sig,V] = svd(double(A),'econ'); -U=single(U); -Sig=single(Sig); -V=single(V); -Sig(Sig(:)< 0.0001)=0; -%% 1st inv -Sig=single(inv(double(Sig))); -%% mul -tmpMulMat = single(double(V)*double(Sig)); -%% last mul -Aplus = single(double(tmpMulMat)*double(U)'); -% -B=single(double(p2)'); -B=single(double(B(:))); -% -S = single(double(Aplus)*double(B)); -%% -%Transform matrix -M = eye(4,4,'single'); -if dim==2 - M(1,1)= S(1); - M(1,2)= S(2); - M(1,4)= S(3); - M(2,1)= S(4); - M(2,2)= S(5); - M(2,4)= S(6); - %M(3,3)=0; -else - M(1,1)= S(1); - M(1,2)= S(2); - M(1,3)= S(3); - M(1,4)= S(4); - M(2,1)= S(5); - M(2,2)= S(6); - M(2,3)= S(7); - M(2,4)= S(8); - M(3,1)= S(9); - M(3,2)= S(10); - M(3,3)= S(11); - M(3,4)= S(12); -end -end \ No newline at end of file diff --git a/reg-test/matlab_tests/LSrigid.m b/reg-test/matlab_tests/LSrigid.m deleted file mode 100644 index d88b90bc..00000000 --- a/reg-test/matlab_tests/LSrigid.m +++ /dev/null @@ -1,61 +0,0 @@ -function M = LSrigid(p1,p2) -%p1=[x1,y1; -% x1',y1'; -% ...]; -%p2=[x2,y2; -% x2',y2']; -%% -%p2 = Rp1+T; -%% 2D or 3D -dim=size(p1,2); -%% -mean_p1=single(mean(double(p1))); -mean_p2=single(mean(double(p2))); -%Coord bary: -if dim == 2 - coordBary_p1=single([double(p1(:,1))-double(mean_p1(1)),double(p1(:,2))-double(mean_p1(2))]); - coordBary_p2=single([double(p2(:,1))-double(mean_p2(1)),double(p2(:,2))-double(mean_p2(2))]); -elseif dim ==3 - coordBary_p1=single([double(p1(:,1))-double(mean_p1(1)), double(p1(:,2))-double(mean_p1(2)), double(p1(:,3))-double(mean_p1(3))]); - coordBary_p2=single([double(p2(:,1))-double(mean_p2(1)), double(p2(:,2))-double(mean_p2(2)), double(p2(:,3))-double(mean_p2(3))]); -else - error('we only handle 2D or 3D points'); -end -%Covariance matrix (2,2) = (2,n)*(n,2); -S=single(double(coordBary_p1)'*double(coordBary_p2)); -%SVD de S : S=U*Sig*V' -[U,Sig,V] = svd(double(S),'econ'); -U=single(U); -Sig=single(Sig); -V=single(V); -R=single(double(V)*double(U)'); -if(det(double(R))<0) - %disp('reflection case'); - V(:,end)=single(-double(V(:,end))); - R=single(double(V)*double(U)'); -end -% -T=single(double(mean_p2)'-double(R)*double(mean_p1)'); -%% Verification -%figure; -%hold on; -%plot(p2(:,1),p2(:,2),'b+'); -%p1Transform = R*p1'+repmat(T,1,size(p1',2)); -%p1Transform = p1Transform'; -%plot(p1Transform(:,1),p1Transform(:,2),'r+'); -%% -%Transform matrix -M = eye(4,4,'single'); -if dim==2 - M(1:2,1:2)=R; - M(1,4)= T(1); - M(2,4)= T(2); - %M(3,3)=0; -else - M(1:3,1:3)=R; - M(1,4)= T(1); - M(2,4)= T(2); - M(3,4)= T(3); -end -% -end \ No newline at end of file diff --git a/reg-test/matlab_tests/LTS_test.m b/reg-test/matlab_tests/LTS_test.m deleted file mode 100644 index d7a208b0..00000000 --- a/reg-test/matlab_tests/LTS_test.m +++ /dev/null @@ -1,40 +0,0 @@ -function LTS_test(output_path) -%% Let's generate two sets of points -%% 2-D and then 3D -nPoints = 100; -max_iter = 30; -% -for dimData = [2 3] - %-5 + (5+5)*rand(10,1) - P1=0 + 10*rand(nPoints,dimData); - P1=single(P1); - P2=0 + 10*rand(nPoints,dimData); - P2=single(P2); - %% - dlmwrite(strcat([output_path,'/P1_',num2str(dimData),'D.txt']),P1,'precision','%.6f','delimiter',' '); - dlmwrite(strcat([output_path,'/P2_',num2str(dimData),'D.txt']),P2,'precision','%.6f','delimiter',' '); - %% READ THE FILE THAT WE HAVE JUST WRITTEN - fileID = fopen(strcat([output_path,'/P1_',num2str(dimData),'D.txt']),'r'); - formatSpec = '%f'; - size = [dimData nPoints]; - P1 = fscanf(fileID,formatSpec,size); - P1 = single(P1'); - fclose(fileID); - fileID = fopen(strcat([output_path,'/P2_',num2str(dimData),'D.txt']),'r'); - formatSpec = '%f'; - size = [dimData nPoints]; - P2 = fscanf(fileID,formatSpec,size); - P2 = single(P2'); - fclose(fileID); - for percent_to_keep = [100 70] - %% - MR = LTSrigid_affine(P1, P2, percent_to_keep, max_iter, 0); - %% SAVE THE MATRIX - dlmwrite(strcat([output_path,'/expectedRigidLTS_',num2str(dimData),'D_',num2str(percent_to_keep),'.txt']),MR,'precision','%.6f','delimiter',' '); - %% - MA = LTSrigid_affine(P1, P2, percent_to_keep, max_iter, 1); - %% SAVE THE MATRIX - dlmwrite(strcat([output_path,'/expectedAffineLTS_',num2str(dimData),'D_',num2str(percent_to_keep),'.txt']),MA,'precision','%.6f','delimiter',' '); - %% - end -end \ No newline at end of file diff --git a/reg-test/matlab_tests/LTSrigid_affine.m b/reg-test/matlab_tests/LTSrigid_affine.m deleted file mode 100644 index 9b7d25be..00000000 --- a/reg-test/matlab_tests/LTSrigid_affine.m +++ /dev/null @@ -1,71 +0,0 @@ -function M = LTSrigid_affine(p1, p2, percent_to_keep, max_iter, isAffine) -%% -%p1=[x1,y1; -% x1',y1'; -% ...]; -%p2=[x2,y2; -% x2',y2']; -% -%% 2D or 3D -dim=size(p1,2); -if dim>3 - error('we only handle 2D or 3D points'); -end -% -nTotalPoints = size(p1,1); -nConsideredPoints = floor(percent_to_keep*nTotalPoints/100); -%Initialisation -%We extract a sublist of size nConsideredPoints from the original list, for -%example the first nConsideredPoints and we estimate the transformation: -p1Temp = p1(1:nTotalPoints,:); -p2Temp = p2(1:nTotalPoints,:); -if(isAffine) - M1 = LSaffine(p1Temp,p2Temp); -else - M1 = LSrigid(p1Temp,p2Temp); -end -M2 = M1; -% -compteur = 0; -lastDistance = single(Inf); -tol=single(0.001); -% -while compteur < max_iter - %We calculate and sort the residuals for all the points: - %r=abs(p2-(Rp1+T)) - if dim == 2 - residalArray=single([double(p2');zeros(1,nTotalPoints);ones(1,nTotalPoints)]-double(M2)*[double(p1');zeros(1,nTotalPoints);ones(1,nTotalPoints)]); - residalArray=double(residalArray); - residalArray=single((residalArray(1,:).^2+residalArray(2,:).^2).^(0.5)); - else - residalArray=single([double(p2');ones(1,nTotalPoints)]-double(M2)*[double(p1');ones(1,nTotalPoints)]); - residalArray=double(residalArray); - residalArray=single((residalArray(1,:).^2+residalArray(2,:).^2+residalArray(3,:).^2).^(0.5)); - end - %residalArray=(residalArray(1,:).^2+residalArray(2,:).^2); - [residalArraySorted,id_sort] = sort(double(residalArray)); - residalArraySorted=single(residalArraySorted); - id_sort=single(id_sort); - distance=single(sum(double(residalArraySorted(1:nConsideredPoints)))); - if((distance > lastDistance) || (lastDistance - distance) < tol) - M2=M1; - break; - end - lastDistance = distance; - M1=M2; - %We select the first nConsideredPoints with the smalest residuals - p1Temp = p1(id_sort(1:nConsideredPoints),:); - p2Temp = p2(id_sort(1:nConsideredPoints),:); - %We recompute the transformation - if(isAffine) - M2 = LSaffine(p1Temp,p2Temp); - else - M2 = LSrigid(p1Temp,p2Temp); - end - % - compteur = compteur+1; -end -% -M=M2; -% -end \ No newline at end of file diff --git a/reg-test/matlab_tests/MINDSSD_test.m b/reg-test/matlab_tests/MINDSSD_test.m deleted file mode 100644 index 0c070019..00000000 --- a/reg-test/matlab_tests/MINDSSD_test.m +++ /dev/null @@ -1,94 +0,0 @@ -function MINDSSD_test(img2D1, img2D2, img3D1, img3D2, output_path) -%% CREATE A MASK FIRST for 2D and 3D !!!! -img2D1Img = load_untouch_nii(img2D1); % read the Nifti file -img2D1Img = img2D1Img.img; -img2D2Img = load_untouch_nii(img2D2); % read the Nifti file -img2D2Img = img2D2Img.img; - -%% RESCALE THE IMAGES FIRST !!! -min2D1Img = double(min(img2D1Img(:))); -max2D1Img = double(max(img2D1Img(:))); -img2D1Img = single((double(img2D1Img)-min2D1Img)./(max2D1Img-min2D1Img)); - -min2D2Img = double(min(img2D2Img(:))); -max2D2Img = double(max(img2D2Img(:))); -img2D2Img = single((double(img2D2Img)-min2D2Img)./(max2D2Img-min2D2Img)); -%% -combinedMask2D1 = zeros(size(img2D1Img)); %img2D2Img should have the same size -combinedMask2D2 = zeros(size(img2D2Img)); %img2D2Img should have the same size -combinedMask2D1(img2D1Img > -1) = 1; -combinedMask2D2(img2D2Img > -1) = 1; -combinedMask2D = combinedMask2D1.*combinedMask2D2; -%% 3D -img3D1Img = load_untouch_nii(img3D1); % read the Nifti file -img3D1Img = img3D1Img.img; -img3D2Img = load_untouch_nii(img3D2); % read the Nifti file -img3D2Img = img3D2Img.img; - -%% RESCALE THE IMAGES FIRST !!! -min3D1Img = double(min(img3D1Img(:))); -max3D1Img = double(max(img3D1Img(:))); -img3D1Img = single((double(img3D1Img)-min3D1Img)./(max3D1Img-min3D1Img)); - -min3D2Img = double(min(img3D2Img(:))); -max3D2Img = double(max(img3D2Img(:))); -img3D2Img = single((double(img3D2Img)-min3D2Img)./(max3D2Img-min3D2Img)); - -combinedMask3D1 = zeros(size(img3D1Img)); %img2D2Img should have the same size -combinedMask3D2 = zeros(size(img3D2Img)); %img2D2Img should have the same size -combinedMask3D1(img3D1Img > -1) = 1; -combinedMask3D2(img3D2Img > -1) = 1; -combinedMask3D = combinedMask3D1.*combinedMask3D2; -%% FIRST MIND DESCRIPTOR !!!! -[expectedMIND2DDescriptorImage_nii1, expectedMIND3DDescriptorImage_nii1] = ... - mindDescriptor_test(img2D1, img3D1, output_path,true,combinedMask2D,combinedMask3D); -delete([output_path,'/expectedMINDDescriptor2D.nii.gz']); -delete([output_path,'/expectedMINDDescriptor3D.nii.gz']); -[expectedMIND2DDescriptorImage_nii2, expectedMIND3DDescriptorImage_nii2] = ... - mindDescriptor_test(img2D2, img3D2, output_path,true,combinedMask2D,combinedMask3D); -delete([output_path,'/expectedMINDDescriptor2D.nii.gz']); -delete([output_path,'/expectedMINDDescriptor3D.nii.gz']); -%% -MINDArray = [expectedMIND2DDescriptorImage_nii1, expectedMIND2DDescriptorImage_nii2;... - expectedMIND3DDescriptorImage_nii1, expectedMIND3DDescriptorImage_nii2]; -%% -for ii=1:size(MINDArray,1) - current1=MINDArray(ii,1); - current2=MINDArray(ii,2); - currentImg1 = double(current1.img); - currentImg2 = double(current2.img); - % - dimVect1 = current1.hdr.dime.dim; - dimVect2 = current2.hdr.dime.dim; - % - nz1 = dimVect1(4); - nz2 = dimVect2(4); - if nz1 ~= nz2 - error('Error. Images must have the same dimension.'); - end - if nz1 > 1 - imgDim = 3; - else - imgDim = 2; - end - % - nt1 = dimVect1(5); - nt2 = dimVect2(5); - % - if (nt1 ~= nt2) - error('Error. Images must have the same dimension.'); - end - % - SSDValue = 0; - for jj=1:nt1 - img1 = currentImg1(:,:,:,jj); - img2 = currentImg2(:,:,:,jj); - diff2 = (img1-img2).^2; - diff2Array=diff2(:); - SSDValue = SSDValue + sum(diff2Array(~isnan(diff2Array)))/length(diff2Array(~isnan(diff2Array))); - end - %% Write the result - expectedSSD = -SSDValue;%/nt1; - dlmwrite([output_path,'/expectedMINDSSDValue',num2str(imgDim),'D.txt'],expectedSSD,'precision','%.6f','delimiter',' '); -end -end \ No newline at end of file diff --git a/reg-test/matlab_tests/NIfTI_20140122/FAQ.pdf b/reg-test/matlab_tests/NIfTI_20140122/FAQ.pdf deleted file mode 100644 index b04456b9250a92b9413811fb42a826167d41281e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206445 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 Ozzr{O1O)M~!2bg?TfVda diff --git a/reg-test/matlab_tests/NIfTI_20140122/NIfTI_tools.pdf b/reg-test/matlab_tests/NIfTI_20140122/NIfTI_tools.pdf deleted file mode 100644 index 592c807bbf13ed0544ca599f4828b62ef930f565..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61950 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 z 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 V?JYi9A1?dDXm-Fy(f+5={|h4amr(!! diff --git a/reg-test/matlab_tests/NIfTI_20140122/affine.m b/reg-test/matlab_tests/NIfTI_20140122/affine.m deleted file mode 100644 index 13b652f3..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/affine.m +++ /dev/null @@ -1,554 +0,0 @@ -% Using 2D or 3D affine matrix to rotate, translate, scale, reflect and -% shear a 2D image or 3D volume. 2D image is represented by a 2D matrix, -% 3D volume is represented by a 3D matrix, and data type can be real -% integer or floating-point. -% -% You may notice that MATLAB has a function called 'imtransform.m' for -% 2D spatial transformation. However, keep in mind that 'imtransform.m' -% assumes y for the 1st dimension, and x for the 2nd dimension. They are -% equivalent otherwise. -% -% In addition, if you adjust the 'new_elem_size' parameter, this 'affine.m' -% is equivalent to 'interp2.m' for 2D image, and equivalent to 'interp3.m' -% for 3D volume. -% -% Usage: [new_img new_M] = ... -% affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]); -% -% old_img - original 2D image or 3D volume. We assume x for the 1st -% dimension, y for the 2nd dimension, and z for the 3rd -% dimension. -% -% old_M - a 3x3 2D affine matrix for 2D image, or a 4x4 3D affine -% matrix for 3D volume. We assume x for the 1st dimension, -% y for the 2nd dimension, and z for the 3rd dimension. -% -% new_elem_size (optional) - size of voxel along x y z direction for -% a transformed 3D volume, or size of pixel along x y for -% a transformed 2D image. We assume x for the 1st dimension -% y for the 2nd dimension, and z for the 3rd dimension. -% 'new_elem_size' is 1 if it is default or empty. -% -% You can increase its value to decrease the resampling rate, -% and make the 2D image or 3D volume more coarse. It works -% just like 'interp3'. -% -% verbose (optional) - 1, 0 -% 1: show transforming progress in percentage -% 2: progress will not be displayed -% 'verbose' is 1 if it is default or empty. -% -% bg (optional) - background voxel intensity in any extra corner that -% is caused by the interpolation. 0 in most cases. If it is -% default or empty, 'bg' will be the average of two corner -% voxel intensities in original data. -% -% method (optional) - 1, 2, or 3 -% 1: for Trilinear interpolation -% 2: for Nearest Neighbor interpolation -% 3: for Fischer's Bresenham interpolation -% 'method' is 1 if it is default or empty. -% -% new_img - transformed 2D image or 3D volume -% -% new_M - transformed affine matrix -% -% Example 1 (3D rotation): -% load mri.mat; old_img = double(squeeze(D)); -% old_M = [0.88 0.5 3 -90; -0.5 0.88 3 -126; 0 0 2 -72; 0 0 0 1]; -% new_img = affine(old_img, old_M, 2); -% [x y z] = meshgrid(1:128,1:128,1:27); -% sz = size(new_img); -% [x1 y1 z1] = meshgrid(1:sz(2),1:sz(1),1:sz(3)); -% figure; slice(x, y, z, old_img, 64, 64, 13.5); -% shading flat; colormap(map); view(-66, 66); -% figure; slice(x1, y1, z1, new_img, sz(1)/2, sz(2)/2, sz(3)/2); -% shading flat; colormap(map); view(-66, 66); -% -% Example 2 (2D interpolation): -% load mri.mat; old_img=D(:,:,1,13)'; -% old_M = [1 0 0; 0 1 0; 0 0 1]; -% new_img = affine(old_img, old_M, [.2 .4]); -% figure; image(old_img); colormap(map); -% figure; image(new_img); colormap(map); -% -% This program is inspired by: -% SPM5 Software from Wellcome Trust Centre for Neuroimaging -% http://www.fil.ion.ucl.ac.uk/spm/software -% Fischer, J., A. del Rio (2004). A Fast Method for Applying Rigid -% Transformations to Volume Data, WSCG2004 Conference. -% http://wscg.zcu.cz/wscg2004/Papers_2004_Short/M19.pdf -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function [new_img, new_M] = affine(old_img, old_M, new_elem_size, verbose, bg, method) - - if ~exist('old_img','var') | ~exist('old_M','var') - error('Usage: [new_img new_M] = affine(old_img, old_M, [new_elem_size], [verbose], [bg], [method]);'); - end - - if ndims(old_img) == 3 - if ~isequal(size(old_M),[4 4]) - error('old_M should be a 4x4 affine matrix for 3D volume.'); - end - elseif ndims(old_img) == 2 - if ~isequal(size(old_M),[3 3]) - error('old_M should be a 3x3 affine matrix for 2D image.'); - end - else - error('old_img should be either 2D image or 3D volume.'); - end - - if ~exist('new_elem_size','var') | isempty(new_elem_size) - new_elem_size = [1 1 1]; - elseif length(new_elem_size) < 2 - new_elem_size = new_elem_size(1)*ones(1,3); - elseif length(new_elem_size) < 3 - new_elem_size = [new_elem_size(:); 1]'; - end - - if ~exist('method','var') | isempty(method) - method = 1; - elseif ~exist('bresenham_line3d.m','file') & method == 3 - error([char(10) char(10) 'Please download 3D Bresenham''s line generation program from:' char(10) char(10) 'http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=21057' char(10) char(10) 'to test Fischer''s Bresenham interpolation method.' char(10) char(10)]); - end - - % Make compatible to MATLAB earlier than version 7 (R14), which - % can only perform arithmetic on double data type - % - old_img = double(old_img); - old_dim = size(old_img); - - if ~exist('bg','var') | isempty(bg) - bg = mean([old_img(1) old_img(end)]); - end - - if ~exist('verbose','var') | isempty(verbose) - verbose = 1; - end - - if ndims(old_img) == 2 - old_dim(3) = 1; - old_M = old_M(:, [1 2 3 3]); - old_M = old_M([1 2 3 3], :); - old_M(3,:) = [0 0 1 0]; - old_M(:,3) = [0 0 1 0]'; - end - - % Vertices of img in voxel - % - XYZvox = [ 1 1 1 - 1 1 old_dim(3) - 1 old_dim(2) 1 - 1 old_dim(2) old_dim(3) - old_dim(1) 1 1 - old_dim(1) 1 old_dim(3) - old_dim(1) old_dim(2) 1 - old_dim(1) old_dim(2) old_dim(3) ]'; - - old_R = old_M(1:3,1:3); - old_T = old_M(1:3,4); - - % Vertices of img in millimeter - % - XYZmm = old_R*(XYZvox-1) + repmat(old_T, [1, 8]); - - % Make scale of new_M according to new_elem_size - % - new_M = diag([new_elem_size 1]); - - % Make translation so minimum vertex is moved to [1,1,1] - % - new_M(1:3,4) = round( min(XYZmm,[],2) ); - - % New dimensions will be the maximum vertices in XYZ direction (dim_vox) - % i.e. compute dim_vox via dim_mm = R*(dim_vox-1)+T - % where, dim_mm = round(max(XYZmm,[],2)); - % - new_dim = ceil(new_M(1:3,1:3) \ ( round(max(XYZmm,[],2))-new_M(1:3,4) )+1)'; - - % Initialize new_img with new_dim - % - new_img = zeros(new_dim(1:3)); - - % Mask out any changes from Z axis of transformed volume, since we - % will traverse it voxel by voxel below. We will only apply unit - % increment of mask_Z(3,4) to simulate the cursor movement - % - % i.e. we will use mask_Z * new_XYZvox to replace new_XYZvox - % - mask_Z = diag(ones(1,4)); - mask_Z(3,3) = 0; - - % It will be easier to do the interpolation if we invert the process - % by not traversing the original volume. Instead, we traverse the - % transformed volume, and backproject each voxel in the transformed - % volume back into the original volume. If the backprojected voxel - % in original volume is within its boundary, the intensity of that - % voxel can be used by the cursor location in the transformed volume. - % - % First, we traverse along Z axis of transformed volume voxel by voxel - % - for z = 1:new_dim(3) - - if verbose & ~mod(z,10) - fprintf('%.2f percent is done.\n', 100*z/new_dim(3)); - end - - % We need to find out the mapping from voxel in the transformed - % volume (new_XYZvox) to voxel in the original volume (old_XYZvox) - % - % The following equation works, because they all equal to XYZmm: - % new_R*(new_XYZvox-1) + new_T == old_R*(old_XYZvox-1) + old_T - % - % We can use modified new_M1 & old_M1 to substitute new_M & old_M - % new_M1 * new_XYZvox == old_M1 * old_XYZvox - % - % where: M1 = M; M1(:,4) = M(:,4) - sum(M(:,1:3),2); - % and: M(:,4) == [T; 1] == sum(M1,2) - % - % Therefore: old_XYZvox = old_M1 \ new_M1 * new_XYZvox; - % - % Since we are traverse Z axis, and new_XYZvox is replaced - % by mask_Z * new_XYZvox, the above formula can be rewritten - % as: old_XYZvox = old_M1 \ new_M1 * mask_Z * new_XYZvox; - % - % i.e. we find the mapping from new_XYZvox to old_XYZvox: - % M = old_M1 \ new_M1 * mask_Z; - % - % First, compute modified old_M1 & new_M1 - % - old_M1 = old_M; old_M1(:,4) = old_M(:,4) - sum(old_M(:,1:3),2); - new_M1 = new_M; new_M1(:,4) = new_M(:,4) - sum(new_M(:,1:3),2); - - % Then, apply unit increment of mask_Z(3,4) to simulate the - % cursor movement - % - mask_Z(3,4) = z; - - % Here is the mapping from new_XYZvox to old_XYZvox - % - M = old_M1 \ new_M1 * mask_Z; - - switch method - case 1 - new_img(:,:,z) = trilinear(old_img, new_dim, old_dim, M, bg); - case 2 - new_img(:,:,z) = nearest_neighbor(old_img, new_dim, old_dim, M, bg); - case 3 - new_img(:,:,z) = bresenham(old_img, new_dim, old_dim, M, bg); - end - - end; % for z - - if ndims(old_img) == 2 - new_M(3,:) = []; - new_M(:,3) = []; - end - - return; % affine - - -%-------------------------------------------------------------------- -function img_slice = trilinear(img, dim1, dim2, M, bg) - - img_slice = zeros(dim1(1:2)); - TINY = 5e-2; % tolerance - - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); - - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); - - % initialize new_Y accumulation - % - Y2X = 0; - Y2Y = 0; - Y2Z = 0; - - for y = 1:ydim1 - - % increment of new_Y accumulation - % - Y2X = Y2X + M(1,2); % new_Y to old_X - Y2Y = Y2Y + M(2,2); % new_Y to old_Y - Y2Z = Y2Z + M(3,2); % new_Y to old_Z - - % backproject new_Y accumulation and translation to old_XYZ - % - old_X = Y2X + M(1,4); - old_Y = Y2Y + M(2,4); - old_Z = Y2Z + M(3,4); - - for x = 1:xdim1 - - % accumulate the increment of new_X, and apply it - % to the backprojected old_XYZ - % - old_X = M(1,1) + old_X ; - old_Y = M(2,1) + old_Y ; - old_Z = M(3,1) + old_Z ; - - % within boundary of original image - % - if ( old_X > 1-TINY & old_X < xdim2+TINY & ... - old_Y > 1-TINY & old_Y < ydim2+TINY & ... - old_Z > 1-TINY & old_Z < zdim2+TINY ) - - % Calculate distance of old_XYZ to its neighbors for - % weighted intensity average - % - dx = old_X - floor(old_X); - dy = old_Y - floor(old_Y); - dz = old_Z - floor(old_Z); - - x000 = floor(old_X); - x100 = x000 + 1; - - if floor(old_X) < 1 - x000 = 1; - x100 = x000; - elseif floor(old_X) > xdim2-1 - x000 = xdim2; - x100 = x000; - end - - x010 = x000; - x001 = x000; - x011 = x000; - - x110 = x100; - x101 = x100; - x111 = x100; - - y000 = floor(old_Y); - y010 = y000 + 1; - - if floor(old_Y) < 1 - y000 = 1; - y100 = y000; - elseif floor(old_Y) > ydim2-1 - y000 = ydim2; - y010 = y000; - end - - y100 = y000; - y001 = y000; - y101 = y000; - - y110 = y010; - y011 = y010; - y111 = y010; - - z000 = floor(old_Z); - z001 = z000 + 1; - - if floor(old_Z) < 1 - z000 = 1; - z001 = z000; - elseif floor(old_Z) > zdim2-1 - z000 = zdim2; - z001 = z000; - end - - z100 = z000; - z010 = z000; - z110 = z000; - - z101 = z001; - z011 = z001; - z111 = z001; - - x010 = x000; - x001 = x000; - x011 = x000; - - x110 = x100; - x101 = x100; - x111 = x100; - - v000 = double(img(x000, y000, z000)); - v010 = double(img(x010, y010, z010)); - v001 = double(img(x001, y001, z001)); - v011 = double(img(x011, y011, z011)); - - v100 = double(img(x100, y100, z100)); - v110 = double(img(x110, y110, z110)); - v101 = double(img(x101, y101, z101)); - v111 = double(img(x111, y111, z111)); - - img_slice(x,y) = v000*(1-dx)*(1-dy)*(1-dz) + ... - v010*(1-dx)*dy*(1-dz) + ... - v001*(1-dx)*(1-dy)*dz + ... - v011*(1-dx)*dy*dz + ... - v100*dx*(1-dy)*(1-dz) + ... - v110*dx*dy*(1-dz) + ... - v101*dx*(1-dy)*dz + ... - v111*dx*dy*dz; - - else - img_slice(x,y) = bg; - - end % if boundary - - end % for x - end % for y - - return; % trilinear - - -%-------------------------------------------------------------------- -function img_slice = nearest_neighbor(img, dim1, dim2, M, bg) - - img_slice = zeros(dim1(1:2)); - - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); - - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); - - % initialize new_Y accumulation - % - Y2X = 0; - Y2Y = 0; - Y2Z = 0; - - for y = 1:ydim1 - - % increment of new_Y accumulation - % - Y2X = Y2X + M(1,2); % new_Y to old_X - Y2Y = Y2Y + M(2,2); % new_Y to old_Y - Y2Z = Y2Z + M(3,2); % new_Y to old_Z - - % backproject new_Y accumulation and translation to old_XYZ - % - old_X = Y2X + M(1,4); - old_Y = Y2Y + M(2,4); - old_Z = Y2Z + M(3,4); - - for x = 1:xdim1 - - % accumulate the increment of new_X and apply it - % to the backprojected old_XYZ - % - old_X = M(1,1) + old_X ; - old_Y = M(2,1) + old_Y ; - old_Z = M(3,1) + old_Z ; - - xi = round(old_X); - yi = round(old_Y); - zi = round(old_Z); - - % within boundary of original image - % - if ( xi >= 1 & xi <= xdim2 & ... - yi >= 1 & yi <= ydim2 & ... - zi >= 1 & zi <= zdim2 ) - - img_slice(x,y) = img(xi,yi,zi); - - else - img_slice(x,y) = bg; - - end % if boundary - - end % for x - end % for y - - return; % nearest_neighbor - - -%-------------------------------------------------------------------- -function img_slice = bresenham(img, dim1, dim2, M, bg) - - img_slice = zeros(dim1(1:2)); - - % Dimension of transformed 3D volume - % - xdim1 = dim1(1); - ydim1 = dim1(2); - - % Dimension of original 3D volume - % - xdim2 = dim2(1); - ydim2 = dim2(2); - zdim2 = dim2(3); - - for y = 1:ydim1 - - start_old_XYZ = round(M*[0 y 0 1]'); - end_old_XYZ = round(M*[xdim1 y 0 1]'); - - [X Y Z] = bresenham_line3d(start_old_XYZ, end_old_XYZ); - - % line error correction - % -% del = end_old_XYZ - start_old_XYZ; - % del_dom = max(del); - % idx_dom = find(del==del_dom); - % idx_dom = idx_dom(1); - % idx_other = [1 2 3]; - % idx_other(idx_dom) = []; - %del_x1 = del(idx_other(1)); -% del_x2 = del(idx_other(2)); - % line_slope = sqrt((del_x1/del_dom)^2 + (del_x2/del_dom)^2 + 1); - % line_error = line_slope - 1; -% line error correction removed because it is too slow - - for x = 1:xdim1 - - % rescale ratio - % - i = round(x * length(X) / xdim1); - - if i < 1 - i = 1; - elseif i > length(X) - i = length(X); - end - - xi = X(i); - yi = Y(i); - zi = Z(i); - - % within boundary of the old XYZ space - % - if ( xi >= 1 & xi <= xdim2 & ... - yi >= 1 & yi <= ydim2 & ... - zi >= 1 & zi <= zdim2 ) - - img_slice(x,y) = img(xi,yi,zi); - -% if line_error > 1 - % x = x + 1; - -% if x <= xdim1 - % img_slice(x,y) = img(xi,yi,zi); - % line_error = line_slope - 1; - % end - % end % if line_error -% line error correction removed because it is too slow - - else - img_slice(x,y) = bg; - - end % if boundary - - end % for x - end % for y - - return; % bresenham - diff --git a/reg-test/matlab_tests/NIfTI_20140122/bipolar.m b/reg-test/matlab_tests/NIfTI_20140122/bipolar.m deleted file mode 100644 index 0c79c373..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/bipolar.m +++ /dev/null @@ -1,94 +0,0 @@ -%BIPOLAR returns an M-by-3 matrix containing a blue-red colormap, in -% in which red stands for positive, blue stands for negative, -% and white stands for 0. -% -% Usage: cmap = bipolar(M, lo, hi, contrast); or cmap = bipolar; -% -% cmap: output M-by-3 matrix for BIPOLAR colormap. -% M: number of shades in the colormap. By default, it is the -% same length as the current colormap. -% lo: the lowest value to represent. -% hi: the highest value to represent. -% -% Inspired from the LORETA PASCAL program: -% http://www.unizh.ch/keyinst/NewLORETA -% -% jimmy@rotman-baycrest.on.ca -% -%---------------------------------------------------------------- -function cmap = bipolar(M, lo, hi, contrast) - - if ~exist('contrast','var') - contrast = 128; - end - - if ~exist('lo','var') - lo = -1; - end - - if ~exist('hi','var') - hi = 1; - end - - if ~exist('M','var') - cmap = colormap; - M = size(cmap,1); - end - - steepness = 10 ^ (1 - (contrast-1)/127); - pos_infs = 1e-99; - neg_infs = -1e-99; - - doubleredc = []; - doublebluec = []; - - if lo >= 0 % all positive - - if lo == 0 - lo = pos_infs; - end - - for i=linspace(hi/M, hi, M) - t = exp(log(i/hi)*steepness); - doubleredc = [doubleredc; [(1-t)+t,(1-t)+0,(1-t)+0]]; - end - - cmap = doubleredc; - - elseif hi <= 0 % all negative - - if hi == 0 - hi = neg_infs; - end - - for i=linspace(abs(lo)/M, abs(lo), M) - t = exp(log(i/abs(lo))*steepness); - doublebluec = [doublebluec; [(1-t)+0,(1-t)+0,(1-t)+t]]; - end - - cmap = flipud(doublebluec); - - else - - if hi > abs(lo) - maxc = hi; - else - maxc = abs(lo); - end - - for i=linspace(maxc/M, hi, round(M*hi/(hi-lo))) - t = exp(log(i/maxc)*steepness); - doubleredc = [doubleredc; [(1-t)+t,(1-t)+0,(1-t)+0]]; - end - - for i=linspace(maxc/M, abs(lo), round(M*abs(lo)/(hi-lo))) - t = exp(log(i/maxc)*steepness); - doublebluec = [doublebluec; [(1-t)+0,(1-t)+0,(1-t)+t]]; - end - - cmap = [flipud(doublebluec); doubleredc]; - - end - - return; % bipolar - diff --git a/reg-test/matlab_tests/NIfTI_20140122/bresenham_line3d.m b/reg-test/matlab_tests/NIfTI_20140122/bresenham_line3d.m deleted file mode 100644 index d4db692a..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/bresenham_line3d.m +++ /dev/null @@ -1,189 +0,0 @@ -% Generate X Y Z coordinates of a 3D Bresenham's line between -% two given points. -% -% A very useful application of this algorithm can be found in the -% implementation of Fischer's Bresenham interpolation method in my -% another program that can rotate three dimensional image volume -% with an affine matrix: -% http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=21080 -% -% Usage: [X Y Z] = bresenham_line3d(P1, P2, [precision]); -% -% P1 - vector for Point1, where P1 = [x1 y1 z1] -% -% P2 - vector for Point2, where P2 = [x2 y2 z2] -% -% precision (optional) - Although according to Bresenham's line -% algorithm, point coordinates x1 y1 z1 and x2 y2 z2 should -% be integer numbers, this program extends its limit to all -% real numbers. If any of them are floating numbers, you -% should specify how many digits of decimal that you would -% like to preserve. Be aware that the length of output X Y -% Z coordinates will increase in 10 times for each decimal -% digit that you want to preserve. By default, the precision -% is 0, which means that they will be rounded to the nearest -% integer. -% -% X - a set of x coordinates on Bresenham's line -% -% Y - a set of y coordinates on Bresenham's line -% -% Z - a set of z coordinates on Bresenham's line -% -% Therefore, all points in XYZ set (i.e. P(i) = [X(i) Y(i) Z(i)]) -% will constitute the Bresenham's line between P1 and P1. -% -% Example: -% P1 = [12 37 6]; P2 = [46 3 35]; -% [X Y Z] = bresenham_line3d(P1, P2); -% figure; plot3(X,Y,Z,'s','markerface','b'); -% -% This program is ported to MATLAB from: -% -% B.Pendleton. line3d - 3D Bresenham's (a 3D line drawing algorithm) -% ftp://ftp.isc.org/pub/usenet/comp.sources.unix/volume26/line3d, 1992 -% -% Which is also referenced by: -% -% Fischer, J., A. del Rio (2004). A Fast Method for Applying Rigid -% Transformations to Volume Data, WSCG2004 Conference. -% http://wscg.zcu.cz/wscg2004/Papers_2004_Short/M19.pdf -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function [X,Y,Z] = bresenham_line3d(P1, P2, precision) - - if ~exist('precision','var') | isempty(precision) | round(precision) == 0 - precision = 0; - P1 = round(P1); - P2 = round(P2); - else - precision = round(precision); - P1 = round(P1*(10^precision)); - P2 = round(P2*(10^precision)); - end - - d = max(abs(P2-P1)+1); - X = zeros(1, d); - Y = zeros(1, d); - Z = zeros(1, d); - - x1 = P1(1); - y1 = P1(2); - z1 = P1(3); - - x2 = P2(1); - y2 = P2(2); - z2 = P2(3); - - dx = x2 - x1; - dy = y2 - y1; - dz = z2 - z1; - - ax = abs(dx)*2; - ay = abs(dy)*2; - az = abs(dz)*2; - - sx = sign(dx); - sy = sign(dy); - sz = sign(dz); - - x = x1; - y = y1; - z = z1; - idx = 1; - - if(ax>=max(ay,az)) % x dominant - yd = ay - ax/2; - zd = az - ax/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(x == x2) % end - break; - end - - if(yd >= 0) % move along y - y = y + sy; - yd = yd - ax; - end - - if(zd >= 0) % move along z - z = z + sz; - zd = zd - ax; - end - - x = x + sx; % move along x - yd = yd + ay; - zd = zd + az; - end - elseif(ay>=max(ax,az)) % y dominant - xd = ax - ay/2; - zd = az - ay/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(y == y2) % end - break; - end - - if(xd >= 0) % move along x - x = x + sx; - xd = xd - ay; - end - - if(zd >= 0) % move along z - z = z + sz; - zd = zd - ay; - end - - y = y + sy; % move along y - xd = xd + ax; - zd = zd + az; - end - elseif(az>=max(ax,ay)) % z dominant - xd = ax - az/2; - yd = ay - az/2; - - while(1) - X(idx) = x; - Y(idx) = y; - Z(idx) = z; - idx = idx + 1; - - if(z == z2) % end - break; - end - - if(xd >= 0) % move along x - x = x + sx; - xd = xd - az; - end - - if(yd >= 0) % move along y - y = y + sy; - yd = yd - az; - end - - z = z + sz; % move along z - xd = xd + ax; - yd = yd + ay; - end - end - - if precision ~= 0 - X = X/(10^precision); - Y = Y/(10^precision); - Z = Z/(10^precision); - end - - return; % bresenham_line3d - diff --git a/reg-test/matlab_tests/NIfTI_20140122/clip_nii.m b/reg-test/matlab_tests/NIfTI_20140122/clip_nii.m deleted file mode 100644 index 40fcb308..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/clip_nii.m +++ /dev/null @@ -1,115 +0,0 @@ -% CLIP_NII: Clip the NIfTI volume from any of the 6 sides -% -% Usage: nii = clip_nii(nii, [option]) -% -% Inputs: -% -% nii - NIfTI volume. -% -% option - struct instructing how many voxel to be cut from which side. -% -% option.cut_from_L = ( number of voxel ) -% option.cut_from_R = ( number of voxel ) -% option.cut_from_P = ( number of voxel ) -% option.cut_from_A = ( number of voxel ) -% option.cut_from_I = ( number of voxel ) -% option.cut_from_S = ( number of voxel ) -% -% Options description in detail: -% ============================== -% -% cut_from_L: Number of voxels from Left side will be clipped. -% -% cut_from_R: Number of voxels from Right side will be clipped. -% -% cut_from_P: Number of voxels from Posterior side will be clipped. -% -% cut_from_A: Number of voxels from Anterior side will be clipped. -% -% cut_from_I: Number of voxels from Inferior side will be clipped. -% -% cut_from_S: Number of voxels from Superior side will be clipped. -% -% NIfTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function nii = clip_nii(nii, opt) - - dims = abs(nii.hdr.dime.dim(2:4)); - origin = abs(nii.hdr.hist.originator(1:3)); - - if isempty(origin) | all(origin == 0) % according to SPM - origin = round((dims+1)/2); - end - - cut_from_L = 0; - cut_from_R = 0; - cut_from_P = 0; - cut_from_A = 0; - cut_from_I = 0; - cut_from_S = 0; - - if nargin > 1 & ~isempty(opt) - if ~isstruct(opt) - error('option argument should be a struct'); - end - - if isfield(opt,'cut_from_L') - cut_from_L = round(opt.cut_from_L); - - if cut_from_L >= origin(1) | cut_from_L < 0 - error('cut_from_L cannot be negative or cut beyond originator'); - end - end - - if isfield(opt,'cut_from_P') - cut_from_P = round(opt.cut_from_P); - - if cut_from_P >= origin(2) | cut_from_P < 0 - error('cut_from_P cannot be negative or cut beyond originator'); - end - end - - if isfield(opt,'cut_from_I') - cut_from_I = round(opt.cut_from_I); - - if cut_from_I >= origin(3) | cut_from_I < 0 - error('cut_from_I cannot be negative or cut beyond originator'); - end - end - - if isfield(opt,'cut_from_R') - cut_from_R = round(opt.cut_from_R); - - if cut_from_R > dims(1)-origin(1) | cut_from_R < 0 - error('cut_from_R cannot be negative or cut beyond originator'); - end - end - - if isfield(opt,'cut_from_A') - cut_from_A = round(opt.cut_from_A); - - if cut_from_A > dims(2)-origin(2) | cut_from_A < 0 - error('cut_from_A cannot be negative or cut beyond originator'); - end - end - - if isfield(opt,'cut_from_S') - cut_from_S = round(opt.cut_from_S); - - if cut_from_S > dims(3)-origin(3) | cut_from_S < 0 - error('cut_from_S cannot be negative or cut beyond originator'); - end - end - end - - nii = make_nii(nii.img( (cut_from_L+1) : (dims(1)-cut_from_R), ... - (cut_from_P+1) : (dims(2)-cut_from_A), ... - (cut_from_I+1) : (dims(3)-cut_from_S), ... - :,:,:,:,:), nii.hdr.dime.pixdim(2:4), ... - [origin(1)-cut_from_L origin(2)-cut_from_P origin(3)-cut_from_I], ... - nii.hdr.dime.datatype, nii.hdr.hist.descrip); - - return; - diff --git a/reg-test/matlab_tests/NIfTI_20140122/collapse_nii_scan.m b/reg-test/matlab_tests/NIfTI_20140122/collapse_nii_scan.m deleted file mode 100644 index 4c000787..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/collapse_nii_scan.m +++ /dev/null @@ -1,260 +0,0 @@ -% Collapse multiple single-scan NIFTI files into a multiple-scan NIFTI file -% -% Usage: collapse_nii_scan(scan_file_pattern, [collapsed_fileprefix], [scan_file_folder]) -% -% Here, scan_file_pattern should look like: 'myscan_0*.img' -% If collapsed_fileprefix is omit, 'multi_scan' will be used -% If scan_file_folder is omit, current file folder will be used -% -% The order of volumes in the collapsed file will be the order of -% corresponding filenames for those selected scan files. -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function collapse_nii_scan(scan_pattern, fileprefix, scan_path) - - if ~exist('fileprefix','var') - fileprefix = 'multi_scan'; - else - [tmp fileprefix] = fileparts(fileprefix); - end - - if ~exist('scan_path','var'), scan_path = pwd; end - pnfn = fullfile(scan_path, scan_pattern); - - file_lst = dir(pnfn); - flist = {file_lst.name}; - flist = flist(:); - filename = flist{1}; - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - end - else - if ~strcmp(filename(end-3:end), '.img') & ... - ~strcmp(filename(end-3:end), '.hdr') & ... - ~strcmp(filename(end-3:end), '.nii') - - error('Please check filename.'); - end - end - - nii = load_untouch_nii(fullfile(scan_path,filename)); - nii.hdr.dime.dim(5) = length(flist); - - if nii.hdr.dime.dim(1) < 4 - nii.hdr.dime.dim(1) = 4; - end - - hdr = nii.hdr; - filetype = nii.filetype; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end - - hdr.hist.magic = 'n+1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - elseif filetype == 1 - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch0_nii_hdr(hdr, fid); - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - glmax = -inf; - glmin = inf; - - for i = 1:length(flist) - nii = load_untouch_nii(fullfile(scan_path,flist{i})); - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end - - if nii.hdr.dime.glmax > glmax - glmax = nii.hdr.dime.glmax; - end - - if nii.hdr.dime.glmin < glmin - glmin = nii.hdr.dime.glmin; - end - - fwrite(fid, nii.img, precision); - end - - hdr.dime.glmax = round(glmax); - hdr.dime.glmin = round(glmin); - - if filetype == 2 - fseek(fid, 140, 'bof'); - fwrite(fid, hdr.dime.glmax, 'int32'); - fwrite(fid, hdr.dime.glmin, 'int32'); - elseif filetype == 1 - fid2 = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid2 < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch_nii_hdr(hdr, fid2); - - if ~isempty(ext) - save_nii_ext(ext, fid2); - end - - fclose(fid2); - else - fid2 = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid2 < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch0_nii_hdr(hdr, fid2); - - fclose(fid2); - end - - fclose(fid); - - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; - - return; % collapse_nii_scan - diff --git a/reg-test/matlab_tests/NIfTI_20140122/examples.txt b/reg-test/matlab_tests/NIfTI_20140122/examples.txt deleted file mode 100644 index 82bec5fe..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/examples.txt +++ /dev/null @@ -1,130 +0,0 @@ - -- Examples to load, make and save a nii struct: - - To load Analyze data or NIFTI data to a structure: - - nii = load_nii(NIFTI_file_name, [img_idx], [old_RGB24]); - - img_idx is a numerical array of image indices along the temporal - axis, which is only available in NIFTI data. After you specify - img_idx, only those images indexed by img_idx will be loaded. If - there is no img_idx or img_idx is empty, all available images - will be loaded. - - For RGB image, most people use RGB triple sequentially for each - voxel, like [R1 G1 B1 R2 G2 B2 ...]. However, some program like - Analyze 6.0 developed by AnalyzeDirect uses old RGB24, in a way - like [R1 R2 ... G1 G2 ... B1 B2 ...] for each slices. In this - case, you can set old_RGB24 flag to 1 and load data correctly: - - nii = load_nii(NIFTI_file_name, [], 1); - - To get a total number of images along the temporal axis: - - num_scan = get_nii_frame(NIFTI_file_name); - - You can also load the header extension if it exists: - - nii.ext = load_nii_ext(NIFTI_file_name); - - You can just load the Analyze or NIFTI header: - (header contains: hk, dime, and hist) - - hdr = load_nii_hdr(NIFTI_file_name); - - You can also save the structure to a new file: - (header extension will be saved if there is nii.ext structure) - - save_nii(nii, NIFTI_file_name); - - To make the structure from any 3D (or 4D) data: - - img = rand(91,109,91); or - img = rand(64,64,21,18); - nii = make_nii(img [, voxel_size, origin, datatype] ); - - Use "help load_nii", "help save_nii", "help make_nii" etc. - to get more detail information. - - -- Examples to plot a nii struct: - (More detail descriptions are available on top of "view_nii.m") - - Simple way to plot a nii struct: - - view_nii(nii); - - The default colormap will use the Gray if all data values are - non-negative; otherwise, the default colormap will use BiPolar. - You can choose other colormap, including customized colormap - from panel. - - To imbed the plot into your existing figure: - - h = gcf; - opt.command = 'init'; - opt.setarea = [0.3 0.1 0.6 0.8]; - view_nii(h, nii, opt); - - To add a colorbar: - - opt.usecolorbar = 1; - view_nii(gcf, opt); - - Here, opt.command is implicitly set to 'update'. - - To display in real aspect ratio: - - opt.usestretch = 0; - view_nii(gcf, opt); - - If you want the data value to be directly used as the index - of colormap, instead of scale to the whole colormap: - - opt.useimagesc = 0; - view_nii(gcf, opt); - - If you modified the data value without changing the dimension, - voxel_size, and origin, you can update the display by: - - opt.command = 'updateimg'; - view_nii(gcf, nii.img, opt); - - If the data is completely different, display can be updated by: - - opt.command = 'updatenii'; - view_nii(gcf, nii, opt); - - This is an example to plot EEG source imaging on top of T1 background: - 1. download overlay.zip and unzip it from: - http://www.rotman-baycrest.on.ca/~jimmy/NIFTI/overlay.zip - 2. T1 = load_nii('T1.nii'); - 3. EEG = load_nii('EEG.nii'); - 4. option.setvalue.idx = find(EEG.img); - 5. option.setvalue.val = EEG.img(option.setvalue.idx); - 6. option.useinterp = 1; - 7. option.setviewpoint = [62 48 46]; - 8. view_nii(T1, option); - - -- Contrast and Brightness are available under Gray and Bipolar colormap: - - Increase contrast in Gray colormap will make high end values - more distinguishable by sacrificing the low end values; The - minimum contrast (default) will display the whole range. - - Increase or decrease contrast in BiPolar colormap will shift - the distinguishable position for both positive and negative - values. - - Increase or decrease brightness in Gray colormap will shift - the distinguishable position. - - Increase or decrease brightness in BiPolar colormap will make - both positive and negative values more distinguishable. - - -- Required files: - - All files in this package. - diff --git a/reg-test/matlab_tests/NIfTI_20140122/expand_nii_scan.m b/reg-test/matlab_tests/NIfTI_20140122/expand_nii_scan.m deleted file mode 100644 index e3a3bf88..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/expand_nii_scan.m +++ /dev/null @@ -1,48 +0,0 @@ -% Expand a multiple-scan NIFTI file into multiple single-scan NIFTI files -% -% Usage: expand_nii_scan(multi_scan_filename, [img_idx], [path_to_save]) -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function expand_nii_scan(filename, img_idx, newpath) - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - end - end - - if ~exist('newpath','var') | isempty(newpath), newpath = pwd; end - if ~exist('img_idx','var') | isempty(img_idx), img_idx = 1:get_nii_frame(filename); end - - for i=img_idx - nii_i = load_untouch_nii(filename, i); - - fn = [nii_i.fileprefix '_' sprintf('%04d',i)]; - pnfn = fullfile(newpath, fn); - - if exist('gzFile', 'var') - pnfn = [pnfn '.nii.gz']; - end - - save_untouch_nii(nii_i, pnfn); - end - - return; % expand_nii_scan - diff --git a/reg-test/matlab_tests/NIfTI_20140122/extra_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/extra_nii_hdr.m deleted file mode 100644 index 99a7f21c..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/extra_nii_hdr.m +++ /dev/null @@ -1,255 +0,0 @@ -% Decode extra NIFTI header information into hdr.extra -% -% Usage: hdr = extra_nii_hdr(hdr) -% -% hdr can be obtained from load_nii_hdr -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function hdr = extra_nii_hdr(hdr) - - switch hdr.dime.datatype - case 1 - extra.NIFTI_DATATYPES = 'DT_BINARY'; - case 2 - extra.NIFTI_DATATYPES = 'DT_UINT8'; - case 4 - extra.NIFTI_DATATYPES = 'DT_INT16'; - case 8 - extra.NIFTI_DATATYPES = 'DT_INT32'; - case 16 - extra.NIFTI_DATATYPES = 'DT_FLOAT32'; - case 32 - extra.NIFTI_DATATYPES = 'DT_COMPLEX64'; - case 64 - extra.NIFTI_DATATYPES = 'DT_FLOAT64'; - case 128 - extra.NIFTI_DATATYPES = 'DT_RGB24'; - case 256 - extra.NIFTI_DATATYPES = 'DT_INT8'; - case 512 - extra.NIFTI_DATATYPES = 'DT_UINT16'; - case 768 - extra.NIFTI_DATATYPES = 'DT_UINT32'; - case 1024 - extra.NIFTI_DATATYPES = 'DT_INT64'; - case 1280 - extra.NIFTI_DATATYPES = 'DT_UINT64'; - case 1536 - extra.NIFTI_DATATYPES = 'DT_FLOAT128'; - case 1792 - extra.NIFTI_DATATYPES = 'DT_COMPLEX128'; - case 2048 - extra.NIFTI_DATATYPES = 'DT_COMPLEX256'; - otherwise - extra.NIFTI_DATATYPES = 'DT_UNKNOWN'; - end - - switch hdr.dime.intent_code - case 2 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CORREL'; - case 3 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST'; - case 4 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST'; - case 5 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ZSCORE'; - case 6 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ'; - case 7 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BETA'; - case 8 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_BINOM'; - case 9 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GAMMA'; - case 10 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POISSON'; - case 11 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NORMAL'; - case 12 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_FTEST_NONC'; - case 13 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHISQ_NONC'; - case 14 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGISTIC'; - case 15 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LAPLACE'; - case 16 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_UNIFORM'; - case 17 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TTEST_NONC'; - case 18 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_WEIBULL'; - case 19 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_CHI'; - case 20 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_INVGAUSS'; - case 21 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_EXTVAL'; - case 22 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_PVAL'; - case 23 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOGPVAL'; - case 24 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LOG10PVAL'; - case 1001 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_ESTIMATE'; - case 1002 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_LABEL'; - case 1003 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NEURONAME'; - case 1004 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_GENMATRIX'; - case 1005 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_SYMMATRIX'; - case 1006 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DISPVECT'; - case 1007 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_VECTOR'; - case 1008 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_POINTSET'; - case 1009 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_TRIANGLE'; - case 1010 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_QUATERNION'; - case 1011 - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_DIMLESS'; - otherwise - extra.NIFTI_INTENT_CODES = 'NIFTI_INTENT_NONE'; - end - - extra.NIFTI_INTENT_NAMES = hdr.hist.intent_name; - - if hdr.hist.sform_code > 0 - switch hdr.hist.sform_code - case 1 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; - case 2 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; - case 3 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; - case 4 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_MNI_152'; - otherwise - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end - - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - elseif hdr.hist.qform_code > 0 - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - - switch hdr.hist.qform_code - case 1 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_SCANNER_ANAT'; - case 2 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_ALIGNED_ANAT'; - case 3 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_TALAIRACH'; - case 4 - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_MNI_152'; - otherwise - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end - else - extra.NIFTI_SFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - extra.NIFTI_QFORM_CODES = 'NIFTI_XFORM_UNKNOWN'; - end - - switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 - case 1 - extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_METER'; - case 2 - extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MM'; % millimeter - case 3 - extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_MICRO'; - otherwise - extra.NIFTI_SPACE_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end - - switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 - case 8 - extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_SEC'; - case 16 - extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_MSEC'; - case 24 - extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_USEC'; % microsecond - otherwise - extra.NIFTI_TIME_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end - - switch hdr.dime.xyzt_units - case 32 - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_HZ'; - case 40 - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_PPM'; % part per million - case 48 - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_RADS'; % radians per second - otherwise - extra.NIFTI_SPECTRAL_UNIT = 'NIFTI_UNITS_UNKNOWN'; - end - - % MRI-specific spatial and temporal information - % - dim_info = hdr.hk.dim_info; - extra.NIFTI_FREQ_DIM = bitand(dim_info, 3); - extra.NIFTI_PHASE_DIM = bitand(bitshift(dim_info, -2), 3); - extra.NIFTI_SLICE_DIM = bitand(bitshift(dim_info, -4), 3); - - % Check slice code - % - switch hdr.dime.slice_code - case 1 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_INC'; % sequential increasing - case 2 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_SEQ_DEC'; % sequential decreasing - case 3 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC'; % alternating increasing - case 4 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC'; % alternating decreasing - case 5 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_INC2'; % ALT_INC # 2 - case 6 - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_ALT_DEC2'; % ALT_DEC # 2 - otherwise - extra.NIFTI_SLICE_ORDER = 'NIFTI_SLICE_UNKNOWN'; - end - - % Check NIFTI version - % - if ~isempty(hdr.hist.magic) & strcmp(hdr.hist.magic(1),'n') & ... - ( strcmp(hdr.hist.magic(2),'i') | strcmp(hdr.hist.magic(2),'+') ) & ... - str2num(hdr.hist.magic(3)) >= 1 & str2num(hdr.hist.magic(3)) <= 9 - - extra.NIFTI_VERSION = str2num(hdr.hist.magic(3)); - else - extra.NIFTI_VERSION = 0; - end - - % Check if data stored in the same file (*.nii) or separate - % files (*.hdr/*.img) - % - if isempty(hdr.hist.magic) - extra.NIFTI_ONEFILE = 0; - else - extra.NIFTI_ONEFILE = strcmp(hdr.hist.magic(2), '+'); - end - - % Swap has been taken care of by checking whether sizeof_hdr is - % 348 (machine is 'ieee-le' or 'ieee-be' etc) - % - % extra.NIFTI_NEEDS_SWAP = (hdr.dime.dim(1) < 0 | hdr.dime.dim(1) > 7); - - % Check NIFTI header struct contains a 5th (vector) dimension - % - if hdr.dime.dim(1) > 4 & hdr.dime.dim(6) > 1 - extra.NIFTI_5TH_DIM = hdr.dime.dim(6); - else - extra.NIFTI_5TH_DIM = 0; - end - - hdr.extra = extra; - - return; % extra_nii_hdr - diff --git a/reg-test/matlab_tests/NIfTI_20140122/flip_lr.m b/reg-test/matlab_tests/NIfTI_20140122/flip_lr.m deleted file mode 100644 index 06543fd1..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/flip_lr.m +++ /dev/null @@ -1,84 +0,0 @@ -% When you load any ANALYZE or NIfTI file with 'load_nii.m', and view -% it with 'view_nii.m', you may find that the image is L-R flipped. -% This is because of the confusion of radiological and neurological -% convention in the medical image before NIfTI format is adopted. You -% can find more details from: -% -% http://www.rotman-baycrest.on.ca/~jimmy/UseANALYZE.htm -% -% Sometime, people even want to convert RAS (standard orientation) back -% to LAS orientation to satisfy the legend programs or processes. This -% program is only written for those purpose. So PLEASE BE VERY CAUTIOUS -% WHEN USING THIS 'FLIP_LR.M' PROGRAM. -% -% With 'flip_lr.m', you can convert any ANALYZE or NIfTI (no matter -% 3D or 4D) file to a flipped NIfTI file. This is implemented simply -% by flipping the affine matrix in the NIfTI header. Since the L-R -% orientation is determined there, so the image will be flipped. -% -% Usage: flip_lr(original_fn, flipped_fn, [old_RGB],[tolerance],[preferredForm]) -% -% original_fn - filename of the original ANALYZE or NIfTI (3D or 4D) file -% -% flipped_fn - filename of the L-R flipped NIfTI file -% -% old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. -% -% tolerance (optional) - distortion allowed for non-orthogonal rotation -% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), -% if it is default or empty. -% -% preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter -% -% Example: flip_lr('avg152T1_LR_nifti.nii', 'flipped_lr.nii'); -% flip_lr('avg152T1_RL_nifti.nii', 'flipped_rl.nii'); -% -% You will find that 'avg152T1_LR_nifti.nii' and 'avg152T1_RL_nifti.nii' -% are the same, and 'flipped_lr.nii' and 'flipped_rl.nii' are also the -% the same, but they are L-R flipped from 'avg152T1_*'. -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function flip_lr(original_fn, flipped_fn, old_RGB, tolerance, preferredForm) - - if ~exist('original_fn','var') | ~exist('flipped_fn','var') - error('Usage: flip_lr(original_fn, flipped_fn, [old_RGB],[tolerance])'); - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - nii = load_nii(original_fn, [], [], [], [], old_RGB, tolerance, preferredForm); - M = diag(nii.hdr.dime.pixdim(2:5)); - M(1:3,4) = -M(1:3,1:3)*(nii.hdr.hist.originator(1:3)-1)'; - M(1,:) = -1*M(1,:); - nii.hdr.hist.sform_code = 1; - nii.hdr.hist.srow_x = M(1,:); - nii.hdr.hist.srow_y = M(2,:); - nii.hdr.hist.srow_z = M(3,:); - save_nii(nii, flipped_fn); - - return; % flip_lr - diff --git a/reg-test/matlab_tests/NIfTI_20140122/get_nii_frame.m b/reg-test/matlab_tests/NIfTI_20140122/get_nii_frame.m deleted file mode 100644 index 154d5262..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/get_nii_frame.m +++ /dev/null @@ -1,164 +0,0 @@ -% Return time frame of a NIFTI dataset. Support both *.nii and -% *.hdr/*.img file extension. If file extension is not provided, -% *.hdr/*.img will be used as default. -% -% It is a lightweighted "load_nii_hdr", and is equivalent to -% hdr.dime.dim(5) -% -% Usage: [ total_scan ] = get_nii_frame(filename) -% -% filename - NIFTI file name. -% -% Returned values: -% -% total_scan - total number of image scans for the time frame -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function [ total_scan ] = get_nii_frame(filename) - - if ~exist('filename','var'), - error('Usage: [ total_scan ] = get_nii_frame(filename)'); - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - fileprefix = filename; - machine = 'ieee-le'; - new_ext = 0; - - if findstr('.nii',fileprefix) & strcmp(fileprefix(end-3:end), '.nii') - new_ext = 1; - fileprefix(end-3:end)=''; - end - - if findstr('.hdr',fileprefix) & strcmp(fileprefix(end-3:end), '.hdr') - fileprefix(end-3:end)=''; - end - - if findstr('.img',fileprefix) & strcmp(fileprefix(end-3:end), '.img') - fileprefix(end-3:end)=''; - end - - if new_ext - fn = sprintf('%s.nii',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - hdr = read_header(fid); - fclose(fid); - end - - if hdr.sizeof_hdr ~= 348 - % first try reading the opposite endian to 'machine' - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - hdr = read_header(fid); - fclose(fid); - end - end - - if hdr.sizeof_hdr ~= 348 - % Now throw an error - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - total_scan = hdr.dim(5); - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end - - return; % get_nii_frame - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - fseek(fid,0,'bof'); - dsr.sizeof_hdr = fread(fid,1,'int32')'; % should be 348! - - fseek(fid,40,'bof'); - dsr.dim = fread(fid,8,'int16')'; - - return; % read_header - diff --git a/reg-test/matlab_tests/NIfTI_20140122/license.txt b/reg-test/matlab_tests/NIfTI_20140122/license.txt deleted file mode 100644 index 5a2d7f87..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/license.txt +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2014, Jimmy Shen -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_nii.m b/reg-test/matlab_tests/NIfTI_20140122/load_nii.m deleted file mode 100644 index c36ba77f..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_nii.m +++ /dev/null @@ -1,198 +0,0 @@ -% Load NIFTI or ANALYZE dataset. Support both *.nii and *.hdr/*.img -% file extension. If file extension is not provided, *.hdr/*.img will -% be used as default. -% -% A subset of NIFTI transform is included. For non-orthogonal rotation, -% shearing etc., please use 'reslice_nii.m' to reslice the NIFTI file. -% It will not cause negative effect, as long as you remember not to do -% slice time correction after reslicing the NIFTI file. Output variable -% nii will be in RAS orientation, i.e. X axis from Left to Right, -% Y axis from Posterior to Anterior, and Z axis from Inferior to -% Superior. -% -% Usage: nii = load_nii(filename, [img_idx], [dim5_idx], [dim6_idx], ... -% [dim7_idx], [old_RGB], [tolerance], [preferredForm]) -% -% filename - NIFTI or ANALYZE file name. -% -% img_idx (optional) - a numerical array of 4th dimension indices, -% which is the indices of image scan volume. The number of images -% scan volumes can be obtained from get_nii_frame.m, or simply -% hdr.dime.dim(5). Only the specified volumes will be loaded. -% All available image volumes will be loaded, if it is default or -% empty. -% -% dim5_idx (optional) - a numerical array of 5th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% dim6_idx (optional) - a numerical array of 6th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% dim7_idx (optional) - a numerical array of 7th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. -% -% tolerance (optional) - distortion allowed in the loaded image for any -% non-orthogonal rotation or shearing of NIfTI affine matrix. If -% you set 'tolerance' to 0, it means that you do not allow any -% distortion. If you set 'tolerance' to 1, it means that you do -% not care any distortion. The image will fail to be loaded if it -% can not be tolerated. The tolerance will be set to 0.1 (10%), if -% it is default or empty. -% -% preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter -% -% Returned values: -% -% nii structure: -% -% hdr - struct with NIFTI header fields. -% -% filetype - Analyze format .hdr/.img (0); -% NIFTI .hdr/.img (1); -% NIFTI .nii (2) -% -% fileprefix - NIFTI filename without extension. -% -% machine - machine string variable. -% -% img - 3D (or 4D) matrix of NIFTI data. -% -% original - the original header before any affine transform. -% -% Part of this file is copied and modified from: -% http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function nii = load_nii(filename, img_idx, dim5_idx, dim6_idx, dim7_idx, ... - old_RGB, tolerance, preferredForm) - - if ~exist('filename','var') - error('Usage: nii = load_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [tolerance], [preferredForm])'); - end - - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; % 10 percent - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - % Read the header extension - % -% nii.ext = load_nii_ext(filename); - - % Read the dataset body - % - [nii.img,nii.hdr] = load_nii_img(nii.hdr,nii.filetype,nii.fileprefix, ... - nii.machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB); - - % Perform some of sform/qform transform - % - nii = xform_nii(nii, tolerance, preferredForm); - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - return % load_nii - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_nii_ext.m b/reg-test/matlab_tests/NIfTI_20140122/load_nii_ext.m deleted file mode 100644 index 56a23170..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_nii_ext.m +++ /dev/null @@ -1,207 +0,0 @@ -% Load NIFTI header extension after its header is loaded using load_nii_hdr. -% -% Usage: ext = load_nii_ext(filename) -% -% filename - NIFTI file name. -% -% Returned values: -% -% ext - Structure of NIFTI header extension, which includes num_ext, -% and all the extended header sections in the header extension. -% Each extended header section will have its esize, ecode, and -% edata, where edata can be plain text, xml, or any raw data -% that was saved in the extended header section. -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function ext = load_nii_ext(filename) - - if ~exist('filename','var'), - error('Usage: ext = load_nii_ext(filename)'); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - machine = 'ieee-le'; - new_ext = 0; - - if findstr('.nii',filename) & strcmp(filename(end-3:end), '.nii') - new_ext = 1; - filename(end-3:end)=''; - end - - if findstr('.hdr',filename) & strcmp(filename(end-3:end), '.hdr') - filename(end-3:end)=''; - end - - if findstr('.img',filename) & strcmp(filename(end-3:end), '.img') - filename(end-3:end)=''; - end - - if new_ext - fn = sprintf('%s.nii',filename); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', filename); - error(msg); - end - else - fn = sprintf('%s.hdr',filename); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', filename); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - vox_offset = 0; - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') == 348 - if new_ext - fseek(fid,108,'bof'); - vox_offset = fread(fid,1,'float32'); - end - - ext = read_extension(fid, vox_offset); - fclose(fid); - else - fclose(fid); - - % first try reading the opposite endian to 'machine' - % - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') ~= 348 - - % Now throw an error - % - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - if new_ext - fseek(fid,108,'bof'); - vox_offset = fread(fid,1,'float32'); - end - - ext = read_extension(fid, vox_offset); - fclose(fid); - end - end - end - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end - - - return % load_nii_ext - - -%--------------------------------------------------------------------- -function ext = read_extension(fid, vox_offset) - - ext = []; - - if vox_offset - end_of_ext = vox_offset; - else - fseek(fid, 0, 'eof'); - end_of_ext = ftell(fid); - end - - if end_of_ext > 352 - fseek(fid, 348, 'bof'); - ext.extension = fread(fid,4)'; - end - - if isempty(ext) | ext.extension(1) == 0 - ext = []; - return; - end - - i = 1; - - while(ftell(fid) < end_of_ext) - ext.section(i).esize = fread(fid,1,'int32'); - ext.section(i).ecode = fread(fid,1,'int32'); - ext.section(i).edata = char(fread(fid,ext.section(i).esize-8)'); - i = i + 1; - end - - ext.num_ext = length(ext.section); - - return % read_extension - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/load_nii_hdr.m deleted file mode 100644 index 01c83f37..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_nii_hdr.m +++ /dev/null @@ -1,280 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function [hdr, filetype, fileprefix, machine] = load_nii_hdr(fileprefix) - - if ~exist('fileprefix','var'), - error('Usage: [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename)'); - end - - machine = 'ieee-le'; - new_ext = 0; - - if findstr('.nii',fileprefix) & strcmp(fileprefix(end-3:end), '.nii') - new_ext = 1; - fileprefix(end-3:end)=''; - end - - if findstr('.hdr',fileprefix) & strcmp(fileprefix(end-3:end), '.hdr') - fileprefix(end-3:end)=''; - end - - if findstr('.img',fileprefix) & strcmp(fileprefix(end-3:end), '.img') - fileprefix(end-3:end)=''; - end - - if new_ext - fn = sprintf('%s.nii',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') == 348 - hdr = read_header(fid); - fclose(fid); - else - fclose(fid); - - % first try reading the opposite endian to 'machine' - % - switch machine, - case 'ieee-le', machine = 'ieee-be'; - case 'ieee-be', machine = 'ieee-le'; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - - if fread(fid,1,'int32') ~= 348 - - % Now throw an error - % - msg = sprintf('File "%s" is corrupted.',fn); - error(msg); - end - - hdr = read_header(fid); - fclose(fid); - end - end - end - - if strcmp(hdr.hist.magic, 'n+1') - filetype = 2; - elseif strcmp(hdr.hist.magic, 'ni1') - filetype = 1; - else - filetype = 0; - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - % For Analyze data format - % - if ~strcmp(dsr.hist.magic, 'n+1') & ~strcmp(dsr.hist.magic, 'ni1') - dsr.hist.qform_code = 0; - dsr.hist.sform_code = 0; - end - - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.dim_info = fread(fid, 1,'uchar')'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - dime.dim = fread(fid,8,'int16')'; - dime.intent_p1 = fread(fid,1,'float32')'; - dime.intent_p2 = fread(fid,1,'float32')'; - dime.intent_p3 = fread(fid,1,'float32')'; - dime.intent_code = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.slice_start = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.scl_slope = fread(fid,1,'float32')'; - dime.scl_inter = fread(fid,1,'float32')'; - dime.slice_end = fread(fid,1,'int16')'; - dime.slice_code = fread(fid,1,'uchar')'; - dime.xyzt_units = fread(fid,1,'uchar')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.slice_duration = fread(fid,1,'float32')'; - dime.toffset = fread(fid,1,'float32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - % Original header structures - % struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.qform_code = fread(fid,1,'int16')'; - hist.sform_code = fread(fid,1,'int16')'; - hist.quatern_b = fread(fid,1,'float32')'; - hist.quatern_c = fread(fid,1,'float32')'; - hist.quatern_d = fread(fid,1,'float32')'; - hist.qoffset_x = fread(fid,1,'float32')'; - hist.qoffset_y = fread(fid,1,'float32')'; - hist.qoffset_z = fread(fid,1,'float32')'; - hist.srow_x = fread(fid,4,'float32')'; - hist.srow_y = fread(fid,4,'float32')'; - hist.srow_z = fread(fid,4,'float32')'; - hist.intent_name = deblank(fread(fid,16,directchar)'); - hist.magic = deblank(fread(fid,4,directchar)'); - - fseek(fid,253,'bof'); - hist.originator = fread(fid, 5,'int16')'; - - return % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_nii_img.m b/reg-test/matlab_tests/NIfTI_20140122/load_nii_img.m deleted file mode 100644 index 60adb7d1..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_nii_img.m +++ /dev/null @@ -1,392 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB) - - if ~exist('hdr','var') | ~exist('filetype','var') | ~exist('fileprefix','var') | ~exist('machine','var') - error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB]);'); - end - - if ~exist('img_idx','var') | isempty(img_idx) | hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) | hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) | hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) | hdr.dime.dim(8)<1 - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - % check img_idx - % - if ~isempty(img_idx) & ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB); - - return % load_nii_img - - -%--------------------------------------------------------------------- -function [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB) - - switch filetype - case {0, 1} - fn = [fileprefix '.img']; - case 2 - fn = [fileprefix '.nii']; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; - - % move pointer to the start of image block - % - switch filetype - case {0, 1} - fseek(fid, 0, 'bof'); - case 2 - fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - % Load whole image block for old Analyze format or binary image; - % otherwise, load images that are specified in img_idx, dim5_idx, - % dim6_idx, and dim7_idx - % - % For binary image, we have to read all because pos can not be - % seeked in bit and can not be calculated the way below. - % - if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(5:8),ones(1,4)) | ... - (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx)) - - % For each frame, precision of value will be read - % in img_siz times, where img_siz is only the - % dimension size of an image, not the byte storage - % size of an image. - % - img_siz = prod(hdr.dime.dim(2:8)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - img = fread(fid, img_siz, sprintf('*%s',precision)); - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - - % compute size of one image - % - img_siz = prod(hdr.dime.dim(2:4)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - % preallocate img - img = zeros(img_siz, length(img_idx)*length(dim5_idx)*length(dim6_idx)*length(dim7_idx) ); - currentIndex = 1; - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, 1, ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fread will read precision of value - % in img_siz times - % - img(:,currentIndex) = fread(fid, img_siz, sprintf('*%s',precision)); - currentIndex = currentIndex +1; - - end - end - end - end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img = reshape(img, [2, length(img)/2]); - img = complex(img(1,:)', img(2,:)'); - end - - fclose(fid); - - % Update the global min and max values - % - hdr.dime.glmax = double(max(img(:))); - hdr.dime.glmin = double(min(img(:))); - - % old_RGB treat RGB slice by slice, now it is treated voxel by voxel - % - if old_RGB & hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) 3 hdr.dime.dim(4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [1 2 4 3 5 6 7 8]); - elseif hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - elseif hdr.dime.datatype == 511 & hdr.dime.bitpix == 96 - img = double(img(:)); - img = single((img - min(img))/(max(img) - min(img))); - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - else - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:4) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - end - - if ~isempty(img_idx) - hdr.dime.dim(5) = length(img_idx); - end - - if ~isempty(dim5_idx) - hdr.dime.dim(6) = length(dim5_idx); - end - - if ~isempty(dim6_idx) - hdr.dime.dim(7) = length(dim6_idx); - end - - if ~isempty(dim7_idx) - hdr.dime.dim(8) = length(dim7_idx); - end - - return % read_image - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_untouch0_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/load_untouch0_nii_hdr.m deleted file mode 100644 index 297afaa9..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_untouch0_nii_hdr.m +++ /dev/null @@ -1,200 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function hdr = load_nii_hdr(fileprefix, machine) - - fn = sprintf('%s.hdr',fileprefix); - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - hdr = read_header(fid); - fclose(fid); - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.hkey_un0 = fread(fid, 1,directchar)'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - %struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % char vox_units[4]; /* 16 + 4 */ - % char cal_units[8]; /* 20 + 8 */ - % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float roi_scale; /* 72 + 4 */ - % float funused1; /* 76 + 4 */ - % float funused2; /* 80 + 4 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % int compressed; /* 92 + 4 */ - % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - dime.dim = fread(fid,8,'int16')'; - dime.vox_units = deblank(fread(fid,4,directchar)'); - dime.cal_units = deblank(fread(fid,8,directchar)'); - dime.unused1 = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.dim_un0 = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.roi_scale = fread(fid,1,'float32')'; - dime.funused1 = fread(fid,1,'float32')'; - dime.funused2 = fread(fid,1,'float32')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.compressed = fread(fid,1,'int32')'; - dime.verified = fread(fid,1,'int32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % char orient; /* 104 + 1 */ - % char originator[10]; /* 105 + 10 */ - % char generated[10]; /* 115 + 10 */ - % char scannum[10]; /* 125 + 10 */ - % char patient_id[10]; /* 135 + 10 */ - % char exp_date[10]; /* 145 + 10 */ - % char exp_time[10]; /* 155 + 10 */ - % char hist_un0[3]; /* 165 + 3 */ - % int views /* 168 + 4 */ - % int vols_added; /* 172 + 4 */ - % int start_field; /* 176 + 4 */ - % int field_skip; /* 180 + 4 */ - % int omax; /* 184 + 4 */ - % int omin; /* 188 + 4 */ - % int smax; /* 192 + 4 */ - % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.orient = fread(fid, 1,'char')'; - hist.originator = fread(fid, 5,'int16')'; - hist.generated = deblank(fread(fid,10,directchar)'); - hist.scannum = deblank(fread(fid,10,directchar)'); - hist.patient_id = deblank(fread(fid,10,directchar)'); - hist.exp_date = deblank(fread(fid,10,directchar)'); - hist.exp_time = deblank(fread(fid,10,directchar)'); - hist.hist_un0 = deblank(fread(fid, 3,directchar)'); - hist.views = fread(fid, 1,'int32')'; - hist.vols_added = fread(fid, 1,'int32')'; - hist.start_field = fread(fid, 1,'int32')'; - hist.field_skip = fread(fid, 1,'int32')'; - hist.omax = fread(fid, 1,'int32')'; - hist.omin = fread(fid, 1,'int32')'; - hist.smax = fread(fid, 1,'int32')'; - hist.smin = fread(fid, 1,'int32')'; - - return % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_header_only.m b/reg-test/matlab_tests/NIfTI_20140122/load_untouch_header_only.m deleted file mode 100644 index 8b68084b..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_header_only.m +++ /dev/null @@ -1,187 +0,0 @@ -% Load NIfTI / Analyze header without applying any appropriate affine -% geometric transform or voxel intensity scaling. It is equivalent to -% hdr field when using load_untouch_nii to load dataset. Support both -% *.nii and *.hdr file extension. If file extension is not provided, -% *.hdr will be used as default. -% -% Usage: [header, ext, filetype, machine] = load_untouch_header_only(filename) -% -% filename - NIfTI / Analyze file name. -% -% Returned values: -% -% header - struct with NIfTI / Analyze header fields. -% -% ext - NIfTI extension if it is not empty. -% -% filetype - 0 for Analyze format (*.hdr/*.img); -% 1 for NIFTI format in 2 files (*.hdr/*.img); -% 2 for NIFTI format in 1 file (*.nii). -% -% machine - a string, see below for details. The default here is 'ieee-le'. -% -% 'native' or 'n' - local machine format - the default -% 'ieee-le' or 'l' - IEEE floating point with little-endian -% byte ordering -% 'ieee-be' or 'b' - IEEE floating point with big-endian -% byte ordering -% 'vaxd' or 'd' - VAX D floating point and VAX ordering -% 'vaxg' or 'g' - VAX G floating point and VAX ordering -% 'cray' or 'c' - Cray floating point with big-endian -% byte ordering -% 'ieee-le.l64' or 'a' - IEEE floating point with little-endian -% byte ordering and 64 bit long data type -% 'ieee-be.l64' or 's' - IEEE floating point with big-endian byte -% ordering and 64 bit long data type. -% -% Part of this file is copied and modified from: -% http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function [hdr, ext, filetype, machine] = load_untouch_header_only(filename) - - if ~exist('filename','var') - error('Usage: [header, ext, filetype, machine] = load_untouch_header_only(filename)'); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [hdr, filetype, fileprefix, machine] = load_nii_hdr(filename); - - if filetype == 0 - hdr = load_untouch0_nii_hdr(fileprefix, machine); - ext = []; - else - hdr = load_untouch_nii_hdr(fileprefix, machine, filetype); - - % Read the header extension - % - ext = load_nii_ext(filename); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - tmp = hdr.dime.dim(2:end); - tmp(find(tmp < 1)) = 1; - hdr.dime.dim(2:end) = tmp; - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - rmdir(tmpDir,'s'); - end - - - return % load_untouch_header_only - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii.m b/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii.m deleted file mode 100644 index 067c4aaf..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii.m +++ /dev/null @@ -1,191 +0,0 @@ -% Load NIFTI or ANALYZE dataset, but not applying any appropriate affine -% geometric transform or voxel intensity scaling. -% -% Although according to NIFTI website, all those header information are -% supposed to be applied to the loaded NIFTI image, there are some -% situations that people do want to leave the original NIFTI header and -% data untouched. They will probably just use MATLAB to do certain image -% processing regardless of image orientation, and to save data back with -% the same NIfTI header. -% -% Since this program is only served for those situations, please use it -% together with "save_untouch_nii.m", and do not use "save_nii.m" or -% "view_nii.m" for the data that is loaded by "load_untouch_nii.m". For -% normal situation, you should use "load_nii.m" instead. -% -% Usage: nii = load_untouch_nii(filename, [img_idx], [dim5_idx], [dim6_idx], ... -% [dim7_idx], [old_RGB], [slice_idx]) -% -% filename - NIFTI or ANALYZE file name. -% -% img_idx (optional) - a numerical array of image volume indices. -% Only the specified volumes will be loaded. All available image -% volumes will be loaded, if it is default or empty. -% -% The number of images scans can be obtained from get_nii_frame.m, -% or simply: hdr.dime.dim(5). -% -% dim5_idx (optional) - a numerical array of 5th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% dim6_idx (optional) - a numerical array of 6th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% dim7_idx (optional) - a numerical array of 7th dimension indices. -% Only the specified range will be loaded. All available range -% will be loaded, if it is default or empty. -% -% old_RGB (optional) - a scale number to tell difference of new RGB24 -% from old RGB24. New RGB24 uses RGB triple sequentially for each -% voxel, like [R1 G1 B1 R2 G2 B2 ...]. Analyze 6.0 from AnalyzeDirect -% uses old RGB24, in a way like [R1 R2 ... G1 G2 ... B1 B2 ...] for -% each slices. If the image that you view is garbled, try to set -% old_RGB variable to 1 and try again, because it could be in -% old RGB24. It will be set to 0, if it is default or empty. -% -% slice_idx (optional) - a numerical array of image slice indices. -% Only the specified slices will be loaded. All available image -% slices will be loaded, if it is default or empty. -% -% Returned values: -% -% nii structure: -% -% hdr - struct with NIFTI header fields. -% -% filetype - Analyze format .hdr/.img (0); -% NIFTI .hdr/.img (1); -% NIFTI .nii (2) -% -% fileprefix - NIFTI filename without extension. -% -% machine - machine string variable. -% -% img - 3D (or 4D) matrix of NIFTI data. -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function nii = load_untouch_nii(filename, img_idx, dim5_idx, dim6_idx, dim7_idx, ... - old_RGB, slice_idx) - - if ~exist('filename','var') - error('Usage: nii = load_untouch_nii(filename, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx], [old_RGB], [slice_idx])'); - end - - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('slice_idx','var') | isempty(slice_idx) - slice_idx = []; - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - if nii.filetype == 0 - nii.hdr = load_untouch0_nii_hdr(nii.fileprefix,nii.machine); - nii.ext = []; - else - nii.hdr = load_untouch_nii_hdr(nii.fileprefix,nii.machine,nii.filetype); - - % Read the header extension - % - nii.ext = load_nii_ext(filename); - end - - % Read the dataset body - % - [nii.img,nii.hdr] = load_untouch_nii_img(nii.hdr,nii.filetype,nii.fileprefix, ... - nii.machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx); - - % Perform some of sform/qform transform - % -% nii = xform_nii(nii, tolerance, preferredForm); - - nii.untouch = 1; - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - - return % load_untouch_nii - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_hdr.m deleted file mode 100644 index a4715360..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_hdr.m +++ /dev/null @@ -1,217 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function hdr = load_nii_hdr(fileprefix, machine, filetype) - - if filetype == 2 - fn = sprintf('%s.nii',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.nii".', fileprefix); - error(msg); - end - else - fn = sprintf('%s.hdr',fileprefix); - - if ~exist(fn) - msg = sprintf('Cannot find file "%s.hdr".', fileprefix); - error(msg); - end - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - else - fseek(fid,0,'bof'); - hdr = read_header(fid); - fclose(fid); - end - - return % load_nii_hdr - - -%--------------------------------------------------------------------- -function [ dsr ] = read_header(fid) - - % Original header structures - % struct dsr - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - dsr.hk = header_key(fid); - dsr.dime = image_dimension(fid); - dsr.hist = data_history(fid); - - % For Analyze data format - % - if ~strcmp(dsr.hist.magic, 'n+1') & ~strcmp(dsr.hist.magic, 'ni1') - dsr.hist.qform_code = 0; - dsr.hist.sform_code = 0; - end - - return % read_header - - -%--------------------------------------------------------------------- -function [ hk ] = header_key(fid) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - % - % int sizeof_header Should be 348. - % char regular Must be 'r' to indicate that all images and - % volumes are the same size. - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hk.sizeof_hdr = fread(fid, 1,'int32')'; % should be 348! - hk.data_type = deblank(fread(fid,10,directchar)'); - hk.db_name = deblank(fread(fid,18,directchar)'); - hk.extents = fread(fid, 1,'int32')'; - hk.session_error = fread(fid, 1,'int16')'; - hk.regular = fread(fid, 1,directchar)'; - hk.dim_info = fread(fid, 1,'uchar')'; - - return % header_key - - -%--------------------------------------------------------------------- -function [ dime ] = image_dimension(fid) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % /* - % dim[0] Number of dimensions in database; usually 4. - % dim[1] Image X dimension; number of *pixels* in an image row. - % dim[2] Image Y dimension; number of *pixel rows* in slice. - % dim[3] Volume Z dimension; number of *slices* in a volume. - % dim[4] Time points; number of volumes in database - % */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width, mm - % pixdim[2] - voxel height, mm - % pixdim[3] - slice thickness, mm - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - dime.dim = fread(fid,8,'int16')'; - dime.intent_p1 = fread(fid,1,'float32')'; - dime.intent_p2 = fread(fid,1,'float32')'; - dime.intent_p3 = fread(fid,1,'float32')'; - dime.intent_code = fread(fid,1,'int16')'; - dime.datatype = fread(fid,1,'int16')'; - dime.bitpix = fread(fid,1,'int16')'; - dime.slice_start = fread(fid,1,'int16')'; - dime.pixdim = fread(fid,8,'float32')'; - dime.vox_offset = fread(fid,1,'float32')'; - dime.scl_slope = fread(fid,1,'float32')'; - dime.scl_inter = fread(fid,1,'float32')'; - dime.slice_end = fread(fid,1,'int16')'; - dime.slice_code = fread(fid,1,'uchar')'; - dime.xyzt_units = fread(fid,1,'uchar')'; - dime.cal_max = fread(fid,1,'float32')'; - dime.cal_min = fread(fid,1,'float32')'; - dime.slice_duration = fread(fid,1,'float32')'; - dime.toffset = fread(fid,1,'float32')'; - dime.glmax = fread(fid,1,'int32')'; - dime.glmin = fread(fid,1,'int32')'; - - return % image_dimension - - -%--------------------------------------------------------------------- -function [ hist ] = data_history(fid) - - % Original header structures - % struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - v6 = version; - if str2num(v6(1))<6 - directchar = '*char'; - else - directchar = 'uchar=>char'; - end - - hist.descrip = deblank(fread(fid,80,directchar)'); - hist.aux_file = deblank(fread(fid,24,directchar)'); - hist.qform_code = fread(fid,1,'int16')'; - hist.sform_code = fread(fid,1,'int16')'; - hist.quatern_b = fread(fid,1,'float32')'; - hist.quatern_c = fread(fid,1,'float32')'; - hist.quatern_d = fread(fid,1,'float32')'; - hist.qoffset_x = fread(fid,1,'float32')'; - hist.qoffset_y = fread(fid,1,'float32')'; - hist.qoffset_z = fread(fid,1,'float32')'; - hist.srow_x = fread(fid,4,'float32')'; - hist.srow_y = fread(fid,4,'float32')'; - hist.srow_z = fread(fid,4,'float32')'; - hist.intent_name = deblank(fread(fid,16,directchar)'); - hist.magic = deblank(fread(fid,4,directchar)'); - - return % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_img.m b/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_img.m deleted file mode 100644 index d9169a9c..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/load_untouch_nii_img.m +++ /dev/null @@ -1,468 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function [img,hdr] = load_untouch_nii_img(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx) - - if ~exist('hdr','var') | ~exist('filetype','var') | ~exist('fileprefix','var') | ~exist('machine','var') - error('Usage: [img,hdr] = load_nii_img(hdr,filetype,fileprefix,machine,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx],[old_RGB],[slice_idx]);'); - end - - if ~exist('img_idx','var') | isempty(img_idx) | hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) | hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) | hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) | hdr.dime.dim(8)<1 - dim7_idx = []; - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - if ~exist('slice_idx','var') | isempty(slice_idx) | hdr.dime.dim(4)<1 - slice_idx = []; - end - - % check img_idx - % - if ~isempty(img_idx) & ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - % check slice_idx - % - if ~isempty(slice_idx) & ~isnumeric(slice_idx) - error('"slice_idx" should be a numerical array.'); - end - - if length(unique(slice_idx)) ~= length(slice_idx) - error('Duplicate index in "slice_idx"'); - end - - if ~isempty(slice_idx) & (min(slice_idx) < 1 | max(slice_idx) > hdr.dime.dim(4)) - max_range = hdr.dime.dim(4); - - if max_range == 1 - error(['"slice_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"slice_idx" should be an integer within the range of [' range '].']); - end - end - - [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx); - - return % load_nii_img - - -%--------------------------------------------------------------------- -function [img,hdr] = read_image(hdr,filetype,fileprefix,machine,img_idx,dim5_idx,dim6_idx,dim7_idx,old_RGB,slice_idx) - - switch filetype - case {0, 1} - fn = [fileprefix '.img']; - case 2 - fn = [fileprefix '.nii']; - end - - fid = fopen(fn,'r',machine); - - if fid < 0, - msg = sprintf('Cannot open file %s.',fn); - error(msg); - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 1, - hdr.dime.bitpix = 1; precision = 'ubit1'; - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 32, - hdr.dime.bitpix = 64; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - case 1792, - hdr.dime.bitpix = 128; precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - tmp = hdr.dime.dim(2:end); - tmp(find(tmp < 1)) = 1; - hdr.dime.dim(2:end) = tmp; - - % move pointer to the start of image block - % - switch filetype - case {0, 1} - fseek(fid, 0, 'bof'); - case 2 - fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - % Load whole image block for old Analyze format or binary image; - % otherwise, load images that are specified in img_idx, dim5_idx, - % dim6_idx, and dim7_idx - % - % For binary image, we have to read all because pos can not be - % seeked in bit and can not be calculated the way below. - % - if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(4:8),ones(1,5)) | ... - (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx) & isempty(slice_idx)) - - % For each frame, precision of value will be read - % in img_siz times, where img_siz is only the - % dimension size of an image, not the byte storage - % size of an image. - % - img_siz = prod(hdr.dime.dim(2:8)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - img = fread(fid, img_siz, sprintf('*%s',precision)); - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; - end - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; - end - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - - %ROMAN: begin - roman = 1; - if(roman) - - % compute size of one slice - % - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - % preallocate img - img = zeros(img_siz, length(slice_idx)*length(img_idx)*length(dim5_idx)*length(dim6_idx)*length(dim7_idx) ); - currentIndex = 1; - else - img = []; - end; %if(roman) - % ROMAN: end - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - for s=1:length(slice_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - % ROMAN: begin - if(roman) - % do nothing - else - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - end; % if (roman) - % ROMAN: end - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fread will read precision of value - % in img_siz times - % - % ROMAN: begin - if(roman) - img(:,currentIndex) = fread(fid, img_siz, sprintf('*%s',precision)); - currentIndex = currentIndex +1; - else - img = [img fread(fid, img_siz, sprintf('*%s',precision))]; - end; %if(roman) - % ROMAN: end - - end - end - end - end - end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img = reshape(img, [2, length(img)/2]); - img = complex(img(1,:)', img(2,:)'); - end - - fclose(fid); - - % Update the global min and max values - % - hdr.dime.glmax = double(max(img(:))); - hdr.dime.glmin = double(min(img(:))); - - % old_RGB treat RGB slice by slice, now it is treated voxel by voxel - % - if old_RGB & hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) 3 length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [1 2 4 3 5 6 7 8]); - elseif hdr.dime.datatype == 128 & hdr.dime.bitpix == 24 - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - elseif hdr.dime.datatype == 511 & hdr.dime.bitpix == 96 - img = double(img(:)); - img = single((img - min(img))/(max(img) - min(img))); - % remove squeeze - img = (reshape(img, [3 hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - img = permute(img, [2 3 4 1 5 6 7 8]); - else - % remove squeeze - img = (reshape(img, [hdr.dime.dim(2:3) length(slice_idx) length(img_idx) length(dim5_idx) length(dim6_idx) length(dim7_idx)])); - end - - if ~isempty(slice_idx) - hdr.dime.dim(4) = length(slice_idx); - end - - if ~isempty(img_idx) - hdr.dime.dim(5) = length(img_idx); - end - - if ~isempty(dim5_idx) - hdr.dime.dim(6) = length(dim5_idx); - end - - if ~isempty(dim6_idx) - hdr.dime.dim(7) = length(dim6_idx); - end - - if ~isempty(dim7_idx) - hdr.dime.dim(8) = length(dim7_idx); - end - - return % read_image - diff --git a/reg-test/matlab_tests/NIfTI_20140122/make_ana.m b/reg-test/matlab_tests/NIfTI_20140122/make_ana.m deleted file mode 100644 index 883fc3ee..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/make_ana.m +++ /dev/null @@ -1,210 +0,0 @@ -% Make ANALYZE 7.5 data structure specified by a 3D or 4D matrix. -% Optional parameters can also be included, such as: voxel_size, -% origin, datatype, and description. -% -% Once the ANALYZE structure is made, it can be saved into ANALYZE 7.5 -% format data file using "save_untouch_nii" command (for more detail, -% type: help save_untouch_nii). -% -% Usage: ana = make_ana(img, [voxel_size], [origin], [datatype], [description]) -% -% Where: -% -% img: a 3D matrix [x y z], or a 4D matrix with time -% series [x y z t]. When image is in RGB format, -% make sure that the size of 4th dimension is -% always 3 (i.e. [R G B]). In that case, make -% sure that you must specify RGB datatype to 128. -% -% voxel_size (optional): Voxel size in millimeter for each -% dimension. Default is [1 1 1]. -% -% origin (optional): The AC origin. Default is [0 0 0]. -% -% datatype (optional): Storage data type: -% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, -% 64 - float64, 128 - RGB24 -% Default will use the data type of 'img' matrix -% For RGB image, you must specify it to 128. -% -% description (optional): Description of data. Default is ''. -% -% e.g.: -% origin = [33 44 13]; datatype = 64; -% ana = make_ana(img, [], origin, datatype); % default voxel_size -% -% ANALYZE 7.5 format: http://www.rotman-baycrest.on.ca/~jimmy/ANALYZE75.pdf -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function ana = make_ana(varargin) - - ana.img = varargin{1}; - dims = size(ana.img); - dims = [4 dims ones(1,8)]; - dims = dims(1:8); - - voxel_size = [0 ones(1,3) zeros(1,4)]; - origin = zeros(1,5); - descrip = ''; - - switch class(ana.img) - case 'uint8' - datatype = 2; - case 'int16' - datatype = 4; - case 'int32' - datatype = 8; - case 'single' - datatype = 16; - case 'double' - datatype = 64; - otherwise - error('Datatype is not supported by make_ana.'); - end - - if nargin > 1 & ~isempty(varargin{2}) - voxel_size(2:4) = double(varargin{2}); - end - - if nargin > 2 & ~isempty(varargin{3}) - origin(1:3) = double(varargin{3}); - end - - if nargin > 3 & ~isempty(varargin{4}) - datatype = double(varargin{4}); - - if datatype == 128 | datatype == 511 - dims(5) = []; - dims = [dims 1]; - end - end - - if nargin > 4 & ~isempty(varargin{5}) - descrip = varargin{5}; - end - - if ndims(ana.img) > 4 - error('NIfTI only allows a maximum of 4 Dimension matrix.'); - end - - maxval = round(double(max(ana.img(:)))); - minval = round(double(min(ana.img(:)))); - - ana.hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval); - ana.filetype = 0; - ana.ext = []; - ana.untouch = 1; - - switch ana.hdr.dime.datatype - case 2 - ana.img = uint8(ana.img); - case 4 - ana.img = int16(ana.img); - case 8 - ana.img = int32(ana.img); - case 16 - ana.img = single(ana.img); - case 64 - ana.img = double(ana.img); - case 128 - ana.img = uint8(ana.img); - otherwise - error('Datatype is not supported by make_ana.'); - end - - return; % make_ana - - -%--------------------------------------------------------------------- -function hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval) - - hdr.hk = header_key; - hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); - hdr.hist = data_history(origin, descrip); - - return; % make_header - - -%--------------------------------------------------------------------- -function hk = header_key - - hk.sizeof_hdr = 348; % must be 348! - hk.data_type = ''; - hk.db_name = ''; - hk.extents = 0; - hk.session_error = 0; - hk.regular = 'r'; - hk.hkey_un0 = '0'; - - return; % header_key - - -%--------------------------------------------------------------------- -function dime = image_dimension(dims, voxel_size, datatype, maxval, minval) - - dime.dim = dims; - dime.vox_units = 'mm'; - dime.cal_units = ''; - dime.unused1 = 0; - dime.datatype = datatype; - - switch dime.datatype - case 2, - dime.bitpix = 8; precision = 'uint8'; - case 4, - dime.bitpix = 16; precision = 'int16'; - case 8, - dime.bitpix = 32; precision = 'int32'; - case 16, - dime.bitpix = 32; precision = 'float32'; - case 64, - dime.bitpix = 64; precision = 'float64'; - case 128 - dime.bitpix = 24; precision = 'uint8'; - otherwise - error('Datatype is not supported by make_ana.'); - end - - dime.dim_un0 = 0; - dime.pixdim = voxel_size; - dime.vox_offset = 0; - dime.roi_scale = 1; - dime.funused1 = 0; - dime.funused2 = 0; - dime.cal_max = 0; - dime.cal_min = 0; - dime.compressed = 0; - dime.verified = 0; - dime.glmax = maxval; - dime.glmin = minval; - - return; % image_dimension - - -%--------------------------------------------------------------------- -function hist = data_history(origin, descrip) - - hist.descrip = descrip; - hist.aux_file = 'none'; - hist.orient = 0; - hist.originator = origin; - hist.generated = ''; - hist.scannum = ''; - hist.patient_id = ''; - hist.exp_date = ''; - hist.exp_time = ''; - hist.hist_un0 = ''; - hist.views = 0; - hist.vols_added = 0; - hist.start_field = 0; - hist.field_skip = 0; - hist.omax = 0; - hist.omin = 0; - hist.smax = 0; - hist.smin = 0; - - return; % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/make_nii.m b/reg-test/matlab_tests/NIfTI_20140122/make_nii.m deleted file mode 100644 index 1af1c5e7..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/make_nii.m +++ /dev/null @@ -1,256 +0,0 @@ -% Make NIfTI structure specified by an N-D matrix. Usually, N is 3 for -% 3D matrix [x y z], or 4 for 4D matrix with time series [x y z t]. -% Optional parameters can also be included, such as: voxel_size, -% origin, datatype, and description. -% -% Once the NIfTI structure is made, it can be saved into NIfTI file -% using "save_nii" command (for more detail, type: help save_nii). -% -% Usage: nii = make_nii(img, [voxel_size], [origin], [datatype], [description]) -% -% Where: -% -% img: Usually, img is a 3D matrix [x y z], or a 4D -% matrix with time series [x y z t]. However, -% NIfTI allows a maximum of 7D matrix. When the -% image is in RGB format, make sure that the size -% of 4th dimension is always 3 (i.e. [R G B]). In -% that case, make sure that you must specify RGB -% datatype, which is either 128 or 511. -% -% voxel_size (optional): Voxel size in millimeter for each -% dimension. Default is [1 1 1]. -% -% origin (optional): The AC origin. Default is [0 0 0]. -% -% datatype (optional): Storage data type: -% 2 - uint8, 4 - int16, 8 - int32, 16 - float32, -% 32 - complex64, 64 - float64, 128 - RGB24, -% 256 - int8, 511 - RGB96, 512 - uint16, -% 768 - uint32, 1792 - complex128 -% Default will use the data type of 'img' matrix -% For RGB image, you must specify it to either 128 -% or 511. -% -% description (optional): Description of data. Default is ''. -% -% e.g.: -% origin = [33 44 13]; datatype = 64; -% nii = make_nii(img, [], origin, datatype); % default voxel_size -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function nii = make_nii(varargin) - - nii.img = varargin{1}; - dims = size(nii.img); - dims = [length(dims) dims ones(1,8)]; - dims = dims(1:8); - - voxel_size = [0 ones(1,7)]; - origin = zeros(1,5); - descrip = ''; - - switch class(nii.img) - case 'uint8' - datatype = 2; - case 'int16' - datatype = 4; - case 'int32' - datatype = 8; - case 'single' - if isreal(nii.img) - datatype = 16; - else - datatype = 32; - end - case 'double' - if isreal(nii.img) - datatype = 64; - else - datatype = 1792; - end - case 'int8' - datatype = 256; - case 'uint16' - datatype = 512; - case 'uint32' - datatype = 768; - otherwise - error('Datatype is not supported by make_nii.'); - end - - if nargin > 1 & ~isempty(varargin{2}) - voxel_size(2:4) = double(varargin{2}); - end - - if nargin > 2 & ~isempty(varargin{3}) - origin(1:3) = double(varargin{3}); - end - - if nargin > 3 & ~isempty(varargin{4}) - datatype = double(varargin{4}); - - if datatype == 128 | datatype == 511 - dims(5) = []; - dims(1) = dims(1) - 1; - dims = [dims 1]; - end - end - - if nargin > 4 & ~isempty(varargin{5}) - descrip = varargin{5}; - end - - if ndims(nii.img) > 7 - error('NIfTI only allows a maximum of 7 Dimension matrix.'); - end - - maxval = round(double(max(nii.img(:)))); - minval = round(double(min(nii.img(:)))); - - nii.hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval); - - switch nii.hdr.dime.datatype - case 2 - nii.img = uint8(nii.img); - case 4 - nii.img = int16(nii.img); - case 8 - nii.img = int32(nii.img); - case 16 - nii.img = single(nii.img); - case 32 - nii.img = single(nii.img); - case 64 - nii.img = double(nii.img); - case 128 - nii.img = uint8(nii.img); - case 256 - nii.img = int8(nii.img); - case 511 - img = double(nii.img(:)); - img = single((img - min(img))/(max(img) - min(img))); - nii.img = reshape(img, size(nii.img)); - nii.hdr.dime.glmax = double(max(img)); - nii.hdr.dime.glmin = double(min(img)); - case 512 - nii.img = uint16(nii.img); - case 768 - nii.img = uint32(nii.img); - case 1792 - nii.img = double(nii.img); - otherwise - error('Datatype is not supported by make_nii.'); - end - - return; % make_nii - - -%--------------------------------------------------------------------- -function hdr = make_header(dims, voxel_size, origin, datatype, ... - descrip, maxval, minval) - - hdr.hk = header_key; - hdr.dime = image_dimension(dims, voxel_size, datatype, maxval, minval); - hdr.hist = data_history(origin, descrip); - - return; % make_header - - -%--------------------------------------------------------------------- -function hk = header_key - - hk.sizeof_hdr = 348; % must be 348! - hk.data_type = ''; - hk.db_name = ''; - hk.extents = 0; - hk.session_error = 0; - hk.regular = 'r'; - hk.dim_info = 0; - - return; % header_key - - -%--------------------------------------------------------------------- -function dime = image_dimension(dims, voxel_size, datatype, maxval, minval) - - dime.dim = dims; - dime.intent_p1 = 0; - dime.intent_p2 = 0; - dime.intent_p3 = 0; - dime.intent_code = 0; - dime.datatype = datatype; - - switch dime.datatype - case 2, - dime.bitpix = 8; precision = 'uint8'; - case 4, - dime.bitpix = 16; precision = 'int16'; - case 8, - dime.bitpix = 32; precision = 'int32'; - case 16, - dime.bitpix = 32; precision = 'float32'; - case 32, - dime.bitpix = 64; precision = 'float32'; - case 64, - dime.bitpix = 64; precision = 'float64'; - case 128 - dime.bitpix = 24; precision = 'uint8'; - case 256 - dime.bitpix = 8; precision = 'int8'; - case 511 - dime.bitpix = 96; precision = 'float32'; - case 512 - dime.bitpix = 16; precision = 'uint16'; - case 768 - dime.bitpix = 32; precision = 'uint32'; - case 1792, - dime.bitpix = 128; precision = 'float64'; - otherwise - error('Datatype is not supported by make_nii.'); - end - - dime.slice_start = 0; - dime.pixdim = voxel_size; - dime.vox_offset = 0; - dime.scl_slope = 0; - dime.scl_inter = 0; - dime.slice_end = 0; - dime.slice_code = 0; - dime.xyzt_units = 0; - dime.cal_max = 0; - dime.cal_min = 0; - dime.slice_duration = 0; - dime.toffset = 0; - dime.glmax = maxval; - dime.glmin = minval; - - return; % image_dimension - - -%--------------------------------------------------------------------- -function hist = data_history(origin, descrip) - - hist.descrip = descrip; - hist.aux_file = 'none'; - hist.qform_code = 0; - hist.sform_code = 0; - hist.quatern_b = 0; - hist.quatern_c = 0; - hist.quatern_d = 0; - hist.qoffset_x = 0; - hist.qoffset_y = 0; - hist.qoffset_z = 0; - hist.srow_x = zeros(1,4); - hist.srow_y = zeros(1,4); - hist.srow_z = zeros(1,4); - hist.intent_name = ''; - hist.magic = ''; - hist.originator = origin; - - return; % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/mat_into_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/mat_into_hdr.m deleted file mode 100644 index dc83a29c..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/mat_into_hdr.m +++ /dev/null @@ -1,83 +0,0 @@ -%MAT_INTO_HDR The old versions of SPM (any version before SPM5) store -% an affine matrix of the SPM Reoriented image into a matlab file -% (.mat extension). The file name of this SPM matlab file is the -% same as the SPM Reoriented image file (.img/.hdr extension). -% -% This program will convert the ANALYZE 7.5 SPM Reoriented image -% file into NIfTI format, and integrate the affine matrix in the -% SPM matlab file into its header file (.hdr extension). -% -% WARNING: Before you run this program, please save the header -% file (.hdr extension) into another file name or into another -% folder location, because all header files (.hdr extension) -% will be overwritten after they are converted into NIfTI -% format. -% -% Usage: mat_into_hdr(filename); -% -% filename: file name(s) with .hdr or .mat file extension, like: -% '*.hdr', or '*.mat', or a single .hdr or .mat file. -% e.g. mat_into_hdr('T1.hdr') -% mat_into_hdr('*.mat') -% - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -%------------------------------------------------------------------------- -function mat_into_hdr(files) - - pn = fileparts(files); - file_lst = dir(files); - file_lst = {file_lst.name}; - file1 = file_lst{1}; - [p n e]= fileparts(file1); - - for i=1:length(file_lst) - [p n e]= fileparts(file_lst{i}); - disp(['working on file ', num2str(i) ,' of ', num2str(length(file_lst)), ': ', n,e]); - process=1; - - if isequal(e,'.hdr') - mat=fullfile(pn, [n,'.mat']); - hdr=fullfile(pn, file_lst{i}); - - if ~exist(mat,'file') - warning(['Cannot find file "',mat , '". File "', n, e, '" will not be processed.']); - process=0; - end - elseif isequal(e,'.mat') - hdr=fullfile(pn, [n,'.hdr']); - mat=fullfile(pn, file_lst{i}); - - if ~exist(hdr,'file') - warning(['Can not find file "',hdr , '". File "', n, e, '" will not be processed.']); - process=0; - end - else - warning(['Input file must have .mat or .hdr extension. File "', n, e, '" will not be processed.']); - process=0; - end - - if process - load(mat); - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; - - [h filetype fileprefix machine]=load_nii_hdr(hdr); - h.hist.qform_code=0; - h.hist.sform_code=1; - h.hist.srow_x=M(1,:); - h.hist.srow_y=M(2,:); - h.hist.srow_z=M(3,:); - h.hist.magic='ni1'; - - fid = fopen(hdr,'w',machine); - save_nii_hdr(h,fid); - fclose(fid); - end - end - - return; % mat_into_hdr - diff --git a/reg-test/matlab_tests/NIfTI_20140122/pad_nii.m b/reg-test/matlab_tests/NIfTI_20140122/pad_nii.m deleted file mode 100644 index 6398f220..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/pad_nii.m +++ /dev/null @@ -1,142 +0,0 @@ -% PAD_NII: Pad the NIfTI volume from any of the 6 sides -% -% Usage: nii = pad_nii(nii, [option]) -% -% Inputs: -% -% nii - NIfTI volume. -% -% option - struct instructing how many voxel to be padded from which side. -% -% option.pad_from_L = ( number of voxel ) -% option.pad_from_R = ( number of voxel ) -% option.pad_from_P = ( number of voxel ) -% option.pad_from_A = ( number of voxel ) -% option.pad_from_I = ( number of voxel ) -% option.pad_from_S = ( number of voxel ) -% option.bg = [0] -% -% Options description in detail: -% ============================== -% -% pad_from_L: Number of voxels from Left side will be padded. -% -% pad_from_R: Number of voxels from Right side will be padded. -% -% pad_from_P: Number of voxels from Posterior side will be padded. -% -% pad_from_A: Number of voxels from Anterior side will be padded. -% -% pad_from_I: Number of voxels from Inferior side will be padded. -% -% pad_from_S: Number of voxels from Superior side will be padded. -% -% bg: Background intensity, which is 0 by default. -% -% NIfTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jshen@research.baycrest.org) -% -function nii = pad_nii(nii, opt) - - dims = abs(nii.hdr.dime.dim(2:4)); - origin = abs(nii.hdr.hist.originator(1:3)); - - if isempty(origin) | all(origin == 0) % according to SPM - origin = round((dims+1)/2); - end - - pad_from_L = 0; - pad_from_R = 0; - pad_from_P = 0; - pad_from_A = 0; - pad_from_I = 0; - pad_from_S = 0; - bg = 0; - - if nargin > 1 & ~isempty(opt) - if ~isstruct(opt) - error('option argument should be a struct'); - end - - if isfield(opt,'pad_from_L') - pad_from_L = round(opt.pad_from_L); - - if pad_from_L >= origin(1) | pad_from_L < 0 - error('pad_from_L cannot be negative'); - end - end - - if isfield(opt,'pad_from_P') - pad_from_P = round(opt.pad_from_P); - - if pad_from_P >= origin(2) | pad_from_P < 0 - error('pad_from_P cannot be negative'); - end - end - - if isfield(opt,'pad_from_I') - pad_from_I = round(opt.pad_from_I); - - if pad_from_I >= origin(3) | pad_from_I < 0 - error('pad_from_I cannot be negative'); - end - end - - if isfield(opt,'pad_from_R') - pad_from_R = round(opt.pad_from_R); - - if pad_from_R > dims(1)-origin(1) | pad_from_R < 0 - error('pad_from_R cannot be negative'); - end - end - - if isfield(opt,'pad_from_A') - pad_from_A = round(opt.pad_from_A); - - if pad_from_A > dims(2)-origin(2) | pad_from_A < 0 - error('pad_from_A cannot be negative'); - end - end - - if isfield(opt,'pad_from_S') - pad_from_S = round(opt.pad_from_S); - - if pad_from_S > dims(3)-origin(3) | pad_from_S < 0 - error('pad_from_S cannot be negative'); - end - end - - if isfield(opt,'bg') - bg = opt.bg; - end - end - - blk = bg * ones( pad_from_L, dims(2), dims(3) ); - nii.img = cat(1, blk, nii.img); - - blk = bg * ones( pad_from_R, dims(2), dims(3) ); - nii.img = cat(1, nii.img, blk); - - dims = size(nii.img); - - blk = bg * ones( dims(1), pad_from_P, dims(3) ); - nii.img = cat(2, blk, nii.img); - - blk = bg * ones( dims(1), pad_from_A, dims(3) ); - nii.img = cat(2, nii.img, blk); - - dims = size(nii.img); - - blk = bg * ones( dims(1), dims(2), pad_from_I ); - nii.img = cat(3, blk, nii.img); - - blk = bg * ones( dims(1), dims(2), pad_from_S ); - nii.img = cat(3, nii.img, blk); - - nii = make_nii(nii.img, nii.hdr.dime.pixdim(2:4), ... - [origin(1)+pad_from_L origin(2)+pad_from_P origin(3)+pad_from_I], ... - nii.hdr.dime.datatype, nii.hdr.hist.descrip); - - return; - diff --git a/reg-test/matlab_tests/NIfTI_20140122/reslice_nii.m b/reg-test/matlab_tests/NIfTI_20140122/reslice_nii.m deleted file mode 100644 index 2e43ad37..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/reslice_nii.m +++ /dev/null @@ -1,321 +0,0 @@ -% The basic application of the 'reslice_nii.m' program is to perform -% any 3D affine transform defined by a NIfTI format image. -% -% In addition, the 'reslice_nii.m' program can also be applied to -% generate an isotropic image from either a NIfTI format image or -% an ANALYZE format image. -% -% The resliced NIfTI file will always be in RAS orientation. -% -% This program only supports real integer or floating-point data type. -% For other data type, the program will exit with an error message -% "Transform of this NIFTI data is not supported by the program". -% -% Usage: reslice_nii(old_fn, new_fn, [voxel_size], [verbose], [bg], ... -% [method], [img_idx], [preferredForm]); -% -% old_fn - filename for original NIfTI file -% -% new_fn - filename for resliced NIfTI file -% -% voxel_size (optional) - size of a voxel in millimeter along x y z -% direction for resliced NIfTI file. 'voxel_size' will use -% the minimum voxel_size in original NIfTI header, -% if it is default or empty. -% -% verbose (optional) - 1, 0 -% 1: show transforming progress in percentage -% 2: progress will not be displayed -% 'verbose' is 1 if it is default or empty. -% -% bg (optional) - background voxel intensity in any extra corner that -% is caused by 3D interpolation. 0 in most cases. 'bg' -% will be the average of two corner voxel intensities -% in original image volume, if it is default or empty. -% -% method (optional) - 1, 2, or 3 -% 1: for Trilinear interpolation -% 2: for Nearest Neighbor interpolation -% 3: for Fischer's Bresenham interpolation -% 'method' is 1 if it is default or empty. -% -% img_idx (optional) - a numerical array of image volume indices. Only -% the specified volumes will be loaded. All available image -% volumes will be loaded, if it is default or empty. -% -% The number of images scans can be obtained from get_nii_frame.m, -% or simply: hdr.dime.dim(5). -% -% preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jshen@research.baycrest.org) -% -function reslice_nii(old_fn, new_fn, voxel_size, verbose, bg, method, img_idx, preferredForm) - - if ~exist('old_fn','var') | ~exist('new_fn','var') - error('Usage: reslice_nii(old_fn, new_fn, [voxel_size], [verbose], [bg], [method], [img_idx])'); - end - - if ~exist('method','var') | isempty(method) - method = 1; - end - - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - end - - if ~exist('verbose','var') | isempty(verbose) - verbose = 1; - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - nii = load_nii_no_xform(old_fn, img_idx, 0, preferredForm); - - if ~ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) - error('Transform of this NIFTI data is not supported by the program.'); - end - - if ~exist('voxel_size','var') | isempty(voxel_size) - voxel_size = abs(min(nii.hdr.dime.pixdim(2:4)))*ones(1,3); - elseif length(voxel_size) < 3 - voxel_size = abs(voxel_size(1))*ones(1,3); - end - - if ~exist('bg','var') | isempty(bg) - bg = mean([nii.img(1) nii.img(end)]); - end - - old_M = nii.hdr.hist.old_affine; - - if nii.hdr.dime.dim(5) > 1 - for i = 1:nii.hdr.dime.dim(5) - if verbose - fprintf('Reslicing %d of %d volumes.\n', i, nii.hdr.dime.dim(5)); - end - - [img(:,:,:,i) M] = ... - affine(nii.img(:,:,:,i), old_M, voxel_size, verbose, bg, method); - end - else - [img M] = affine(nii.img, old_M, voxel_size, verbose, bg, method); - end - - new_dim = size(img); - nii.img = img; - nii.hdr.dime.dim(2:4) = new_dim(1:3); - nii.hdr.dime.datatype = 16; - nii.hdr.dime.bitpix = 32; - nii.hdr.dime.pixdim(2:4) = voxel_size(:)'; - nii.hdr.dime.glmax = max(img(:)); - nii.hdr.dime.glmin = min(img(:)); - nii.hdr.hist.qform_code = 0; - nii.hdr.hist.sform_code = 1; - nii.hdr.hist.srow_x = M(1,:); - nii.hdr.hist.srow_y = M(2,:); - nii.hdr.hist.srow_z = M(3,:); - nii.hdr.hist.new_affine = M; - - save_nii(nii, new_fn); - - return; % reslice_nii - - -%-------------------------------------------------------------------- -function [nii] = load_nii_no_xform(filename, img_idx, old_RGB, preferredForm) - - if ~exist('filename','var'), - error('Usage: [nii] = load_nii(filename, [img_idx], [old_RGB])'); - end - - if ~exist('img_idx','var'), img_idx = []; end - if ~exist('old_RGB','var'), old_RGB = 0; end - if ~exist('preferredForm','var'), preferredForm= 's'; end % Jeff - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - % Read the header extension - % -% nii.ext = load_nii_ext(filename); - - % Read the dataset body - % - [nii.img,nii.hdr] = ... - load_nii_img(nii.hdr,nii.filetype,nii.fileprefix,nii.machine,img_idx,'','','',old_RGB); - - % Perform some of sform/qform transform - % -% nii = xform_nii(nii, preferredForm); - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); - rmdir(tmpDir,'s'); - end - - - hdr = nii.hdr; - - % NIFTI can have both sform and qform transform. This program - % will check sform_code prior to qform_code by default. - % - % If user specifys "preferredForm", user can then choose the - % priority. - Jeff - % - useForm=[]; % Jeff - - if isequal(preferredForm,'S') - if isequal(hdr.hist.sform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='s'; - end - end % Jeff - - if isequal(preferredForm,'Q') - if isequal(hdr.hist.qform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'s') - if hdr.hist.sform_code > 0 - useForm='s'; - elseif hdr.hist.qform_code > 0 - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'q') - if hdr.hist.qform_code > 0 - useForm='q'; - elseif hdr.hist.sform_code > 0 - useForm='s'; - end - end % Jeff - - if isequal(useForm,'s') - R = [hdr.hist.srow_x(1:3) - hdr.hist.srow_y(1:3) - hdr.hist.srow_z(1:3)]; - - T = [hdr.hist.srow_x(4) - hdr.hist.srow_y(4) - hdr.hist.srow_z(4)]; - - nii.hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ]; - - elseif isequal(useForm,'q') - b = hdr.hist.quatern_b; - c = hdr.hist.quatern_c; - d = hdr.hist.quatern_d; - - if 1.0-(b*b+c*c+d*d) < 0 - if abs(1.0-(b*b+c*c+d*d)) < 1e-5 - a = 0; - else - error('Incorrect quaternion values in this NIFTI data.'); - end - else - a = sqrt(1.0-(b*b+c*c+d*d)); - end - - qfac = hdr.dime.pixdim(1); - i = hdr.dime.pixdim(2); - j = hdr.dime.pixdim(3); - k = qfac * hdr.dime.pixdim(4); - - R = [a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c - 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b - 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b]; - - T = [hdr.hist.qoffset_x - hdr.hist.qoffset_y - hdr.hist.qoffset_z]; - - nii.hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ]; - - elseif nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file') - load([nii.fileprefix '.mat']); % old SPM affine matrix - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; - nii.hdr.hist.old_affine = M; - - else - M = diag(hdr.dime.pixdim(2:5)); - M(1:3,4) = -M(1:3,1:3)*(hdr.hist.originator(1:3)-1)'; - M(4,4) = 1; - nii.hdr.hist.old_affine = M; - end - - return % load_nii_no_xform - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_file_menu.m b/reg-test/matlab_tests/NIfTI_20140122/rri_file_menu.m deleted file mode 100644 index ec54bf34..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_file_menu.m +++ /dev/null @@ -1,179 +0,0 @@ -% Imbed a file menu to any figure. If file menu exist, it will append -% to the existing file menu. This file menu includes: Copy to clipboard, -% print, save, close etc. -% -% Usage: rri_file_menu(fig); -% -% rri_file_menu(fig,0) means no 'Close' menu. -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -%-------------------------------------------------------------------- - -function rri_file_menu(action, varargin) - - if isnumeric(action) - fig = action; - action = 'init'; - end - - % clear the message line, - % - h = findobj(gcf,'Tag','MessageLine'); - set(h,'String',''); - - if ~strcmp(action, 'init') - set(gcbf, 'InvertHardcopy','off'); -% set(gcbf, 'PaperPositionMode','auto'); - end - - switch action - case {'init'} - if nargin > 1 - init(fig, 1); % no 'close' menu - else - init(fig, 0); - end - case {'print_fig'} - printdlg(gcbf); - case {'copy_fig'} - copy_fig; - case {'export_fig'} - export_fig; - end - - return % rri_file_menu - - -%------------------------------------------------ -% -% Create (or append) File menu -% -function init(fig, no_close) - - % search for file menu - % - h_file = []; - menuitems = findobj(fig, 'type', 'uimenu'); - - for i=1:length(menuitems) - filelabel = get(menuitems(i),'label'); - - if strcmpi(strrep(filelabel, '&', ''), 'file') - h_file = menuitems(i); - break; - end - end - - set(fig, 'menubar', 'none'); - - if isempty(h_file) - if isempty(menuitems) - h_file = uimenu('parent', fig, 'label', 'File'); - else - h_file = uimenu('parent', fig, 'label', 'Copy Figure'); - end - - h1 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''copy_fig'');', ... - 'label','Copy to Clipboard'); - else - h1 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''copy_fig'');', ... - 'separator','on', ... - 'label','Copy to Clipboard'); - end - - h2 = uimenu(h_file, ... - 'callback','pagesetupdlg(gcbf);', ... - 'label','Page Setup...'); - - h2 = uimenu(h_file, ... - 'callback','printpreview(gcbf);', ... - 'label','Print Preview...'); - - h2 = uimenu('parent', h_file, ... - 'callback','printdlg(gcbf);', ... - 'label','Print Figure ...'); - - h2 = uimenu('parent', h_file, ... - 'callback','rri_file_menu(''export_fig'');', ... - 'label','Save Figure ...'); - - arch = computer; - if ~strcmpi(arch(1:2),'PC') - set(h1, 'enable', 'off'); - end - - if ~no_close - h1 = uimenu('parent', h_file, ... - 'callback','close(gcbf);', ... - 'separator','on', ... - 'label','Close'); - end - - return; % init - - -%------------------------------------------------ -% -% Copy to clipboard -% -function copy_fig - - arch = computer; - if(~strcmpi(arch(1:2),'PC')) - error('copy to clipboard can only be used under MS Windows'); - return; - end - - print -noui -dbitmap; - - return % copy_fig - - -%------------------------------------------------ -% -% Save as an image file -% -function export_fig - - curr = pwd; - if isempty(curr) - curr = filesep; - end - - [selected_file, selected_path] = rri_select_file(curr,'Save As'); - - if isempty(selected_file) | isempty(selected_path) - return; - end - - filename = [selected_path selected_file]; - - if(exist(filename,'file')==2) % file exist - - dlg_title = 'Confirm File Overwrite'; - msg = ['File ',filename,' exist. Are you sure you want to overwrite it?']; - response = questdlg(msg,dlg_title,'Yes','No','Yes'); - - if(strcmp(response,'No')) - return; - end - - end - - old_pointer = get(gcbf,'pointer'); - set(gcbf,'pointer','watch'); - - try - saveas(gcbf,filename); - catch - msg = 'ERROR: Cannot save file'; - set(findobj(gcf,'Tag','MessageLine'),'String',msg); - end - - set(gcbf,'pointer',old_pointer); - - return; % export_fig - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_orient.m b/reg-test/matlab_tests/NIfTI_20140122/rri_orient.m deleted file mode 100644 index 9efcc3f3..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_orient.m +++ /dev/null @@ -1,106 +0,0 @@ -% Convert image of different orientations to standard Analyze orientation -% -% Usage: nii = rri_orient(nii); - -% Jimmy Shen (jimmy@rotman-baycrest.on.ca), 26-APR-04 -%___________________________________________________________________ - -function [nii, orient, pattern] = rri_orient(nii, varargin) - - if nargin > 1 - pattern = varargin{1}; - else - pattern = []; - end - - if(nargin > 2) - orient = varargin{2}; - if(length(find(orient>6)) || length(find(orient<1))) %value checking - orient=[1 2 3]; %set to default if bogus values set - end - else - orient = [1 2 3]; - end - - - dim = double(nii.hdr.dime.dim([2:4])); - - if ~isempty(pattern) & ~isequal(length(pattern), prod(dim)) - return; - end - - % get orient of the current image - % - if isequal(orient, [1 2 3]) - orient = rri_orient_ui; - pause(.1); - end - - % no need for conversion - % - if isequal(orient, [1 2 3]) - return; - end - - if isempty(pattern) - pattern = 1:prod(dim); - end - - pattern = reshape(pattern, dim); - img = nii.img; - - % calculate after flip orient - % - rot_orient = mod(orient + 2, 3) + 1; - - % do flip: - % - flip_orient = orient - rot_orient; - - for i = 1:3 - if flip_orient(i) - pattern = flipdim(pattern, i); - img = flipdim(img, i); - end - end - - % get index of orient (do inverse) - % - [tmp rot_orient] = sort(rot_orient); - - % do rotation: - % - pattern = permute(pattern, rot_orient); - img = permute(img, [rot_orient 4 5 6]); - - % rotate resolution, or 'dim' - % - new_dim = nii.hdr.dime.dim([2:4]); - new_dim = new_dim(rot_orient); - nii.hdr.dime.dim([2:4]) = new_dim; - - % rotate voxel_size, or 'pixdim' - % - tmp = nii.hdr.dime.pixdim([2:4]); - tmp = tmp(rot_orient); - nii.hdr.dime.pixdim([2:4]) = tmp; - - % re-calculate originator - % - tmp = nii.hdr.hist.originator([1:3]); - tmp = tmp(rot_orient); - flip_orient = flip_orient(rot_orient); - - for i = 1:3 - if flip_orient(i) & ~isequal(double(tmp(i)), 0) - tmp(i) = int16(double(new_dim(i)) - double(tmp(i)) + 1); - end - end - - nii.hdr.hist.originator([1:3]) = tmp; - - nii.img = img; - pattern = pattern(:); - - return; % rri_orient - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_orient_ui.m b/reg-test/matlab_tests/NIfTI_20140122/rri_orient_ui.m deleted file mode 100644 index 97cd1fbd..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_orient_ui.m +++ /dev/null @@ -1,251 +0,0 @@ -% Return orientation of the current image: -% orient is orientation 1x3 matrix, in that: -% Three elements represent: [x y z] -% Element value: 1 - Left to Right; 2 - Posterior to Anterior; -% 3 - Inferior to Superior; 4 - Right to Left; -% 5 - Anterior to Posterior; 6 - Superior to Inferior; -% e.g.: -% Standard RAS Orientation: [1 2 3] -% Standard RHOS Orientation: [2 4 3] - -% Jimmy Shen (jimmy@rotman-baycrest.on.ca), 26-APR-04 -% -function orient = rri_orient_ui(varargin) - - if nargin == 0 - init; - orient_ui_fig = gcf; - uiwait; % wait for user finish - - orient = getappdata(gcf, 'orient'); - - if isempty(orient) - orient = [1 2 3]; - end - - if ishandle(orient_ui_fig) - close(gcf); - end - - return; - end - - action = varargin{1}; - - if strcmp(action, 'done') - click_done; - elseif strcmp(action, 'cancel') - uiresume; - end - - return; % rri_orient_ui - - -%---------------------------------------------------------------------- -function init - - save_setting_status = 'on'; - rri_orient_pos = []; - - try - load('pls_profile'); - catch - end - - try - load('rri_pos_profile'); - catch - end - - if ~isempty(rri_orient_pos) & strcmp(save_setting_status,'on') - - pos = rri_orient_pos; - - else - - w = 0.35; - h = 0.4; - x = (1-w)/2; - y = (1-h)/2; - - pos = [x y w h]; - - end - - handles.figure = figure('Color',[0.8 0.8 0.8], ... - 'Units','normal', ... - 'Name', 'Convert to standard RAS orientation', ... - 'NumberTitle','off', ... - 'MenuBar','none', ... - 'Position',pos, ... - 'WindowStyle', 'normal', ... - 'ToolBar','none'); - - h0 = handles.figure; - Font.FontUnits = 'point'; - Font.FontSize = 12; - - margin = .1; - line_num = 6; - line_ht = (1 - margin*2) / line_num; - - x = margin; - y = 1 - margin - line_ht; - w = 1 - margin * 2; - h = line_ht * .7; - - pos = [x y w h]; - - handles.Ttit = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Please input orientation of the current image:'); - - y = y - line_ht; - w = .2; - - pos = [x y w h]; - - handles.Tx_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'X Axes:'); - - y = y - line_ht; - - pos = [x y w h]; - - handles.Ty_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Y Axes:'); - - y = y - line_ht; - - pos = [x y w h]; - - handles.Tz_orient = uicontrol('parent', h0, ... - 'style','text', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'background', [0.8 0.8 0.8], ... - 'string', 'Z Axes:'); - - choice = { 'From Left to Right', 'From Posterior to Anterior', ... - 'From Inferior to Superior', 'From Right to Left', ... - 'From Anterior to Posterior', 'From Superior to Inferior' }; - - y = 1 - margin - line_ht; - y = y - line_ht; - w = 1 - margin - x - w; - x = 1 - margin - w; - - pos = [x y w h]; - - handles.x_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 1, ... - 'background', [1 1 1]); - - y = y - line_ht; - - pos = [x y w h]; - - handles.y_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 2, ... - 'background', [1 1 1]); - - y = y - line_ht; - - pos = [x y w h]; - - handles.z_orient = uicontrol('parent', h0, ... - 'style','popupmenu', ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','left',... - 'string', choice, ... - 'value', 3, ... - 'background', [1 1 1]); - - x = margin; - y = y - line_ht * 1.5; - w = .3; - - pos = [x y w h]; - - handles.done = uicontrol('parent', h0, ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','center',... - 'callback', 'rri_orient_ui(''done'');', ... - 'string', 'Done'); - - x = 1 - margin - w; - - pos = [x y w h]; - - handles.cancel = uicontrol('parent', h0, ... - 'unit', 'normal', ... - Font, ... - 'Position',pos, ... - 'HorizontalAlignment','center',... - 'callback', 'rri_orient_ui(''cancel'');', ... - 'string', 'Cancel'); - - setappdata(h0, 'handles', handles); - setappdata(h0, 'orient', [1 2 3]); - - return; % init - - -%---------------------------------------------------------------------- -function click_done - - handles = getappdata(gcf, 'handles'); - - x_orient = get(handles.x_orient, 'value'); - y_orient = get(handles.y_orient, 'value'); - z_orient = get(handles.z_orient, 'value'); - - orient = [x_orient y_orient z_orient]; - test_orient = [orient, orient + 3]; - test_orient = mod(test_orient, 3); - - if length(unique(test_orient)) ~= 3 - msgbox('Please don''t choose same or opposite direction','Error','modal'); - return; - end - - setappdata(gcf, 'orient', [x_orient y_orient z_orient]); - uiresume; - - return; % click_done - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_select_file.m b/reg-test/matlab_tests/NIfTI_20140122/rri_select_file.m deleted file mode 100644 index fe7d47e8..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_select_file.m +++ /dev/null @@ -1,636 +0,0 @@ -function [selected_file, selected_path] = rri_select_file(varargin) -% -% USAGE: [selected_file, selected_path] = ... -% rri_select_file(dir_name, fig_title) -% -% Allow user to select a file from a list of Matlab competible -% file format -% -% Example: -% -% [selected_file, selected_path] = ... -% rri_select_file('/usr','Select Data File'); -% -% See Also RRI_GETFILES - -% -- Created June 2001 by Wilkin Chau, Rotman Research Institute -% -% use rri_select_file to open & save Matlab recognized format -% -- Modified Dec 2002 by Jimmy Shen, Rotman Research Institute -% - - if nargin == 0 | ischar(varargin{1}) % create rri_select_file figure - - dir_name = ''; - fig_title = 'Select a File'; - - if nargin > 0 - dir_name = varargin{1}; - end - - if nargin > 1 - fig_title = varargin{2}; - end - - Init(fig_title,dir_name); - uiwait; % wait for user finish - - selected_path = getappdata(gcf,'SelectedDirectory'); - selected_file = getappdata(gcf,'SelectedFile'); - - cd (getappdata(gcf,'StartDirectory')); - close(gcf); - return; - end; - - % clear the message line, - % - h = findobj(gcf,'Tag','MessageLine'); - set(h,'String',''); - - action = varargin{1}{1}; - - % change 'File format': - % update 'Files' & 'File selection' based on file pattern - % - if strcmp(action,'EditFilter'), - EditFilter; - - % run delete_fig when figure is closing - % - elseif strcmp(action,'delete_fig'), - delete_fig; - - % select 'Directories': - % go into the selected dir - % update 'Files' & 'File selection' based on file pattern - % - elseif strcmp(action,'select_dir'), - select_dir; - - % select 'Files': - % update 'File selection' - % - elseif strcmp(action,'select_file'), - select_file; - - % change 'File selection': - % if it is a file, select that, - % if it is more than a file (*), select those, - % if it is a directory, select based on file pattern - % - elseif strcmp(action,'EditSelection'), - EditSelection; - - % clicked 'Select' - % - elseif strcmp(action,'DONE_BUTTON_PRESSED'), - h = findobj(gcf,'Tag','SelectionEdit'); - [filepath,filename,fileext] = fileparts(get(h,'String')); - - if isempty(filepath) | isempty(filename) | isempty(fileext) - setappdata(gcf,'SelectedDirectory',[]); - setappdata(gcf,'SelectedFile',[]); - else - if ~strcmp(filepath(end),filesep) % not end with filesep - filepath = [filepath filesep]; % add a filesep to filepath - end - - setappdata(gcf,'SelectedDirectory',filepath); - setappdata(gcf,'SelectedFile',[filename fileext]); - end - - if getappdata(gcf,'ready') % ready to exit - uiresume; - end - - % clicked 'cancel' - % - elseif strcmp(action,'CANCEL_BUTTON_PRESSED'), - setappdata(gcf,'SelectedDirectory',[]); - setappdata(gcf,'SelectedFile',[]); - set(findobj(gcf,'Tag','FileList'),'String',''); - uiresume; - end; - - return; - - -% -------------------------------------------------------------------- -function Init(fig_title,dir_name), - - StartDirectory = pwd; - if isempty(StartDirectory), - StartDirectory = filesep; - end; - - filter_disp = {'JPEG image (*.jpg)', ... - 'TIFF image, compressed (*.tif)', ... - 'EPS Level 1 (*.eps)', ... - 'Adobe Illustrator 88 (*.ai)', ... - 'Enhanced metafile (*.emf)', ... - 'Matlab Figure (*.fig)', ... - 'Matlab M-file (*.m)', ... - 'Portable bitmap (*.pbm)', ... - 'Paintbrush 24-bit (*.pcx)', ... - 'Portable Graymap (*.pgm)', ... - 'Portable Network Graphics (*.png)', ... - 'Portable Pixmap (*.ppm)', ... - }; - - filter_string = {'*.jpg', ... - '*.tif', ... - '*.eps', ... - '*.ai', ... - '*.emf', ... - '*.fig', ... - '*.m', ... - '*.pbm', ... - '*.pcx', ... - '*.pgm', ... - '*.png', ... - '*.ppm', ... - }; - -% filter_disp = char(filter_disp); - filter_string = char(filter_string); - - margine = 0.05; - line_height = 0.07; - char_height = line_height*0.8; - - save_setting_status = 'on'; - rri_select_file_pos = []; - - try - load('pls_profile'); - catch - end - - if ~isempty(rri_select_file_pos) & strcmp(save_setting_status,'on') - - pos = rri_select_file_pos; - - else - - w = 0.4; - h = 0.6; - x = (1-w)/2; - y = (1-h)/2; - - pos = [x y w h]; - - end - - h0 = figure('parent',0, 'Color',[0.8 0.8 0.8], ... - 'Units','normal', ... - 'Name',fig_title, ... - 'NumberTitle','off', ... - 'MenuBar','none', ... - 'Position', pos, ... - 'deleteFcn','rri_select_file({''delete_fig''});', ... - 'WindowStyle', 'modal', ... - 'Tag','GetFilesFigure', ... - 'ToolBar','none'); - - x = margine; - y = 1 - 1*line_height - margine; - w = 1-2*x; - h = char_height; - - pos = [x y w h]; - - h1 = uicontrol('Parent',h0, ... % Filter Label - 'Style','text', ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'Position', pos, ... - 'String','Choose one of the file format:', ... - 'Tag','FilterLabel'); - - y = 1 - 2*line_height - margine + line_height*0.2; - w = 1-2*x; - - pos = [x y w h]; - - h_filter = uicontrol('Parent',h0, ... % Filter list - 'Style','popupmenu', ... - 'Units','normal', ... - 'BackgroundColor',[1 1 1], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'Position', pos, ... - 'String', filter_disp, ... - 'user', filter_string, ... - 'value', 1, ... - 'Callback','rri_select_file({''EditFilter''});', ... - 'Tag','FilterEdit'); - - y = 1 - 3*line_height - margine; - w = 0.5 - x - margine/2; - - pos = [x y w h]; - - h1 = uicontrol('Parent',h0, ... % Directory Label - 'Style','text', ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'ListboxTop',0, ... - 'Position', pos, ... - 'String','Directories', ... - 'Tag','DirectoryLabel'); - - x = 0.5; - y = 1 - 3*line_height - margine; - w = 0.5 - margine; - - pos = [x y w h]; - - h1 = uicontrol('Parent',h0, ... % File Label - 'Style','text', ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'ListboxTop',0, ... - 'Position', pos, ... - 'String','Files', ... - 'Tag','FileLabel'); - - x = margine; - y = 4*line_height + margine; - w = 0.5 - x - margine/2; - h = 1 - 7*line_height - 2*margine; - - pos = [x y w h]; - - h_dir = uicontrol('Parent',h0, ... % Directory Listbox - 'Style','listbox', ... - 'Units','normal', ... - 'fontunit','normal', ... - 'FontSize',0.08, ... - 'HorizontalAlignment','left', ... - 'Interruptible', 'off', ... - 'ListboxTop',1, ... - 'Position', pos, ... - 'String', '', ... - 'Callback','rri_select_file({''select_dir''});', ... - 'Tag','DirectoryList'); - - x = 0.5; - y = 4*line_height + margine; - w = 0.5 - margine; - h = 1 - 7*line_height - 2*margine; - - pos = [x y w h]; - - h_file = uicontrol('Parent',h0, ... % File Listbox - 'Style','listbox', ... - 'Units','normal', ... - 'fontunit','normal', ... - 'FontSize',0.08, ... - 'HorizontalAlignment','left', ... - 'ListboxTop',1, ... - 'Position', pos, ... - 'String', '', ... - 'Callback','rri_select_file({''select_file''});', ... - 'Tag','FileList'); - - x = margine; - y = 3*line_height + margine - line_height*0.2; - w = 1-2*x; - h = char_height; - - pos = [x y w h]; - - h1 = uicontrol('Parent',h0, ... % Selection Label - 'Style','text', ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'Position', pos, ... - 'String','File you selected:', ... - 'Tag','SelectionLabel'); - - y = 2*line_height + margine; - w = 1-2*x; - - pos = [x y w h]; - - h_select = uicontrol('Parent',h0, ... % Selection Edit - 'Style','edit', ... - 'Units','normal', ... - 'BackgroundColor',[1 1 1], ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'HorizontalAlignment','left', ... - 'Position', pos, ... - 'String', '', ... - 'Callback','rri_select_file({''EditSelection''});', ... - 'Tag','SelectionEdit'); - - x = 2*margine; - y = line_height/2 + margine; - w = 0.2; - h = line_height; - - pos = [x y w h]; - - h_done = uicontrol('Parent',h0, ... % DONE - 'Units','normal', ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'ListboxTop',0, ... - 'Position', pos, ... - 'HorizontalAlignment','center', ... - 'String','Save', ... % 'Select', ... - 'Callback','rri_select_file({''DONE_BUTTON_PRESSED''});', ... - 'Tag','DONEButton'); - - x = 1 - x - w; - - pos = [x y w h]; - - h_cancel = uicontrol('Parent',h0, ... % CANCEL - 'Units','normal', ... - 'fontunit','normal', ... - 'FontSize',0.5, ... - 'ListboxTop',0, ... - 'Position', pos, ... - 'HorizontalAlignment','center', ... - 'String','Cancel', ... - 'Callback','rri_select_file({''CANCEL_BUTTON_PRESSED''});', ... - 'Tag','CANCELButton'); - - if isempty(dir_name) - dir_name = StartDirectory; - end - - set(h_select,'string',dir_name); - - filter_select = get(h_filter,'value'); - filter_pattern = filter_string(filter_select,:); - - setappdata(gcf,'FilterPattern',deblank(filter_pattern)); - setappdata(gcf,'filter_string',filter_string); - - setappdata(gcf,'h_filter', h_filter); - setappdata(gcf,'h_dir', h_dir); - setappdata(gcf,'h_file', h_file); - setappdata(gcf,'h_select', h_select); - setappdata(gcf,'h_done', h_done); - setappdata(gcf,'h_cancel', h_cancel); - setappdata(gcf,'StartDirectory',StartDirectory); - - EditSelection; - - h_file = getappdata(gcf,'h_file'); - if isempty(get(h_file,'string')) - setappdata(gcf,'ready',0); - else - setappdata(gcf,'ready',1); - end - - return; % Init - - -% called by all the actions, to update 'Directories' or 'Files' -% based on filter_pattern. Select first file in filelist. -% -% -------------------------------------------------------------------- - -function update_dirlist; - - filter_path = getappdata(gcf,'curr_dir'); - filter_pattern = getappdata(gcf,'FilterPattern'); - - if exist(filter_pattern) == 2 % user input specific filename - is_single_file = 1; % need manually take path out later - else - is_single_file = 0; - end - - % take the file path out from filter_pattern - % - [fpath fname fext] = fileparts(filter_pattern); - filter_pattern = [fname fext]; - - dir_struct = dir(filter_path); - if isempty(dir_struct) - msg = 'ERROR: Directory not found!'; - uiwait(msgbox(msg,'File Selection Error','modal')); - return; - end; - - old_pointer = get(gcf,'Pointer'); - set(gcf,'Pointer','watch'); - - dir_list = dir_struct(find([dir_struct.isdir] == 1)); - [sorted_dir_names,sorted_dir_index] = sortrows({dir_list.name}'); - - dir_struct = dir([filter_path filesep filter_pattern]); - if isempty(dir_struct) - sorted_file_names = []; - else - file_list = dir_struct(find([dir_struct.isdir] == 0)); - - if is_single_file % take out path - tmp = file_list.name; - [fpath fname fext] = fileparts(tmp); - file_list.name = [fname fext]; - end - - [sorted_file_names,sorted_file_index] = sortrows({file_list.name}'); - end; - - disp_dir_names = []; % if need full path, use this - % instead of sorted_dir_names - for i=1:length(sorted_dir_names) - tmp = [filter_path filesep sorted_dir_names{i}]; - disp_dir_names = [disp_dir_names {tmp}]; - end - - h = findobj(gcf,'Tag','DirectoryList'); - set(h,'String',sorted_dir_names,'Value',1); - - h = findobj(gcf,'Tag','FileList'); - set(h,'String',sorted_file_names,'value',1); - - h_select = getappdata(gcf,'h_select'); - if strcmp(filter_path(end),filesep) % filepath end with filesep - filter_path = filter_path(1:end-1); % take filesep out - end - - if isempty(sorted_file_names) - set(h_select,'string',[filter_path filesep]); - else - set(h_select,'string',[filter_path filesep sorted_file_names{1}]); - end - - set(gcf,'Pointer',old_pointer); - - return; % update_dirlist - - -% change 'File format': -% update 'Files' & 'File selection' based on file pattern -% -% -------------------------------------------------------------------- - -function EditFilter() - - filter_select = get(gcbo,'value'); - filter_string = getappdata(gcf,'filter_string'); - filter_pattern = filter_string(filter_select,:); - filter_path = getappdata(gcf,'curr_dir'); - - % update filter_pattern - setappdata(gcf,'FilterPattern',deblank(filter_pattern)); - - if isempty(filter_path), - filter_path = filesep; - end; - - update_dirlist; - - h_file = getappdata(gcf,'h_file'); - if isempty(get(h_file,'string')) - setappdata(gcf,'ready',0); - else - setappdata(gcf,'ready',1); - end - - return; % EditFilter - - -% select 'Directories': -% go into the selected dir -% update 'Files' & 'File selection' based on file pattern -% -% -------------------------------------------------------------------- - -function select_dir() - - listed_dir = get(gcbo,'String'); - selected_dir_idx = get(gcbo,'Value'); - selected_dir = listed_dir{selected_dir_idx}; - curr_dir = getappdata(gcf,'curr_dir'); - - % update the selection box - % - try - cd ([curr_dir filesep selected_dir]); - catch - msg = 'ERROR: Cannot access directory'; - uiwait(msgbox(msg,'File Selection Error','modal')); - return; - end; - - if isempty(pwd) - curr_dir = filesep; - else - curr_dir = pwd; - end; - - setappdata(gcf,'curr_dir',curr_dir); - update_dirlist; - - h_file = getappdata(gcf,'h_file'); - if isempty(get(h_file,'string')) - setappdata(gcf,'ready',0); - else - setappdata(gcf,'ready',1); - end - - return; % select_dir - - -% select 'Files': -% update 'File selection' -% -% -------------------------------------------------------------------- - -function select_file() - - setappdata(gcf,'ready',1); - listed_file = get(gcbo,'String'); - selected_file_idx = get(gcbo,'Value'); - selected_file = listed_file{selected_file_idx}; - curr_dir = getappdata(gcf,'curr_dir'); - - if strcmp(curr_dir(end),filesep) % filepath end with filesep - curr_dir = curr_dir(1:end-1); % take filesep out - end - - h_select = getappdata(gcf,'h_select'); - set(h_select,'string',[curr_dir filesep selected_file]); - - return; % select_file - - -% change 'File selection': -% if it is a file, select that, -% if it is more than a file (*), select those, -% if it is a directory, select based on file pattern -% -% -------------------------------------------------------------------- - -function EditSelection() - - filter_string = getappdata(gcf,'filter_string'); - h_select = getappdata(gcf,'h_select'); - selected_file = get(h_select,'string'); - - if exist(selected_file) == 7 % if user enter a dir - setappdata(gcf,'ready',0); - setappdata(gcf,'curr_dir',selected_file); % get new dir - update_dirlist; - else - - setappdata(gcf,'ready',1); - - [fpath fname fext]= fileparts(selected_file); - if exist(fpath) ~=7 % fpath is not a dir - setappdata(gcf,'ready',0); - msg = 'ERROR: Cannot access directory'; - uiwait(msgbox(msg,'File Selection Error','modal')); - end - - % if the file format user entered is not supported by matlab - if isempty(strmatch(['*',fext],filter_string,'exact')) - setappdata(gcf,'ready',0); - msg = 'ERROR: File format is not supported by Matlab.'; - uiwait(msgbox(msg,'File Selection Error','modal')); - end - - end - - return; % EditSelection - - -% -------------------------------------------------------------------- - -function delete_fig() - - try - load('pls_profile'); - pls_profile = which('pls_profile.mat'); - - rri_select_file_pos = get(gcbf,'position'); - - save(pls_profile, '-append', 'rri_select_file_pos'); - catch - end - - return; - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_xhair.m b/reg-test/matlab_tests/NIfTI_20140122/rri_xhair.m deleted file mode 100644 index 9a9951ad..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_xhair.m +++ /dev/null @@ -1,92 +0,0 @@ -% rri_xhair: create a pair of full_cross_hair at point [x y] in -% axes h_ax, and return xhair struct -% -% Usage: xhair = rri_xhair([x y], xhair, h_ax); -% -% If omit xhair, rri_xhair will create a pair of xhair; otherwise, -% rri_xhair will update the xhair. If omit h_ax, current axes will -% be used. -% - -% 24-nov-2003 jimmy (jimmy@rotman-baycrest.on.ca) -% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function xhair = rri_xhair(varargin) - - if nargin == 0 - error('Please enter a point position as first argument'); - return; - end - - if nargin > 0 - p = varargin{1}; - - if ~isnumeric(p) | length(p) ~= 2 - error('Invalid point position'); - return; - else - xhair = []; - end - end - - if nargin > 1 - xhair = varargin{2}; - - if ~isempty(xhair) - if ~isstruct(xhair) - error('Invalid xhair struct'); - return; - elseif ~isfield(xhair,'lx') | ~isfield(xhair,'ly') - error('Invalid xhair struct'); - return; - elseif ~ishandle(xhair.lx) | ~ishandle(xhair.ly) - error('Invalid xhair struct'); - return; - end - - lx = xhair.lx; - ly = xhair.ly; - else - lx = []; - ly = []; - end - end - - if nargin > 2 - h_ax = varargin{3}; - - if ~ishandle(h_ax) - error('Invalid axes handle'); - return; - elseif ~strcmp(lower(get(h_ax,'type')), 'axes') - error('Invalid axes handle'); - return; - end - else - h_ax = gca; - end - - x_range = get(h_ax,'xlim'); - y_range = get(h_ax,'ylim'); - - if ~isempty(xhair) - set(lx, 'ydata', [p(2) p(2)]); - set(ly, 'xdata', [p(1) p(1)]); - set(h_ax, 'selected', 'on'); - set(h_ax, 'selected', 'off'); - else - figure(get(h_ax,'parent')); - axes(h_ax); - - xhair.lx = line('xdata', x_range, 'ydata', [p(2) p(2)], ... - 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); - xhair.ly = line('xdata', [p(1) p(1)], 'ydata', y_range, ... - 'zdata', [11 11], 'color', [1 0 0], 'hittest', 'off'); - end - - set(h_ax,'xlim',x_range); - set(h_ax,'ylim',y_range); - - return; - diff --git a/reg-test/matlab_tests/NIfTI_20140122/rri_zoom_menu.m b/reg-test/matlab_tests/NIfTI_20140122/rri_zoom_menu.m deleted file mode 100644 index e1c9c6ce..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/rri_zoom_menu.m +++ /dev/null @@ -1,33 +0,0 @@ -% Imbed a zoom menu to any figure. -% -% Usage: rri_zoom_menu(fig); -% - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -%-------------------------------------------------------------------- -function menu_hdl = rri_zoom_menu(fig) - - if isnumeric(fig) - menu_hdl = uimenu('Parent',fig, ... - 'Label','Zoom on', ... - 'Userdata', 1, ... - 'Callback','rri_zoom_menu(''zoom'');'); - - return; - end - - zoom_on_state = get(gcbo,'Userdata'); - - if (zoom_on_state == 1) - zoom on; - set(gcbo,'Userdata',0,'Label','Zoom off'); - set(gcbf,'pointer','crosshair'); - else - zoom off; - set(gcbo,'Userdata',1,'Label','Zoom on'); - set(gcbf,'pointer','arrow'); - end - - return % rri_zoom_menu - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_nii.m b/reg-test/matlab_tests/NIfTI_20140122/save_nii.m deleted file mode 100644 index b2d0dd44..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_nii.m +++ /dev/null @@ -1,286 +0,0 @@ -% Save NIFTI dataset. Support both *.nii and *.hdr/*.img file extension. -% If file extension is not provided, *.hdr/*.img will be used as default. -% -% Usage: save_nii(nii, filename, [old_RGB]) -% -% nii.hdr - struct with NIFTI header fields (from load_nii.m or make_nii.m) -% -% nii.img - 3D (or 4D) matrix of NIFTI data. -% -% filename - NIFTI file name. -% -% old_RGB - an optional boolean variable to handle special RGB data -% sequence [R1 R2 ... G1 G2 ... B1 B2 ...] that is used only by -% AnalyzeDirect (Analyze Software). Since both NIfTI and Analyze -% file format use RGB triple [R1 G1 B1 R2 G2 B2 ...] sequentially -% for each voxel, this variable is set to FALSE by default. If you -% would like the saved image only to be opened by AnalyzeDirect -% Software, set old_RGB to TRUE (or 1). It will be set to 0, if it -% is default or empty. -% -% Tip: to change the data type, set nii.hdr.dime.datatype, -% and nii.hdr.dime.bitpix to: -% -% 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN -% 1 Binary (ubit1, bitpix=1) % DT_BINARY -% 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 -% 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 -% 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 -% 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 -% 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 -% 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 -% 128 uint RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 -% 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 -% 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 -% 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 -% 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 -% 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 -% 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 -% 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 -% 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 -% 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 -% -% Part of this file is copied and modified from: -% http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% - "old_RGB" related codes in "save_nii.m" are added by Mike Harms (2006.06.28) -% -function save_nii(nii, fileprefix, old_RGB) - - if ~exist('nii','var') | isempty(nii) | ~isfield(nii,'hdr') | ... - ~isfield(nii,'img') | ~exist('fileprefix','var') | isempty(fileprefix) - - error('Usage: save_nii(nii, filename, [old_RGB])'); - end - - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''save_untouch_nii.m'' for the untouched structure.'); - end - - if ~exist('old_RGB','var') | isempty(old_RGB) - old_RGB = 0; - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(fileprefix) > 2 & strcmp(fileprefix(end-2:end), '.gz') - - if ~strcmp(fileprefix(end-6:end), '.img.gz') & ... - ~strcmp(fileprefix(end-6:end), '.hdr.gz') & ... - ~strcmp(fileprefix(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - fileprefix = fileprefix(1:end-3); - end - end - - filetype = 1; - - % Note: fileprefix is actually the filename you want to save - % - if findstr('.nii',fileprefix) & strcmp(fileprefix(end-3:end), '.nii') - filetype = 2; - fileprefix(end-3:end)=''; - end - - if findstr('.hdr',fileprefix) & strcmp(fileprefix(end-3:end), '.hdr') - fileprefix(end-3:end)=''; - end - - if findstr('.img',fileprefix) & strcmp(fileprefix(end-3:end), '.img') - fileprefix(end-3:end)=''; - end - - write_nii(nii, filetype, fileprefix, old_RGB); - - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; - - if filetype == 1 - - % So earlier versions of SPM can also open it with correct originator - % - M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]]; - save([fileprefix '.mat'], 'M'); - end - - return % save_nii - - -%----------------------------------------------------------------------------------- -function write_nii(nii, filetype, fileprefix, old_RGB) - - hdr = nii.hdr; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 511, - hdr.dime.bitpix = int16(96); precision = 'float32'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - - hdr.dime.glmax = round(double(max(nii.img(:)))); - hdr.dime.glmin = round(double(min(nii.img(:)))); - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end - - hdr.hist.magic = 'n+1'; - save_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - ScanDim = double(hdr.dime.dim(5)); % t - SliceDim = double(hdr.dime.dim(4)); % z - RowDim = double(hdr.dime.dim(3)); % y - PixelDim = double(hdr.dime.dim(2)); % x - SliceSz = double(hdr.dime.pixdim(4)); - RowSz = double(hdr.dime.pixdim(3)); - PixelSz = double(hdr.dime.pixdim(2)); - - x = 1:PixelDim; - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - if old_RGB - nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); - else - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - end - - if double(hdr.dime.datatype) == 511 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - if old_RGB - nii.img = permute(nii.img, [1 2 4 3 5 6 7 8]); - else - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - fwrite(fid, nii.img, precision); -% fwrite(fid, nii.img, precision, skip_bytes); % error using skip - fclose(fid); - - return; % write_nii - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_nii_ext.m b/reg-test/matlab_tests/NIfTI_20140122/save_nii_ext.m deleted file mode 100644 index 5e1509c2..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_nii_ext.m +++ /dev/null @@ -1,38 +0,0 @@ -% Save NIFTI header extension. -% -% Usage: save_nii_ext(ext, fid) -% -% ext - struct with NIFTI header extension fields. -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function save_nii_ext(ext, fid) - - if ~exist('ext','var') | ~exist('fid','var') - error('Usage: save_nii_ext(ext, fid)'); - end - - if ~isfield(ext,'extension') | ~isfield(ext,'section') | ~isfield(ext,'num_ext') - error('Wrong header extension'); - end - - write_ext(ext, fid); - - return; % save_nii_ext - - -%--------------------------------------------------------------------- -function write_ext(ext, fid) - - fwrite(fid, ext.extension, 'uchar'); - - for i=1:ext.num_ext - fwrite(fid, ext.section(i).esize, 'int32'); - fwrite(fid, ext.section(i).ecode, 'int32'); - fwrite(fid, ext.section(i).edata, 'uchar'); - end - - return; % write_ext - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/save_nii_hdr.m deleted file mode 100644 index 225f297c..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_nii_hdr.m +++ /dev/null @@ -1,227 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function save_nii_hdr(hdr, fid) - - if ~exist('hdr','var') | ~exist('fid','var') - error('Usage: save_nii_hdr(hdr, fid)'); - end - - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end - - if hdr.hist.qform_code == 0 & hdr.hist.sform_code == 0 - hdr.hist.sform_code = 1; - hdr.hist.srow_x(1) = hdr.dime.pixdim(2); - hdr.hist.srow_x(2) = 0; - hdr.hist.srow_x(3) = 0; - hdr.hist.srow_y(1) = 0; - hdr.hist.srow_y(2) = hdr.dime.pixdim(3); - hdr.hist.srow_y(3) = 0; - hdr.hist.srow_z(1) = 0; - hdr.hist.srow_z(2) = 0; - hdr.hist.srow_z(3) = hdr.dime.pixdim(4); - hdr.hist.srow_x(4) = (1-hdr.hist.originator(1))*hdr.dime.pixdim(2); - hdr.hist.srow_y(4) = (1-hdr.hist.originator(2))*hdr.dime.pixdim(3); - hdr.hist.srow_z(4) = (1-hdr.hist.originator(3))*hdr.dime.pixdim(4); - end - - write_header(hdr, fid); - - return; % save_nii_hdr - - -%--------------------------------------------------------------------- -function write_header(hdr, fid) - - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); - - % check the file size is 348 bytes - % - fbytes = ftell(fid); - - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end - - return; % write_header - - -%--------------------------------------------------------------------- -function header_key(fid, hk) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 - - % fwrite(fid, hk.hkey_un0(1), 'uchar'); - % fwrite(fid, hk.hkey_un0(1), 'uint8'); - fwrite(fid, hk.dim_info(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- -function image_dimension(fid, dime) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - fwrite(fid, dime.intent_p1(1), 'float32'); - fwrite(fid, dime.intent_p2(1), 'float32'); - fwrite(fid, dime.intent_p3(1), 'float32'); - fwrite(fid, dime.intent_code(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.slice_start(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.scl_slope(1), 'float32'); - fwrite(fid, dime.scl_inter(1), 'float32'); - fwrite(fid, dime.slice_end(1), 'int16'); - fwrite(fid, dime.slice_code(1), 'uchar'); - fwrite(fid, dime.xyzt_units(1), 'uchar'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.slice_duration(1), 'float32'); - fwrite(fid, dime.toffset(1), 'float32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- -function data_history(fid, hist) - - % Original header structures - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.qform_code, 'int16'); - fwrite(fid, hist.sform_code, 'int16'); - fwrite(fid, hist.quatern_b, 'float32'); - fwrite(fid, hist.quatern_c, 'float32'); - fwrite(fid, hist.quatern_d, 'float32'); - fwrite(fid, hist.qoffset_x, 'float32'); - fwrite(fid, hist.qoffset_y, 'float32'); - fwrite(fid, hist.qoffset_z, 'float32'); - fwrite(fid, hist.srow_x(1:4), 'float32'); - fwrite(fid, hist.srow_y(1:4), 'float32'); - fwrite(fid, hist.srow_z(1:4), 'float32'); - - % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left - % fwrite(fid, intent_name(1:16), 'uchar'); - pad = zeros(1, 16-length(hist.intent_name)); - hist.intent_name = [hist.intent_name char(pad)]; - fwrite(fid, hist.intent_name(1:16), 'uchar'); - - % magic = sprintf('%-4s', hist.magic); % 4 chars from left - % fwrite(fid, magic(1:4), 'uchar'); - pad = zeros(1, 4-length(hist.magic)); - hist.magic = [hist.magic char(pad)]; - fwrite(fid, hist.magic(1:4), 'uchar'); - - return; % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_untouch0_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/save_untouch0_nii_hdr.m deleted file mode 100644 index ba1f3226..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_untouch0_nii_hdr.m +++ /dev/null @@ -1,219 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function save_nii_hdr(hdr, fid) - - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end - - write_header(hdr, fid); - - return; % save_nii_hdr - - -%--------------------------------------------------------------------- -function write_header(hdr, fid) - - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); - - % check the file size is 348 bytes - % - fbytes = ftell(fid); - - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end - - return; % write_header - - -%--------------------------------------------------------------------- -function header_key(fid, hk) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); - - fwrite(fid, hk.hkey_un0(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- -function image_dimension(fid, dime) - - %struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % char vox_units[4]; /* 16 + 4 */ - % char cal_units[8]; /* 20 + 8 */ - % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float roi_scale; /* 72 + 4 */ - % float funused1; /* 76 + 4 */ - % float funused2; /* 80 + 4 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % int compressed; /* 92 + 4 */ - % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - - pad = zeros(1, 4-length(dime.vox_units)); - dime.vox_units = [dime.vox_units char(pad)]; - fwrite(fid, dime.vox_units(1:4), 'uchar'); - - pad = zeros(1, 8-length(dime.cal_units)); - dime.cal_units = [dime.cal_units char(pad)]; - fwrite(fid, dime.cal_units(1:8), 'uchar'); - - fwrite(fid, dime.unused1(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.dim_un0(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.roi_scale(1), 'float32'); - fwrite(fid, dime.funused1(1), 'float32'); - fwrite(fid, dime.funused2(1), 'float32'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.compressed(1), 'int32'); - fwrite(fid, dime.verified(1), 'int32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- -function data_history(fid, hist) - - % Original header structures - ANALYZE 7.5 - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % char orient; /* 104 + 1 */ - % char originator[10]; /* 105 + 10 */ - % char generated[10]; /* 115 + 10 */ - % char scannum[10]; /* 125 + 10 */ - % char patient_id[10]; /* 135 + 10 */ - % char exp_date[10]; /* 145 + 10 */ - % char exp_time[10]; /* 155 + 10 */ - % char hist_un0[3]; /* 165 + 3 */ - % int views /* 168 + 4 */ - % int vols_added; /* 172 + 4 */ - % int start_field; /* 176 + 4 */ - % int field_skip; /* 180 + 4 */ - % int omax; /* 184 + 4 */ - % int omin; /* 188 + 4 */ - % int smax; /* 192 + 4 */ - % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.orient(1), 'uchar'); - fwrite(fid, hist.originator(1:5), 'int16'); - - pad = zeros(1, 10-length(hist.generated)); - hist.generated = [hist.generated char(pad)]; - fwrite(fid, hist.generated(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.scannum)); - hist.scannum = [hist.scannum char(pad)]; - fwrite(fid, hist.scannum(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.patient_id)); - hist.patient_id = [hist.patient_id char(pad)]; - fwrite(fid, hist.patient_id(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.exp_date)); - hist.exp_date = [hist.exp_date char(pad)]; - fwrite(fid, hist.exp_date(1:10), 'uchar'); - - pad = zeros(1, 10-length(hist.exp_time)); - hist.exp_time = [hist.exp_time char(pad)]; - fwrite(fid, hist.exp_time(1:10), 'uchar'); - - pad = zeros(1, 3-length(hist.hist_un0)); - hist.hist_un0 = [hist.hist_un0 char(pad)]; - fwrite(fid, hist.hist_un0(1:3), 'uchar'); - - fwrite(fid, hist.views(1), 'int32'); - fwrite(fid, hist.vols_added(1), 'int32'); - fwrite(fid, hist.start_field(1),'int32'); - fwrite(fid, hist.field_skip(1), 'int32'); - fwrite(fid, hist.omax(1), 'int32'); - fwrite(fid, hist.omin(1), 'int32'); - fwrite(fid, hist.smax(1), 'int32'); - fwrite(fid, hist.smin(1), 'int32'); - - return; % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_header_only.m b/reg-test/matlab_tests/NIfTI_20140122/save_untouch_header_only.m deleted file mode 100644 index b1450582..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_header_only.m +++ /dev/null @@ -1,71 +0,0 @@ -% This function is only used to save Analyze or NIfTI header that is -% ended with .hdr and loaded by load_untouch_header_only.m. If you -% have NIfTI file that is ended with .nii and you want to change its -% header only, you can use load_untouch_nii / save_untouch_nii pair. -% -% Usage: save_untouch_header_only(hdr, new_header_file_name) -% -% hdr - struct with NIfTI / Analyze header fields, which is obtained from: -% hdr = load_untouch_header_only(original_header_file_name) -% -% new_header_file_name - NIfTI / Analyze header name ended with .hdr. -% You can either copy original.img(.gz) to new.img(.gz) manually, -% or simply input original.hdr(.gz) in save_untouch_header_only.m -% to overwrite the original header. -% -% - Jimmy Shen (jshen@research.baycrest.org) -% -function save_untouch_header_only(hdr, filename) - - if ~exist('hdr','var') | isempty(hdr) | ~exist('filename','var') | isempty(filename) - error('Usage: save_untouch_header_only(hdr, filename)'); - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.hdr.gz') - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - filename = filename(1:end-3); - end - end - - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); - - write_hdr(hdr, fileprefix); - - % gzip output file if requested - % - if exist('gzFile', 'var') - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - end; - - return % save_untouch_header_only - - -%----------------------------------------------------------------------------------- -function write_hdr(hdr, fileprefix) - - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if isfield(hdr.hist,'magic') - save_untouch_nii_hdr(hdr, fid); - else - save_untouch0_nii_hdr(hdr, fid); - end - - fclose(fid); - - return % write_hdr - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii.m b/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii.m deleted file mode 100644 index 80021721..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii.m +++ /dev/null @@ -1,232 +0,0 @@ -% Save NIFTI or ANALYZE dataset that is loaded by "load_untouch_nii.m". -% The output image format and file extension will be the same as the -% input one (NIFTI.nii, NIFTI.img or ANALYZE.img). Therefore, any file -% extension that you specified will be ignored. -% -% Usage: save_untouch_nii(nii, filename) -% -% nii - nii structure that is loaded by "load_untouch_nii.m" -% -% filename - NIFTI or ANALYZE file name. -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function save_untouch_nii(nii, filename) - - if ~exist('nii','var') | isempty(nii) | ~isfield(nii,'hdr') | ... - ~isfield(nii,'img') | ~exist('filename','var') | isempty(filename) - - error('Usage: save_untouch_nii(nii, filename)'); - end - - if ~isfield(nii,'untouch') | nii.untouch == 0 - error('Usage: please use ''save_nii.m'' for the modified structure.'); - end - - if isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'ni1') - filetype = 1; - elseif isfield(nii.hdr.hist,'magic') & strcmp(nii.hdr.hist.magic(1:3),'n+1') - filetype = 2; - else - filetype = 0; - end - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - else - gzFile = 1; - filename = filename(1:end-3); - end - end - - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); - - write_nii(nii, filetype, fileprefix); - - % gzip output file if requested - % - if exist('gzFile', 'var') - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - end; - end; - -% % So earlier versions of SPM can also open it with correct originator - % % - % if filetype == 0 - % M=[[diag(nii.hdr.dime.pixdim(2:4)) -[nii.hdr.hist.originator(1:3).*nii.hdr.dime.pixdim(2:4)]'];[0 0 0 1]]; - % save(fileprefix, 'M'); -% elseif filetype == 1 - % M=[]; - % save(fileprefix, 'M'); - %end - - return % save_untouch_nii - - -%----------------------------------------------------------------------------------- -function write_nii(nii, filetype, fileprefix) - - hdr = nii.hdr; - - if isfield(nii,'ext') & ~isempty(nii.ext) - ext = nii.ext; - [ext, esize_total] = verify_nii_ext(ext); - else - ext = []; - end - - switch double(hdr.dime.datatype), - case 1, - hdr.dime.bitpix = int16(1 ); precision = 'ubit1'; - case 2, - hdr.dime.bitpix = int16(8 ); precision = 'uint8'; - case 4, - hdr.dime.bitpix = int16(16); precision = 'int16'; - case 8, - hdr.dime.bitpix = int16(32); precision = 'int32'; - case 16, - hdr.dime.bitpix = int16(32); precision = 'float32'; - case 32, - hdr.dime.bitpix = int16(64); precision = 'float32'; - case 64, - hdr.dime.bitpix = int16(64); precision = 'float64'; - case 128, - hdr.dime.bitpix = int16(24); precision = 'uint8'; - case 256 - hdr.dime.bitpix = int16(8 ); precision = 'int8'; - case 512 - hdr.dime.bitpix = int16(16); precision = 'uint16'; - case 768 - hdr.dime.bitpix = int16(32); precision = 'uint32'; - case 1024 - hdr.dime.bitpix = int16(64); precision = 'int64'; - case 1280 - hdr.dime.bitpix = int16(64); precision = 'uint64'; - case 1792, - hdr.dime.bitpix = int16(128); precision = 'float64'; - otherwise - error('This datatype is not supported'); - end - -% hdr.dime.glmax = round(double(max(nii.img(:)))); - % hdr.dime.glmin = round(double(min(nii.img(:)))); - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 352; - - if ~isempty(ext) - hdr.dime.vox_offset = hdr.dime.vox_offset + esize_total; - end - - hdr.hist.magic = 'n+1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - elseif filetype == 1 - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - hdr.dime.vox_offset = 0; - hdr.hist.magic = 'ni1'; - save_untouch_nii_hdr(hdr, fid); - - if ~isempty(ext) - save_nii_ext(ext, fid); - end - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - else - fid = fopen(sprintf('%s.hdr',fileprefix),'w'); - - if fid < 0, - msg = sprintf('Cannot open file %s.hdr.',fileprefix); - error(msg); - end - - save_untouch0_nii_hdr(hdr, fid); - - fclose(fid); - fid = fopen(sprintf('%s.img',fileprefix),'w'); - end - - ScanDim = double(hdr.dime.dim(5)); % t - SliceDim = double(hdr.dime.dim(4)); % z - RowDim = double(hdr.dime.dim(3)); % y - PixelDim = double(hdr.dime.dim(2)); % x - SliceSz = double(hdr.dime.pixdim(4)); - RowSz = double(hdr.dime.pixdim(3)); - PixelSz = double(hdr.dime.pixdim(2)); - - x = 1:PixelDim; - - if filetype == 2 & isempty(ext) - skip_bytes = double(hdr.dime.vox_offset) - 348; - else - skip_bytes = 0; - end - - if double(hdr.dime.datatype) == 128 - - % RGB planes are expected to be in the 4th dimension of nii.img - % - if(size(nii.img,4)~=3) - error(['The NII structure does not appear to have 3 RGB color planes in the 4th dimension']); - end - - nii.img = permute(nii.img, [4 1 2 3 5 6 7 8]); - end - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - real_img = real(nii.img(:))'; - nii.img = imag(nii.img(:))'; - nii.img = [real_img; nii.img]; - end - - if skip_bytes - fwrite(fid, zeros(1,skip_bytes), 'uint8'); - end - - fwrite(fid, nii.img, precision); -% fwrite(fid, nii.img, precision, skip_bytes); % error using skip - fclose(fid); - - return; % write_nii - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii_hdr.m b/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii_hdr.m deleted file mode 100644 index 33fa9eda..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_nii_hdr.m +++ /dev/null @@ -1,207 +0,0 @@ -% internal function - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) - -function save_nii_hdr(hdr, fid) - - if ~isequal(hdr.hk.sizeof_hdr,348), - error('hdr.hk.sizeof_hdr must be 348.'); - end - - write_header(hdr, fid); - - return; % save_nii_hdr - - -%--------------------------------------------------------------------- -function write_header(hdr, fid) - - % Original header structures - % struct dsr /* dsr = hdr */ - % { - % struct header_key hk; /* 0 + 40 */ - % struct image_dimension dime; /* 40 + 108 */ - % struct data_history hist; /* 148 + 200 */ - % }; /* total= 348 bytes*/ - - header_key(fid, hdr.hk); - image_dimension(fid, hdr.dime); - data_history(fid, hdr.hist); - - % check the file size is 348 bytes - % - fbytes = ftell(fid); - - if ~isequal(fbytes,348), - msg = sprintf('Header size is not 348 bytes.'); - warning(msg); - end - - return; % write_header - - -%--------------------------------------------------------------------- -function header_key(fid, hk) - - fseek(fid,0,'bof'); - - % Original header structures - % struct header_key /* header key */ - % { /* off + size */ - % int sizeof_hdr /* 0 + 4 */ - % char data_type[10]; /* 4 + 10 */ - % char db_name[18]; /* 14 + 18 */ - % int extents; /* 32 + 4 */ - % short int session_error; /* 36 + 2 */ - % char regular; /* 38 + 1 */ - % char dim_info; % char hkey_un0; /* 39 + 1 */ - % }; /* total=40 bytes */ - - fwrite(fid, hk.sizeof_hdr(1), 'int32'); % must be 348. - - % data_type = sprintf('%-10s',hk.data_type); % ensure it is 10 chars from left - % fwrite(fid, data_type(1:10), 'uchar'); - pad = zeros(1, 10-length(hk.data_type)); - hk.data_type = [hk.data_type char(pad)]; - fwrite(fid, hk.data_type(1:10), 'uchar'); - - % db_name = sprintf('%-18s', hk.db_name); % ensure it is 18 chars from left - % fwrite(fid, db_name(1:18), 'uchar'); - pad = zeros(1, 18-length(hk.db_name)); - hk.db_name = [hk.db_name char(pad)]; - fwrite(fid, hk.db_name(1:18), 'uchar'); - - fwrite(fid, hk.extents(1), 'int32'); - fwrite(fid, hk.session_error(1), 'int16'); - fwrite(fid, hk.regular(1), 'uchar'); % might be uint8 - - % fwrite(fid, hk.hkey_un0(1), 'uchar'); - % fwrite(fid, hk.hkey_un0(1), 'uint8'); - fwrite(fid, hk.dim_info(1), 'uchar'); - - return; % header_key - - -%--------------------------------------------------------------------- -function image_dimension(fid, dime) - - % Original header structures - % struct image_dimension - % { /* off + size */ - % short int dim[8]; /* 0 + 16 */ - % float intent_p1; % char vox_units[4]; /* 16 + 4 */ - % float intent_p2; % char cal_units[8]; /* 20 + 4 */ - % float intent_p3; % char cal_units[8]; /* 24 + 4 */ - % short int intent_code; % short int unused1; /* 28 + 2 */ - % short int datatype; /* 30 + 2 */ - % short int bitpix; /* 32 + 2 */ - % short int slice_start; % short int dim_un0; /* 34 + 2 */ - % float pixdim[8]; /* 36 + 32 */ - % /* - % pixdim[] specifies the voxel dimensions: - % pixdim[1] - voxel width - % pixdim[2] - voxel height - % pixdim[3] - interslice distance - % pixdim[4] - volume timing, in msec - % ..etc - % */ - % float vox_offset; /* 68 + 4 */ - % float scl_slope; % float roi_scale; /* 72 + 4 */ - % float scl_inter; % float funused1; /* 76 + 4 */ - % short slice_end; % float funused2; /* 80 + 2 */ - % char slice_code; % float funused2; /* 82 + 1 */ - % char xyzt_units; % float funused2; /* 83 + 1 */ - % float cal_max; /* 84 + 4 */ - % float cal_min; /* 88 + 4 */ - % float slice_duration; % int compressed; /* 92 + 4 */ - % float toffset; % int verified; /* 96 + 4 */ - % int glmax; /* 100 + 4 */ - % int glmin; /* 104 + 4 */ - % }; /* total=108 bytes */ - - fwrite(fid, dime.dim(1:8), 'int16'); - fwrite(fid, dime.intent_p1(1), 'float32'); - fwrite(fid, dime.intent_p2(1), 'float32'); - fwrite(fid, dime.intent_p3(1), 'float32'); - fwrite(fid, dime.intent_code(1), 'int16'); - fwrite(fid, dime.datatype(1), 'int16'); - fwrite(fid, dime.bitpix(1), 'int16'); - fwrite(fid, dime.slice_start(1), 'int16'); - fwrite(fid, dime.pixdim(1:8), 'float32'); - fwrite(fid, dime.vox_offset(1), 'float32'); - fwrite(fid, dime.scl_slope(1), 'float32'); - fwrite(fid, dime.scl_inter(1), 'float32'); - fwrite(fid, dime.slice_end(1), 'int16'); - fwrite(fid, dime.slice_code(1), 'uchar'); - fwrite(fid, dime.xyzt_units(1), 'uchar'); - fwrite(fid, dime.cal_max(1), 'float32'); - fwrite(fid, dime.cal_min(1), 'float32'); - fwrite(fid, dime.slice_duration(1), 'float32'); - fwrite(fid, dime.toffset(1), 'float32'); - fwrite(fid, dime.glmax(1), 'int32'); - fwrite(fid, dime.glmin(1), 'int32'); - - return; % image_dimension - - -%--------------------------------------------------------------------- -function data_history(fid, hist) - - % Original header structures - %struct data_history - % { /* off + size */ - % char descrip[80]; /* 0 + 80 */ - % char aux_file[24]; /* 80 + 24 */ - % short int qform_code; /* 104 + 2 */ - % short int sform_code; /* 106 + 2 */ - % float quatern_b; /* 108 + 4 */ - % float quatern_c; /* 112 + 4 */ - % float quatern_d; /* 116 + 4 */ - % float qoffset_x; /* 120 + 4 */ - % float qoffset_y; /* 124 + 4 */ - % float qoffset_z; /* 128 + 4 */ - % float srow_x[4]; /* 132 + 16 */ - % float srow_y[4]; /* 148 + 16 */ - % float srow_z[4]; /* 164 + 16 */ - % char intent_name[16]; /* 180 + 16 */ - % char magic[4]; % int smin; /* 196 + 4 */ - % }; /* total=200 bytes */ - - % descrip = sprintf('%-80s', hist.descrip); % 80 chars from left - % fwrite(fid, descrip(1:80), 'uchar'); - pad = zeros(1, 80-length(hist.descrip)); - hist.descrip = [hist.descrip char(pad)]; - fwrite(fid, hist.descrip(1:80), 'uchar'); - - % aux_file = sprintf('%-24s', hist.aux_file); % 24 chars from left - % fwrite(fid, aux_file(1:24), 'uchar'); - pad = zeros(1, 24-length(hist.aux_file)); - hist.aux_file = [hist.aux_file char(pad)]; - fwrite(fid, hist.aux_file(1:24), 'uchar'); - - fwrite(fid, hist.qform_code, 'int16'); - fwrite(fid, hist.sform_code, 'int16'); - fwrite(fid, hist.quatern_b, 'float32'); - fwrite(fid, hist.quatern_c, 'float32'); - fwrite(fid, hist.quatern_d, 'float32'); - fwrite(fid, hist.qoffset_x, 'float32'); - fwrite(fid, hist.qoffset_y, 'float32'); - fwrite(fid, hist.qoffset_z, 'float32'); - fwrite(fid, hist.srow_x(1:4), 'float32'); - fwrite(fid, hist.srow_y(1:4), 'float32'); - fwrite(fid, hist.srow_z(1:4), 'float32'); - - % intent_name = sprintf('%-16s', hist.intent_name); % 16 chars from left - % fwrite(fid, intent_name(1:16), 'uchar'); - pad = zeros(1, 16-length(hist.intent_name)); - hist.intent_name = [hist.intent_name char(pad)]; - fwrite(fid, hist.intent_name(1:16), 'uchar'); - - % magic = sprintf('%-4s', hist.magic); % 4 chars from left - % fwrite(fid, magic(1:4), 'uchar'); - pad = zeros(1, 4-length(hist.magic)); - hist.magic = [hist.magic char(pad)]; - fwrite(fid, hist.magic(1:4), 'uchar'); - - return; % data_history - diff --git a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_slice.m b/reg-test/matlab_tests/NIfTI_20140122/save_untouch_slice.m deleted file mode 100644 index af73521a..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/save_untouch_slice.m +++ /dev/null @@ -1,580 +0,0 @@ -% Save back to the original image with a portion of slices that was -% loaded by "load_untouch_nii". You can process those slices matrix -% in any way, as long as their dimension is not altered. -% -% Usage: save_untouch_slice(slice, filename, ... -% slice_idx, [img_idx], [dim5_idx], [dim6_idx], [dim7_idx]) -% -% slice - a portion of slices that was loaded by "load_untouch_nii". -% This should be a numeric matrix (i.e. only the .img field in the -% loaded structure) -% -% filename - NIfTI or ANALYZE file name. -% -% slice_idx (depending on slice size) - a numerical array of image -% slice indices, which should be the same as that you entered -% in "load_untouch_nii" command. -% -% img_idx (depending on slice size) - a numerical array of image -% volume indices, which should be the same as that you entered -% in "load_untouch_nii" command. -% -% dim5_idx (depending on slice size) - a numerical array of 5th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. -% -% dim6_idx (depending on slice size) - a numerical array of 6th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. -% -% dim7_idx (depending on slice size) - a numerical array of 7th -% dimension indices, which should be the same as that you entered -% in "load_untouch_nii" command. -% -% Example: -% nii = load_nii('avg152T1_LR_nifti.nii'); -% save_nii(nii, 'test.nii'); -% view_nii(nii); -% nii = load_untouch_nii('test.nii','','','','','',[40 51:53]); -% nii.img = ones(91,109,4)*122; -% save_untouch_slice(nii.img, 'test.nii', [40 51:52]); -% nii = load_nii('test.nii'); -% view_nii(nii); -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function save_untouch_slice(slice, filename, slice_idx, img_idx, dim5_idx, dim6_idx, dim7_idx) - - if ~exist('slice','var') | ~isnumeric(slice) - msg = [char(10) '"slice" argument should be a portion of slices that was loaded' char(10)]; - msg = [msg 'by "load_untouch_nii.m". This should be a numeric matrix (i.e.' char(10)]; - msg = [msg 'only the .img field in the loaded structure).']; - error(msg); - end - - if ~exist('filename','var') | ~exist(filename,'file') - error('In order to save back, original NIfTI or ANALYZE file must exist.'); - end - - if ~exist('slice_idx','var') | isempty(slice_idx) | ~isequal(size(slice,3),length(slice_idx)) - msg = [char(10) '"slice_idx" is a numerical array of image slice indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - - if ~exist('img_idx','var') | isempty(img_idx) - img_idx = []; - - if ~isequal(size(slice,4),1) - msg = [char(10) '"img_idx" is a numerical array of image volume indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,4),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of image volume indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) - dim5_idx = []; - - if ~isequal(size(slice,5),1) - msg = [char(10) '"dim5_idx" is a numerical array of 5th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,5),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 5th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) - dim6_idx = []; - - if ~isequal(size(slice,6),1) - msg = [char(10) '"dim6_idx" is a numerical array of 6th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,6),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 6th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) - dim7_idx = []; - - if ~isequal(size(slice,7),1) - msg = [char(10) '"dim7_idx" is a numerical array of 7th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - elseif ~isequal(size(slice,7),length(img_idx)) - msg = [char(10) '"img_idx" is a numerical array of 7th dimension indices, which' char(10)]; - msg = [msg 'should be the same as that you entered in "load_untouch_nii.m"' char(10)]; - msg = [msg 'command.']; - error(msg); - end - - - v = version; - - % Check file extension. If .gz, unpack it into temp folder - % - if length(filename) > 2 & strcmp(filename(end-2:end), '.gz') - - if ~strcmp(filename(end-6:end), '.img.gz') & ... - ~strcmp(filename(end-6:end), '.hdr.gz') & ... - ~strcmp(filename(end-6:end), '.nii.gz') - - error('Please check filename.'); - end - - if str2num(v(1:3)) < 7.1 | ~usejava('jvm') - error('Please use MATLAB 7.1 (with java) and above, or run gunzip outside MATLAB.'); - elseif strcmp(filename(end-6:end), '.img.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.hdr.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.hdr.gz') - filename1 = filename; - filename2 = filename; - filename2(end-6:end) = ''; - filename2 = [filename2, '.img.gz']; - - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - - filename1 = gunzip(filename1, tmpDir); - filename2 = gunzip(filename2, tmpDir); - filename = char(filename1); % convert from cell to string - elseif strcmp(filename(end-6:end), '.nii.gz') - tmpDir = tempname; - mkdir(tmpDir); - gzFileName = filename; - filename = gunzip(filename, tmpDir); - filename = char(filename); % convert from cell to string - end - end - - % Read the dataset header - % - [nii.hdr,nii.filetype,nii.fileprefix,nii.machine] = load_nii_hdr(filename); - - if nii.filetype == 0 - nii.hdr = load_untouch0_nii_hdr(nii.fileprefix,nii.machine); - else - nii.hdr = load_untouch_nii_hdr(nii.fileprefix,nii.machine,nii.filetype); - end - - - % Clean up after gunzip - % - if exist('gzFileName', 'var') - - % fix fileprefix so it doesn't point to temp location - % - nii.fileprefix = gzFileName(1:end-7); -% rmdir(tmpDir,'s'); - end - - [p,f] = fileparts(filename); - fileprefix = fullfile(p, f); -% fileprefix = nii.fileprefix; - filetype = nii.filetype; - - if ~isequal( nii.hdr.dime.dim(2:3), [size(slice,1),size(slice,2)] ) - msg = [char(10) 'The first two dimensions of slice matrix should be the same as' char(10)]; - msg = [msg 'the first two dimensions of image loaded by "load_untouch_nii".']; - error(msg); - end - - - % Save the dataset body - % - save_untouch_slice_img(slice, nii.hdr, filetype, fileprefix, ... - nii.machine, slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx); - - % gzip output file if requested - % - if exist('gzFileName', 'var') - [p,f] = fileparts(gzFileName); - - if filetype == 1 - gzip([fileprefix, '.img']); - delete([fileprefix, '.img']); - movefile([fileprefix, '.img.gz']); - gzip([fileprefix, '.hdr']); - delete([fileprefix, '.hdr']); - movefile([fileprefix, '.hdr.gz']); - elseif filetype == 2 - gzip([fileprefix, '.nii']); - delete([fileprefix, '.nii']); - movefile([fileprefix, '.nii.gz']); - end; - - rmdir(tmpDir,'s'); - end; - - return % save_untouch_slice - - -%-------------------------------------------------------------------------- -function save_untouch_slice_img(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx) - - if ~exist('hdr','var') | ~exist('filetype','var') | ~exist('fileprefix','var') | ~exist('machine','var') - error('Usage: save_untouch_slice_img(slice,hdr,filetype,fileprefix,machine,slice_idx,[img_idx],[dim5_idx],[dim6_idx],[dim7_idx]);'); - end - - if ~exist('slice_idx','var') | isempty(slice_idx) | hdr.dime.dim(4)<1 - slice_idx = []; - end - - if ~exist('img_idx','var') | isempty(img_idx) | hdr.dime.dim(5)<1 - img_idx = []; - end - - if ~exist('dim5_idx','var') | isempty(dim5_idx) | hdr.dime.dim(6)<1 - dim5_idx = []; - end - - if ~exist('dim6_idx','var') | isempty(dim6_idx) | hdr.dime.dim(7)<1 - dim6_idx = []; - end - - if ~exist('dim7_idx','var') | isempty(dim7_idx) | hdr.dime.dim(8)<1 - dim7_idx = []; - end - - % check img_idx - % - if ~isempty(img_idx) & ~isnumeric(img_idx) - error('"img_idx" should be a numerical array.'); - end - - if length(unique(img_idx)) ~= length(img_idx) - error('Duplicate image index in "img_idx"'); - end - - if ~isempty(img_idx) & (min(img_idx) < 1 | max(img_idx) > hdr.dime.dim(5)) - max_range = hdr.dime.dim(5); - - if max_range == 1 - error(['"img_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"img_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim5_idx - % - if ~isempty(dim5_idx) & ~isnumeric(dim5_idx) - error('"dim5_idx" should be a numerical array.'); - end - - if length(unique(dim5_idx)) ~= length(dim5_idx) - error('Duplicate index in "dim5_idx"'); - end - - if ~isempty(dim5_idx) & (min(dim5_idx) < 1 | max(dim5_idx) > hdr.dime.dim(6)) - max_range = hdr.dime.dim(6); - - if max_range == 1 - error(['"dim5_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim5_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim6_idx - % - if ~isempty(dim6_idx) & ~isnumeric(dim6_idx) - error('"dim6_idx" should be a numerical array.'); - end - - if length(unique(dim6_idx)) ~= length(dim6_idx) - error('Duplicate index in "dim6_idx"'); - end - - if ~isempty(dim6_idx) & (min(dim6_idx) < 1 | max(dim6_idx) > hdr.dime.dim(7)) - max_range = hdr.dime.dim(7); - - if max_range == 1 - error(['"dim6_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim6_idx" should be an integer within the range of [' range '].']); - end - end - - % check dim7_idx - % - if ~isempty(dim7_idx) & ~isnumeric(dim7_idx) - error('"dim7_idx" should be a numerical array.'); - end - - if length(unique(dim7_idx)) ~= length(dim7_idx) - error('Duplicate index in "dim7_idx"'); - end - - if ~isempty(dim7_idx) & (min(dim7_idx) < 1 | max(dim7_idx) > hdr.dime.dim(8)) - max_range = hdr.dime.dim(8); - - if max_range == 1 - error(['"dim7_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"dim7_idx" should be an integer within the range of [' range '].']); - end - end - - % check slice_idx - % - if ~isempty(slice_idx) & ~isnumeric(slice_idx) - error('"slice_idx" should be a numerical array.'); - end - - if length(unique(slice_idx)) ~= length(slice_idx) - error('Duplicate index in "slice_idx"'); - end - - if ~isempty(slice_idx) & (min(slice_idx) < 1 | max(slice_idx) > hdr.dime.dim(4)) - max_range = hdr.dime.dim(4); - - if max_range == 1 - error(['"slice_idx" should be 1.']); - else - range = ['1 ' num2str(max_range)]; - error(['"slice_idx" should be an integer within the range of [' range '].']); - end - end - - write_image(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx); - - return % save_untouch_slice_img - - -%--------------------------------------------------------------------- -function write_image(slice,hdr,filetype,fileprefix,machine,slice_idx,img_idx,dim5_idx,dim6_idx,dim7_idx) - - if filetype == 2 - fid = fopen(sprintf('%s.nii',fileprefix),'r+'); - - if fid < 0, - msg = sprintf('Cannot open file %s.nii.',fileprefix); - error(msg); - end - else - fid = fopen(sprintf('%s.img',fileprefix),'r+'); - - if fid < 0, - msg = sprintf('Cannot open file %s.img.',fileprefix); - error(msg); - end - end - - % Set bitpix according to datatype - % - % /*Acceptable values for datatype are*/ - % - % 0 None (Unknown bit per voxel) % DT_NONE, DT_UNKNOWN - % 1 Binary (ubit1, bitpix=1) % DT_BINARY - % 2 Unsigned char (uchar or uint8, bitpix=8) % DT_UINT8, NIFTI_TYPE_UINT8 - % 4 Signed short (int16, bitpix=16) % DT_INT16, NIFTI_TYPE_INT16 - % 8 Signed integer (int32, bitpix=32) % DT_INT32, NIFTI_TYPE_INT32 - % 16 Floating point (single or float32, bitpix=32) % DT_FLOAT32, NIFTI_TYPE_FLOAT32 - % 32 Complex, 2 float32 (Use float32, bitpix=64) % DT_COMPLEX64, NIFTI_TYPE_COMPLEX64 - % 64 Double precision (double or float64, bitpix=64) % DT_FLOAT64, NIFTI_TYPE_FLOAT64 - % 128 uint8 RGB (Use uint8, bitpix=24) % DT_RGB24, NIFTI_TYPE_RGB24 - % 256 Signed char (schar or int8, bitpix=8) % DT_INT8, NIFTI_TYPE_INT8 - % 511 Single RGB (Use float32, bitpix=96) % DT_RGB96, NIFTI_TYPE_RGB96 - % 512 Unsigned short (uint16, bitpix=16) % DT_UNINT16, NIFTI_TYPE_UNINT16 - % 768 Unsigned integer (uint32, bitpix=32) % DT_UNINT32, NIFTI_TYPE_UNINT32 - % 1024 Signed long long (int64, bitpix=64) % DT_INT64, NIFTI_TYPE_INT64 - % 1280 Unsigned long long (uint64, bitpix=64) % DT_UINT64, NIFTI_TYPE_UINT64 - % 1536 Long double, float128 (Unsupported, bitpix=128) % DT_FLOAT128, NIFTI_TYPE_FLOAT128 - % 1792 Complex128, 2 float64 (Use float64, bitpix=128) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % 2048 Complex256, 2 float128 (Unsupported, bitpix=256) % DT_COMPLEX128, NIFTI_TYPE_COMPLEX128 - % - switch hdr.dime.datatype - case 2, - hdr.dime.bitpix = 8; precision = 'uint8'; - case 4, - hdr.dime.bitpix = 16; precision = 'int16'; - case 8, - hdr.dime.bitpix = 32; precision = 'int32'; - case 16, - hdr.dime.bitpix = 32; precision = 'float32'; - case 64, - hdr.dime.bitpix = 64; precision = 'float64'; - case 128, - hdr.dime.bitpix = 24; precision = 'uint8'; - case 256 - hdr.dime.bitpix = 8; precision = 'int8'; - case 511 - hdr.dime.bitpix = 96; precision = 'float32'; - case 512 - hdr.dime.bitpix = 16; precision = 'uint16'; - case 768 - hdr.dime.bitpix = 32; precision = 'uint32'; - case 1024 - hdr.dime.bitpix = 64; precision = 'int64'; - case 1280 - hdr.dime.bitpix = 64; precision = 'uint64'; - otherwise - error('This datatype is not supported'); - end - - hdr.dime.dim(find(hdr.dime.dim < 1)) = 1; - - % move pointer to the start of image block - % - switch filetype - case {0, 1} - fseek(fid, 0, 'bof'); - case 2 - fseek(fid, hdr.dime.vox_offset, 'bof'); - end - - if hdr.dime.datatype == 1 | isequal(hdr.dime.dim(4:8),ones(1,5)) | ... - (isempty(img_idx) & isempty(dim5_idx) & isempty(dim6_idx) & isempty(dim7_idx) & isempty(slice_idx)) - - msg = [char(10) char(10) ' "save_untouch_slice" is used to save back to the original image a' char(10)]; - msg = [msg ' portion of slices that were loaded by "load_untouch_nii". You can' char(10)]; - msg = [msg ' process those slices matrix in any way, as long as their dimension' char(10)]; - msg = [msg ' is not changed.']; - error(msg); - else - - d1 = hdr.dime.dim(2); - d2 = hdr.dime.dim(3); - d3 = hdr.dime.dim(4); - d4 = hdr.dime.dim(5); - d5 = hdr.dime.dim(6); - d6 = hdr.dime.dim(7); - d7 = hdr.dime.dim(8); - - if isempty(slice_idx) - slice_idx = 1:d3; - end - - if isempty(img_idx) - img_idx = 1:d4; - end - - if isempty(dim5_idx) - dim5_idx = 1:d5; - end - - if isempty(dim6_idx) - dim6_idx = 1:d6; - end - - if isempty(dim7_idx) - dim7_idx = 1:d7; - end - - %ROMAN: begin - roman = 1; - if(roman) - - % compute size of one slice - % - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - - end; %if(roman) - % ROMAN: end - - for i7=1:length(dim7_idx) - for i6=1:length(dim6_idx) - for i5=1:length(dim5_idx) - for t=1:length(img_idx) - for s=1:length(slice_idx) - - % Position is seeked in bytes. To convert dimension size - % to byte storage size, hdr.dime.bitpix/8 will be - % applied. - % - pos = sub2ind([d1 d2 d3 d4 d5 d6 d7], 1, 1, slice_idx(s), ... - img_idx(t), dim5_idx(i5),dim6_idx(i6),dim7_idx(i7)) -1; - pos = pos * hdr.dime.bitpix/8; - - % ROMAN: begin - if(roman) - % do nothing - else - img_siz = prod(hdr.dime.dim(2:3)); - - % For complex float32 or complex float64, voxel values - % include [real, imag] - % - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 - img_siz = img_siz * 2; - end - - %MPH: For RGB24, voxel values include 3 separate color planes - % - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - img_siz = img_siz * 3; - end - end; % if (roman) - % ROMAN: end - - if filetype == 2 - fseek(fid, pos + hdr.dime.vox_offset, 'bof'); - else - fseek(fid, pos, 'bof'); - end - - % For each frame, fwrite will write precision of value - % in img_siz times - % - fwrite(fid, slice(:,:,s,t,i5,i6,i7), sprintf('*%s',precision)); - - end - end - end - end - end - end - - fclose(fid); - - return % write_image - diff --git a/reg-test/matlab_tests/NIfTI_20140122/unxform_nii.m b/reg-test/matlab_tests/NIfTI_20140122/unxform_nii.m deleted file mode 100644 index f98ef32f..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/unxform_nii.m +++ /dev/null @@ -1,40 +0,0 @@ -% Undo the flipping and rotations performed by xform_nii; spit back only -% the raw img data block. Initial cut will only deal with 3D volumes -% strongly assume we have called xform_nii to write down the steps used -% in xform_nii. -% -% Usage: a = load_nii('original_name'); -% manipulate a.img to make array b; -% -% if you use unxform_nii to un-tranform the image (img) data -% block, then nii.original.hdr is the corresponding header. -% -% nii.original.img = unxform_nii(a, b); -% save_nii(nii.original,'newname'); -% -% Where, 'newname' is created with data in the same space as the -% original_name data -% -% - Jeff Gunter, 26-JUN-06 -% -function outblock = unxform_nii(nii, inblock) - - if isempty(nii.hdr.hist.rot_orient) - outblock=inblock; - else - [dummy unrotate_orient] = sort(nii.hdr.hist.rot_orient); - outblock = permute(inblock, unrotate_orient); - end - - if ~isempty(nii.hdr.hist.flip_orient) - flip_orient = nii.hdr.hist.flip_orient(unrotate_orient); - - for i = 1:3 - if flip_orient(i) - outblock = flipdim(outblock, i); - end - end - end; - - return; - diff --git a/reg-test/matlab_tests/NIfTI_20140122/verify_nii_ext.m b/reg-test/matlab_tests/NIfTI_20140122/verify_nii_ext.m deleted file mode 100644 index c904df41..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/verify_nii_ext.m +++ /dev/null @@ -1,45 +0,0 @@ -% Verify NIFTI header extension to make sure that each extension section -% must be an integer multiple of 16 byte long that includes the first 8 -% bytes of esize and ecode. If the length of extension section is not the -% above mentioned case, edata should be padded with all 0. -% -% Usage: [ext, esize_total] = verify_nii_ext(ext) -% -% ext - Structure of NIFTI header extension, which includes num_ext, -% and all the extended header sections in the header extension. -% Each extended header section will have its esize, ecode, and -% edata, where edata can be plain text, xml, or any raw data -% that was saved in the extended header section. -% -% esize_total - Sum of all esize variable in all header sections. -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function [ext, esize_total] = verify_nii_ext(ext) - - if ~isfield(ext, 'section') - error('Incorrect NIFTI header extension structure.'); - elseif ~isfield(ext, 'num_ext') - ext.num_ext = length(ext.section); - elseif ~isfield(ext, 'extension') - ext.extension = [1 0 0 0]; - end - - esize_total = 0; - - for i=1:ext.num_ext - if ~isfield(ext.section(i), 'ecode') | ~isfield(ext.section(i), 'edata') - error('Incorrect NIFTI header extension structure.'); - end - - ext.section(i).esize = ceil((length(ext.section(i).edata)+8)/16)*16; - ext.section(i).edata = ... - [ext.section(i).edata ... - zeros(1,ext.section(i).esize-length(ext.section(i).edata)-8)]; - esize_total = esize_total + ext.section(i).esize; - end - - return % verify_nii_ext - diff --git a/reg-test/matlab_tests/NIfTI_20140122/view_nii.m b/reg-test/matlab_tests/NIfTI_20140122/view_nii.m deleted file mode 100644 index 45e26b59..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/view_nii.m +++ /dev/null @@ -1,4873 +0,0 @@ -% VIEW_NII: Create or update a 3-View (Front, Top, Side) of the -% brain data that is specified by nii structure -% -% Usage: status = view_nii([h], nii, [option]) or -% status = view_nii(h, [option]) -% -% Where, h is the figure on which the 3-View will be plotted; -% nii is the brain data in NIFTI format; -% option is a struct that configures the view plotted, can be: -% -% option.command = 'init' -% option.command = 'update' -% option.command = 'clearnii' -% option.command = 'updatenii' -% option.command = 'updateimg' (nii is nii.img here) -% -% option.usecolorbar = 0 | [1] -% option.usepanel = 0 | [1] -% option.usecrosshair = 0 | [1] -% option.usestretch = 0 | [1] -% option.useimagesc = 0 | [1] -% option.useinterp = [0] | 1 -% -% option.setarea = [x y w h] | [0.05 0.05 0.9 0.9] -% option.setunit = ['vox'] | 'mm' -% option.setviewpoint = [x y z] | [origin] -% option.setscanid = [t] | [1] -% option.setcrosshaircolor = [r g b] | [1 0 0] -% option.setcolorindex = From 1 to 9 (default is 2 or 3) -% option.setcolormap = (Mx3 matrix, 0 <= val <= 1) -% option.setcolorlevel = No more than 256 (default 256) -% option.sethighcolor = [] -% option.setcbarminmax = [] -% option.setvalue = [] -% option.glblocminmax = [] -% option.setbuttondown = '' -% option.setcomplex = [0] | 1 | 2 -% -% Options description in detail: -% ============================== -% -% 1. command: A char string that can control program. -% -% init: If option.command='init', the program will display -% a 3-View plot on the figure specified by figure h -% or on a new figure. If there is already a 3-View -% plot on the figure, please use option.command = -% 'updatenii' (see detail below); otherwise, the -% new 3-View plot will superimpose on the old one. -% If there is no option provided, the program will -% assume that this is an initial plot. If the figure -% handle is omitted, the program knows that it is -% an initial plot. -% -% update: If there is no command specified, and a figure -% handle of the existing 3-View plot is provided, -% the program will choose option.command='update' -% to update the 3-View plot with some new option -% items. -% -% clearnii: Clear 3-View plot on specific figure -% -% updatenii: If a new nii is going to be loaded on a fig -% that has already 3-View plot on it, use this -% command to clear existing 3-View plot, and then -% display with new nii. So, the new nii will not -% superimpose on the existing one. All options -% for 'init' can be used for 'updatenii'. -% -% updateimg: If a new 3D matrix with the same dimension -% is going to be loaded, option.command='updateimg' -% can be used as a light-weighted 'updatenii, since -% it only updates the 3 slices with new values. -% inputing argument nii should be a 3D matrix -% (nii.img) instead of nii struct. No other option -% should be used together with 'updateimg' to keep -% this command as simple as possible. -% -% -% 2. usecolorbar: If specified and usecolorbar=0, the program -% will not include the colorbar in plot area; otherwise, -% a colorbar will be included in plot area. -% -% 3. usepanel: If specified and usepanel=0, the control panel -% at lower right cornor will be invisible; otherwise, -% it will be visible. -% -% 4. usecrosshair: If specified and usecrosshair=0, the crosshair -% will be invisible; otherwise, it will be visible. -% -% 5. usestretch: If specified and usestretch=0, the 3 slices will -% not be stretched, and will be displayed according to -% the actual voxel size; otherwise, the 3 slices will be -% stretched to the edge. -% -% 6. useimagesc: If specified and useimagesc=0, images data will -% be used directly to match the colormap (like 'image' -% command); otherwise, image data will be scaled to full -% colormap with 'imagesc' command in Matlab. -% -% 7. useinterp: If specified and useinterp=1, the image will be -% displayed using interpolation. Otherwise, it will be -% displayed like mosaic, and each tile stands for a -% pixel. This option does not apply to 'setvalue' option -% is set. -% -% -% 8. setarea: 3-View plot will be displayed on this specific -% region. If it is not specified, program will set the -% plot area to [0.05 0.05 0.9 0.9]. -% -% 9. setunit: It can be specified to setunit='voxel' or 'mm' -% and the view will change the axes unit of [X Y Z] -% accordingly. -% -% 10. setviewpoint: If specified, [X Y Z] values will be used -% to set the viewpoint of 3-View plot. -% -% 11. setscanid: If specified, [t] value will be used to display -% the specified image scan in NIFTI data. -% -% 12. setcrosshaircolor: If specified, [r g b] value will be used -% for Crosshair Color. Otherwise, red will be the default. -% -% 13. setcolorindex: If specified, the 3-View will choose the -% following colormap: 2 - Bipolar; 3 - Gray; 4 - Jet; -% 5 - Cool; 6 - Bone; 7 - Hot; 8 - Copper; 9 - Pink; -% If not specified, it will choose 3 - Gray if all data -% values are not less than 0; otherwise, it will choose -% 2 - Bipolar if there is value less than 0. (Contrast -% control can only apply to 3 - Gray colormap. -% -% 14. setcolormap: 3-View plot will use it as a customized colormap. -% It is a 3-column matrix with value between 0 and 1. If -% using MS-Windows version of Matlab, the number of rows -% can not be more than 256, because of Matlab limitation. -% When colormap is used, setcolorlevel option will be -% disabled automatically. -% -% 15. setcolorlevel: If specified (must be no more than 256, and -% cannot be used for customized colormap), row number of -% colormap will be squeezed down to this level; otherwise, -% it will assume that setcolorlevel=256. -% -% 16. sethighcolor: If specified, program will squeeze down the -% colormap, and allocate sethighcolor (an Mx3 matrix) -% to high-end portion of the colormap. The sum of M and -% setcolorlevel should be less than 256. If setcolormap -% option is used, sethighcolor will be inserted on top -% of the setcolormap, and the setcolorlevel option will -% be disabled automatically. -% -% 17. setcbarminmax: if specified, the [min max] will be used to -% set the min and max of the colorbar, which does not -% include any data for highcolor. -% -% 18. setvalue: If specified, setvalue.val (with the same size as -% the source data on solution points) in the source area -% setvalue.idx will be superimposed on the current nii -% image. So, the size of setvalue.val should be equal to -% the size of setvalue.idx. To use this feature, it needs -% single or double nii structure for background image. -% -% 19. glblocminmax: If specified, pgm will use glblocminmax to -% calculate the colormap, instead of minmax of image. -% -% 20. setbuttondown: If specified, pgm will evaluate the command -% after a click or slide action is invoked to the new -% view point. -% -% 21. setcomplex: This option will decide how complex data to be -% displayed: 0 - Real part of complex data; 1 - Imaginary -% part of complex data; 2 - Modulus (magnitude) of complex -% data; If not specified, it will be set to 0 (Real part -% of complex data as default option. This option only apply -% when option.command is set to 'init or 'updatenii'. -% -% -% Additional Options for 'update' command: -% ======================================= -% -% option.enablecursormove = [1] | 0 -% option.enableviewpoint = 0 | [1] -% option.enableorigin = 0 | [1] -% option.enableunit = 0 | [1] -% option.enablecrosshair = 0 | [1] -% option.enablehistogram = 0 | [1] -% option.enablecolormap = 0 | [1] -% option.enablecontrast = 0 | [1] -% option.enablebrightness = 0 | [1] -% option.enableslider = 0 | [1] -% option.enabledirlabel = 0 | [1] -% -% -% e.g.: -% nii = load_nii('T1'); % T1.img/hdr -% view_nii(nii); -% -% or -% -% h = figure('unit','normal','pos', [0.18 0.08 0.64 0.85]); -% opt.setarea = [0.05 0.05 0.9 0.9]; -% view_nii(h, nii, opt); -% -% -% Part of this file is copied and modified from: -% http://www.mathworks.com/matlabcentral/fileexchange/1878-mri-analyze-tools -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function status = view_nii(varargin) - - if nargin < 1 - error('Please check inputs using ''help view_nii'''); - end; - - nii = ''; - opt = ''; - command = ''; - - usecolorbar = []; - usepanel = []; - usecrosshair = ''; - usestretch = []; - useimagesc = []; - useinterp = []; - - setarea = []; - setunit = ''; - setviewpoint = []; - setscanid = []; - setcrosshaircolor = []; - setcolorindex = ''; - setcolormap = 'NA'; - setcolorlevel = []; - sethighcolor = 'NA'; - setcbarminmax = []; - setvalue = []; - glblocminmax = []; - setbuttondown = ''; - setcomplex = 0; - - status = []; - - if ishandle(varargin{1}) % plot on top of this figure - - fig = varargin{1}; - - if nargin < 2 - command = 'update'; % just to get 3-View status - end - - if nargin == 2 - if ~isstruct(varargin{2}) - error('2nd parameter should be either nii struct or option struct'); - end - - opt = varargin{2}; - - if isfield(opt,'hdr') & isfield(opt,'img') - nii = opt; - elseif isfield(opt, 'command') & (strcmpi(opt.command,'init') ... - | strcmpi(opt.command,'updatenii') ... - | strcmpi(opt.command,'updateimg') ) - - error('Option here cannot contain "init", "updatenii", or "updateimg" comand'); - end - end - - if nargin == 3 - nii = varargin{2}; - opt = varargin{3}; - - if ~isstruct(opt) - error('3rd parameter should be option struct'); - end - - if ~isfield(opt,'command') | ~strcmpi(opt.command,'updateimg') - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('2nd parameter should be nii struct'); - end - - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); - end - end - end - - set(fig, 'menubar', 'none'); - - elseif ischar(varargin{1}) % call back by event - - command = lower(varargin{1}); - fig = gcbf; - - else % start nii with a new figure - - nii = varargin{1}; - - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('1st parameter should be either a figure handle or nii struct'); - end - - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); - end - - if nargin > 1 - opt = varargin{2}; - - if isfield(opt, 'command') & ~strcmpi(opt.command,'init') - error('Option here must use "init" comand'); - end - end - - command = 'init'; - fig = figure('unit','normal','position',[0.15 0.08 0.70 0.85]); - view_nii_menu(fig); - rri_file_menu(fig); - - end - - if ~isempty(opt) - - if isfield(opt,'command') - command = lower(opt.command); - end - - if isempty(command) - command = 'update'; - end - - if isfield(opt,'usecolorbar') - usecolorbar = opt.usecolorbar; - end - - if isfield(opt,'usepanel') - usepanel = opt.usepanel; - end - - if isfield(opt,'usecrosshair') - usecrosshair = opt.usecrosshair; - end - - if isfield(opt,'usestretch') - usestretch = opt.usestretch; - end - - if isfield(opt,'useimagesc') - useimagesc = opt.useimagesc; - end - - if isfield(opt,'useinterp') - useinterp = opt.useinterp; - end - - if isfield(opt,'setarea') - setarea = opt.setarea; - end - - if isfield(opt,'setunit') - setunit = opt.setunit; - end - - if isfield(opt,'setviewpoint') - setviewpoint = opt.setviewpoint; - end - - if isfield(opt,'setscanid') - setscanid = opt.setscanid; - end - - if isfield(opt,'setcrosshaircolor') - setcrosshaircolor = opt.setcrosshaircolor; - - if ~isempty(setcrosshaircolor) & (~isnumeric(setcrosshaircolor) | ~isequal(size(setcrosshaircolor),[1 3]) | min(setcrosshaircolor(:))<0 | max(setcrosshaircolor(:))>1) - error('Crosshair Color should be a 1x3 matrix with value between 0 and 1'); - end - end - - if isfield(opt,'setcolorindex') - setcolorindex = round(opt.setcolorindex); - - if ~isnumeric(setcolorindex) | setcolorindex < 1 | setcolorindex > 9 - error('Colorindex should be a number between 1 and 9'); - end - end - - if isfield(opt,'setcolormap') - setcolormap = opt.setcolormap; - - if ~isempty(setcolormap) & (~isnumeric(setcolormap) | size(setcolormap,2) ~= 3 | min(setcolormap(:))<0 | max(setcolormap(:))>1) - error('Colormap should be a Mx3 matrix with value between 0 and 1'); - end - end - - if isfield(opt,'setcolorlevel') - setcolorlevel = round(opt.setcolorlevel); - - if ~isnumeric(setcolorlevel) | setcolorlevel > 256 | setcolorlevel < 1 - error('Colorlevel should be a number between 1 and 256'); - end - end - - if isfield(opt,'sethighcolor') - sethighcolor = opt.sethighcolor; - - if ~isempty(sethighcolor) & (~isnumeric(sethighcolor) | size(sethighcolor,2) ~= 3 | min(sethighcolor(:))<0 | max(sethighcolor(:))>1) - error('Highcolor should be a Mx3 matrix with value between 0 and 1'); - end - end - - if isfield(opt,'setcbarminmax') - setcbarminmax = opt.setcbarminmax; - - if isempty(setcbarminmax) | ~isnumeric(setcbarminmax) | length(setcbarminmax) ~= 2 - error('Colorbar MinMax should contain 2 values: [min max]'); - end - end - - if isfield(opt,'setvalue') - setvalue = opt.setvalue; - - if isempty(setvalue) | ~isstruct(setvalue) | ... - ~isfield(opt.setvalue,'idx') | ~isfield(opt.setvalue,'val') - error('setvalue should be a struct contains idx and val'); - end - - if length(opt.setvalue.idx(:)) ~= length(opt.setvalue.val(:)) - error('length of idx and val fields should be the same'); - end - - if ~strcmpi(class(opt.setvalue.idx),'single') - opt.setvalue.idx = single(opt.setvalue.idx); - end - - if ~strcmpi(class(opt.setvalue.val),'single') - opt.setvalue.val = single(opt.setvalue.val); - end - end - - if isfield(opt,'glblocminmax') - glblocminmax = opt.glblocminmax; - end - - if isfield(opt,'setbuttondown') - setbuttondown = opt.setbuttondown; - end - - if isfield(opt,'setcomplex') - setcomplex = opt.setcomplex; - end - - end - - switch command - - case {'init'} - - set(fig, 'InvertHardcopy','off'); - set(fig, 'PaperPositionMode','auto'); - - fig = init(nii, fig, setarea, setunit, setviewpoint, setscanid, setbuttondown, ... - setcolorindex, setcolormap, setcolorlevel, sethighcolor, setcbarminmax, ... - usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, useinterp, ... - setvalue, glblocminmax, setcrosshaircolor, setcomplex); - - % get status - % - status = get_status(fig); - - case {'update'} - - nii_view = getappdata(fig,'nii_view'); - h = fig; - - if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); - end - - if ~isempty(opt) - - % Order of the following update matters. - % - update_shape(h, setarea, usecolorbar, usestretch, useimagesc); - update_useinterp(h, useinterp); - update_useimagesc(h, useimagesc); - update_usepanel(h, usepanel); - update_colorindex(h, setcolorindex); - update_colormap(h, setcolormap); - update_highcolor(h, sethighcolor, setcolorlevel); - update_cbarminmax(h, setcbarminmax); - update_unit(h, setunit); - update_viewpoint(h, setviewpoint); - update_scanid(h, setscanid); - update_buttondown(h, setbuttondown); - update_crosshaircolor(h, setcrosshaircolor); - update_usecrosshair(h, usecrosshair); - - % Enable/Disable object - % - update_enable(h, opt); - - end - - % get status - % - status = get_status(h); - - case {'updateimg'} - - if ~exist('nii','var') - msg = sprintf('Please input a 3D matrix brain data'); - error(msg); - end - - % Note: nii is not nii, nii should be a 3D matrix here - % - if ~isnumeric(nii) - msg = sprintf('2nd parameter should be a 3D matrix, not nii struct'); - error(msg); - end - - nii_view = getappdata(fig,'nii_view'); - - if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); - end - - img = nii; - update_img(img, fig, opt); - - % get status - % - status = get_status(fig); - - case {'updatenii'} - - nii_view = getappdata(fig,'nii_view'); - - if isempty(nii_view) - error('The figure should already contain a 3-View plot.'); - end - - if ~isstruct(nii) | ~isfield(nii,'hdr') | ~isfield(nii,'img') - error('2nd parameter should be nii struct'); - end - - if isfield(nii,'untouch') & nii.untouch == 1 - error('Usage: please use ''load_nii.m'' to load the structure.'); - end - - opt.command = 'clearnii'; - view_nii(fig, opt); - - opt.command = 'init'; - view_nii(fig, nii, opt); - - % get status - % - status = get_status(fig); - - case {'clearnii'} - - nii_view = getappdata(fig,'nii_view'); - - handles = struct2cell(nii_view.handles); - - for i=1:length(handles) - if ishandle(handles{i}) % in case already del by parent - delete(handles{i}); - end - end - - rmappdata(fig,'nii_view'); - buttonmotion = get(fig,'windowbuttonmotion'); - mymotion = '; view_nii(''move_cursor'');'; - buttonmotion = strrep(buttonmotion, mymotion, ''); - set(fig, 'windowbuttonmotion', buttonmotion); - - case {'axial_image','coronal_image','sagittal_image'} - - switch command - case 'axial_image', view = 'axi'; axi = 0; cor = 1; sag = 1; - case 'coronal_image', view = 'cor'; axi = 1; cor = 0; sag = 1; - case 'sagittal_image', view = 'sag'; axi = 1; cor = 1; sag = 0; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view = get_slice_position(nii_view,view); - - if isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - % CData must be double() for Matlab 6.5 for Windows - % - if axi, - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); - end; - end - - if cor, - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); - end; - end; - - if sag, - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); - end; - end; - - update_nii_view(nii_view); - - if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); - end - - case {'axial_slider','coronal_slider','sagittal_slider'}, - - switch command - case 'axial_slider', view = 'axi'; axi = 1; cor = 0; sag = 0; - case 'coronal_slider', view = 'cor'; axi = 0; cor = 1; sag = 0; - case 'sagittal_slider', view = 'sag'; axi = 0; cor = 0; sag = 1; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view = get_slider_position(nii_view); - - if isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - if axi, - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); - end - end - - if cor, - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); - end - end - - if sag, - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); - end - end - - update_nii_view(nii_view); - - if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); - end - - case {'impos_edit'} - - nii_view = getappdata(fig,'nii_view'); - impos = str2num(get(nii_view.handles.impos,'string')); - - if isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - if isempty(impos) | ~all(size(impos) == [1 3]) - msg = 'Please use 3 numbers to represent X,Y and Z'; - msgbox(msg,'Error'); - return; - end - - slices.sag = round(impos(1)); - slices.cor = round(impos(2)); - slices.axi = round(impos(3)); - - nii_view = convert2voxel(nii_view,slices); - nii_view = check_slices(nii_view); - - impos(1) = nii_view.slices.sag; - impos(2) = nii_view.dims(2) - nii_view.slices.cor + 1; - impos(3) = nii_view.slices.axi; - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',impos(1)); - end - - if isfield(nii_view.handles,'coronal_slider'), - set(nii_view.handles.coronal_slider,'Value',impos(2)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',impos(3)); - end - - nii_view = get_slider_position(nii_view); - update_nii_view(nii_view); - - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) & nii_view.useinterp - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(img(:,:,nii_view.slices.axi,:,nii_view.scanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(img(:,:,nii_view.slices.axi,nii_view.scanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - if isfield(nii_view.handles,'axial_slider'), - set(nii_view.handles.axial_slider,'Value',nii_view.slices.axi); - end - - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) & nii_view.useinterp - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(img(:,nii_view.slices.cor,:,:,nii_view.scanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(img(:,nii_view.slices.cor,:,nii_view.scanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - if isfield(nii_view.handles,'coronal_slider'), - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - set(nii_view.handles.coronal_slider,'Value',slider_val); - end - - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) & nii_view.useinterp - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(img(nii_view.slices.sag,:,:,:,nii_view.scanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(img(nii_view.slices.sag,:,:,nii_view.scanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - if isfield(nii_view.handles,'sagittal_slider'), - set(nii_view.handles.sagittal_slider,'Value',nii_view.slices.sag); - end - - axes(nii_view.handles.axial_axes); - axes(nii_view.handles.coronal_axes); - axes(nii_view.handles.sagittal_axes); - - if ~isempty(nii_view.buttondown) - eval(nii_view.buttondown); - end - - case 'coordinates', - - nii_view = getappdata(fig,'nii_view'); - set_image_value(nii_view); - - case 'crosshair', - - nii_view = getappdata(fig,'nii_view'); - - if get(nii_view.handles.xhair,'value') == 2 % off - set(nii_view.axi_xhair.lx,'visible','off'); - set(nii_view.axi_xhair.ly,'visible','off'); - set(nii_view.cor_xhair.lx,'visible','off'); - set(nii_view.cor_xhair.ly,'visible','off'); - set(nii_view.sag_xhair.lx,'visible','off'); - set(nii_view.sag_xhair.ly,'visible','off'); - else - set(nii_view.axi_xhair.lx,'visible','on'); - set(nii_view.axi_xhair.ly,'visible','on'); - set(nii_view.cor_xhair.lx,'visible','on'); - set(nii_view.cor_xhair.ly,'visible','on'); - set(nii_view.sag_xhair.lx,'visible','on'); - set(nii_view.sag_xhair.ly,'visible','on'); - - set(nii_view.handles.axial_axes,'selected','on'); - set(nii_view.handles.axial_axes,'selected','off'); - set(nii_view.handles.coronal_axes,'selected','on'); - set(nii_view.handles.coronal_axes,'selected','off'); - set(nii_view.handles.sagittal_axes,'selected','on'); - set(nii_view.handles.sagittal_axes,'selected','off'); - end - - case 'xhair_color', - - old_color = get(gcbo,'user'); - new_color = uisetcolor(old_color); - update_crosshaircolor(fig, new_color); - - case {'color','contrast_def'} - - nii_view = getappdata(fig,'nii_view'); - - if nii_view.numscan == 1 - if get(nii_view.handles.colorindex,'value') == 2 - set(nii_view.handles.contrast,'value',128); - elseif get(nii_view.handles.colorindex,'value') == 3 - set(nii_view.handles.contrast,'value',1); - end - end - - [custom_color_map, custom_colorindex] = change_colormap(fig); - - if strcmpi(command, 'color') - - setcolorlevel = nii_view.colorlevel; - - if ~isempty(custom_color_map) % isfield(nii_view, 'color_map') - setcolormap = custom_color_map; % nii_view.color_map; - else - setcolormap = []; - end - - if isfield(nii_view, 'highcolor') - sethighcolor = nii_view.highcolor; - else - sethighcolor = []; - end - - redraw_cbar(fig, setcolorlevel, setcolormap, sethighcolor); - - if nii_view.numscan == 1 & ... - (custom_colorindex < 2 | custom_colorindex > 3) - contrastopt.enablecontrast = 0; - else - contrastopt.enablecontrast = 1; - end - - update_enable(fig, contrastopt); - - end - - case {'neg_color','brightness','contrast'} - - change_colormap(fig); - - case {'brightness_def'} - - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.brightness,'value',0); - change_colormap(fig); - - case 'hist_plot' - - hist_plot(fig); - - case 'hist_eq' - - hist_eq(fig); - - case 'move_cursor' - - move_cursor(fig); - - case 'edit_change_scan' - - change_scan('edit_change_scan'); - - case 'slider_change_scan' - - change_scan('slider_change_scan'); - - end - - return; % view_nii - - -%---------------------------------------------------------------- -function fig = init(nii, fig, area, setunit, setviewpoint, setscanid, buttondown, ... - colorindex, color_map, colorlevel, highcolor, cbarminmax, ... - usecolorbar, usepanel, usecrosshair, usestretch, useimagesc, ... - useinterp, setvalue, glblocminmax, setcrosshaircolor, ... - setcomplex) - - % Support data type COMPLEX64 & COMPLEX128 - % - if nii.hdr.dime.datatype == 32 | nii.hdr.dime.datatype == 1792 - switch setcomplex, - case 0, - nii.img = real(nii.img); - case 1, - nii.img = imag(nii.img); - case 2, - if isa(nii.img, 'double') - nii.img = abs(double(nii.img)); - else - nii.img = single(abs(double(nii.img))); - end - end - end - - if isempty(area) - area = [0.05 0.05 0.9 0.9]; - end - - if isempty(setscanid) - setscanid = 1; - else - setscanid = round(setscanid); - - if setscanid < 1 - setscanid = 1; - end - - if setscanid > nii.hdr.dime.dim(5) - setscanid = nii.hdr.dime.dim(5); - end - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - usecolorbar = 0; - elseif isempty(usecolorbar) - usecolorbar = 1; - end - - if isempty(usepanel) - usepanel = 1; - end - - if isempty(usestretch) - usestretch = 1; - end - - if isempty(useimagesc) - useimagesc = 1; - end - - if isempty(useinterp) - useinterp = 0; - end - - if isempty(colorindex) - tmp = min(nii.img(:,:,:,setscanid)); - - if min(tmp(:)) < 0 - colorindex = 2; - setcrosshaircolor = [1 1 0]; - else - colorindex = 3; - end - end - - if isempty(color_map) | ischar(color_map) - color_map = []; - else - colorindex = 1; - end - - bgimg = []; - - if ~isempty(glblocminmax) - minvalue = glblocminmax(1); - maxvalue = glblocminmax(2); - else - minvalue = nii.img(:,:,:,setscanid); - minvalue = double(minvalue(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = nii.img(:,:,:,setscanid); - maxvalue = double(maxvalue(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - end - - if ~isempty(setvalue) - if ~isempty(glblocminmax) - minvalue = glblocminmax(1); - maxvalue = glblocminmax(2); - else - minvalue = double(min(setvalue.val)); - maxvalue = double(max(setvalue.val)); - end - - bgimg = double(nii.img); - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - - bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 - - % 56 level for brain structure - % -% highcolor = [zeros(1,3);gray(55)]; - highcolor = gray(56); - cbarminmax = [minvalue maxvalue]; - - if useinterp - - % scale signal data to 1~200 - % - nii.img = repmat(nan, size(nii.img)); - nii.img(setvalue.idx) = setvalue.val; - - % 200 level for source image - % - bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); - else - - bgimg(setvalue.idx) = NaN; - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - bgimg(setvalue.idx) = minbg; - - % bgimg must be normalized to [201 256] - % - bgimg = 55 * (bgimg-min(bgimg(:))) / (max(bgimg(:))-min(bgimg(:))) + 201; - bgimg(setvalue.idx) = 0; - - % scale signal data to 1~200 - % - nii.img = zeros(size(nii.img)); - nii.img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); - nii.img = nii.img + bgimg; - bgimg = []; - nii.img = scale_out(nii.img, cbarminmax(1), cbarminmax(2), 199); - - minvalue = double(nii.img(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = double(nii.img(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - - if ~isempty(glblocminmax) % maxvalue is gray - minvalue = glblocminmax(1); - end - - end - - colorindex = 2; - setcrosshaircolor = [1 1 0]; - - end - - if isempty(highcolor) | ischar(highcolor) - highcolor = []; - num_highcolor = 0; - else - num_highcolor = size(highcolor,1); - end - - if isempty(colorlevel) - colorlevel = 256 - num_highcolor; - end - - if usecolorbar - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes - else - cbar_area = []; - end - - % init color (gray) scaling to make sure the slice clim take the - % global clim [min(nii.img(:)) max(nii.img(:))] - % - if isempty(bgimg) - clim = [minvalue maxvalue]; - else - clim = [minvalue double(max(bgimg(:)))]; - end - - if clim(1) == clim(2) - clim(2) = clim(1) + 0.000001; - end - - if isempty(cbarminmax) - cbarminmax = [minvalue maxvalue]; - end - - xdim = size(nii.img, 1); - ydim = size(nii.img, 2); - zdim = size(nii.img, 3); - - dims = [xdim ydim zdim]; - voxel_size = abs(nii.hdr.dime.pixdim(2:4)); % vol in mm - - if any(voxel_size <= 0) - voxel_size(find(voxel_size <= 0)) = 1; - end - - origin = abs(nii.hdr.hist.originator(1:3)); - - if isempty(origin) | all(origin == 0) % according to SPM - origin = (dims+1)/2; - end; - - origin = round(origin); - - if any(origin > dims) % simulate fMRI - origin(find(origin > dims)) = dims(find(origin > dims)); - end - - if any(origin <= 0) - origin(find(origin <= 0)) = 1; - end - - nii_view.dims = dims; - nii_view.voxel_size = voxel_size; - nii_view.origin = origin; - - nii_view.slices.sag = 1; - nii_view.slices.cor = 1; - nii_view.slices.axi = 1; - if xdim > 1, nii_view.slices.sag = origin(1); end - if ydim > 1, nii_view.slices.cor = origin(2); end - if zdim > 1, nii_view.slices.axi = origin(3); end - - nii_view.area = area; - nii_view.fig = fig; - nii_view.nii = nii; % image data - nii_view.bgimg = bgimg; % background - nii_view.setvalue = setvalue; - nii_view.minvalue = minvalue; - nii_view.maxvalue = maxvalue; - nii_view.numscan = nii.hdr.dime.dim(5); - nii_view.scanid = setscanid; - - Font.FontUnits = 'point'; - Font.FontSize = 12; - - % create axes for colorbar - % - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - - if isempty(cbar_area) - nii_view.cbar_area = []; - else - nii_view.cbar_area = cbar_area; - end - - % create axes for top/front/side view - % - vol_size = voxel_size .* dims; - [top_ax, front_ax, side_ax] ... - = create_ax(fig, area, vol_size, usestretch); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - % Sagittal Slider - % - x = side_pos(1); - y = top_pos(2) + top_pos(4); - w = side_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - - pos = [x y w h]; - - if xdim > 1, - slider_step(1) = 1/(xdim); - slider_step(2) = 1.00001/(xdim); - - handles.sagittal_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Sagittal slice navigation',... - 'Min',1,'Max',xdim,'SliderStep',slider_step, ... - 'Value',nii_view.slices.sag,... - 'Callback','view_nii(''sagittal_slider'');'); - - set(handles.sagittal_slider,'position',pos); % linux66 - end - - % Coronal Slider - % - x = top_pos(1); - y = top_pos(2) + top_pos(4); - w = top_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - - pos = [x y w h]; - - if ydim > 1, - slider_step(1) = 1/(ydim); - slider_step(2) = 1.00001/(ydim); - - slider_val = nii_view.dims(2) - nii_view.slices.cor + 1; - - handles.coronal_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Coronal slice navigation',... - 'Min',1,'Max',ydim,'SliderStep',slider_step, ... - 'Value',slider_val,... - 'Callback','view_nii(''coronal_slider'');'); - - set(handles.coronal_slider,'position',pos); % linux66 - end - - % Axial Slider - % -% x = front_pos(1) + front_pos(3); -% y = front_pos(2); -% w = side_pos(1) - x; -% h = front_pos(4); - - x = top_pos(1); - y = area(2); - w = top_pos(3); - h = top_pos(2) - y; - - pos = [x y w h]; - - if zdim > 1, - slider_step(1) = 1/(zdim); - slider_step(2) = 1.00001/(zdim); - - handles.axial_slider = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment','center',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Axial slice navigation',... - 'Min',1,'Max',zdim,'SliderStep',slider_step, ... - 'Value',nii_view.slices.axi,... - 'Callback','view_nii(''axial_slider'');'); - - set(handles.axial_slider,'position',pos); % linux66 - end - - % plot info view - % -% info_pos = [side_pos([1,3]); top_pos([2,4])]; -% info_pos = info_pos(:); - gap = side_pos(1)-(top_pos(1)+top_pos(3)); - info_pos(1) = side_pos(1) + gap; - info_pos(2) = area(2); - info_pos(3) = side_pos(3) - gap; - info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; - - num_inputline = 10; - inputline_space =info_pos(4) / num_inputline; - - - % for any info_area change, update_usestretch should also be changed - - - % Image Intensity Value at Cursor - % - x = info_pos(1); - y = info_pos(2); - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - - handles.Timvalcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Value at cursor:'); - - if usepanel - set(handles.Timvalcur, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imvalcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' '); - - if usepanel - set(handles.imvalcur, 'visible', 'on'); - end - - % Position at Cursor - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timposcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at cursor:'); - - if usepanel - set(handles.Timposcur, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imposcur = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.imposcur, 'visible', 'on'); - end - - % Image Intensity Value at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timval = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Value at crosshair:'); - - if usepanel - set(handles.Timval, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.imval = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' '); - - if usepanel - set(handles.imval, 'visible', 'on'); - end - - % Viewpoint Position at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Timpos = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at crosshair:'); - - if usepanel - set(handles.Timpos, 'visible', 'on'); - end - - x = x + w + 0.005; - y = y - 0.008; - w = info_pos(3)*0.5; - h = inputline_space*0.9; - - pos = [x y w h]; - - handles.impos = uicontrol('Parent',fig,'Style','edit', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'Callback','view_nii(''impos_edit'');', ... - 'TooltipString','Viewpoint Location in Axes Unit', ... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.impos, 'visible', 'on'); - end - - % Origin Position - % - x = info_pos(1); - y = y + inputline_space*1.2; - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - - handles.Torigin = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','[X Y Z] at origin:'); - - if usepanel - set(handles.Torigin, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.origin = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'right',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String',' ','Value',[0 0 0]); - - if usepanel - set(handles.origin, 'visible', 'on'); - end - -if 0 - % Voxel Unit - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - - handles.Tcoord = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Axes Unit:'); - - if usepanel - set(handles.Tcoord, 'visible', 'on'); - end - - x = x + w + 0.005; - w = info_pos(3)*0.5 - 0.005; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.coord = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Choose Voxel or Millimeter',... - 'String',{'Voxel','Millimeter'},... - 'visible','off', ... - 'Callback','view_nii(''coordinates'');'); - -% 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... -% 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... - - Font.FontSize = 12; - - if usepanel - set(handles.coord, 'visible', 'on'); - end -end - - % Crosshair - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.4; - - pos = [x y w h]; - - handles.Txhair = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Crosshair:'); - - if usepanel - set(handles.Txhair, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.xhair_color = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Crosshair Color',... - 'User',[1 0 0],... - 'String','Color',... - 'visible','off', ... - 'Callback','view_nii(''xhair_color'');'); - - if usepanel - set(handles.xhair_color, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.3; - - pos = [x y w h]; - - handles.xhair = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Display or Hide Crosshair',... - 'String',{'On','Off'},... - 'visible','off', ... - 'Callback','view_nii(''crosshair'');'); - - if usepanel - set(handles.xhair, 'visible', 'on'); - end - - % Histogram & Color - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 1.5; - - pos = [x, y+inputline_space*0.9, w, h]; - - handles.hist_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel -% set(handles.hist_frame, 'visible', 'on'); - end - - handles.coord_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.coord_frame, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - h = inputline_space * 1.5; - - pos = [x, y+inputline_space*0.9, w, h]; - - handles.color_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.color_frame, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space*1.2; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.hist_eq = uicontrol('Parent',fig,'Style','toggle', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Histogram Equalization',... - 'String','Hist EQ',... - 'visible','off', ... - 'Callback','view_nii(''hist_eq'');'); - - if usepanel -% set(handles.hist_eq, 'visible', 'on'); - end - - x = x + w; - w = info_pos(3)*0.2; - - pos = [x y w h]; - - handles.hist_plot = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Histogram Plot',... - 'String','Hist Plot',... - 'visible','off', ... - 'Callback','view_nii(''hist_plot'');'); - - if usepanel -% set(handles.hist_plot, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.025; - w = info_pos(3)*0.4; - - pos = [x y w h]; - - handles.coord = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Choose Voxel or Millimeter',... - 'String',{'Voxel','Millimeter'},... - 'visible','off', ... - 'Callback','view_nii(''coordinates'');'); - -% 'TooltipString','Choose Voxel, MNI or Talairach Coordinates',... -% 'String',{'Voxel','MNI (mm)','Talairach (mm)'},... - - if usepanel - set(handles.coord, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - - pos = [x y w h]; - - handles.neg_color = uicontrol('Parent',fig,'Style','toggle', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Negative Colormap',... - 'String','Negative',... - 'visible','off', ... - 'Callback','view_nii(''neg_color'');'); - - if usepanel - set(handles.neg_color, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.neg_color, 'enable', 'off'); - end - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.275; - - pos = [x y w h]; - - handles.colorindex = uicontrol('Parent',fig,'Style','popupmenu', ... - 'Units','Normalized', Font, ... - 'Position',pos, ... - 'BackgroundColor', [1 1 1], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change Colormap',... - 'String',{'Custom','Bipolar','Gray','Jet','Cool','Bone','Hot','Copper','Pink'},... - 'value', colorindex, ... - 'visible','off', ... - 'Callback','view_nii(''color'');'); - - if usepanel - set(handles.colorindex, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.colorindex, 'enable', 'off'); - end - - x = info_pos(1) + info_pos(3)*0.1; - y = y + inputline_space; - w = info_pos(3)*0.28; - h = inputline_space*0.6; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.Thist = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Histogram'); - - handles.Tcoord = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Axes Unit'); - - if usepanel -% set(handles.Thist, 'visible', 'on'); - set(handles.Tcoord, 'visible', 'on'); - end - - x = info_pos(1) + info_pos(3)*0.60; - w = info_pos(3)*0.28; - - pos = [x y w h]; - - handles.Tcolor = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Colormap'); - - if usepanel - set(handles.Tcolor, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.Tcolor, 'enable', 'off'); - end - - % Contrast Frame - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 2; - - pos = [x, y+inputline_space*0.8, w, h]; - - handles.contrast_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.contrast_frame, 'visible', 'on'); - end - - if colorindex < 2 | colorindex > 3 - set(handles.contrast_frame, 'visible', 'off'); - end - - % Brightness Frame - % - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - - pos = [x, y+inputline_space*0.8, w, h]; - - handles.brightness_frame = uicontrol('Parent',fig, ... - 'Units','normal', ... - 'BackgroundColor',[0.8 0.8 0.8], ... - 'Position',pos, ... - 'visible','off', ... - 'Style','frame'); - - if usepanel - set(handles.brightness_frame, 'visible', 'on'); - end - - % Contrast - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.4; - h = inputline_space*0.6; - - pos = [x y w h]; - - Font.FontSize = 12; - - slider_step(1) = 5/255; - slider_step(2) = 5.00001/255; - - handles.contrast = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change contrast',... - 'Min',1,'Max',256,'SliderStep',slider_step, ... - 'Value',1, ... - 'visible','off', ... - 'Callback','view_nii(''contrast'');'); - - if usepanel - set(handles.contrast, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.contrast, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.contrast, 'min', 1, 'max', nii_view.numscan, ... - 'sliderstep',[1/(nii_view.numscan-1) 1.00001/(nii_view.numscan-1)], ... - 'Callback', 'view_nii(''slider_change_scan'');'); - elseif colorindex < 2 | colorindex > 3 - set(handles.contrast, 'visible', 'off'); - elseif colorindex == 2 - set(handles.contrast,'value',128); - end - - set(handles.contrast,'position',pos); % linux66 - - % Brightness - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.475; - - pos = [x y w h]; - - Font.FontSize = 12; - - slider_step(1) = 1/50; - slider_step(2) = 1.00001/50; - - handles.brightness = uicontrol('Parent',fig, ... - 'Style','slider','Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor',[0.5 0.5 0.5],'ForegroundColor',[0 0 0],... - 'BusyAction','queue',... - 'TooltipString','Change brightness',... - 'Min',-1,'Max',1,'SliderStep',slider_step, ... - 'Value',0, ... - 'visible','off', ... - 'Callback','view_nii(''brightness'');'); - - if usepanel - set(handles.brightness, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.brightness, 'enable', 'off'); - end - - set(handles.brightness,'position',pos); % linux66 - - % Contrast text/def - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.22; - - pos = [x y w h]; - - handles.Tcontrast = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Contrast:'); - - if usepanel - set(handles.Tcontrast, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.Tcontrast, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.Tcontrast, 'string', 'Scan ID:'); - set(handles.contrast, 'TooltipString', 'Change Scan ID'); - elseif colorindex < 2 | colorindex > 3 - set(handles.Tcontrast, 'visible', 'off'); - end - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.contrast_def = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Restore initial contrast',... - 'String','Reset',... - 'visible','off', ... - 'Callback','view_nii(''contrast_def'');'); - - if usepanel - set(handles.contrast_def, 'visible', 'on'); - end - - if (nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511) & nii_view.numscan <= 1 - set(handles.contrast_def, 'enable', 'off'); - end - - if nii_view.numscan > 1 - set(handles.contrast_def, 'style', 'edit', 'background', 'w', ... - 'TooltipString','Scan (or volume) index in the time series',... - 'string', '1', 'Callback', 'view_nii(''edit_change_scan'');'); - elseif colorindex < 2 | colorindex > 3 - set(handles.contrast_def, 'visible', 'off'); - end - - % Brightness text/def - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.295; - - pos = [x y w h]; - - Font.FontSize = 12; - - handles.Tbrightness = uicontrol('Parent',fig,'Style','text', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'left',... - 'BackgroundColor', [0.8 0.8 0.8], 'ForegroundColor', [0 0 0],... - 'BusyAction','queue',... - 'visible','off', ... - 'String','Brightness:'); - - if usepanel - set(handles.Tbrightness, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.Tbrightness, 'enable', 'off'); - end - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - - Font.FontSize = 8; - - handles.brightness_def = uicontrol('Parent',fig,'Style','push', ... - 'Units','Normalized', Font, ... - 'Position',pos, 'HorizontalAlignment', 'center',... - 'TooltipString','Restore initial brightness',... - 'String','Reset',... - 'visible','off', ... - 'Callback','view_nii(''brightness_def'');'); - - if usepanel - set(handles.brightness_def, 'visible', 'on'); - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(handles.brightness_def, 'enable', 'off'); - end - - % init image handles - % - handles.axial_image = []; - handles.coronal_image = []; - handles.sagittal_image = []; - - % plot axial view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(:,:,nii_view.slices.axi)); - h1 = plot_view(fig, xdim, ydim, top_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.axial_bg = h1; - else - handles.axial_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(:,:,nii_view.slices.axi,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(:,:,nii_view.slices.axi,setscanid)); - img_slice = img_slice'; - end - h1 = plot_view(fig, xdim, ydim, top_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''axial_image'');'); - handles.axial_image = h1; - handles.axial_axes = top_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(top_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end - - % plot coronal view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(:,nii_view.slices.cor,:)); - h1 = plot_view(fig, xdim, zdim, front_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.coronal_bg = h1; - else - handles.coronal_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(:,nii_view.slices.cor,:,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(:,nii_view.slices.cor,:,setscanid)); - img_slice = img_slice'; - end - h1 = plot_view(fig, xdim, zdim, front_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''coronal_image'');'); - handles.coronal_image = h1; - handles.coronal_axes = front_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(front_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end - - % plot sagittal view - % - if ~isempty(nii_view.bgimg) - bg_slice = squeeze(bgimg(nii_view.slices.sag,:,:)); - - h1 = plot_view(fig, ydim, zdim, side_ax, bg_slice', clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - handles.sagittal_bg = h1; - else - handles.sagittal_bg = []; - end - - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - img_slice = squeeze(nii.img(nii_view.slices.sag,:,:,:,setscanid)); - img_slice = permute(img_slice, [2 1 3]); - else - img_slice = squeeze(nii.img(nii_view.slices.sag,:,:,setscanid)); - img_slice = img_slice'; - end - - h1 = plot_view(fig, ydim, zdim, side_ax, img_slice, clim, cbarminmax, ... - handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, nii_view.numscan); - set(h1,'buttondown','view_nii(''sagittal_image'');'); - set(side_ax,'Xdir', 'reverse'); - handles.sagittal_image = h1; - handles.sagittal_axes = side_ax; - - if size(img_slice,1) == 1 | size(img_slice,2) == 1 - set(side_ax,'visible','off'); - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', 'off'); - end - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', 'off'); - end - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', 'off'); - end - end - - [top1_label, top2_label, side1_label, side2_label] = ... - dir_label(fig, top_ax, front_ax, side_ax); - - % store label handles - % - handles.top1_label = top1_label; - handles.top2_label = top2_label; - handles.side1_label = side1_label; - handles.side2_label = side2_label; - - % plot colorbar - % - if ~isempty(cbar_axes) & ~isempty(cbarminmax_axes) - -if 0 - if isempty(color_map) - level = colorlevel + num_highcolor; - else - level = size([color_map; highcolor], 1); - end -end - - if isempty(color_map) - level = colorlevel; - else - level = size([color_map], 1); - end - - niiclass = class(nii.img); - - h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... - level, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, niiclass, nii_view.numscan); - handles.cbar_image = h1; - handles.cbar_axes = cbar_axes; - handles.cbarminmax_axes = cbarminmax_axes; - - end - - nii_view.handles = handles; % store handles - - nii_view.usepanel = usepanel; % whole panel at low right cornor - nii_view.usestretch = usestretch; % stretch display of voxel_size - nii_view.useinterp = useinterp; % use interpolation - nii_view.colorindex = colorindex; % store colorindex variable - nii_view.buttondown = buttondown; % command after button down click - nii_view.cbarminmax = cbarminmax; % store min max value for colorbar - - set_coordinates(nii_view,useinterp); % coord unit - - if ~isfield(nii_view, 'axi_xhair') | ... - ~isfield(nii_view, 'cor_xhair') | ... - ~isfield(nii_view, 'sag_xhair') - - nii_view.axi_xhair = []; % top cross hair - nii_view.cor_xhair = []; % front cross hair - nii_view.sag_xhair = []; % side cross hair - - end - - if ~isempty(color_map) - nii_view.color_map = color_map; - end - - if ~isempty(colorlevel) - nii_view.colorlevel = colorlevel; - end - - if ~isempty(highcolor) - nii_view.highcolor = highcolor; - end - - update_nii_view(nii_view); - - if ~isempty(setunit) - update_unit(fig, setunit); - end - - if ~isempty(setviewpoint) - update_viewpoint(fig, setviewpoint); - end - - if ~isempty(setcrosshaircolor) - update_crosshaircolor(fig, setcrosshaircolor); - end - - if ~isempty(usecrosshair) - update_usecrosshair(fig, usecrosshair); - end - - nii_menu = getappdata(fig, 'nii_menu'); - - if ~isempty(nii_menu) - if nii.hdr.dime.datatype == 128 | nii.hdr.dime.datatype == 511 - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on','enable','off'); - elseif useinterp - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - else - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - end - end - - windowbuttonmotion = get(fig, 'windowbuttonmotion'); - windowbuttonmotion = [windowbuttonmotion '; view_nii(''move_cursor'');']; - set(fig, 'windowbuttonmotion', windowbuttonmotion); - - return; % init - - -%---------------------------------------------------------------- -function fig = update_img(img, fig, opt) - - nii_menu = getappdata(fig,'nii_menu'); - - if ~isempty(nii_menu) - set(nii_menu.Mzoom,'Userdata',1,'Label','Zoom on'); - set(fig,'pointer','arrow'); - zoom off; - end - - nii_view = getappdata(fig,'nii_view'); - change_interp = 0; - - if isfield(opt, 'useinterp') & opt.useinterp ~= nii_view.useinterp - nii_view.useinterp = opt.useinterp; - change_interp = 1; - end - - setscanid = 1; - - if isfield(opt, 'setscanid') - setscanid = round(opt.setscanid); - - if setscanid < 1 - setscanid = 1; - end - - if setscanid > nii_view.numscan - setscanid = nii_view.numscan; - end - end - - if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) - minvalue = opt.glblocminmax(1); - maxvalue = opt.glblocminmax(2); - else - minvalue = img(:,:,:,setscanid); - minvalue = double(minvalue(:)); - minvalue = min(minvalue(~isnan(minvalue))); - maxvalue = img(:,:,:,setscanid); - maxvalue = double(maxvalue(:)); - maxvalue = max(maxvalue(~isnan(maxvalue))); - end - - if isfield(opt, 'setvalue') - setvalue = opt.setvalue; - - if isfield(opt, 'glblocminmax') & ~isempty(opt.glblocminmax) - minvalue = opt.glblocminmax(1); - maxvalue = opt.glblocminmax(2); - else - minvalue = double(min(setvalue.val)); - maxvalue = double(max(setvalue.val)); - end - - bgimg = double(img); - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - - bgimg = scale_in(bgimg, minbg, maxbg, 55) + 200; % scale to 201~256 - - cbarminmax = [minvalue maxvalue]; - - if nii_view.useinterp - - % scale signal data to 1~200 - % - img = repmat(nan, size(img)); - img(setvalue.idx) = setvalue.val; - - % 200 level for source image - % - bgimg = single(scale_out(bgimg, cbarminmax(1), cbarminmax(2), 199)); - - else - - bgimg(setvalue.idx) = NaN; - minbg = double(min(bgimg(:))); - maxbg = double(max(bgimg(:))); - bgimg(setvalue.idx) = minbg; - - % bgimg must be normalized to [201 256] - % - bgimg = 55 * (bgimg-min(bgimg(:))) / (max(bgimg(:))-min(bgimg(:))) + 201; - bgimg(setvalue.idx) = 0; - - % scale signal data to 1~200 - % - img = zeros(size(img)); - img(setvalue.idx) = scale_in(setvalue.val, minvalue, maxvalue, 199); - img = img + bgimg; - bgimg = []; - img = scale_out(img, cbarminmax(1), cbarminmax(2), 199); - - minvalue = double(min(img(:))); - maxvalue = double(max(img(:))); - - if isfield(opt,'glblocminmax') & ~isempty(opt.glblocminmax) - minvalue = opt.glblocminmax(1); - end - - end - - nii_view.bgimg = bgimg; - nii_view.setvalue = setvalue; - - else - cbarminmax = [minvalue maxvalue]; - end - - update_cbarminmax(fig, cbarminmax); - nii_view.cbarminmax = cbarminmax; - nii_view.nii.img = img; - nii_view.minvalue = minvalue; - nii_view.maxvalue = maxvalue; - nii_view.scanid = setscanid; - change_colormap(fig); - - % init color (gray) scaling to make sure the slice clim take the - % global clim [min(nii.img(:)) max(nii.img(:))] - % - if isempty(nii_view.bgimg) - clim = [minvalue maxvalue]; - else - clim = [minvalue double(max(nii_view.bgimg(:)))]; - end - - if clim(1) == clim(2) - clim(2) = clim(1) + 0.000001; - end - - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - - if ~isempty(nii_view.bgimg) % with interpolation - - Saxi = squeeze(nii_view.bgimg(:,:,nii_view.slices.axi)); - - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - set(nii_view.handles.axial_bg,'CData',double(Saxi)'); - else - axes(nii_view.handles.axial_axes); - - if useimagesc - nii_view.handles.axial_bg = surface(zeros(size(Saxi')),double(Saxi'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.axial_bg = surface(zeros(size(Saxi')),double(Saxi'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.axial_bg)) = []; - order = [order; nii_view.handles.axial_bg]; - set(gca, 'child', order); - end - - end - - if isfield(nii_view.handles,'axial_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Saxi = squeeze(nii_view.nii.img(:,:,nii_view.slices.axi,:,setscanid)); - Saxi = permute(Saxi, [2 1 3]); - else - Saxi = squeeze(nii_view.nii.img(:,:,nii_view.slices.axi,setscanid)); - Saxi = Saxi'; - end - - set(nii_view.handles.axial_image,'CData',double(Saxi)); - end - - set(nii_view.handles.axial_axes,'CLim',clim); - - if ~isempty(nii_view.bgimg) - Scor = squeeze(nii_view.bgimg(:,nii_view.slices.cor,:)); - - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - set(nii_view.handles.coronal_bg,'CData',double(Scor)'); - else - axes(nii_view.handles.coronal_axes); - - if useimagesc - nii_view.handles.coronal_bg = surface(zeros(size(Scor')),double(Scor'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.coronal_bg = surface(zeros(size(Scor')),double(Scor'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.coronal_bg)) = []; - order = [order; nii_view.handles.coronal_bg]; - set(gca, 'child', order); - end - end - - if isfield(nii_view.handles,'coronal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Scor = squeeze(nii_view.nii.img(:,nii_view.slices.cor,:,:,setscanid)); - Scor = permute(Scor, [2 1 3]); - else - Scor = squeeze(nii_view.nii.img(:,nii_view.slices.cor,:,setscanid)); - Scor = Scor'; - end - - set(nii_view.handles.coronal_image,'CData',double(Scor)); - end - - set(nii_view.handles.coronal_axes,'CLim',clim); - - if ~isempty(nii_view.bgimg) - Ssag = squeeze(nii_view.bgimg(nii_view.slices.sag,:,:)); - - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - set(nii_view.handles.sagittal_bg,'CData',double(Ssag)'); - else - axes(nii_view.handles.sagittal_axes); - - if useimagesc - nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')),double(Ssag'),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.sagittal_bg = surface(zeros(size(Ssag')),double(Ssag'),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - order = get(gca,'child'); - order(find(order == nii_view.handles.sagittal_bg)) = []; - order = [order; nii_view.handles.sagittal_bg]; - set(gca, 'child', order); - end - end - - if isfield(nii_view.handles,'sagittal_image'), - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag,:,:,:,setscanid)); - Ssag = permute(Ssag, [2 1 3]); - else - Ssag = squeeze(nii_view.nii.img(nii_view.slices.sag,:,:,setscanid)); - Ssag = Ssag'; - end - - set(nii_view.handles.sagittal_image,'CData',double(Ssag)); - end - - set(nii_view.handles.sagittal_axes,'CLim',clim); - - update_nii_view(nii_view); - - if isfield(opt, 'setvalue') - - if ~isfield(nii_view,'highcolor') | ~isequal(size(nii_view.highcolor),[56 3]) - - % 55 level for brain structure (paded 0 for highcolor level 1, i.e. normal level 201, to make 56 highcolor) - % - update_highcolor(fig, [zeros(1,3);gray(55)], []); - - end - - if nii_view.colorindex ~= 2 - update_colorindex(fig, 2); - end - - old_color = get(nii_view.handles.xhair_color,'user'); - - if isequal(old_color, [1 0 0]) - update_crosshaircolor(fig, [1 1 0]); - end - -% if change_interp - % update_useinterp(fig, nii_view.useinterp); - % end - - end - - if change_interp - update_useinterp(fig, nii_view.useinterp); - end - - return; % update_img - - -%---------------------------------------------------------------- -function [top_pos, front_pos, side_pos] = ... - axes_pos(fig,area,vol_size,usestretch) - - set(fig,'unit','pixel'); - - fig_pos = get(fig,'position'); - - gap_x = 15/fig_pos(3); % width of vertical scrollbar - gap_y = 15/fig_pos(4); % width of horizontal scrollbar - - a = (area(3) - gap_x * 1.3) * fig_pos(3) / (vol_size(1) + vol_size(2)); % no crosshair lost in zoom - b = (area(4) - gap_y * 3) * fig_pos(4) / (vol_size(2) + vol_size(3)); - c = min([a b]); % make sure 'ax' is inside 'area' - - top_w = vol_size(1) * c / fig_pos(3); - side_w = vol_size(2) * c / fig_pos(3); - top_h = vol_size(2) * c / fig_pos(4); - side_h = vol_size(3) * c / fig_pos(4); - side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom - side_y = area(2) + top_h + gap_y * 3; - - if usestretch - if a > b % top touched ceiling, use b - d = (area(3) - gap_x * 1.3) / (top_w + side_w); % no crosshair lost in zoom - top_w = top_w * d; - side_w = side_w * d; - side_x = area(1) + top_w + gap_x * 1.3; % no crosshair lost in zoom - else - d = (area(4) - gap_y * 3) / (top_h + side_h); - top_h = top_h * d; - side_h = side_h * d; - side_y = area(2) + top_h + gap_y * 3; - end - end - - top_pos = [area(1) area(2)+gap_y top_w top_h]; - front_pos = [area(1) side_y top_w side_h]; - side_pos = [side_x side_y side_w side_h]; - - set(fig,'unit','normal'); - - return; % axes_pos - - -%---------------------------------------------------------------- -function [top_ax, front_ax, side_ax] ... - = create_ax(fig, area, vol_size, usestretch) - - cur_fig = gcf; % save h_wait fig - figure(fig); - - [top_pos, front_pos, side_pos] = ... - axes_pos(fig,area,vol_size,usestretch); - - nii_view = getappdata(fig, 'nii_view'); - - if isempty(nii_view) - top_ax = axes('position', top_pos); - front_ax = axes('position', front_pos); - side_ax = axes('position', side_pos); - else - top_ax = nii_view.handles.axial_axes; - front_ax = nii_view.handles.coronal_axes; - side_ax = nii_view.handles.sagittal_axes; - - set(top_ax, 'position', top_pos); - set(front_ax, 'position', front_pos); - set(side_ax, 'position', side_pos); - end - - figure(cur_fig); - - return; % create_ax - - -%---------------------------------------------------------------- -function [cbar_axes, cbarminmax_axes] = create_cbar_axes(fig, cbar_area, nii_view) - - if isempty(cbar_area) % without_cbar - cbar_axes = []; - cbarminmax_axes = []; - return; - end - - cur_fig = gcf; % save h_wait fig - figure(fig); - - if ~exist('nii_view', 'var') - nii_view = getappdata(fig, 'nii_view'); - end - - if isempty(nii_view) | ~isfield(nii_view.handles,'cbar_axes') | isempty(nii_view.handles.cbar_axes) - cbarminmax_axes = axes('position', cbar_area); - cbar_axes = axes('position', cbar_area); - else - cbarminmax_axes = nii_view.handles.cbarminmax_axes; - cbar_axes = nii_view.handles.cbar_axes; - set(cbarminmax_axes, 'position', cbar_area); - set(cbar_axes, 'position', cbar_area); - end - - figure(cur_fig); - - return; % create_cbar_axes - - -%---------------------------------------------------------------- -function h1 = plot_view(fig, x, y, img_ax, img_slice, clim, ... - cbarminmax, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, useinterp, numscan) - - h1 = []; - - if x > 1 & y > 1, - - axes(img_ax); - - nii_view = getappdata(fig, 'nii_view'); - - if isempty(nii_view) - - % set colormap first - % - nii.handles = handles; - nii.handles.axial_axes = img_ax; - nii.colorindex = colorindex; - nii.color_map = color_map; - nii.colorlevel = colorlevel; - nii.highcolor = highcolor; - nii.numscan = numscan; - - change_colormap(fig, nii, colorindex, cbarminmax); - - if useinterp - if useimagesc - h1 = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - h1 = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - - set(gca,'clim',clim); - else - if useimagesc - h1 = imagesc(img_slice,clim); - else - h1 = image(img_slice); - end - - set(gca,'clim',clim); - end - - else - - h1 = nii_view.handles.axial_image; - - if ~isequal(get(h1,'parent'), img_ax) - h1 = nii_view.handles.coronal_image; - end - - if ~isequal(get(h1,'parent'), img_ax) - h1 = nii_view.handles.sagittal_image; - end - - set(h1, 'cdata', double(img_slice)); - set(h1, 'xdata', 1:size(img_slice,2)); - set(h1, 'ydata', 1:size(img_slice,1)); - - end - - set(img_ax,'YDir','normal','XLimMode','manual','YLimMode','manual',... - 'ClimMode','manual','visible','off', ... - 'xtick',[],'ytick',[], 'clim', clim); - - end - - return; % plot_view - - -%---------------------------------------------------------------- -function h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, cbarminmax, ... - level, handles, useimagesc, colorindex, color_map, ... - colorlevel, highcolor, niiclass, numscan, nii_view) - - cbar_image = [1:level]'; - - % In a uint8 or uint16 indexed image, 0 points to the first row - % in the colormap - % - if 0 % strcmpi(niiclass,'uint8') | strcmpi(niiclass,'uint16') - % we use single for display anyway - ylim = [0, level-1]; - else - ylim = [1, level]; - end - - axes(cbarminmax_axes); - - plot([0 0], cbarminmax, 'w'); - axis tight; - - set(cbarminmax_axes,'YDir','normal', ... - 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... - 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); - - ylimb = get(cbarminmax_axes,'ylim'); - ytickb = get(cbarminmax_axes,'ytick'); - ytick=(ylim(2)-ylim(1))*(ytickb-ylimb(1))/(ylimb(2)-ylimb(1))+ylim(1); - - axes(cbar_axes); - - if ~exist('nii_view', 'var') - nii_view = getappdata(fig, 'nii_view'); - end - - if isempty(nii_view) | ~isfield(nii_view.handles,'cbar_image') | isempty(nii_view.handles.cbar_image) - - % set colormap first - % - nii.handles = handles; - nii.colorindex = colorindex; - nii.color_map = color_map; - nii.colorlevel = colorlevel; - nii.highcolor = highcolor; - nii.numscan = numscan; - - change_colormap(fig, nii, colorindex, cbarminmax); - h1 = image([0,1], [ylim(1),ylim(2)], cbar_image); - - else - h1 = nii_view.handles.cbar_image; - set(h1, 'cdata', double(cbar_image)); - end - - set(cbar_axes,'YDir','normal','XLimMode','manual', ... - 'YLimMode','manual','YColor',[0 0 0],'XColor',[0 0 0],'xtick',[], ... - 'YAxisLocation','right','ylim',ylim,'ytick',ytick,'yticklabel',''); - - return; % plot_cbar - - -%---------------------------------------------------------------- -function set_coordinates(nii_view,useinterp) - - imgPlim.vox = nii_view.dims; - imgNlim.vox = [1 1 1]; - - if useinterp - xdata_ax = [imgNlim.vox(1) imgPlim.vox(1)]; - ydata_ax = [imgNlim.vox(2) imgPlim.vox(2)]; - zdata_ax = [imgNlim.vox(3) imgPlim.vox(3)]; - else - xdata_ax = [imgNlim.vox(1)-0.5 imgPlim.vox(1)+0.5]; - ydata_ax = [imgNlim.vox(2)-0.5 imgPlim.vox(2)+0.5]; - zdata_ax = [imgNlim.vox(3)-0.5 imgPlim.vox(3)+0.5]; - end - - if isfield(nii_view.handles,'axial_image') & ~isempty(nii_view.handles.axial_image) - set(nii_view.handles.axial_axes,'Xlim',xdata_ax); - set(nii_view.handles.axial_axes,'Ylim',ydata_ax); - end; - if isfield(nii_view.handles,'coronal_image') & ~isempty(nii_view.handles.coronal_image) - set(nii_view.handles.coronal_axes,'Xlim',xdata_ax); - set(nii_view.handles.coronal_axes,'Ylim',zdata_ax); - end; - if isfield(nii_view.handles,'sagittal_image') & ~isempty(nii_view.handles.sagittal_image) - set(nii_view.handles.sagittal_axes,'Xlim',ydata_ax); - set(nii_view.handles.sagittal_axes,'Ylim',zdata_ax); - end; - - return % set_coordinates - - -%---------------------------------------------------------------- -function set_image_value(nii_view), - - % get coordinates of selected voxel and the image intensity there - % - sag = round(nii_view.slices.sag); - cor = round(nii_view.slices.cor); - axi = round(nii_view.slices.axi); - - if 0 % isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imval,'Value',imgvalue); - set(nii_view.handles.imval,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imval,'Value',imgvalue); - imgvalue = [R G B]; - set(nii_view.handles.imval,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - else - imgvalue = double(img(sag,cor,axi,nii_view.scanid)); - set(nii_view.handles.imval,'Value',imgvalue); - - if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) - imgvalue = 0; - end - - set(nii_view.handles.imval,'String',sprintf('%.6g',imgvalue)); - end - - % Now update the coordinates of the selected voxel - - nii_view = update_imgXYZ(nii_view); - - if get(nii_view.handles.coord,'value') == 1, - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - org = nii_view.origin; - elseif get(nii_view.handles.coord,'value') == 2, - sag = nii_view.imgXYZ.mm(1); - cor = nii_view.imgXYZ.mm(2); - axi = nii_view.imgXYZ.mm(3); - org = [0 0 0]; - elseif get(nii_view.handles.coord,'value') == 3, - sag = nii_view.imgXYZ.tal(1); - cor = nii_view.imgXYZ.tal(2); - axi = nii_view.imgXYZ.tal(3); - org = [0 0 0]; - end - - set(nii_view.handles.impos,'Value',[sag,cor,axi]); - - if get(nii_view.handles.coord,'value') == 1, - string = sprintf('%7.0f %7.0f %7.0f',sag,cor,axi); - org_str = sprintf('%7.0f %7.0f %7.0f', org(1), org(2), org(3)); - else - string = sprintf('%7.1f %7.1f %7.1f',sag,cor,axi); - org_str = sprintf('%7.1f %7.1f %7.1f', org(1), org(2), org(3)); - end; - - set(nii_view.handles.impos,'String',string); - set(nii_view.handles.origin, 'string', org_str); - - return % set_image_value - - -%---------------------------------------------------------------- -function nii_view = get_slice_position(nii_view,view), - - % obtain slices that is in correct unit, then update slices - % - slices = nii_view.slices; - - switch view, - case 'sag', - currentpoint = get(nii_view.handles.sagittal_axes,'CurrentPoint'); - slices.cor = currentpoint(1,1); - slices.axi = currentpoint(1,2); - case 'cor', - currentpoint = get(nii_view.handles.coronal_axes,'CurrentPoint'); - slices.sag = currentpoint(1,1); - slices.axi = currentpoint(1,2); - case 'axi', - currentpoint = get(nii_view.handles.axial_axes,'CurrentPoint'); - slices.sag = currentpoint(1,1); - slices.cor = currentpoint(1,2); - end - - % update nii_view.slices with the updated slices - % - nii_view.slices.axi = round(slices.axi); - nii_view.slices.cor = round(slices.cor); - nii_view.slices.sag = round(slices.sag); - - return % get_slice_position - - -%---------------------------------------------------------------- -function nii_view = get_slider_position(nii_view), - - [nii_view.slices.sag,nii_view.slices.cor,nii_view.slices.axi] = deal(0); - - if isfield(nii_view.handles,'sagittal_slider'), - if ishandle(nii_view.handles.sagittal_slider), - nii_view.slices.sag = ... - round(get(nii_view.handles.sagittal_slider,'Value')); - end - end - - if isfield(nii_view.handles,'coronal_slider'), - if ishandle(nii_view.handles.coronal_slider), - nii_view.slices.cor = ... - round(nii_view.dims(2) - ... - get(nii_view.handles.coronal_slider,'Value') + 1); - end - end - - if isfield(nii_view.handles,'axial_slider'), - if ishandle(nii_view.handles.axial_slider), - nii_view.slices.axi = ... - round(get(nii_view.handles.axial_slider,'Value')); - end - end - - nii_view = check_slices(nii_view); - - return % get_slider_position - - -%---------------------------------------------------------------- -function nii_view = update_imgXYZ(nii_view), - - nii_view.imgXYZ.vox = ... - [nii_view.slices.sag,nii_view.slices.cor,nii_view.slices.axi]; - nii_view.imgXYZ.mm = ... - (nii_view.imgXYZ.vox - nii_view.origin) .* nii_view.voxel_size; -% nii_view.imgXYZ.tal = mni2tal(nii_view.imgXYZ.mni); - - return % update_imgXYZ - - -%---------------------------------------------------------------- -function nii_view = convert2voxel(nii_view,slices), - - if get(nii_view.handles.coord,'value') == 1, - - % [slices.axi, slices.cor, slices.sag] are in vox - % - nii_view.slices.axi = round(slices.axi); - nii_view.slices.cor = round(slices.cor); - nii_view.slices.sag = round(slices.sag); - - elseif get(nii_view.handles.coord,'value') == 2, - - % [slices.axi, slices.cor, slices.sag] are in mm - % - xpix = nii_view.voxel_size(1); - ypix = nii_view.voxel_size(2); - zpix = nii_view.voxel_size(3); - - nii_view.slices.axi = round(slices.axi / zpix + nii_view.origin(3)); - nii_view.slices.cor = round(slices.cor / ypix + nii_view.origin(2)); - nii_view.slices.sag = round(slices.sag / xpix + nii_view.origin(1)); - elseif get(nii_view.handles.coord,'value') == 3, - - % [slices.axi, slices.cor, slices.sag] are in talairach - % - xpix = nii_view.voxel_size(1); - ypix = nii_view.voxel_size(2); - zpix = nii_view.voxel_size(3); - - xyz_tal = [slices.sag, slices.cor, slices.axi]; - xyz_mni = tal2mni(xyz_tal); - - nii_view.slices.axi = round(xyz_mni(3) / zpix + nii_view.origin(3)); - nii_view.slices.cor = round(xyz_mni(2) / ypix + nii_view.origin(2)); - nii_view.slices.sag = round(xyz_mni(1) / xpix + nii_view.origin(1)); - - end - - return % convert2voxel - - -%---------------------------------------------------------------- -function nii_view = check_slices(nii_view), - - img = nii_view.nii.img; - - [ SagSize, CorSize, AxiSize, TimeSize ] = size(img); - if nii_view.slices.sag > SagSize, nii_view.slices.sag = SagSize; end; - if nii_view.slices.sag < 1, nii_view.slices.sag = 1; end; - if nii_view.slices.cor > CorSize, nii_view.slices.cor = CorSize; end; - if nii_view.slices.cor < 1, nii_view.slices.cor = 1; end; - if nii_view.slices.axi > AxiSize, nii_view.slices.axi = AxiSize; end; - if nii_view.slices.axi < 1, nii_view.slices.axi = 1; end; - if nii_view.scanid > TimeSize, nii_view.scanid = TimeSize; end; - if nii_view.scanid < 1, nii_view.scanid = 1; end; - - return % check_slices - - -%---------------------------------------------------------------- -% -% keep this function small, since it will be called for every click -% -function nii_view = update_nii_view(nii_view) - - % add imgXYZ into nii_view struct - % - nii_view = check_slices(nii_view); - nii_view = update_imgXYZ(nii_view); - - % update xhair - % - p_axi = nii_view.imgXYZ.vox([1 2]); - p_cor = nii_view.imgXYZ.vox([1 3]); - p_sag = nii_view.imgXYZ.vox([2 3]); - - nii_view.axi_xhair = ... - rri_xhair(p_axi, nii_view.axi_xhair, nii_view.handles.axial_axes); - - nii_view.cor_xhair = ... - rri_xhair(p_cor, nii_view.cor_xhair, nii_view.handles.coronal_axes); - - nii_view.sag_xhair = ... - rri_xhair(p_sag, nii_view.sag_xhair, nii_view.handles.sagittal_axes); - - setappdata(nii_view.fig, 'nii_view', nii_view); - set_image_value(nii_view); - - return; % update_nii_view - - -%---------------------------------------------------------------- -function hist_plot(fig) - - nii_view = getappdata(fig,'nii_view'); - - if isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - img = double(img(:)); - - if length(unique(round(img))) == length(unique(img)) - is_integer = 1; - range = max(img) - min(img) + 1; - figure; hist(img, range); - set(gca, 'xlim', [-range/5, max(img)]); - else - is_integer = 0; - figure; hist(img); - end - - xlabel('Voxel Intensity'); - ylabel('Voxel Numbers for Each Intensity'); - set(gcf, 'NumberTitle','off','Name','Histogram Plot'); - - return; % hist_plot - - -%---------------------------------------------------------------- -function hist_eq(fig) - - nii_view = getappdata(fig,'nii_view'); - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - if get(nii_view.handles.hist_eq,'value') - max_img = double(max(nii_view.nii.img(:))); - tmp = double(nii_view.nii.img) / max_img; % normalize for histeq - tmp = histeq(tmp(:)); - nii_view.disp = reshape(tmp, size(nii_view.nii.img)); - min_disp = min(nii_view.disp(:)); - nii_view.disp = (nii_view.disp - min_disp); % range having eq hist - nii_view.disp = nii_view.disp * max_img / max(nii_view.disp(:)); - nii_view.disp = single(nii_view.disp); - else - if isfield(nii_view, 'disp') - nii_view.disp = nii_view.nii.img; - else - set(fig,'Pointer',old_pointer); - return; - end - end - - % update axial view - % - img_slice = squeeze(double(nii_view.disp(:,:,nii_view.slices.axi))); - h1 = nii_view.handles.axial_image; - set(h1, 'cdata', double(img_slice)'); - - % update coronal view - % - img_slice = squeeze(double(nii_view.disp(:,nii_view.slices.cor,:))); - h1 = nii_view.handles.coronal_image; - set(h1, 'cdata', double(img_slice)'); - - % update sagittal view - % - img_slice = squeeze(double(nii_view.disp(nii_view.slices.sag,:,:))); - - h1 = nii_view.handles.sagittal_image; - set(h1, 'cdata', double(img_slice)'); - - % remove disp field if un-check 'histeq' button - % - if ~get(nii_view.handles.hist_eq,'value') & isfield(nii_view, 'disp') - nii_view = rmfield(nii_view, 'disp'); - end - - update_nii_view(nii_view); - - set(fig,'Pointer',old_pointer); - - return; % hist_eq - - -%---------------------------------------------------------------- -function [top1_label, top2_label, side1_label, side2_label] = ... - dir_label(fig, top_ax, front_ax, side_ax) - - nii_view = getappdata(fig,'nii_view'); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - top_gap_x = (side_pos(1)-top_pos(1)-top_pos(3)) / (2*top_pos(3)); - top_gap_y = (front_pos(2)-top_pos(2)-top_pos(4)) / (2*top_pos(4)); - side_gap_x = (side_pos(1)-top_pos(1)-top_pos(3)) / (2*side_pos(3)); - side_gap_y = (front_pos(2)-top_pos(2)-top_pos(4)) / (2*side_pos(4)); - - top1_label_pos = [0, 1]; % rot0 - top2_label_pos = [1, 0]; % rot90 - side1_label_pos = [1, - side_gap_y]; % rot0 - side2_label_pos = [0, 0]; % rot90 - - if isempty(nii_view) - axes(top_ax); - top1_label = text(double(top1_label_pos(1)),double(top1_label_pos(2)), ... - '== X =>', ... - 'vertical', 'bottom', ... - 'unit', 'normal', 'fontsize', 8); - - axes(top_ax); - top2_label = text(double(top2_label_pos(1)),double(top2_label_pos(2)), ... - '== Y =>', ... - 'rotation', 90, 'vertical', 'top', ... - 'unit', 'normal', 'fontsize', 8); - - axes(side_ax); - side1_label = text(double(side1_label_pos(1)),double(side1_label_pos(2)), ... - '<= Y ==', ... - 'horizontal', 'right', 'vertical', 'top', ... - 'unit', 'normal', 'fontsize', 8); - - axes(side_ax); - side2_label = text(double(side2_label_pos(1)),double(side2_label_pos(2)), ... - '== Z =>', ... - 'rotation', 90, 'vertical', 'bottom', ... - 'unit', 'normal', 'fontsize', 8); - else - top1_label = nii_view.handles.top1_label; - top2_label = nii_view.handles.top2_label; - side1_label = nii_view.handles.side1_label; - side2_label = nii_view.handles.side2_label; - - set(top1_label, 'position', [top1_label_pos 0]); - set(top2_label, 'position', [top2_label_pos 0]); - set(side1_label, 'position', [side1_label_pos 0]); - set(side2_label, 'position', [side2_label_pos 0]); - end - - return; % dir_label - - -%---------------------------------------------------------------- -function update_enable(h, opt); - - nii_view = getappdata(h,'nii_view'); - handles = nii_view.handles; - - if isfield(opt,'enablecursormove') - if opt.enablecursormove - v = 'on'; - else - v = 'off'; - end - - set(handles.Timposcur, 'visible', v); - set(handles.imposcur, 'visible', v); - set(handles.Timvalcur, 'visible', v); - set(handles.imvalcur, 'visible', v); - end - - if isfield(opt,'enableviewpoint') - if opt.enableviewpoint - v = 'on'; - else - v = 'off'; - end - - set(handles.Timpos, 'visible', v); - set(handles.impos, 'visible', v); - set(handles.Timval, 'visible', v); - set(handles.imval, 'visible', v); - end - - if isfield(opt,'enableorigin') - if opt.enableorigin - v = 'on'; - else - v = 'off'; - end - - set(handles.Torigin, 'visible', v); - set(handles.origin, 'visible', v); - end - - if isfield(opt,'enableunit') - if opt.enableunit - v = 'on'; - else - v = 'off'; - end - - set(handles.Tcoord, 'visible', v); - set(handles.coord_frame, 'visible', v); - set(handles.coord, 'visible', v); - end - - if isfield(opt,'enablecrosshair') - if opt.enablecrosshair - v = 'on'; - else - v = 'off'; - end - - set(handles.Txhair, 'visible', v); - set(handles.xhair_color, 'visible', v); - set(handles.xhair, 'visible', v); - end - - if isfield(opt,'enablehistogram') - if opt.enablehistogram - v = 'on'; - vv = 'off'; - else - v = 'off'; - vv = 'on'; - end - - set(handles.Tcoord, 'visible', vv); - set(handles.coord_frame, 'visible', vv); - set(handles.coord, 'visible', vv); - - set(handles.Thist, 'visible', v); - set(handles.hist_frame, 'visible', v); - set(handles.hist_eq, 'visible', v); - set(handles.hist_plot, 'visible', v); - end - - if isfield(opt,'enablecolormap') - if opt.enablecolormap - v = 'on'; - else - v = 'off'; - end - - set(handles.Tcolor, 'visible', v); - set(handles.color_frame, 'visible', v); - set(handles.neg_color, 'visible', v); - set(handles.colorindex, 'visible', v); - end - - if isfield(opt,'enablecontrast') - if opt.enablecontrast - v = 'on'; - else - v = 'off'; - end - - set(handles.Tcontrast, 'visible', v); - set(handles.contrast_frame, 'visible', v); - set(handles.contrast_def, 'visible', v); - set(handles.contrast, 'visible', v); - end - - if isfield(opt,'enablebrightness') - if opt.enablebrightness - v = 'on'; - else - v = 'off'; - end - - set(handles.Tbrightness, 'visible', v); - set(handles.brightness_frame, 'visible', v); - set(handles.brightness_def, 'visible', v); - set(handles.brightness, 'visible', v); - end - - if isfield(opt,'enabledirlabel') - if opt.enabledirlabel - v = 'on'; - else - v = 'off'; - end - - set(handles.top1_label, 'visible', v); - set(handles.top2_label, 'visible', v); - set(handles.side1_label, 'visible', v); - set(handles.side2_label, 'visible', v); - end - - if isfield(opt,'enableslider') - if opt.enableslider - v = 'on'; - else - v = 'off'; - end - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider, 'visible', v); - end - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider, 'visible', v); - end - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider, 'visible', v); - end - end - - return; % update_enable - - -%---------------------------------------------------------------- -function update_usepanel(fig, usepanel) - - if isempty(usepanel) - return; - end - - if usepanel - opt.enablecursormove = 1; - opt.enableviewpoint = 1; - opt.enableorigin = 1; - opt.enableunit = 1; - opt.enablecrosshair = 1; -% opt.enablehistogram = 1; - opt.enablecolormap = 1; - opt.enablecontrast = 1; - opt.enablebrightness = 1; - else - opt.enablecursormove = 0; - opt.enableviewpoint = 0; - opt.enableorigin = 0; - opt.enableunit = 0; - opt.enablecrosshair = 0; -% opt.enablehistogram = 0; - opt.enablecolormap = 0; - opt.enablecontrast = 0; - opt.enablebrightness = 0; - end - - update_enable(fig, opt); - - nii_view = getappdata(fig,'nii_view'); - nii_view.usepanel = usepanel; - setappdata(fig,'nii_view',nii_view); - - return; % update_usepanel - - -%---------------------------------------------------------------- -function update_usecrosshair(fig, usecrosshair) - - if isempty(usecrosshair) - return; - end - - if usecrosshair - v=1; - else - v=2; - end - - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.xhair,'value',v); - - opt.command = 'crosshair'; - view_nii(fig, opt); - - return; % update_usecrosshair - - -%---------------------------------------------------------------- -function update_usestretch(fig, usestretch) - - nii_view = getappdata(fig,'nii_view'); - - handles = nii_view.handles; - fig = nii_view.fig; - area = nii_view.area; - vol_size = nii_view.voxel_size .* nii_view.dims; - - % Three Axes & label - % - [top_ax, front_ax, side_ax] = ... - create_ax(fig, area, vol_size, usestretch); - - dir_label(fig, top_ax, front_ax, side_ax); - - top_pos = get(top_ax,'position'); - front_pos = get(front_ax,'position'); - side_pos = get(side_ax,'position'); - - % Sagittal Slider - % - x = side_pos(1); - y = top_pos(2) + top_pos(4); - w = side_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - pos = [x y w h]; - - if isfield(handles,'sagittal_slider') & ishandle(handles.sagittal_slider) - set(handles.sagittal_slider,'position',pos); - end - - % Coronal Slider - % - x = top_pos(1); - y = top_pos(2) + top_pos(4); - w = top_pos(3); - h = (front_pos(2) - y) / 2; - y = y + h; - pos = [x y w h]; - - if isfield(handles,'coronal_slider') & ishandle(handles.coronal_slider) - set(handles.coronal_slider,'position',pos); - end - - % Axial Slider - % - x = top_pos(1); - y = area(2); - w = top_pos(3); - h = top_pos(2) - y; - pos = [x y w h]; - - if isfield(handles,'axial_slider') & ishandle(handles.axial_slider) - set(handles.axial_slider,'position',pos); - end - - % plot info view - % -% info_pos = [side_pos([1,3]); top_pos([2,4])]; -% info_pos = info_pos(:); - gap = side_pos(1)-(top_pos(1)+top_pos(3)); - info_pos(1) = side_pos(1) + gap; - info_pos(2) = area(2); - info_pos(3) = side_pos(3) - gap; - info_pos(4) = top_pos(2) + top_pos(4) - area(2) - gap; - - num_inputline = 10; - inputline_space =info_pos(4) / num_inputline; - - - % Image Intensity Value at Cursor - % - x = info_pos(1); - y = info_pos(2); - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.Timvalcur,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imvalcur,'position',pos); - - % Position at Cursor - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timposcur,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imposcur,'position',pos); - - % Image Intensity Value at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timval,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.imval,'position',pos); - - % Viewpoint Position at Mouse Click - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Timpos,'position',pos); - - x = x + w + 0.005; - y = y - 0.008; - w = info_pos(3)*0.5; - h = inputline_space*0.9; - - pos = [x y w h]; - set(handles.impos,'position',pos); - - % Origin Position - % - x = info_pos(1); - y = y + inputline_space*1.2; - w = info_pos(3)*0.5; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.Torigin,'position',pos); - - x = x + w; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.origin,'position',pos); - -if 0 - % Axes Unit - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.5; - - pos = [x y w h]; - set(handles.Tcoord,'position',pos); - - x = x + w + 0.005; - w = info_pos(3)*0.5 - 0.005; - - pos = [x y w h]; - set(handles.coord,'position',pos); -end - - % Crosshair - % - x = info_pos(1); - y = y + inputline_space; - w = info_pos(3)*0.4; - - pos = [x y w h]; - set(handles.Txhair,'position',pos); - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - set(handles.xhair_color,'position',pos); - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.3; - - pos = [x y w h]; - set(handles.xhair,'position',pos); - - % Histogram & Color - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 1.5; - pos = [x, y+inputline_space*0.9, w, h]; - set(handles.hist_frame,'position',pos); - set(handles.coord_frame,'position',pos); - - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - h = inputline_space * 1.5; - - pos = [x, y+inputline_space*0.9, w, h]; - set(handles.color_frame,'position',pos); - - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space*1.2; - w = info_pos(3)*0.2; - h = inputline_space*0.7; - - pos = [x y w h]; - set(handles.hist_eq,'position',pos); - - x = x + w; - w = info_pos(3)*0.2; - - pos = [x y w h]; - set(handles.hist_plot,'position',pos); - - x = info_pos(1) + info_pos(3)*0.025; - w = info_pos(3)*0.4; - - pos = [x y w h]; - set(handles.coord,'position',pos); - - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.2; - pos = [x y w h]; - set(handles.neg_color,'position',pos); - - x = info_pos(1) + info_pos(3)*0.7; - w = info_pos(3)*0.275; - - pos = [x y w h]; - set(handles.colorindex,'position',pos); - - x = info_pos(1) + info_pos(3)*0.1; - y = y + inputline_space; - w = info_pos(3)*0.28; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.Thist,'position',pos); - set(handles.Tcoord,'position',pos); - - x = info_pos(1) + info_pos(3)*0.60; - w = info_pos(3)*0.28; - - pos = [x y w h]; - set(handles.Tcolor,'position',pos); - - % Contrast Frame - % - x = info_pos(1); - w = info_pos(3)*0.45; - h = inputline_space * 2; - - pos = [x, y+inputline_space*0.8, w, h]; - set(handles.contrast_frame,'position',pos); - - % Brightness Frame - % - x = info_pos(1) + info_pos(3)*0.475; - w = info_pos(3)*0.525; - - pos = [x, y+inputline_space*0.8, w, h]; - set(handles.brightness_frame,'position',pos); - - % Contrast - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.4; - h = inputline_space*0.6; - - pos = [x y w h]; - set(handles.contrast,'position',pos); - - % Brightness - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.475; - - pos = [x y w h]; - set(handles.brightness,'position',pos); - - % Contrast text/def - % - x = info_pos(1) + info_pos(3)*0.025; - y = y + inputline_space; - w = info_pos(3)*0.22; - - pos = [x y w h]; - set(handles.Tcontrast,'position',pos); - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - set(handles.contrast_def,'position',pos); - - % Brightness text/def - % - x = info_pos(1) + info_pos(3)*0.5; - w = info_pos(3)*0.295; - - pos = [x y w h]; - set(handles.Tbrightness,'position',pos); - - x = x + w; - w = info_pos(3)*0.18; - - pos = [x y w h]; - set(handles.brightness_def,'position',pos); - - return; % update_usestretch - - -%---------------------------------------------------------------- -function update_useinterp(fig, useinterp) - - if isempty(useinterp) - return; - end - - nii_menu = getappdata(fig, 'nii_menu'); - - if ~isempty(nii_menu) - if get(nii_menu.Minterp,'user') - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - else - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - end - end - - nii_view = getappdata(fig, 'nii_view'); - nii_view.useinterp = useinterp; - - if ~isempty(nii_view.handles.axial_image) - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - elseif ~isempty(nii_view.handles.coronal_image) - if strcmpi(get(nii_view.handles.coronal_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - else - if strcmpi(get(nii_view.handles.sagittal_image,'cdatamapping'), 'direct') - useimagesc = 0; - else - useimagesc = 1; - end - end - - if ~isempty(nii_view.handles.axial_image) - img_slice = get(nii_view.handles.axial_image, 'cdata'); - delete(nii_view.handles.axial_image); - axes(nii_view.handles.axial_axes); - clim = get(gca,'clim'); - - if useinterp - if useimagesc - nii_view.handles.axial_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.axial_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - else - if useimagesc - nii_view.handles.axial_image = imagesc('cdata',img_slice); - else - nii_view.handles.axial_image = image('cdata',img_slice); - end - end - - set(gca,'clim',clim); - - order = get(gca,'child'); - order(find(order == nii_view.handles.axial_image)) = []; - order = [order; nii_view.handles.axial_image]; - - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - order(find(order == nii_view.handles.axial_bg)) = []; - order = [order; nii_view.handles.axial_bg]; - end - - set(gca, 'child', order); - - if ~useinterp - if isfield(nii_view.handles,'axial_bg') & ~isempty(nii_view.handles.axial_bg) - delete(nii_view.handles.axial_bg); - nii_view.handles.axial_bg = []; - end - end - - set(nii_view.handles.axial_image,'buttondown','view_nii(''axial_image'');'); - end - - if ~isempty(nii_view.handles.coronal_image) - img_slice = get(nii_view.handles.coronal_image, 'cdata'); - delete(nii_view.handles.coronal_image); - axes(nii_view.handles.coronal_axes); - clim = get(gca,'clim'); - - if useinterp - if useimagesc - nii_view.handles.coronal_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.coronal_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - else - if useimagesc - nii_view.handles.coronal_image = imagesc('cdata',img_slice); - else - nii_view.handles.coronal_image = image('cdata',img_slice); - end - end - - set(gca,'clim',clim); - - order = get(gca,'child'); - order(find(order == nii_view.handles.coronal_image)) = []; - order = [order; nii_view.handles.coronal_image]; - - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - order(find(order == nii_view.handles.coronal_bg)) = []; - order = [order; nii_view.handles.coronal_bg]; - end - - set(gca, 'child', order); - - if ~useinterp - if isfield(nii_view.handles,'coronal_bg') & ~isempty(nii_view.handles.coronal_bg) - delete(nii_view.handles.coronal_bg); - nii_view.handles.coronal_bg = []; - end - end - - set(nii_view.handles.coronal_image,'buttondown','view_nii(''coronal_image'');'); - end - - if ~isempty(nii_view.handles.sagittal_image) - img_slice = get(nii_view.handles.sagittal_image, 'cdata'); - delete(nii_view.handles.sagittal_image); - axes(nii_view.handles.sagittal_axes); - clim = get(gca,'clim'); - - if useinterp - if useimagesc - nii_view.handles.sagittal_image = surface(zeros(size(img_slice)),double(img_slice),'edgecolor','none','facecolor','interp'); - else - nii_view.handles.sagittal_image = surface(zeros(size(img_slice)),double(img_slice),'cdatamapping','direct','edgecolor','none','facecolor','interp'); - end - else - if useimagesc - nii_view.handles.sagittal_image = imagesc('cdata',img_slice); - else - nii_view.handles.sagittal_image = image('cdata',img_slice); - end - end - - set(gca,'clim',clim); - - order = get(gca,'child'); - order(find(order == nii_view.handles.sagittal_image)) = []; - order = [order; nii_view.handles.sagittal_image]; - - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - order(find(order == nii_view.handles.sagittal_bg)) = []; - order = [order; nii_view.handles.sagittal_bg]; - end - - set(gca, 'child', order); - - if ~useinterp - if isfield(nii_view.handles,'sagittal_bg') & ~isempty(nii_view.handles.sagittal_bg) - delete(nii_view.handles.sagittal_bg); - nii_view.handles.sagittal_bg = []; - end - end - - set(nii_view.handles.sagittal_image,'buttondown','view_nii(''sagittal_image'');'); - end - - if ~useinterp - nii_view.bgimg = []; - end - - set_coordinates(nii_view,useinterp); - setappdata(fig, 'nii_view', nii_view); - - return; % update_useinterp - - -%---------------------------------------------------------------- -function update_useimagesc(fig, useimagesc) - - if isempty(useimagesc) - return; - end - - if useimagesc - v='scaled'; - else - v='direct'; - end - - nii_view = getappdata(fig,'nii_view'); - handles = nii_view.handles; - - if isfield(handles,'cbar_image') & ishandle(handles.cbar_image) -% set(handles.cbar_image,'cdatamapping',v); - end - - set(handles.axial_image,'cdatamapping',v); - set(handles.coronal_image,'cdatamapping',v); - set(handles.sagittal_image,'cdatamapping',v); - - return; % update_useimagesc - - -%---------------------------------------------------------------- -function update_shape(fig, area, usecolorbar, usestretch, useimagesc) - - nii_view = getappdata(fig,'nii_view'); - - if isempty(usestretch) % no change, get usestretch - stretchchange = 0; - usestretch = nii_view.usestretch; - else % change, set usestretch - stretchchange = 1; - nii_view.usestretch = usestretch; - end - - if isempty(area) % no change, get area - - areachange = 0; - area = nii_view.area; - - elseif ~isempty(nii_view.cbar_area) % change, set area & cbar_area - - areachange = 1; - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes - - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - - nii_view.area = area; - nii_view.cbar_area = cbar_area; - - else % change, set area only - areachange = 1; - nii_view.area = area; - end - - % Add colorbar - % - if ~isempty(usecolorbar) & usecolorbar & isempty(nii_view.cbar_area) - - colorbarchange = 1; - - cbar_area = area; - cbar_area(1) = area(1) + area(3)*0.93; - cbar_area(3) = area(3)*0.04; - area(3) = area(3)*0.9; % 90% used for main axes - - % create axes for colorbar - % - [cbar_axes cbarminmax_axes] = create_cbar_axes(fig, cbar_area); - - nii_view.area = area; - nii_view.cbar_area = cbar_area; - - % useimagesc follows axial image - % - if isempty(useimagesc) - if strcmpi(get(nii_view.handles.axial_image,'cdatamap'),'scaled') - useimagesc = 1; - else - useimagesc = 0; - end - end - - if isfield(nii_view, 'highcolor') & ~isempty(highcolor) - num_highcolor = size(nii_view.highcolor,1); - else - num_highcolor = 0; - end - - if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) - colorlevel = nii_view.colorlevel; - else - colorlevel = 256 - num_highcolor; - end - - if isfield(nii_view, 'color_map') - color_map = nii_view.color_map; - else - color_map = []; - end - - if isfield(nii_view, 'highcolor') - highcolor = nii_view.highcolor; - else - highcolor = []; - end - - % plot colorbar - % -if 0 - if isempty(color_map) - level = colorlevel + num_highcolor; - else - level = size([color_map; highcolor], 1); - end -end - - if isempty(color_map) - level = colorlevel; - else - level = size([color_map], 1); - end - - cbar_image = [1:level]'; - - niiclass = class(nii_view.nii.img); - - h1 = plot_cbar(fig, cbar_axes, cbarminmax_axes, nii_view.cbarminmax, ... - level, nii_view.handles, useimagesc, nii_view.colorindex, ... - color_map, colorlevel, highcolor, niiclass, nii_view.numscan); - nii_view.handles.cbar_image = h1; - nii_view.handles.cbar_axes = cbar_axes; - nii_view.handles.cbarminmax_axes = cbar_axes; - - % remove colorbar - % - elseif ~isempty(usecolorbar) & ~usecolorbar & ~isempty(nii_view.cbar_area) - - colorbarchange = 1; - - area(3) = area(3) / 0.9; - - nii_view.area = area; - nii_view.cbar_area = []; - - nii_view.handles = rmfield(nii_view.handles,'cbar_image'); - delete(nii_view.handles.cbarminmax_axes); - nii_view.handles = rmfield(nii_view.handles,'cbarminmax_axes'); - delete(nii_view.handles.cbar_axes); - nii_view.handles = rmfield(nii_view.handles,'cbar_axes'); - - else - colorbarchange = 0; - end - - if colorbarchange | stretchchange | areachange - setappdata(fig,'nii_view',nii_view); - update_usestretch(fig, usestretch); - end - - return; % update_shape - - -%---------------------------------------------------------------- -function update_unit(fig, setunit) - - if isempty(setunit) - return; - end - - if strcmpi(setunit,'mm') | strcmpi(setunit,'millimeter') | strcmpi(setunit,'mni') - v = 2; -% elseif strcmpi(setunit,'tal') | strcmpi(setunit,'talairach') - % v = 3; - elseif strcmpi(setunit,'vox') | strcmpi(setunit,'voxel') - v = 1; - else - v = 1; - end - - nii_view = getappdata(fig,'nii_view'); - set(nii_view.handles.coord, 'value', v); - set_image_value(nii_view); - - return; % update_unit - - -%---------------------------------------------------------------- -function update_viewpoint(fig, setviewpoint) - - if isempty(setviewpoint) - return; - end - - nii_view = getappdata(fig,'nii_view'); - - if length(setviewpoint) ~= 3 - error('Viewpoint position should contain [x y z]'); - end - - set(nii_view.handles.impos,'string',num2str(setviewpoint)); - - opt.command = 'impos_edit'; - view_nii(fig, opt); - - set(nii_view.handles.axial_axes,'selected','on'); - set(nii_view.handles.axial_axes,'selected','off'); - set(nii_view.handles.coronal_axes,'selected','on'); - set(nii_view.handles.coronal_axes,'selected','off'); - set(nii_view.handles.sagittal_axes,'selected','on'); - set(nii_view.handles.sagittal_axes,'selected','off'); - - return; % update_viewpoint - - -%---------------------------------------------------------------- -function update_scanid(fig, setscanid) - - if isempty(setscanid) - return; - end - - nii_view = getappdata(fig,'nii_view'); - - if setscanid < 1 - setscanid = 1; - end - - if setscanid > nii_view.numscan - setscanid = nii_view.numscan; - end - - set(nii_view.handles.contrast_def,'string',num2str(setscanid)); - set(nii_view.handles.contrast,'value',setscanid); - - opt.command = 'updateimg'; - opt.setscanid = setscanid; - - view_nii(fig, nii_view.nii.img, opt); - - return; % update_scanid - - -%---------------------------------------------------------------- -function update_crosshaircolor(fig, new_color) - - if isempty(new_color) - return; - end - - nii_view = getappdata(fig,'nii_view'); - xhair_color = nii_view.handles.xhair_color; - - set(xhair_color,'user',new_color); - set(nii_view.axi_xhair.lx,'color',new_color); - set(nii_view.axi_xhair.ly,'color',new_color); - set(nii_view.cor_xhair.lx,'color',new_color); - set(nii_view.cor_xhair.ly,'color',new_color); - set(nii_view.sag_xhair.lx,'color',new_color); - set(nii_view.sag_xhair.ly,'color',new_color); - - return; % update_crosshaircolor - - -%---------------------------------------------------------------- -function update_colorindex(fig, colorindex) - - if isempty(colorindex) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view.colorindex = colorindex; - setappdata(fig, 'nii_view', nii_view); - set(nii_view.handles.colorindex,'value',colorindex); - - opt.command = 'color'; - view_nii(fig, opt); - - return; % update_colorindex - - -%---------------------------------------------------------------- -function redraw_cbar(fig, colorlevel, color_map, highcolor) - - nii_view = getappdata(fig,'nii_view'); - - if isempty(nii_view.cbar_area) - return; - end - - colorindex = nii_view.colorindex; - - if isempty(highcolor) - num_highcolor = 0; - else - num_highcolor = size(highcolor,1); - end - - if isempty(colorlevel) - colorlevel=256; - end - - if colorindex == 1 - colorlevel = size(color_map, 1); - end - -% level = colorlevel + num_highcolor; - level = colorlevel; - - cbar_image = [1:level]'; - - cbar_area = nii_view.cbar_area; - - % useimagesc follows axial image - % - if strcmpi(get(nii_view.handles.axial_image,'cdatamap'),'scaled') - useimagesc = 1; - else - useimagesc = 0; - end - - niiclass = class(nii_view.nii.img); - - delete(nii_view.handles.cbar_image); - delete(nii_view.handles.cbar_axes); - delete(nii_view.handles.cbarminmax_axes); - - [nii_view.handles.cbar_axes nii_view.handles.cbarminmax_axes] = ... - create_cbar_axes(fig, cbar_area, []); - - nii_view.handles.cbar_image = plot_cbar(fig, ... - nii_view.handles.cbar_axes, nii_view.handles.cbarminmax_axes, ... - nii_view.cbarminmax, level, nii_view.handles, useimagesc, ... - colorindex, color_map, colorlevel, highcolor, niiclass, ... - nii_view.numscan, []); - - setappdata(fig, 'nii_view', nii_view); - - return; % redraw_cbar - - -%---------------------------------------------------------------- -function update_buttondown(fig, setbuttondown) - - if isempty(setbuttondown) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii_view.buttondown = setbuttondown; - setappdata(fig, 'nii_view', nii_view); - - return; % update_buttondown - - -%---------------------------------------------------------------- -function update_cbarminmax(fig, cbarminmax) - - if isempty(cbarminmax) - return; - end - - nii_view = getappdata(fig, 'nii_view'); - - if ~isfield(nii_view.handles, 'cbarminmax_axes') - return; - end - - nii_view.cbarminmax = cbarminmax; - setappdata(fig, 'nii_view', nii_view); - - axes(nii_view.handles.cbarminmax_axes); - - plot([0 0], cbarminmax, 'w'); - axis tight; - - set(nii_view.handles.cbarminmax_axes,'YDir','normal', ... - 'XLimMode','manual','YLimMode','manual','YColor',[0 0 0], ... - 'XColor',[0 0 0],'xtick',[],'YAxisLocation','right'); - - ylim = get(nii_view.handles.cbar_axes,'ylim'); - ylimb = get(nii_view.handles.cbarminmax_axes,'ylim'); - ytickb = get(nii_view.handles.cbarminmax_axes,'ytick'); - ytick=(ylim(2)-ylim(1))*(ytickb-ylimb(1))/(ylimb(2)-ylimb(1))+ylim(1); - - axes(nii_view.handles.cbar_axes); - - set(nii_view.handles.cbar_axes,'YDir','normal','XLimMode','manual', ... - 'YLimMode','manual','YColor',[0 0 0],'XColor',[0 0 0],'xtick',[], ... - 'YAxisLocation','right','ylim',ylim,'ytick',ytick,'yticklabel',''); - - return; % update_cbarminmax - - -%---------------------------------------------------------------- -function update_highcolor(fig, highcolor, colorlevel) - - nii_view = getappdata(fig,'nii_view'); - - if ischar(highcolor) & (isempty(colorlevel) | nii_view.colorindex == 1) - return; - end - - if ~ischar(highcolor) - nii_view.highcolor = highcolor; - - if isempty(highcolor) - nii_view = rmfield(nii_view, 'highcolor'); - end - else - highcolor = []; - end - - if isempty(colorlevel) | nii_view.colorindex == 1 - nii_view.colorlevel = nii_view.colorlevel - size(highcolor,1); - else - nii_view.colorlevel = colorlevel; - end - - setappdata(fig, 'nii_view', nii_view); - - if isfield(nii_view,'color_map') - color_map = nii_view.color_map; - else - color_map = []; - end - - redraw_cbar(fig, nii_view.colorlevel, color_map, highcolor); - change_colormap(fig); - - return; % update_highcolor - - -%---------------------------------------------------------------- -function update_colormap(fig, color_map) - - if ischar(color_map) - return; - end - - nii_view = getappdata(fig,'nii_view'); - nii = nii_view.nii; - minvalue = nii_view.minvalue; - - if isempty(color_map) - if minvalue < 0 - colorindex = 2; - else - colorindex = 3; - end - - nii_view = rmfield(nii_view, 'color_map'); - setappdata(fig,'nii_view',nii_view); - update_colorindex(fig, colorindex); - return; - else - colorindex = 1; - nii_view.color_map = color_map; - nii_view.colorindex = colorindex; - setappdata(fig,'nii_view',nii_view); - set(nii_view.handles.colorindex,'value',colorindex); - end - - colorlevel = nii_view.colorlevel; - - if isfield(nii_view, 'highcolor') - highcolor = nii_view.highcolor; - else - highcolor = []; - end - - redraw_cbar(fig, colorlevel, color_map, highcolor); - change_colormap(fig); - - opt.enablecontrast = 0; - update_enable(fig, opt); - - return; % update_colormap - - -%---------------------------------------------------------------- -function status = get_status(h); - - nii_view = getappdata(h,'nii_view'); - - status.fig = h; - status.area = nii_view.area; - - if isempty(nii_view.cbar_area) - status.usecolorbar = 0; - else - status.usecolorbar = 1; - width = status.area(3) / 0.9; - status.area(3) = width; - end - - if strcmpi(get(nii_view.handles.imval,'visible'), 'on') - status.usepanel = 1; - else - status.usepanel = 0; - end - - if get(nii_view.handles.xhair,'value') == 1 - status.usecrosshair = 1; - else - status.usecrosshair = 0; - end - - status.usestretch = nii_view.usestretch; - - if strcmpi(get(nii_view.handles.axial_image,'cdatamapping'), 'direct') - status.useimagesc = 0; - else - status.useimagesc = 1; - end - - status.useinterp = nii_view.useinterp; - - if get(nii_view.handles.coord,'value') == 1 - status.unit = 'vox'; - elseif get(nii_view.handles.coord,'value') == 2 - status.unit = 'mm'; - elseif get(nii_view.handles.coord,'value') == 3 - status.unit = 'tal'; - end - - status.viewpoint = get(nii_view.handles.impos,'value'); - status.scanid = nii_view.scanid; - status.intensity = get(nii_view.handles.imval,'value'); - status.colorindex = get(nii_view.handles.colorindex,'value'); - - if isfield(nii_view,'color_map') - status.colormap = nii_view.color_map; - else - status.colormap = []; - end - - status.colorlevel = nii_view.colorlevel; - - if isfield(nii_view,'highcolor') - status.highcolor = nii_view.highcolor; - else - status.highcolor = []; - end - - status.cbarminmax = nii_view.cbarminmax; - status.buttondown = nii_view.buttondown; - - return; % get_status - - -%---------------------------------------------------------------- -function [custom_color_map, colorindex] ... - = change_colormap(fig, nii, colorindex, cbarminmax) - - custom_color_map = []; - - if ~exist('nii', 'var') - nii_view = getappdata(fig,'nii_view'); - else - nii_view = nii; - end - - if ~exist('colorindex', 'var') - colorindex = get(nii_view.handles.colorindex,'value'); - end - - if ~exist('cbarminmax', 'var') - cbarminmax = nii_view.cbarminmax; - end - - if isfield(nii_view, 'highcolor') & ~isempty(nii_view.highcolor) - highcolor = nii_view.highcolor; - num_highcolor = size(highcolor,1); - else - highcolor = []; - num_highcolor = 0; - end - -% if isfield(nii_view, 'colorlevel') & ~isempty(nii_view.colorlevel) - if nii_view.colorlevel < 256 - num_color = nii_view.colorlevel; - else - num_color = 256 - num_highcolor; - end - - contrast = []; - - if colorindex == 3 % for gray - if nii_view.numscan > 1 - contrast = 1; - else - contrast = (num_color-1)*(get(nii_view.handles.contrast,'value')-1)/255+1; - contrast = floor(contrast); - end - elseif colorindex == 2 % for bipolar - if nii_view.numscan > 1 - contrast = 128; - else - contrast = get(nii_view.handles.contrast,'value'); - end - end - - if isfield(nii_view,'color_map') & ~isempty(nii_view.color_map) - color_map = nii_view.color_map; - custom_color_map = color_map; - elseif colorindex == 1 - [f p] = uigetfile('*.txt', 'Input colormap text file'); - - if p==0 - colorindex = nii_view.colorindex; - set(nii_view.handles.colorindex,'value',colorindex); - return; - end; - - try - custom_color_map = load(fullfile(p,f)); - loadfail = 0; - catch - loadfail = 1; - end - - if loadfail | isempty(custom_color_map) | size(custom_color_map,2)~=3 ... - | min(custom_color_map(:)) < 0 | max(custom_color_map(:)) > 1 - - msg = 'Colormap should be a Mx3 matrix with value between 0 and 1'; - msgbox(msg,'Error in colormap file'); - colorindex = nii_view.colorindex; - set(nii_view.handles.colorindex,'value',colorindex); - return; - end - - color_map = custom_color_map; - nii_view.color_map = color_map; - end - - switch colorindex - case {2} - color_map = bipolar(num_color, cbarminmax(1), cbarminmax(2), contrast); - case {3} - color_map = gray(num_color - contrast + 1); - case {4} - color_map = jet(num_color); - case {5} - color_map = cool(num_color); - case {6} - color_map = bone(num_color); - case {7} - color_map = hot(num_color); - case {8} - color_map = copper(num_color); - case {9} - color_map = pink(num_color); - end - - nii_view.colorindex = colorindex; - - if ~exist('nii', 'var') - setappdata(fig,'nii_view',nii_view); - end - - if colorindex == 3 - color_map = [zeros(contrast,3); color_map(2:end,:)]; - end - - if get(nii_view.handles.neg_color,'value') & isempty(highcolor) - color_map = flipud(color_map); - elseif get(nii_view.handles.neg_color,'value') & ~isempty(highcolor) - highcolor = flipud(highcolor); - end - - brightness = get(nii_view.handles.brightness,'value'); - color_map = brighten(color_map, brightness); - - color_map = [color_map; highcolor]; - - set(fig, 'colormap', color_map); - - return; % change_colormap - - -%---------------------------------------------------------------- -function move_cursor(fig) - - nii_view = getappdata(fig, 'nii_view'); - - if isempty(nii_view) - return; - end - - axi = get(nii_view.handles.axial_axes, 'pos'); - cor = get(nii_view.handles.coronal_axes, 'pos'); - sag = get(nii_view.handles.sagittal_axes, 'pos'); - curr = get(fig, 'currentpoint'); - - if curr(1) >= axi(1) & curr(1) <= axi(1)+axi(3) & ... - curr(2) >= axi(2) & curr(2) <= axi(2)+axi(4) - - curr = get(nii_view.handles.axial_axes, 'current'); - sag = curr(1,1); - cor = curr(1,2); - axi = nii_view.slices.axi; - - elseif curr(1) >= cor(1) & curr(1) <= cor(1)+cor(3) & ... - curr(2) >= cor(2) & curr(2) <= cor(2)+cor(4) - - curr = get(nii_view.handles.coronal_axes, 'current'); - sag = curr(1,1); - cor = nii_view.slices.cor; - axi = curr(1,2); - - elseif curr(1) >= sag(1) & curr(1) <= sag(1)+sag(3) & ... - curr(2) >= sag(2) & curr(2) <= sag(2)+sag(4) - - curr = get(nii_view.handles.sagittal_axes, 'current'); - - sag = nii_view.slices.sag; - cor = curr(1,1); - axi = curr(1,2); - - else - - set(nii_view.handles.imvalcur,'String',' '); - set(nii_view.handles.imposcur,'String',' '); - return; - - end - - sag = round(sag); - cor = round(cor); - axi = round(axi); - - if sag < 1 - sag = 1; - elseif sag > nii_view.dims(1) - sag = nii_view.dims(1); - end - - if cor < 1 - cor = 1; - elseif cor > nii_view.dims(2) - cor = nii_view.dims(2); - end - - if axi < 1 - axi = 1; - elseif axi > nii_view.dims(3) - axi = nii_view.dims(3); - end - - if 0 % isfield(nii_view, 'disp') - img = nii_view.disp; - else - img = nii_view.nii.img; - end - - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(img(sag,cor,axi,1,nii_view.scanid)) double(img(sag,cor,axi,2,nii_view.scanid)) double(img(sag,cor,axi,3,nii_view.scanid))]; - set(nii_view.handles.imvalcur,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [R G B]; - set(nii_view.handles.imvalcur,'String',sprintf('%7.4g %7.4g %7.4g',imgvalue)); - else - imgvalue = double(img(sag,cor,axi,nii_view.scanid)); - - if isnan(imgvalue) | imgvalue > nii_view.cbarminmax(2) - imgvalue = 0; - end - - set(nii_view.handles.imvalcur,'String',sprintf('%.6g',imgvalue)); - end - - nii_view.slices.sag = sag; - nii_view.slices.cor = cor; - nii_view.slices.axi = axi; - - nii_view = update_imgXYZ(nii_view); - - if get(nii_view.handles.coord,'value') == 1, - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - elseif get(nii_view.handles.coord,'value') == 2, - sag = nii_view.imgXYZ.mm(1); - cor = nii_view.imgXYZ.mm(2); - axi = nii_view.imgXYZ.mm(3); - elseif get(nii_view.handles.coord,'value') == 3, - sag = nii_view.imgXYZ.tal(1); - cor = nii_view.imgXYZ.tal(2); - axi = nii_view.imgXYZ.tal(3); - end - - if get(nii_view.handles.coord,'value') == 1, - string = sprintf('%7.0f %7.0f %7.0f',sag,cor,axi); - else - string = sprintf('%7.1f %7.1f %7.1f',sag,cor,axi); - end; - - set(nii_view.handles.imposcur,'String',string); - - return; % move_cursor - - -%---------------------------------------------------------------- -function change_scan(hdl_str) - - fig = gcbf; - nii_view = getappdata(fig,'nii_view'); - - if strcmpi(hdl_str, 'edit_change_scan') % edit - hdl = nii_view.handles.contrast_def; - setscanid = round(str2num(get(hdl, 'string'))); - else % slider - hdl = nii_view.handles.contrast; - setscanid = round(get(hdl, 'value')); - end - - update_scanid(fig, setscanid); - - return; % change_scan - - -%---------------------------------------------------------------- -function val = scale_in(val, minval, maxval, range) - - % scale value into range - % - val = range*(double(val)-double(minval))/(double(maxval)-double(minval))+1; - - return; % scale_in - - -%---------------------------------------------------------------- -function val = scale_out(val, minval, maxval, range) - - % according to [minval maxval] and range of color levels (e.g. 199) - % scale val back from any thing between 1~256 to a small number that - % is corresonding to [minval maxval]. - % - val = (double(val)-1)*(double(maxval)-double(minval))/range+double(minval); - - return; % scale_out - diff --git a/reg-test/matlab_tests/NIfTI_20140122/view_nii_menu.m b/reg-test/matlab_tests/NIfTI_20140122/view_nii_menu.m deleted file mode 100644 index 2269c937..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/view_nii_menu.m +++ /dev/null @@ -1,480 +0,0 @@ -% Imbed Zoom, Interp, and Info menu to view_nii window. -% -% Usage: view_nii_menu(fig); -% - -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -%-------------------------------------------------------------------- -function menu_hdl = view_nii_menu(fig, varargin) - - if isnumeric(fig) - menu_hdl = init(fig); - return; - end - - menu_hdl = []; - - switch fig - case 'interp' - if nargin > 1 - fig = varargin{1}; - else - fig = gcbf; - end - - nii_menu = getappdata(fig, 'nii_menu'); - interp_on_state = get(nii_menu.Minterp,'Userdata'); - - if (interp_on_state == 1) - opt.useinterp = 1; - view_nii(fig,opt); - set(nii_menu.Minterp,'Userdata',0,'Label','Interp off'); - reset_zoom(fig); - else - opt.useinterp = 0; - view_nii(fig,opt); - set(nii_menu.Minterp,'Userdata',1,'Label','Interp on'); - reset_zoom(fig); - end - case 'reset_zoom' - if nargin > 1 - fig = varargin{1}; - else - fig = gcbf; - end - - reset_zoom(fig); - case 'orient' - orient; - case 'editvox' - editvox; - case 'img_info' - img_info; - case 'img_hist' - img_hist; - case 'save_disp' - save_disp; - end - - return % view_nii_menu - - -%-------------------------------------------------------------------- -function menu_hdl = init(fig) - - % search for edit, view menu - % - nii_menu.Mfile = []; - nii_menu.Medit = []; - nii_menu.Mview = []; - menuitems = findobj(fig, 'type', 'uimenu'); - - for i=1:length(menuitems) - filelabel = get(menuitems(i),'label'); - - if strcmpi(strrep(filelabel, '&', ''), 'file') - nii_menu.Mfile = menuitems(i); - end - - editlabel = get(menuitems(i),'label'); - - if strcmpi(strrep(editlabel, '&', ''), 'edit') - nii_menu.Medit = menuitems(i); - end - - viewlabel = get(menuitems(i),'label'); - - if strcmpi(strrep(viewlabel, '&', ''), 'view') - nii_menu.Mview = menuitems(i); - end - end - - set(fig, 'menubar', 'none'); - - if isempty(nii_menu.Mfile) - nii_menu.Mfile = uimenu('Parent',fig, ... - 'Label','File'); - - nii_menu.Mfile_save = uimenu('Parent',nii_menu.Mfile, ... - 'Label','Save displayed image as ...', ... - 'Callback','view_nii_menu(''save_disp'');'); - else - nii_menu.Mfile_save = uimenu('Parent',nii_menu.Mfile, ... - 'Label','Save displayed image as ...', ... - 'separator','on', ... - 'Callback','view_nii_menu(''save_disp'');'); - end - - if isempty(nii_menu.Medit) - nii_menu.Medit = uimenu('Parent',fig, ... - 'Label','Edit'); - - nii_menu.Medit_orient = uimenu('Parent',nii_menu.Medit, ... - 'Label','Convert to RAS orientation', ... - 'Callback','view_nii_menu(''orient'');'); - - nii_menu.Medit_editvox = uimenu('Parent',nii_menu.Medit, ... - 'Label','Edit voxel value at crosshair', ... - 'Callback','view_nii_menu(''editvox'');'); - else - nii_menu.Medit_orient = uimenu('Parent',nii_menu.Medit, ... - 'Label','Convert to RAS orientation', ... - 'separator','on', ... - 'Callback','view_nii_menu(''orient'');'); - - nii_menu.Medit_editvox = uimenu('Parent',nii_menu.Medit, ... - 'Label','Edit voxel value at crosshair', ... - 'Callback','view_nii_menu(''editvox'');'); - end - - if isempty(nii_menu.Mview) - nii_menu.Mview = uimenu('Parent',fig, ... - 'Label','View'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Image Information', ... - 'Callback','view_nii_menu(''img_info'');'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Volume Histogram', ... - 'Callback','view_nii_menu(''img_hist'');'); - else - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Image Information', ... - 'separator','on', ... - 'Callback','view_nii_menu(''img_info'');'); - - nii_menu.Mview_info = uimenu('Parent',nii_menu.Mview, ... - 'Label','Volume Histogram', ... - 'Callback','view_nii_menu(''img_hist'');'); - end - - nii_menu.Mzoom = rri_zoom_menu(fig); - - nii_menu.Minterp = uimenu('Parent',fig, ... - 'Label','Interp on', ... - 'Userdata', 1, ... - 'Callback','view_nii_menu(''interp'');'); - - setappdata(fig,'nii_menu',nii_menu); - menu_hdl = nii_menu.Minterp; - - return % init - - -%---------------------------------------------------------------- -function reset_zoom(fig) - - old_handle_vis = get(fig, 'HandleVisibility'); - set(fig, 'HandleVisibility', 'on'); - - nii_view = getappdata(fig, 'nii_view'); - nii_menu = getappdata(fig, 'nii_menu'); - - set(nii_menu.Mzoom,'Userdata',1,'Label','Zoom on'); - set(fig,'pointer','arrow'); - zoom off; - - axes(nii_view.handles.axial_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; - - axes(nii_view.handles.coronal_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; - - axes(nii_view.handles.sagittal_axes); - setappdata(get(gca,'zlabel'), 'ZOOMAxesData', ... - [get(gca, 'xlim') get(gca, 'ylim')]) -% zoom reset; - % zoom getlimits; - zoom out; - - set(fig, 'HandleVisibility', old_handle_vis); - - return; % reset_zoom - - -%---------------------------------------------------------------- -function img_info - - nii_view = getappdata(gcbf, 'nii_view'); - hdr = nii_view.nii.hdr; - - max_value = num2str(double(max(nii_view.nii.img(:)))); - min_value = num2str(double(min(nii_view.nii.img(:)))); - - dim = sprintf('%d %d %d', double(hdr.dime.dim(2:4))); - vox = sprintf('%.3f %.3f %.3f', double(hdr.dime.pixdim(2:4))); - - if double(hdr.dime.datatype) == 1 - type = '1-bit binary'; - elseif double(hdr.dime.datatype) == 2 - type = '8-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 4 - type = '16-bit signed integer'; - elseif double(hdr.dime.datatype) == 8 - type = '32-bit signed integer'; - elseif double(hdr.dime.datatype) == 16 - type = '32-bit single float'; - elseif double(hdr.dime.datatype) == 64 - type = '64-bit double precision'; - elseif double(hdr.dime.datatype) == 128 - type = '24-bit RGB true color'; - elseif double(hdr.dime.datatype) == 256 - type = '8-bit signed integer'; - elseif double(hdr.dime.datatype) == 511 - type = '96-bit RGB true color'; - elseif double(hdr.dime.datatype) == 512 - type = '16-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 768 - type = '32-bit unsigned integer'; - elseif double(hdr.dime.datatype) == 1024 - type = '64-bit signed integer'; - elseif double(hdr.dime.datatype) == 1280 - type = '64-bit unsigned integer'; - end - - msg = {}; - msg = [msg {''}]; - msg = [msg {['Dimension: [', dim, ']']}]; - msg = [msg {''}]; - msg = [msg {['Voxel Size: [', vox, ']']}]; - msg = [msg {''}]; - msg = [msg {['Data Type: [', type, ']']}]; - msg = [msg {''}]; - msg = [msg {['Max Value: [', max_value, ']']}]; - msg = [msg {''}]; - msg = [msg {['Min Value: [', min_value, ']']}]; - msg = [msg {''}]; - - if isfield(nii_view.nii, 'fileprefix') - if isfield(nii_view.nii, 'filetype') & nii_view.nii.filetype == 2 - msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.nii]']}]; - msg = [msg {''}]; - elseif isfield(nii_view.nii, 'filetype') - msg = [msg {['File Name: [', nii_view.nii.fileprefix, '.img]']}]; - msg = [msg {''}]; - else - msg = [msg {['File Prefix: [', nii_view.nii.fileprefix, ']']}]; - msg = [msg {''}]; - end - end - - h = msgbox(msg, 'Image Information', 'modal'); - set(h,'color',[1 1 1]); - - return; % img_info - - -%---------------------------------------------------------------- -function orient - - fig = gcbf; - nii_view = getappdata(fig, 'nii_view'); - nii = nii_view.nii; - - if ~isempty(nii_view.bgimg) - msg = 'You can not modify an overlay image'; - h = msgbox(msg, 'Error', 'modal'); - return; - end - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - [nii orient] = rri_orient(nii); - - if isequal(orient, [1 2 3]) % do nothing - set(fig,'Pointer',old_pointer); - return; - end - - oldopt = view_nii(fig); - opt.command = 'updatenii'; - opt.usecolorbar = oldopt.usecolorbar; - opt.usepanel = oldopt.usepanel; - opt.usecrosshair = oldopt.usecrosshair; - opt.usestretch = oldopt.usestretch; - opt.useimagesc = oldopt.useimagesc; - opt.useinterp = oldopt.useinterp; - opt.setarea = oldopt.area; - opt.setunit = oldopt.unit; - opt.setviewpoint = oldopt.viewpoint; - opt.setscanid = oldopt.scanid; - opt.setcbarminmax = oldopt.cbarminmax; - opt.setcolorindex = oldopt.colorindex; - opt.setcolormap = oldopt.colormap; - opt.setcolorlevel = oldopt.colorlevel; - - if isfield(oldopt,'highcolor') - opt.sethighcolor = oldopt.highcolor; - end - - view_nii(fig, nii, opt); - set(fig,'Pointer',old_pointer); - reset_zoom(fig); - - return; % orient - - -%---------------------------------------------------------------- -function editvox - - fig = gcbf; - nii_view = getappdata(fig, 'nii_view'); - - if ~isempty(nii_view.bgimg) - msg = 'You can not modify an overlay image'; - h = msgbox(msg, 'Error', 'modal'); - return; - end - - nii = nii_view.nii; - oldopt = view_nii(fig); - sag = nii_view.imgXYZ.vox(1); - cor = nii_view.imgXYZ.vox(2); - axi = nii_view.imgXYZ.vox(3); - - if nii_view.nii.hdr.dime.datatype == 128 - imgvalue = [double(nii.img(sag,cor,axi,1,nii_view.scanid)) double(nii.img(sag,cor,axi,2,nii_view.scanid)) double(nii.img(sag,cor,axi,3,nii_view.scanid))]; - init_val = sprintf('%7.4g %7.4g %7.4g',imgvalue); - elseif nii_view.nii.hdr.dime.datatype == 511 - R = double(nii.img(sag,cor,axi,1,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - G = double(nii.img(sag,cor,axi,2,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - B = double(nii.img(sag,cor,axi,3,nii_view.scanid)) * (nii_view.nii.hdr.dime.glmax - ... - nii_view.nii.hdr.dime.glmin) + nii_view.nii.hdr.dime.glmin; - imgvalue = [R G B]; - init_val = sprintf('%7.4g %7.4g %7.4g',imgvalue); - else - imgvalue = double(nii.img(sag,cor,axi,nii_view.scanid)); - init_val = sprintf('%.6g',imgvalue); - end - - old_pointer = get(fig,'Pointer'); - set(fig,'Pointer','watch'); - - repeat = 1; - while repeat - if nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511 - init_val = inputdlg({'Replace the current voxel values with 3 new numbers:'}, ... - 'Edit voxel value at crosshair', 1, {num2str(init_val)}); - else - init_val = inputdlg({'Replace the current voxel value with 1 new number:'}, ... - 'Edit voxel value at crosshair', 1, {num2str(init_val)}); - end - - if isempty(init_val) - set(fig,'Pointer',old_pointer); - return - end - - imgvalue = str2num(init_val{1}); - - if ( (nii_view.nii.hdr.dime.datatype == 128 | nii_view.nii.hdr.dime.datatype == 511) ... - & length(imgvalue) ~= 3 ) | ... - ( (nii_view.nii.hdr.dime.datatype ~= 128 & nii_view.nii.hdr.dime.datatype ~= 511) ... - & length(imgvalue) ~= 1 ) - % do nothing - else - repeat = 0; - end - end - - if nii_view.nii.hdr.dime.datatype == 128 - nii.img(sag,cor,axi,1,nii_view.scanid) = imgvalue(1); - nii.img(sag,cor,axi,2,nii_view.scanid) = imgvalue(2); - nii.img(sag,cor,axi,3,nii_view.scanid) = imgvalue(3); - elseif nii_view.nii.hdr.dime.datatype == 511 - nii.img(sag,cor,axi,1,nii_view.scanid) = (imgvalue(1) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - nii.img(sag,cor,axi,2,nii_view.scanid) = (imgvalue(2) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - nii.img(sag,cor,axi,3,nii_view.scanid) = (imgvalue(3) - nii_view.nii.hdr.dime.glmin) ... - / (nii_view.nii.hdr.dime.glmax - nii_view.nii.hdr.dime.glmin); - else - nii.img(sag,cor,axi,nii_view.scanid) = imgvalue; - end - - opt.command = 'updatenii'; - opt.usecolorbar = oldopt.usecolorbar; - opt.usepanel = oldopt.usepanel; - opt.usecrosshair = oldopt.usecrosshair; - opt.usestretch = oldopt.usestretch; - opt.useimagesc = oldopt.useimagesc; - opt.useinterp = oldopt.useinterp; - opt.setarea = oldopt.area; - opt.setunit = oldopt.unit; - opt.setviewpoint = oldopt.viewpoint; - opt.setscanid = oldopt.scanid; - opt.setcbarminmax = oldopt.cbarminmax; - opt.setcolorindex = oldopt.colorindex; - opt.setcolormap = oldopt.colormap; - opt.setcolorlevel = oldopt.colorlevel; - - if isfield(oldopt,'highcolor') - opt.sethighcolor = oldopt.highcolor; - end - - view_nii(fig, nii, opt); - set(fig,'Pointer',old_pointer); - reset_zoom(fig); - - return; % editvox - - -%---------------------------------------------------------------- -function save_disp - - [filename pathname] = uiputfile('*.*', 'Save displayed image as (*.nii or *.img)'); - - if isequal(filename,0) | isequal(pathname,0) - return; - else - out_imgfile = fullfile(pathname, filename); % original image file - end - - old_pointer = get(gcbf,'Pointer'); - set(gcbf,'Pointer','watch'); - - nii_view = getappdata(gcbf, 'nii_view'); - nii = nii_view.nii; - - try - save_nii(nii, out_imgfile); - catch - msg = 'File can not be saved.'; - msgbox(msg, 'File write error', 'modal'); - end - - set(gcbf,'Pointer',old_pointer); - - return; % save_disp - - -%---------------------------------------------------------------- -function img_hist - - nii_view = getappdata(gcbf, 'nii_view'); - N = hist(double(nii_view.nii.img(:)),256); - x = linspace(double(min(nii_view.nii.img(:))), double(max(nii_view.nii.img(:))), 256); - figure;bar(x,N); - set(gcf, 'number', 'off', 'name', 'Volume Histogram'); - set(gcf, 'windowstyle', 'modal'); % no zoom ... - - xspan = max(x) - min(x) + 1; - yspan = max(N) + 1; - set(gca, 'xlim', [min(x)-xspan/20, max(x)+xspan/20]); - set(gca, 'ylim', [-yspan/20, max(N)+yspan/20]); - - return; % img_hist - diff --git a/reg-test/matlab_tests/NIfTI_20140122/xform_nii.m b/reg-test/matlab_tests/NIfTI_20140122/xform_nii.m deleted file mode 100644 index 7252a7c5..00000000 --- a/reg-test/matlab_tests/NIfTI_20140122/xform_nii.m +++ /dev/null @@ -1,521 +0,0 @@ -% internal function - -% 'xform_nii.m' is an internal function called by "load_nii.m", so -% you do not need run this program by yourself. It does simplified -% NIfTI sform/qform affine transform, and supports some of the -% affine transforms, including translation, reflection, and -% orthogonal rotation (N*90 degree). -% -% For other affine transforms, e.g. any degree rotation, shearing -% etc. you will have to use the included 'reslice_nii.m' program -% to reslice the image volume. 'reslice_nii.m' is not called by -% any other program, and you have to run 'reslice_nii.m' explicitly -% for those NIfTI files that you want to reslice them. -% -% Since 'xform_nii.m' does not involve any interpolation or any -% slice change, the original image volume is supposed to be -% untouched, although it is translated, reflected, or even -% orthogonally rotated, based on the affine matrix in the -% NIfTI header. -% -% However, the affine matrix in the header of a lot NIfTI files -% contain slightly non-orthogonal rotation. Therefore, optional -% input parameter 'tolerance' is used to allow some distortion -% in the loaded image for any non-orthogonal rotation or shearing -% of NIfTI affine matrix. If you set 'tolerance' to 0, it means -% that you do not allow any distortion. If you set 'tolerance' to -% 1, it means that you do not care any distortion. The image will -% fail to be loaded if it can not be tolerated. The tolerance will -% be set to 0.1 (10%), if it is default or empty. -% -% Because 'reslice_nii.m' has to perform 3D interpolation, it can -% be slow depending on image size and affine matrix in the header. -% -% After you perform the affine transform, the 'nii' structure -% generated from 'xform_nii.m' or new NIfTI file created from -% 'reslice_nii.m' will be in RAS orientation, i.e. X axis from -% Left to Right, Y axis from Posterior to Anterior, and Z axis -% from Inferior to Superior. -% -% NOTE: This function should be called immediately after load_nii. -% -% Usage: [ nii ] = xform_nii(nii, [tolerance], [preferredForm]) -% -% nii - NIFTI structure (returned from load_nii) -% -% tolerance (optional) - distortion allowed for non-orthogonal rotation -% or shearing in NIfTI affine matrix. It will be set to 0.1 (10%), -% if it is default or empty. -% -% preferredForm (optional) - selects which transformation from voxels -% to RAS coordinates; values are s,q,S,Q. Lower case s,q indicate -% "prefer sform or qform, but use others if preferred not present". -% Upper case indicate the program is forced to use the specificied -% tranform or fail loading. 'preferredForm' will be 's', if it is -% default or empty. - Jeff Gunter -% -% NIFTI data format can be found on: http://nifti.nimh.nih.gov -% -% - Jimmy Shen (jimmy@rotman-baycrest.on.ca) -% -function nii = xform_nii(nii, tolerance, preferredForm) - - % save a copy of the header as it was loaded. This is the - % header before any sform, qform manipulation is done. - % - nii.original.hdr = nii.hdr; - - if ~exist('tolerance','var') | isempty(tolerance) - tolerance = 0.1; - elseif(tolerance<=0) - tolerance = eps; - end - - if ~exist('preferredForm','var') | isempty(preferredForm) - preferredForm= 's'; % Jeff - end - - % if scl_slope field is nonzero, then each voxel value in the - % dataset should be scaled as: y = scl_slope * x + scl_inter - % I bring it here because hdr will be modified by change_hdr. - % - if nii.hdr.dime.scl_slope ~= 0 & ... - ismember(nii.hdr.dime.datatype, [2,4,8,16,64,256,512,768]) & ... - (nii.hdr.dime.scl_slope ~= 1 | nii.hdr.dime.scl_inter ~= 0) - - nii.img = ... - nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; - - if nii.hdr.dime.datatype == 64 - - nii.hdr.dime.datatype = 64; - nii.hdr.dime.bitpix = 64; - else - nii.img = single(nii.img); - - nii.hdr.dime.datatype = 16; - nii.hdr.dime.bitpix = 32; - end - - nii.hdr.dime.glmax = max(double(nii.img(:))); - nii.hdr.dime.glmin = min(double(nii.img(:))); - - % set scale to non-use, because it is applied in xform_nii - % - nii.hdr.dime.scl_slope = 0; - - end - - % However, the scaling is to be ignored if datatype is DT_RGB24. - - % If datatype is a complex type, then the scaling is to be applied - % to both the real and imaginary parts. - % - if nii.hdr.dime.scl_slope ~= 0 & ... - ismember(nii.hdr.dime.datatype, [32,1792]) - - nii.img = ... - nii.hdr.dime.scl_slope * double(nii.img) + nii.hdr.dime.scl_inter; - - if nii.hdr.dime.datatype == 32 - nii.img = single(nii.img); - end - - nii.hdr.dime.glmax = max(double(nii.img(:))); - nii.hdr.dime.glmin = min(double(nii.img(:))); - - % set scale to non-use, because it is applied in xform_nii - % - nii.hdr.dime.scl_slope = 0; - - end - - % There is no need for this program to transform Analyze data - % - if nii.filetype == 0 & exist([nii.fileprefix '.mat'],'file') - load([nii.fileprefix '.mat']); % old SPM affine matrix - R=M(1:3,1:3); - T=M(1:3,4); - T=R*ones(3,1)+T; - M(1:3,4)=T; - nii.hdr.hist.qform_code=0; - nii.hdr.hist.sform_code=1; - nii.hdr.hist.srow_x=M(1,:); - nii.hdr.hist.srow_y=M(2,:); - nii.hdr.hist.srow_z=M(3,:); - elseif nii.filetype == 0 - nii.hdr.hist.rot_orient = []; - nii.hdr.hist.flip_orient = []; - return; % no sform/qform for Analyze format - end - - hdr = nii.hdr; - - [hdr,orient]=change_hdr(hdr,tolerance,preferredForm); - - % flip and/or rotate image data - % - if ~isequal(orient, [1 2 3]) - - old_dim = hdr.dime.dim([2:4]); - - % More than 1 time frame - % - if ndims(nii.img) > 3 - pattern = 1:prod(old_dim); - else - pattern = []; - end - - if ~isempty(pattern) - pattern = reshape(pattern, old_dim); - end - - % calculate for rotation after flip - % - rot_orient = mod(orient + 2, 3) + 1; - - % do flip: - % - flip_orient = orient - rot_orient; - - for i = 1:3 - if flip_orient(i) - if ~isempty(pattern) - pattern = flipdim(pattern, i); - else - nii.img = flipdim(nii.img, i); - end - end - end - - % get index of orient (rotate inversely) - % - [tmp rot_orient] = sort(rot_orient); - - new_dim = old_dim; - new_dim = new_dim(rot_orient); - hdr.dime.dim([2:4]) = new_dim; - - new_pixdim = hdr.dime.pixdim([2:4]); - new_pixdim = new_pixdim(rot_orient); - hdr.dime.pixdim([2:4]) = new_pixdim; - - % re-calculate originator - % - tmp = hdr.hist.originator([1:3]); - tmp = tmp(rot_orient); - flip_orient = flip_orient(rot_orient); - - for i = 1:3 - if flip_orient(i) & ~isequal(tmp(i), 0) - tmp(i) = new_dim(i) - tmp(i) + 1; - end - end - - hdr.hist.originator([1:3]) = tmp; - hdr.hist.rot_orient = rot_orient; - hdr.hist.flip_orient = flip_orient; - - % do rotation: - % - if ~isempty(pattern) - pattern = permute(pattern, rot_orient); - pattern = pattern(:); - - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... - hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - - tmp = reshape(nii.img(:,:,:,1), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,1) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - - tmp = reshape(nii.img(:,:,:,2), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,2) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - tmp = reshape(nii.img(:,:,:,3), [prod(new_dim) hdr.dime.dim(5:8)]); - tmp = tmp(pattern, :); - nii.img(:,:,:,3) = reshape(tmp, [new_dim hdr.dime.dim(5:8)]); - end - - else - nii.img = reshape(nii.img, [prod(new_dim) hdr.dime.dim(5:8)]); - nii.img = nii.img(pattern, :); - nii.img = reshape(nii.img, [new_dim hdr.dime.dim(5:8)]); - end - else - if hdr.dime.datatype == 32 | hdr.dime.datatype == 1792 | ... - hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - - nii.img(:,:,:,1) = permute(nii.img(:,:,:,1), rot_orient); - nii.img(:,:,:,2) = permute(nii.img(:,:,:,2), rot_orient); - - if hdr.dime.datatype == 128 | hdr.dime.datatype == 511 - nii.img(:,:,:,3) = permute(nii.img(:,:,:,3), rot_orient); - end - else - nii.img = permute(nii.img, rot_orient); - end - end - else - hdr.hist.rot_orient = []; - hdr.hist.flip_orient = []; - end - - nii.hdr = hdr; - - return; % xform_nii - - -%----------------------------------------------------------------------- -function [hdr, orient] = change_hdr(hdr, tolerance, preferredForm) - - orient = [1 2 3]; - affine_transform = 1; - - % NIFTI can have both sform and qform transform. This program - % will check sform_code prior to qform_code by default. - % - % If user specifys "preferredForm", user can then choose the - % priority. - Jeff - % - useForm=[]; % Jeff - - if isequal(preferredForm,'S') - if isequal(hdr.hist.sform_code,0) - error('User requires sform, sform not set in header'); - else - useForm='s'; - end - end % Jeff - - if isequal(preferredForm,'Q') - if isequal(hdr.hist.qform_code,0) - error('User requires qform, qform not set in header'); - else - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'s') - if hdr.hist.sform_code > 0 - useForm='s'; - elseif hdr.hist.qform_code > 0 - useForm='q'; - end - end % Jeff - - if isequal(preferredForm,'q') - if hdr.hist.qform_code > 0 - useForm='q'; - elseif hdr.hist.sform_code > 0 - useForm='s'; - end - end % Jeff - - if isequal(useForm,'s') - R = [hdr.hist.srow_x(1:3) - hdr.hist.srow_y(1:3) - hdr.hist.srow_z(1:3)]; - - T = [hdr.hist.srow_x(4) - hdr.hist.srow_y(4) - hdr.hist.srow_z(4)]; - - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - hdr.hist.old_affine = [ [R;[0 0 0]] [T;1] ]; - R_sort = sort(abs(R(:))); - R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0; - hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ]; - - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; - msg = [msg 'found inside the affine matrix' char(10)]; - msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; - msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; - msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; - msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; - msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; - msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; - msg = [msg ' without applying any affine geometric transformation or' char(10)]; - msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; - msg = [msg ' to do some image processing regardless of image orientation' char(10)]; - msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; - msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; - msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; - msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; - msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; - error(msg); - end - end - - elseif isequal(useForm,'q') - b = hdr.hist.quatern_b; - c = hdr.hist.quatern_c; - d = hdr.hist.quatern_d; - - if 1.0-(b*b+c*c+d*d) < 0 - if abs(1.0-(b*b+c*c+d*d)) < 1e-5 - a = 0; - else - error('Incorrect quaternion values in this NIFTI data.'); - end - else - a = sqrt(1.0-(b*b+c*c+d*d)); - end - - qfac = hdr.dime.pixdim(1); - if qfac==0, qfac = 1; end - i = hdr.dime.pixdim(2); - j = hdr.dime.pixdim(3); - k = qfac * hdr.dime.pixdim(4); - - R = [a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c - 2*b*c+2*a*d a*a+c*c-b*b-d*d 2*c*d-2*a*b - 2*b*d-2*a*c 2*c*d+2*a*b a*a+d*d-c*c-b*b]; - - T = [hdr.hist.qoffset_x - hdr.hist.qoffset_y - hdr.hist.qoffset_z]; - - % qforms are expected to generate rotation matrices R which are - % det(R) = 1; we'll make sure that happens. - % - % now we make the same checks as were done above for sform data - % BUT we do it on a transform that is in terms of voxels not mm; - % after we figure out the angles and squash them to closest - % rectilinear direction. After that, the voxel sizes are then - % added. - % - % This part is modified by Jeff Gunter. - % - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - - % det(R) == 0 is not a common trigger for this --- - % R(find(R)) is a list of non-zero elements in R; if that - % is straight (not oblique) then it should be the same as - % columnwise summation. Could just as well have checked the - % lengths of R(find(R)) and sum(R)' (which should be 3) - % - hdr.hist.old_affine = [ [R * diag([i j k]);[0 0 0]] [T;1] ]; - R_sort = sort(abs(R(:))); - R( find( abs(R) < tolerance*min(R_sort(end-2:end)) ) ) = 0; - R = R * diag([i j k]); - hdr.hist.new_affine = [ [R;[0 0 0]] [T;1] ]; - - if det(R) == 0 | ~isequal(R(find(R)), sum(R)') - msg = [char(10) char(10) ' Non-orthogonal rotation or shearing ']; - msg = [msg 'found inside the affine matrix' char(10)]; - msg = [msg ' in this NIfTI file. You have 3 options:' char(10) char(10)]; - msg = [msg ' 1. Using included ''reslice_nii.m'' program to reslice the NIfTI' char(10)]; - msg = [msg ' file. I strongly recommand this, because it will not cause' char(10)]; - msg = [msg ' negative effect, as long as you remember not to do slice' char(10)]; - msg = [msg ' time correction after using ''reslice_nii.m''.' char(10) char(10)]; - msg = [msg ' 2. Using included ''load_untouch_nii.m'' program to load image' char(10)]; - msg = [msg ' without applying any affine geometric transformation or' char(10)]; - msg = [msg ' voxel intensity scaling. This is only for people who want' char(10)]; - msg = [msg ' to do some image processing regardless of image orientation' char(10)]; - msg = [msg ' and to save data back with the same NIfTI header.' char(10) char(10)]; - msg = [msg ' 3. Increasing the tolerance to allow more distortion in loaded' char(10)]; - msg = [msg ' image, but I don''t suggest this.' char(10) char(10)]; - msg = [msg ' To get help, please type:' char(10) char(10) ' help reslice_nii.m' char(10)]; - msg = [msg ' help load_untouch_nii.m' char(10) ' help load_nii.m']; - error(msg); - end - - else - R = R * diag([i j k]); - end % 1st det(R) - - else - affine_transform = 0; % no sform or qform transform - end - - if affine_transform == 1 - voxel_size = abs(sum(R,1)); - inv_R = inv(R); - originator = inv_R*(-T)+1; - orient = get_orient(inv_R); - - % modify pixdim and originator - % - hdr.dime.pixdim(2:4) = voxel_size; - hdr.hist.originator(1:3) = originator; - - % set sform or qform to non-use, because they have been - % applied in xform_nii - % - hdr.hist.qform_code = 0; - hdr.hist.sform_code = 0; - end - - % apply space_unit to pixdim if not 1 (mm) - % - space_unit = get_units(hdr); - - if space_unit ~= 1 - hdr.dime.pixdim(2:4) = hdr.dime.pixdim(2:4) * space_unit; - - % set space_unit of xyzt_units to millimeter, because - % voxel_size has been re-scaled - % - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,1,0)); - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,2,1)); - hdr.dime.xyzt_units = char(bitset(hdr.dime.xyzt_units,3,0)); - end - - hdr.dime.pixdim = abs(hdr.dime.pixdim); - - return; % change_hdr - - -%----------------------------------------------------------------------- -function orient = get_orient(R) - - orient = []; - - for i = 1:3 - switch find(R(i,:)) * sign(sum(R(i,:))) - case 1 - orient = [orient 1]; % Left to Right - case 2 - orient = [orient 2]; % Posterior to Anterior - case 3 - orient = [orient 3]; % Inferior to Superior - case -1 - orient = [orient 4]; % Right to Left - case -2 - orient = [orient 5]; % Anterior to Posterior - case -3 - orient = [orient 6]; % Superior to Inferior - end - end - - return; % get_orient - - -%----------------------------------------------------------------------- -function [space_unit, time_unit] = get_units(hdr) - - switch bitand(hdr.dime.xyzt_units, 7) % mask with 0x07 - case 1 - space_unit = 1e+3; % meter, m - case 3 - space_unit = 1e-3; % micrometer, um - otherwise - space_unit = 1; % millimeter, mm - end - - switch bitand(hdr.dime.xyzt_units, 56) % mask with 0x38 - case 16 - time_unit = 1e-3; % millisecond, ms - case 24 - time_unit = 1e-6; % microsecond, us - otherwise - time_unit = 1; % second, s - end - - return; % get_units - diff --git a/reg-test/matlab_tests/SSD_test.m b/reg-test/matlab_tests/SSD_test.m deleted file mode 100644 index 0368733b..00000000 --- a/reg-test/matlab_tests/SSD_test.m +++ /dev/null @@ -1,55 +0,0 @@ -function SSD_test(img1, img2, output_path) - current1 = load_untouch_nii(img1); % read the Nifti file - current2 = load_untouch_nii(img2); % read the Nifti file - currentImg1 = double(current1.img); - currentImg2 = double(current2.img); - % - dimVect1 = current1.hdr.dime.dim; - dimVect2 = current2.hdr.dime.dim; - % - nz1 = dimVect1(4); - nz2 = dimVect2(4); - if nz1 ~= nz2 - error('Error. Images must have the same dimension.'); - end - if nz1 > 1 - imgDim = 3; - else - imgDim = 2; - end - % - nt1 = dimVect1(5); - nt2 = dimVect2(5); - % - if (nt1 ~= nt2) - error('Error. Images must have the same dimension.'); - end - % - - SSDValue = 0; - for ii=1:nt1 - img1 = currentImg1(:,:,:,ii); - img2 = currentImg2(:,:,:,ii); - minImg1 = min(img1(:)); - maxImg1 = max(img1(:)); - minImg2 = min(img2(:)); - maxImg2 = max(img2(:)); - min12 = min(minImg1, minImg2); - max12 = max(maxImg1, maxImg2); - range12 = max12 - min12; - min1=(minImg1 - min12)/range12; - min2=(minImg2 - min12)/range12; - max1=1 - ((max12 - maxImg1) / range12); - max2=1 - ((max12 - maxImg2) / range12); - img1 = ((img1-minImg1) ./ (maxImg1-minImg1)) .* ... - (max1-min1) + min1; - img2 = ((img2-minImg2) ./ (maxImg2-minImg2)) .* ... - (max2-min2) + min2; - diff2 = (img1-img2).^2; - diff2Array=diff2(:); - SSDValue = SSDValue + sum(diff2Array(~isnan(diff2Array)))/sum(~isnan(diff2Array)); - end - %% Write the result - expectedSSD = -SSDValue; - dlmwrite([output_path,'/expectedSSDValue',num2str(imgDim),'D.txt'],expectedSSD,'precision','%.6f','delimiter',' '); -end \ No newline at end of file diff --git a/reg-test/matlab_tests/affineDeformationField_test.m b/reg-test/matlab_tests/affineDeformationField_test.m deleted file mode 100644 index 46070131..00000000 --- a/reg-test/matlab_tests/affineDeformationField_test.m +++ /dev/null @@ -1,134 +0,0 @@ -function affineDeformationField_test(refImg2D_name, refImg3D_name, output_path) -%% cout float 3.333333253860474 -%% cout double 3.333333333333334 -%% The number of digits of precision a value has depends on both the size -%% (floats have less precision than doubles) and the particular value being stored -%% (some values have more precision than others). -%% Float values have between 6 and 9 digits of precision, -%% with most float values having at least 7 significant digits -%% (which is why everything after that many digits in our answer above is junk). -%% Double values have between 15 and 18 digits of precision, -%% with most double values having at least 16 significant digits. -%% Long double has a minimum precision of 15, 18, or 33 significant digits -%% depending on how many bytes it occupies. -%% -%% Create a vector field image from an affine transformation matrix -%% -double_datatype = 64; -double_bitpix = 64; -float_datatype = 16; -float_bitpix = 32; -uchar_datatype = 2; -uchar_bitpix = 8; -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3D -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -refImg3D = load_untouch_nii(refImg3D_name); % read the Nifti file -HMatrix = eye(4,4,'single'); -HMatrix(1,:) = refImg3D.hdr.hist.srow_x; -HMatrix(2,:) = refImg3D.hdr.hist.srow_y; -HMatrix(3,:) = refImg3D.hdr.hist.srow_z; -%HMatrix -%% Generate a random affine tansformation matrix: -%A=rand(4,4,'single'); -A=rand(4,4)+ eye(4,4); -A(end,:) =[0 0 0 1]; -A=single(A); -%% -% A(1,:)=[0.9074931 0.01773963 -0.01136361 -1.596654]; -% A(2,:)=[0.009297408 0.9127377 -0.1370281 -7.986477]; -% A(3,:)=[0.01029972 0.1200682 0.8102149 -0.5980158]; -% A(4,:)=[0 0 0 1]; -% A -dlmwrite([output_path,'/affine_mat3D.txt'],A,'precision','%.6f','delimiter',' '); -%% READ THE FILE THAT WE HAVE JUST WRITTEN -fileID = fopen([output_path,'/affine_mat3D.txt'],'r'); -formatSpec = '%f'; -sizeA = [4 4]; -A = fscanf(fileID,formatSpec,sizeA); -fclose(fileID); -A=single(A'); -%% -expectedField = zeros(refImg3D.hdr.dime.dim(2),refImg3D.hdr.dime.dim(3),refImg3D.hdr.dime.dim(4),1,3,'single'); -%% -globalMatrix = single(double(A) * double(HMatrix)); -%% -for kk=1:size(refImg3D.img,3) - for jj=1:size(refImg3D.img,2) - for ii=1:size(refImg3D.img,1) - newPosition = single(double(globalMatrix) * double([ii-1 jj-1 kk-1 1]')); - expectedField(ii,jj,kk,1,1)=newPosition(1); - expectedField(ii,jj,kk,1,2)=newPosition(2); - expectedField(ii,jj,kk,1,3)=newPosition(3); - end - end -end -%% Usage: nii = make_nii(img, [voxel_size], [origin], [datatype], [description]) -%% 16 = float32 -%% 64 = float64 -expectedField_nii = make_nii(expectedField,... - [refImg3D.hdr.dime.pixdim(2),... - refImg3D.hdr.dime.pixdim(3),... - refImg3D.hdr.dime.pixdim(4)],... - [],float_datatype); -% -save_nii(expectedField_nii, [output_path,'/affine_def3D.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 2D -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -refImg2D = load_untouch_nii(refImg2D_name); % read the Nifti file -HMatrix = eye(4,4,'single'); -HMatrix(1,:) = refImg2D.hdr.hist.srow_x; -HMatrix(2,:) = refImg2D.hdr.hist.srow_y; -HMatrix(3,:) = refImg2D.hdr.hist.srow_z; -%HMatrix -%% Generate a random affine tansformation matrix: -%A=rand(4,4,'single'); -A=rand(4,4) + eye(4,4); -A(1,3)=0; -A(2,3)=0; -A(end-1,:) = [0 0 1 0]; -A(end,:) = [0 0 0 1]; -A=single(A); -%% -% A(1,:)=[1.032482 -0.007169947 0 -0.6956705]; -% A(2,:)=[0.07810403 1.005479 0 0.01127497]; -% A(3,:)=[0 0 1 0]; -% A(4,:)=[0 0 0 1]; -% A -dlmwrite([output_path,'/affine_mat2D.txt'],A,'precision','%.6f','delimiter',' '); -%% READ THE FILE THAT WE HAVE JUST WRITTEN -fileID = fopen([output_path,'/affine_mat2D.txt'],'r'); -formatSpec = '%f'; -sizeA = [4 4]; -A = fscanf(fileID,formatSpec,sizeA); -fclose(fileID); -A=single(A'); -%% -expectedField = zeros(refImg2D.hdr.dime.dim(2),refImg2D.hdr.dime.dim(3),refImg2D.hdr.dime.dim(4),1,2,'single'); -%% -globalMatrix = single(double(A) * double(HMatrix)); -%% -for kk=1:size(refImg2D.img,3) - for jj=1:size(refImg2D.img,2) - for ii=1:size(refImg2D.img,1) - newPosition = single(double(globalMatrix) * double([ii-1 jj-1 kk-1 1]')); - expectedField(ii,jj,kk,1,1)=newPosition(1); - expectedField(ii,jj,kk,1,2)=newPosition(2); - end - end -end -%% Usage: nii = make_nii(img, [voxel_size], [origin], [datatype], [description]) -%% 16 = float32 -%% 64 = float64 -expectedField_nii = make_nii(expectedField,... - [refImg2D.hdr.dime.pixdim(2),... - refImg2D.hdr.dime.pixdim(3),... - refImg2D.hdr.dime.pixdim(4)],... - [],float_datatype); -% -save_nii(expectedField_nii, [output_path,'/affine_def2D.nii.gz']); \ No newline at end of file diff --git a/reg-test/matlab_tests/blockMatching_test.m b/reg-test/matlab_tests/blockMatching_test.m deleted file mode 100644 index a5767d7d..00000000 --- a/reg-test/matlab_tests/blockMatching_test.m +++ /dev/null @@ -1,235 +0,0 @@ -function blockMatching_test(refImg2D_name, refImg3D_name, output_path) -indexImage=1; -vectImageName = {refImg2D_name, refImg3D_name}; -%%2D and 3D version -for dim=[2,3] - BLOCK_WIDTH = 4; - BLOCK_WIDTH_MINUS1 = BLOCK_WIDTH-1; - refImg=load_untouch_nii(vectImageName{indexImage}); - HMatrix = eye(4,4,'single'); - HMatrix(1,:) = refImg.hdr.hist.srow_x; - HMatrix(2,:) = refImg.hdr.hist.srow_y; - HMatrix(3,:) = refImg.hdr.hist.srow_z; - refImgImg = refImg.img; - %% Size of the input image: - [m,n,o]=size(refImgImg); - %% - floImg=load_untouch_nii(vectImageName{indexImage}); - floImgImg = floImg.img; - expectedBlockMatching = zeros(0,6); - expectedBlockMatchingPixel = zeros(0,6); - %% Let's cut our image in BLOCK_WIDTH*BLOCK_WIDTH size - nbTotalBlock_m = ceil(m/BLOCK_WIDTH); - nbTotalBlock_n = ceil(n/BLOCK_WIDTH); - nbTotalBlock_o = ceil(o/BLOCK_WIDTH); - if dim==2 - nMax_o=2; - else - nMax_o=nbTotalBlock_o-1; - end - %% Let's move the middle blocks - %[-5,5]. -5 + (5+5)*rand(10,1); - blockIndex=1; - for oo=2:3:nMax_o - for nn=2:3:nbTotalBlock_n-1 - for mm=2:3:nbTotalBlock_m-1 - if dim==2 - blockCoord_mno=[(1+(mm-1)*BLOCK_WIDTH),(1+(nn-1)*BLOCK_WIDTH),1]; - else - blockCoord_mno=[(1+(mm-1)*BLOCK_WIDTH),(1+(nn-1)*BLOCK_WIDTH),(1+(oo-1)*BLOCK_WIDTH)]; - end - - blockCoord_xyz=[blockCoord_mno(1)-1, blockCoord_mno(2)-1, blockCoord_mno(3)-1]; - - if dim==2 - if(blockCoord_mno(1) >= 1 && (blockCoord_mno(1)+BLOCK_WIDTH_MINUS1) <=m... - && blockCoord_mno(2) >= 1 && (blockCoord_mno(2)+BLOCK_WIDTH_MINUS1) <=n) - - tmpImg = refImgImg(blockCoord_mno(1):blockCoord_mno(1)+BLOCK_WIDTH_MINUS1,... - blockCoord_mno(2):blockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - - else - mEnd = min(m,blockCoord_mno(1)+BLOCK_WIDTH_MINUS1); - nEnd = min(n,blockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - - tmpImg = refImgImg(blockCoord_mno(1):mEnd,blockCoord_mno(2):nEnd); - - end - elseif dim==3 - if(blockCoord_mno(1) >= 1 && (blockCoord_mno(1)+BLOCK_WIDTH_MINUS1) <=m... - && blockCoord_mno(2) >= 1 && (blockCoord_mno(2)+BLOCK_WIDTH_MINUS1) <=n... - && blockCoord_mno(3) >= 1 && (blockCoord_mno(3)+BLOCK_WIDTH_MINUS1) <=o) - - tmpImg = refImgImg(blockCoord_mno(1):blockCoord_mno(1)+BLOCK_WIDTH_MINUS1,... - blockCoord_mno(2):blockCoord_mno(2)+BLOCK_WIDTH_MINUS1,... - blockCoord_mno(3):blockCoord_mno(3)+BLOCK_WIDTH_MINUS1); - - else - mEnd = min(m,blockCoord_mno(1)+BLOCK_WIDTH_MINUS1); - nEnd = min(n,blockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - oEnd = min(o,blockCoord_mno(3)+BLOCK_WIDTH_MINUS1); - - tmpImg = refImgImg(blockCoord_mno(1):mEnd,... - blockCoord_mno(2):nEnd,... - blockCoord_mno(3):oEnd); - end - else - error('dimension of the image not supported'); - end - - refVar = std(double(tmpImg(:)),1)^2; - [mT,nT,oT] = size(tmpImg); - nbPixel = mT*nT*oT; - - if(refVar > 0 && nbPixel > (BLOCK_WIDTH^dim)/2) - noiseOK = 1; - while noiseOK - %BLOCK_WIDTH_MINUS1 = in order to keep an overlap of at - %least 1 pixel - moveOK = 1; - - while moveOK - moveVect=round(-BLOCK_WIDTH_MINUS1 + (BLOCK_WIDTH_MINUS1+BLOCK_WIDTH_MINUS1)*rand(1,3)); - if dim==2 - moveVect(end)=0; - end - newBlockCoord_mno = blockCoord_mno+moveVect; - - newmStart = newBlockCoord_mno(1); - newnStart = newBlockCoord_mno(2); - newoStart = newBlockCoord_mno(3); - - newmEnd = min(m,newBlockCoord_mno(1)+BLOCK_WIDTH_MINUS1); - newnEnd = min(n,newBlockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - newoEnd = min(o,newBlockCoord_mno(3)+BLOCK_WIDTH_MINUS1); - - if (newmStart >=1 && newmStart <= m... - && newnStart >=1 && newnStart <= n... - && newoStart >=1 && newoStart <= o) - [mT,nT,oT] = size(refImgImg(newmStart:newmEnd,newnStart:newnEnd,newoStart:newoEnd)); - nbPixel = mT*nT*oT; - - if(nbPixel > (BLOCK_WIDTH^dim)/2) - moveOK = 0; - end - end - - end - - newBlockCoord_xyz = [newBlockCoord_mno(1)-1,newBlockCoord_mno(2)-1, newBlockCoord_mno(3)-1]; - % - referencePosition=HMatrix*[blockCoord_xyz,1]'; - warpedPosition=HMatrix*[newBlockCoord_xyz,1]'; - % - expectedBlockMatching(blockIndex,:)=[referencePosition(1) referencePosition(2) referencePosition(3) warpedPosition(1) warpedPosition(2) warpedPosition(3)]; - expectedBlockMatchingPixel(blockIndex,:)=[blockCoord_xyz(1) blockCoord_xyz(2) blockCoord_xyz(3) newBlockCoord_xyz(1) newBlockCoord_xyz(2) newBlockCoord_xyz(3)]; - - mStart=blockCoord_mno(1); - nStart=blockCoord_mno(2); - oStart=blockCoord_mno(3); - mEnd = min(m,blockCoord_mno(1)+BLOCK_WIDTH_MINUS1); - nEnd = min(n,blockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - oEnd = min(o,blockCoord_mno(3)+BLOCK_WIDTH_MINUS1); - - newmStart=newBlockCoord_mno(1); - newnStart=newBlockCoord_mno(2); - newoStart=newBlockCoord_mno(3); - if dim==2 - newoStart=1; - end - newmEnd = min(m,newBlockCoord_mno(1)+BLOCK_WIDTH_MINUS1); - newnEnd = min(n,newBlockCoord_mno(2)+BLOCK_WIDTH_MINUS1); - newoEnd = min(o,newBlockCoord_mno(3)+BLOCK_WIDTH_MINUS1); - if dim==2 - newoEnd=1; - end - - %% Lets add noise to our floatingImage in order to not have the same block more than once - floImgImg(mStart:mEnd,nStart:nEnd,oStart:oEnd)=... - 255*rand(length(mStart:mEnd),length(nStart:nEnd),length(oStart:oEnd)); - %% Lets move the block - floImgImg(newmStart:newmEnd,newnStart:newnEnd,newoStart:newoEnd)=... - refImgImg(mStart:mStart+newmEnd-newmStart,nStart:nStart+newnEnd-newnStart,oStart:oStart+newoEnd-newoStart); - - %% Let's check that the new block does not have multiple candidate - otherwise redo - %% It should be 1 ! - tmpImg1 = floImgImg(newmStart:newmEnd,newnStart:newnEnd,newoStart:newoEnd); - tmpImg2 = refImgImg(mStart:mStart+newmEnd-newmStart,nStart:nStart+newnEnd-newnStart,oStart:oStart+newoEnd-newoStart); - bestCC = corr(double(tmpImg1(:)),double(tmpImg2(:))); - - if dim == 2 - %% - for vv=-3:1:3 - for uu=-3:1:3 - %% we should never have edge problems - currentmStart=mStart+uu; - currentnStart=nStart+vv; - currentmEnd=min(m,currentmStart+BLOCK_WIDTH_MINUS1); - currentnEnd=min(n,currentnStart+BLOCK_WIDTH_MINUS1); - - if (currentmStart >=1 && currentmStart <= m && currentnStart >=1 && currentnStart <= n) - tmpImg1 = floImgImg(currentmStart:currentmEnd,currentnStart:currentnEnd); - tmpImg2 = refImgImg(mStart:mStart+currentmEnd-currentmStart,nStart:nStart+currentnEnd-currentnStart); - currentCC = abs(corr(double(tmpImg1(:)),double(tmpImg2(:)))); - - if (currentCC >= bestCC && currentmStart ~= newmStart && currentnStart ~= newnStart) - %% redo the noise - noiseOK = -1; - end - else - disp('edge problems 2D... strange') - end - end - end - if noiseOK==-1 - noiseOK = 1; - else - noiseOK = 0; - end - else - for ww=-3:1:3 - for vv=-3:1:3 - for uu=-3:1:3 - currentmStart=mStart+uu; - currentnStart=nStart+vv; - currentoStart=oStart+ww; - currentmEnd=min(m,currentmStart+BLOCK_WIDTH_MINUS1); - currentnEnd=min(n,currentnStart+BLOCK_WIDTH_MINUS1); - currentoEnd=min(o,currentoStart+BLOCK_WIDTH_MINUS1); - - if (currentmStart >=1 && currentmStart <= m && currentnStart >=1 && currentnStart <= n && currentoStart >=1 && currentoStart <= o) - tmpImg1 = floImgImg(currentmStart:currentmEnd,currentnStart:currentnEnd,currentoStart:currentoEnd); - tmpImg2 = refImgImg(mStart:mStart+currentmEnd-currentmStart,nStart:nStart+currentnEnd-currentnStart,oStart:oStart+currentoEnd-currentoStart); - currentCC = abs(corr(double(tmpImg1(:)),double(tmpImg2(:)))); - if (currentCC >= bestCC && currentmStart ~= newmStart && currentnStart ~= newnStart && currentoEnd ~= newoStart) - %% redo the noise - noiseOK = -1; - end - else - disp('edge problems 3D... strange'); - end - end - end - end - if noiseOK==-1 - noiseOK = 1; - else - noiseOK = 0; - end - end - %% - end - blockIndex = blockIndex+1; - end - end - end - end - %% SAVE THE EXPECTED BLOCK MATCHING MATRIX - dlmwrite(strcat([output_path, '/expectedBlockMatching_mat',num2str(dim),'D.txt']), ... - expectedBlockMatching,'precision','%.6f','delimiter',' '); - %% SAVE THE IMAGE - floImg.img = floImgImg; - save_untouch_nii(floImg, strcat([output_path, '/warpedBlockMatchingImg',num2str(dim),'D.nii.gz'])); - - indexImage=indexImage+1; -end \ No newline at end of file diff --git a/reg-test/matlab_tests/changeDataType_test.m b/reg-test/matlab_tests/changeDataType_test.m deleted file mode 100644 index 5899810c..00000000 --- a/reg-test/matlab_tests/changeDataType_test.m +++ /dev/null @@ -1,80 +0,0 @@ -function changeDataType_test(refImg2D_name, refImg3D_name, output_path) -%% -%% Change image datatype tests -%% -double_datatype = 64; -double_bitpix = 64; -float_datatype = 16; -float_bitpix = 32; -uchar_datatype = 2; -uchar_bitpix = 8; -%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3D -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% DOUBLE -refImg3D=load_untouch_nii(refImg3D_name); -% -double_nii = refImg3D; -double_nii.img = double(refImg3D.img); -double_nii.hdr.dime.datatype = double_datatype; -double_nii.hdr.dime.bitpix = double_bitpix; -% -save_untouch_nii(double_nii, [output_path,'/refImg3D_double.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% SINGLE -refImg3D=load_untouch_nii(refImg3D_name); -% -single_nii = refImg3D; -single_nii.img = single(refImg3D.img); -single_nii.hdr.dime.datatype = float_datatype; -single_nii.hdr.dime.bitpix = float_bitpix; -% -save_untouch_nii(single_nii, [output_path,'/refImg3D_float.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% UCHAR -refImg3D=load_untouch_nii(refImg3D_name); -% -uchar_nii = refImg3D; -uchar_nii.img = uint8(refImg3D.img); -uchar_nii.hdr.dime.datatype = uchar_datatype; -uchar_nii.hdr.dime.bitpix = uchar_bitpix; -% -save_untouch_nii(uchar_nii, [output_path,'/refImg3D_uchar.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 2D -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% DOUBLE -refImg2D=load_untouch_nii(refImg2D_name); -% -double_nii = refImg2D; -double_nii.img = double(refImg2D.img); -double_nii.hdr.dime.datatype = double_datatype; -double_nii.hdr.dime.bitpix = double_bitpix; -% -save_untouch_nii(double_nii, [output_path,'/refImg2D_double.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% SINGLE -refImg2D=load_untouch_nii(refImg2D_name); -% -single_nii = refImg2D; -single_nii.img = single(refImg2D.img); -single_nii.hdr.dime.datatype = float_datatype; -single_nii.hdr.dime.bitpix = float_bitpix; -% -save_untouch_nii(single_nii, [output_path,'/refImg2D_float.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% UCHAR -refImg2D=load_untouch_nii(refImg2D_name); -% -uchar_nii = refImg2D; -uchar_nii.img = uint8(refImg2D.img); -uchar_nii.hdr.dime.datatype = uchar_datatype; -uchar_nii.hdr.dime.bitpix = uchar_bitpix; -% -save_untouch_nii(uchar_nii, [output_path,'/refImg2D_uchar.nii.gz']); -end \ No newline at end of file diff --git a/reg-test/matlab_tests/create_test_data.m b/reg-test/matlab_tests/create_test_data.m deleted file mode 100644 index efa87961..00000000 --- a/reg-test/matlab_tests/create_test_data.m +++ /dev/null @@ -1,296 +0,0 @@ -function create_test_data(ref2D_path, ref3D_path, output_path) -%% Add the required files to read and write nifti images -addpath('./NIfTI_20140122'); -%% Create the folder to store the result files -if (strcmp(output_path,'.') == 1 || strcmp(output_path,'..') == 1) - error('[NiftyReg Build Tests] cannot create in . or ..'); -end -if ~exist(output_path, 'dir') - mkdir(output_path); -end -%% Copy the input images into the output folder -if ~exist([output_path, '/refImg2D.nii.gz'], 'file') - [~,~,ext] = fileparts(ref2D_path); - if strcmp(ext,'.gz') - copyfile(ref2D_path, [output_path, '/refImg2D.nii.gz'], 'f'); - else - copyfile(ref2D_path, [output_path, '/refImg2D.nii'], 'f'); - gzip([output_path, '/refImg2D.nii']) - end - fprintf('[NiftyReg Build Tests] Input 2D file copied\n'); -else - fprintf(['[NiftyReg Build Tests] ', output_path, ... - '/refImg2D.nii.gz already exists\n']) -end -if ~exist([output_path, '/refImg3D.nii.gz'], 'file') - - [~,~,ext] = fileparts(ref2D_path); - if strcmp(ext,'.gz') - copyfile(ref3D_path, [output_path, '/refImg3D.nii.gz'], 'f'); - else - copyfile(ref3D_path, [output_path, '/refImg3D.nii'], 'f'); - gzip([output_path, '/refImg3D.nii']) - - end - fprintf('[NiftyReg Build Tests]Input 3D file copied\n'); -else - fprintf(['[NiftyReg Build Tests] ', output_path, ... - '/refImg3D.nii.gz already exists\n']) -end - -ref2D=[output_path, '/refImg2D.nii.gz']; -ref3D=[output_path, '/refImg3D.nii.gz']; - -%% Create affine matrix and associated deformation fields -fprintf('[NiftyReg Build Tests] Starting to generate affine deformation fields\n') -if ~exist([output_path,'/affine_mat2D.txt'], 'file') || ... - ~exist([output_path,'/affine_mat3D.txt'], 'file') || ... - ~exist([output_path,'/affine_def2D.nii.gz'], 'file') || ... - ~exist([output_path,'/affine_def2D.nii.gz'], 'file') - affineDeformationField_test(ref2D, ref3D, output_path); - fprintf('[NiftyReg Build Tests] Affine deformation fields created\n'); -else - fprintf('[NiftyReg Build Tests] Affine deformation fields already exist\n'); -end -%% Create control point grids and associated deformation fields -fprintf('[NiftyReg Build Tests] Starting to generate spline deformation fields\n') -if ~exist([output_path,'/bspline_def2D.nii.gz'], 'file') || ... - ~exist([output_path,'/bspline_def3D.nii.gz'], 'file') || ... - ~exist([output_path,'/bspline_grid2D.nii.gz'], 'file') || ... - ~exist([output_path,'/bspline_grid3D.nii.gz'], 'file') - getBSplineField_test(ref2D, ref3D, output_path); - fprintf('[NiftyReg Build Tests] Spline deformation fields created\n'); -else - fprintf('[NiftyReg Build Tests] Spline deformation fields already exist\n'); -end -%% Create data to test the block matching -fprintf('[NiftyReg Build Tests] Starting to generate Block matching test data\n') -if ~exist([output_path,'/expectedBlockMatching_mat2D.txt'], 'file') || ... - ~exist([output_path,'/expectedBlockMatching_mat3D.txt'], 'file') || ... - ~exist([output_path,'/warpedBlockMatchingImg2D.nii.gz'], 'file') || ... - ~exist([output_path,'/warpedBlockMatchingImg3D.nii.gz'], 'file') - blockMatching_test(ref2D, ref3D, output_path); - fprintf('[NiftyReg Build Tests] Block matching test data created\n'); -else - fprintf('[NiftyReg Build Tests] Block matching test data already exist\n'); -end - -%% Convert input images to different data type -fprintf('[NiftyReg Build Tests] Starting to generate datatype tests\n') -if ~exist([output_path,'/refImg2D_uchar.nii.gz'], 'file') || ... - ~exist([output_path,'/refImg3D_uchar.nii.gz'], 'file') || ... - ~exist([output_path,'/refImg2D_float.nii.gz'], 'file') || ... - ~exist([output_path,'/refImg3D_float.nii.gz'], 'file') || ... - ~exist([output_path,'/refImg2D_double.nii.gz'], 'file') || ... - ~exist([output_path,'/refImg3D_double.nii.gz'], 'file') - changeDataType_test(ref2D, ref3D, output_path); - fprintf('[NiftyReg Build Tests] datatype tests data created\n'); -else - fprintf('[NiftyReg Build Tests] datatype tests data already exist\n'); -end -%% Create interpolated images -fprintf('[NiftyReg Build Tests] Starting to generate interpolation tests\n') -if ~exist([output_path,'/warped_nearest2D.nii.gz'], 'file') || ... - ~exist([output_path,'/warped_linear2D.nii.gz'], 'file') || ... - ~exist([output_path,'/warped_cubic2D.nii.gz'], 'file') || ... - ~exist([output_path,'/warped_nearest3D.nii.gz'], 'file') || ... - ~exist([output_path,'/warped_linear3D.nii.gz'], 'file') || ... - ~exist([output_path,'/warped_cubic3D.nii.gz'], 'file') - interpolation_test(ref2D, ref3D, ... - [output_path,'/affine_def2D.nii.gz'], ... - [output_path,'/affine_def3D.nii.gz'], ... - output_path); - fprintf('[NiftyReg Build Tests] interpolation tests data created\n'); -else - fprintf('[NiftyReg Build Tests] interpolation tests data already exist\n'); -end -%% Create data to test the SVD function -fprintf('[NiftyReg Build Tests] Starting to generate SVD tests\n') -if ~exist([output_path,'/inputSVDMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedUMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedSMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedVMatrix.txt'], 'file') - svd_test(output_path); - fprintf('[NiftyReg Build Tests] SVD tests data created\n'); -else - fprintf('[NiftyReg Build Tests] SVD tests data already exist\n'); -end -%% Create data to test each matrix operations -fprintf('[NiftyReg Build Tests] Starting to generate matrix operation tests\n') -if ~exist([output_path,'/inputMatrix1.txt'], 'file') || ... - ~exist([output_path,'/inputMatrix2.txt'], 'file') || ... - ~exist([output_path,'/expectedMulMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedAddMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedSubMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedExpMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedLogMatrix.txt'], 'file') || ... - ~exist([output_path,'/expectedInvMatrix.txt'], 'file') - matrix_operation_test(output_path); - fprintf('[NiftyReg Build Tests] matrix operation tests data created\n'); -else - fprintf('[NiftyReg Build Tests] matrix operation tests data already exist\n'); -end -%% Create data to test the LS and LTS functions -fprintf('[NiftyReg Build Tests] Starting to generate LTS tests\n') -if ~exist([output_path,'/P1_2D.txt'], 'file') || ... - ~exist([output_path,'/P1_3D.txt'], 'file') || ... - ~exist([output_path,'/expectedRigidLTS_2D_70.txt'], 'file') || ... - ~exist([output_path,'/expectedRigidLTS_2D_100.txt'], 'file') || ... - ~exist([output_path,'/expectedRigidLTS_3D_70.txt'], 'file') || ... - ~exist([output_path,'/expectedRigidLTS_3D_100.txt'], 'file') || ... - ~exist([output_path,'/expectedAffineLTS_2D_70.txt'], 'file') || ... - ~exist([output_path,'/expectedAffineLTS_2D_100.txt'], 'file') || ... - ~exist([output_path,'/expectedAffineLTS_3D_70.txt'], 'file') || ... - ~exist([output_path,'/expectedAffineLTS_3D_100.txt'], 'file') - LTS_test(output_path); - fprintf('[NiftyReg Build Tests] LTS tests data created\n'); -else - fprintf('[NiftyReg Build Tests] LTS tests data already exist\n'); -end -%% Create data to test MIND -fprintf('[NiftyReg Build Tests] Starting to generate MIND tests\n') -if ~exist([output_path,'/P1_2D.txt'], 'file') || ... - ~exist([output_path,'/P1_3D.txt'], 'file') || ... - ~exist([output_path,'/expectedMINDDescriptor2D_1.nii.gz'], 'file') || ... - ~exist([output_path,'/expectedMINDDescriptor3D_1.nii.gz'], 'file') - mindDescriptor_test(ref2D, ref3D, output_path, false); - movefile([output_path,'/expectedMINDDescriptor2D.nii.gz'], ... - [output_path,'/expectedMINDDescriptor2D_1.nii.gz']); - movefile([output_path,'/expectedMINDDescriptor3D.nii.gz'], ... - [output_path,'/expectedMINDDescriptor3D_1.nii.gz']); - fprintf('[NiftyReg Build Tests] MIND 1 tests data created\n'); -else - fprintf('[NiftyReg Build Tests] MIND 1 tests data already exist\n'); -end -if ~exist([output_path,'/P1_2D.txt'], 'file') || ... - ~exist([output_path,'/P1_3D.txt'], 'file') || ... - ~exist([output_path,'/expectedMINDDescriptor2D_2.nii.gz'], 'file') || ... - ~exist([output_path,'/expectedMINDDescriptor3D_2.nii.gz'], 'file') - mindDescriptor_test([output_path,'/warped_linear2D.nii.gz'], ... - [output_path,'/warped_linear3D.nii.gz'], ... - output_path, false); - movefile([output_path,'/expectedMINDDescriptor2D.nii.gz'], ... - [output_path,'/expectedMINDDescriptor2D_2.nii.gz']); - movefile([output_path,'/expectedMINDDescriptor3D.nii.gz'], ... - [output_path,'/expectedMINDDescriptor3D_2.nii.gz']); - fprintf('[NiftyReg Build Tests] MIND 2 tests data created\n'); -else - fprintf('[NiftyReg Build Tests] MIND 2 tests data already exist\n'); -end -%% Create data to test SSD similarity measure -fprintf('[NiftyReg Build Tests] Starting to generate SSD tests\n') -if ~exist([output_path,'/expectedSSDValue2D.txt'], 'file') || ... - ~exist([output_path,'/expectedSSDValue3D.txt'], 'file') - SSD_test([output_path,'/expectedMINDDescriptor2D_1.nii.gz'], ... - [output_path,'/expectedMINDDescriptor2D_2.nii.gz'], ... - output_path); - SSD_test([output_path,'/expectedMINDDescriptor3D_1.nii.gz'], ... - [output_path,'/expectedMINDDescriptor3D_2.nii.gz'], ... - output_path); - fprintf('[NiftyReg Build Tests] SSD tests data created\n'); -else - fprintf('[NiftyReg Build Tests] SSD tests data already exist\n'); -end -%% Create data to test MIND similarity measure -fprintf('[NiftyReg Build Tests] Starting to generate MIND SSD tests\n') -if ~exist([output_path,'/expectedMINDSSDValue2D.txt'], 'file') || ... - ~exist([output_path,'/expectedMINDSSDValue3D.txt'], 'file') - MINDSSD_test(ref2D,[output_path,'/warped_linear2D.nii.gz'], ... - ref3D,[output_path,'/warped_linear3D.nii.gz'], ... - output_path); - fprintf('[NiftyReg Build Tests] MIND SSD tests data created\n'); -else - fprintf('[NiftyReg Build Tests] MIND SSD tests data already exist\n'); -end -%% Create data to test gradient -fprintf('[NiftyReg Build Tests] Starting to generate gradient tests\n') -if ~exist([output_path,'/expectedImageGradient2D.nii.gz'], 'file') || ... - ~exist([output_path,'/expectedImageGradient3D.nii.gz'], 'file') - imageGradient_test([output_path,'/expectedMINDDescriptor2D_1.nii.gz'],... - [output_path,'/expectedMINDDescriptor3D_1.nii.gz'],... - output_path); - fprintf('[NiftyReg Build Tests] gradient tests data created\n'); -else - fprintf('[NiftyReg Build Tests] gradient tests data already exist\n'); -end -%% Create data to test MIND-SSC -fprintf('[NiftyReg Build Tests] Starting to generate MINDSSC tests\n') -if ~exist([output_path,'/P1_2D.txt'], 'file') || ... - ~exist([output_path,'/P1_3D.txt'], 'file') || ... - ~exist([output_path,'/expectedMINDSSCDescriptor2D_1.nii.gz'], 'file') || ... - ~exist([output_path,'/expectedMINDSSCDescriptor3D_1.nii.gz'], 'file') - mindsscDescriptor_test(ref2D, ref3D, output_path, false); - movefile([output_path,'/expectedMINDSSCDescriptor2D.nii.gz'], ... - [output_path,'/expectedMINDSSCDescriptor2D_1.nii.gz']); - movefile([output_path,'/expectedMINDSSCDescriptor3D.nii.gz'], ... - [output_path,'/expectedMINDSSCDescriptor3D_1.nii.gz']); - fprintf('[NiftyReg Build Tests] MINDSSC 1 tests data created\n'); -else - fprintf('[NiftyReg Build Tests] MINDSSC 1 tests data already exist\n'); -end -if ~exist([output_path,'/P1_2D.txt'], 'file') || ... - ~exist([output_path,'/P1_3D.txt'], 'file') || ... - ~exist([output_path,'/expectedMINDSSCDescriptor2D_2.nii.gz'], 'file') || ... - ~exist([output_path,'/expectedMINDSSCDescriptor3D_2.nii.gz'], 'file') - mindsscDescriptor_test([output_path,'/warped_linear2D.nii.gz'], ... - [output_path,'/warped_linear3D.nii.gz'], ... - output_path, false); - movefile([output_path,'/expectedMINDSSCDescriptor2D.nii.gz'], ... - [output_path,'/expectedMINDSSCDescriptor2D_2.nii.gz']); - movefile([output_path,'/expectedMINDSSCDescriptor3D.nii.gz'], ... - [output_path,'/expectedMINDSSCDescriptor3D_2.nii.gz']); - fprintf('[NiftyReg Build Tests] MINDSSC 2 tests data created\n'); -else - fprintf('[NiftyReg Build Tests] MINDSSC 2 tests data already exist\n'); -end -%% Create data to test the convolution -fprintf('[NiftyReg Build Tests] Starting to generate convolution\n') -if ~exist([output_path,'/convolution2D_mea.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution2D_lin.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution2D_gau.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution2D_spl.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution3D_mea.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution3D_lin.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution3D_gau.nii.gz'], 'file') || ... - ~exist([output_path,'/convolution3D_spl.nii.gz'], 'file') - getConvolution_test(ref2D, ref3D, output_path); - fprintf('[NiftyReg Build Tests] Convolution tests data created\n'); -else - fprintf('[NiftyReg Build Tests] Convolution tests data already exist\n'); -end -%% Create data to test the linear elasticity -fprintf('[NiftyReg Build Tests] Starting to generate linear elasticity data\n') -if ~exist([output_path,'/le_spline_approx2D.txt'], 'file') || ... - ~exist([output_path,'/le_spline_dense2D.txt'], 'file') || ... - ~exist([output_path,'/le_field_dense2D.txt'], 'file') || ... - ~exist([output_path,'/le_spline_approx3D.txt'], 'file') || ... - ~exist([output_path,'/le_spline_dense3D.txt'], 'file') || ... - ~exist([output_path,'/le_field_dense3D.txt'], 'file') - getLinearElasticityValue_test( ... - [output_path,'/bspline_grid2D.nii.gz'] , ... - [output_path,'/bspline_def2D.nii.gz'] , ... - [output_path,'/bspline_grid3D.nii.gz'] , ... - [output_path,'/bspline_def3D.nii.gz'] , ... - output_path); - fprintf('[NiftyReg Build Tests] Linear elasticity data tests data created\n'); -else - fprintf('[NiftyReg Build Tests] Linear elasticity data tests data already exist\n'); -end -%% Create data to test the linear elasticity gradient -fprintf('[NiftyReg Build Tests] Starting to generate linear elasticity gradient data\n') -if ~exist([output_path,'/le_grad_spline_approx2D.nii.gz'], 'file') || ... - ~exist([output_path,'/le_grad_spline_dense2D.nii.gz'], 'file') || ... - ~exist([output_path,'/le_grad_field_dense2D.nii.gz'], 'file') || ... - ~exist([output_path,'/le_grad_spline_approx3D.nii.gz'], 'file') || ... - ~exist([output_path,'/le_grad_spline_dense3D.nii.gz'], 'file') || ... - ~exist([output_path,'/le_grad_field_dense3D.nii.gz'], 'file') - getLinearElasticityGradient_test( ... - [output_path,'/bspline_grid2D.nii.gz'] , ... - [output_path,'/bspline_def2D.nii.gz'] , ... - [output_path,'/bspline_grid3D.nii.gz'] , ... - [output_path,'/bspline_def3D.nii.gz'] , ... - output_path); - fprintf('[NiftyReg Build Tests] Linear elasticity gradient data tests data created\n'); -else - fprintf('[NiftyReg Build Tests] Linear elasticity gradient data tests data already exist\n'); -end diff --git a/reg-test/matlab_tests/fGaussian3D.m b/reg-test/matlab_tests/fGaussian3D.m deleted file mode 100644 index eaa7a7ca..00000000 --- a/reg-test/matlab_tests/fGaussian3D.m +++ /dev/null @@ -1,32 +0,0 @@ -function h = fGaussian3D(siz, sigma) -% -if(nargin == 1) - if length(siz) ~= 1 - error('size not well defined'); - end - sig = siz/(4*sqrt(2*log(2))); -elseif nargin == 2 - if length(siz) ~= 1 - error('size not well defined'); - end - if length(sigma) == 1 - sig = sigma; - else - error('sigma not well defined'); - end -else - error('not enought input arguments'); -end -% -size = (siz-1)/2 * ones(1,3); -[x,y,z] = ndgrid(-size(1):size(1),-size(2):size(2),-size(3):size(3)); -arg = -(x.*x + y.*y + z.*z)/(2*sig*sig); -h = exp(arg); -h(h 1 - input_ndim=3; - end - input_dim=[input_image.hdr.dime.dim(2), ... - input_image.hdr.dime.dim(3), ... - input_image.hdr.dime.dim(4) ... - ]; - expectedField = zeros(input_dim(1), ... - input_dim(2), ... - input_dim(3),... - 1,... - input_ndim,... - 'single' ... - ); - %% Overlay a control point grid of random value - grid_dim=[ceil(3+input_dim(1)/spacing), ... - ceil(3+input_dim(2)/spacing), ... - ceil(3+input_dim(3)/spacing), ... - ]; - if input_ndim==2 - grid_dim(3)=1; - end - gridField = random('unif', -spacing, spacing, ... - grid_dim(1), ... - grid_dim(2), ... - grid_dim(3),... - 1,... - input_ndim ... - ); - % Convert from displacement to deformation - def_matrix = eye(4,4,'single'); - def_matrix(1,:)=input_image.hdr.hist.srow_x; - def_matrix(2,:)=input_image.hdr.hist.srow_y; - def_matrix(3,:)=input_image.hdr.hist.srow_z; - spl_matrix = eye(4,4,'single'); - spl_matrix(1:3, 1) = def_matrix(1:3, 1) .* spacing; - spl_matrix(1:3, 2) = def_matrix(1:3, 2) .* spacing; - spl_matrix(1:3, 3) = def_matrix(1:3, 3) .* spacing; - spl_matrix(:,4) = def_matrix(:,4); - new_origin = spl_matrix * [-1, -1, -1, 1]'; - spl_matrix(:,4) = new_origin; - for kk=1:grid_dim(3) - for jj=1:grid_dim(2) - for ii=1:grid_dim(1) - newPosition = single(double(spl_matrix) * ... - double([ii-1 jj-1 kk-1 1]')); - gridField(ii,jj,kk,1,1)= ... - gridField(ii,jj,kk,1,1)+newPosition(1); - gridField(ii,jj,kk,1,2)= ... - gridField(ii,jj,kk,1,2)+newPosition(2); - if grid_dim(3) > 1 - gridField(ii,jj,kk,1,3)= ... - gridField(ii,jj,kk,1,3)+newPosition(3); - end - end - end - end - %% Fill the deformation field image using the slowest approach - % known to mankind - for x=0:input_dim(1)-1 - first_x = floor(x/spacing); - norm_x = x/spacing - first_x; - basis_x = getBSplineCoefficient(norm_x); - for y=0:input_dim(2)-1 - first_y = floor(y/spacing); - norm_y = y/spacing - first_y; - basis_y = getBSplineCoefficient(norm_y); - - if input_ndim==2 - current_value_x=0; - current_value_y=0; - for a=1:4 - for b=1:4 - basis = basis_x(a) * basis_y(b); - current_value_x=current_value_x + basis * ... - gridField(first_x+a, first_y+b, 1, 1, 1); - current_value_y=current_value_y + basis * ... - gridField(first_x+a, first_y+b, 1, 1, 2); - end - end - expectedField(x+1, y+1, 1, 1, 1)=current_value_x; - expectedField(x+1, y+1, 1, 1, 2)=current_value_y; - else - for z=0:input_dim(3)-1 - first_z = floor(z/spacing); - norm_z = z/spacing - first_z; - basis_z = getBSplineCoefficient(norm_z); - - current_value_x=0; - current_value_y=0; - current_value_z=0; - for a=1:4 - for b=1:4 - for c=1:4 - basis = basis_x(a) * basis_y(b) * ... - basis_z(c); - current_value_x=current_value_x+basis*... - gridField(first_x+a, ... - first_y+b, ... - first_z+c, ... - 1, 1); - current_value_y=current_value_y+basis*... - gridField(first_x+a, ... - first_y+b, ... - first_z+c, ... - 1, 2); - current_value_z=current_value_z+basis*... - gridField(first_x+a, ... - first_y+b, ... - first_z+c, ... - 1, 3); - end - end - end - expectedField(x+1, y+1, z+1, 1, 1)=current_value_x; - expectedField(x+1, y+1, z+1, 1, 2)=current_value_y; - expectedField(x+1, y+1, z+1, 1, 3)=current_value_z; - end - end - end - end - %% Save the deformation field image - expectedField_nii=make_nii(expectedField,... - [input_image.hdr.dime.pixdim(2),... - input_image.hdr.dime.pixdim(3),... - input_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - expectedField_nii.hdr.dime.pixdim(1)=input_image.hdr.dime.pixdim(1); - expectedField_nii.hdr.hist.quatern_b=input_image.hdr.hist.quatern_b; - expectedField_nii.hdr.hist.quatern_c=input_image.hdr.hist.quatern_c; - expectedField_nii.hdr.hist.quatern_d=input_image.hdr.hist.quatern_d; - expectedField_nii.hdr.hist.qoffset_x=input_image.hdr.hist.qoffset_x; - expectedField_nii.hdr.hist.qoffset_y=input_image.hdr.hist.qoffset_y; - expectedField_nii.hdr.hist.qoffset_z=input_image.hdr.hist.qoffset_z; - expectedField_nii.hdr.hist.srow_x=def_matrix(1,:); - expectedField_nii.hdr.hist.srow_y=def_matrix(2,:); - expectedField_nii.hdr.hist.srow_z=def_matrix(3,:); - expectedField_nii.hdr.hist=input_image.hdr.hist; - save_nii(expectedField_nii, [output_path,'/bspline_def', ... - int2str(input_ndim), 'D.nii.gz']); - %% Save the control point grid - gridField_nii=make_nii(gridField,... - [spacing*input_image.hdr.dime.pixdim(2),... - spacing*input_image.hdr.dime.pixdim(3),... - spacing*input_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - gridField_nii.hdr.dime.pixdim(1)=input_image.hdr.dime.pixdim(1); - gridField_nii.hdr.hist.quatern_b=input_image.hdr.hist.quatern_b; - gridField_nii.hdr.hist.quatern_c=input_image.hdr.hist.quatern_c; - gridField_nii.hdr.hist.quatern_d=input_image.hdr.hist.quatern_d; - gridField_nii.hdr.hist.qoffset_x=input_image.hdr.hist.qoffset_x; - gridField_nii.hdr.hist.qoffset_y=input_image.hdr.hist.qoffset_y; - gridField_nii.hdr.hist.qoffset_z=input_image.hdr.hist.qoffset_z; - gridField_nii.hdr.hist=input_image.hdr.hist; - gridField_nii.hdr.hist.srow_x=spl_matrix(1,:); - gridField_nii.hdr.hist.srow_y=spl_matrix(2,:); - gridField_nii.hdr.hist.srow_z=spl_matrix(3,:); - save_nii(gridField_nii, [output_path,'/bspline_grid', ... - int2str(input_ndim), 'D.nii.gz']); -end - -function basis = getBSplineCoefficient(dist) -%% Given a normalise position return the 4 corresponding basis values -basis(1) = (1-dist)*(1-dist)*(1-dist)/6; -basis(2) = (3*dist*dist*dist - 6*dist*dist + 4)/6.0; -basis(3) = (-3*dist*dist*dist + 3*dist*dist + 3*dist + 1)/6; -basis(4) = dist*dist*dist/6; diff --git a/reg-test/matlab_tests/getConvolution_test.m b/reg-test/matlab_tests/getConvolution_test.m deleted file mode 100644 index 46d1cc6e..00000000 --- a/reg-test/matlab_tests/getConvolution_test.m +++ /dev/null @@ -1,92 +0,0 @@ -function getConvolution_test(refImg2D_name, refImg3D_name, output_path) -%% Apply convolution to the input images -%% Read the input images -input_image_name = {refImg2D_name, refImg3D_name}; -%% Initialse the kernels -convolution_type = {'mea', 'lin', 'gau', 'spl'}; -%% Mean kernel computation -med_kernel=ones(11,11,11) ./ 125; -%% Linear kernel computation -lin_function = [0:0.2:1, 0.8:-0.2:0]; -lin_kernel=zeros(length(lin_function), ... - length(lin_function), ... - length(lin_function)); -for z=1:length(lin_function) - for y=1:length(lin_function) - for x=1:length(lin_function) - lin_kernel(x, y, z) = lin_function(x) * ... - lin_function(y) * lin_function(z); - end - end -end -%% Gaussian kernel computation -gau_kernel=zeros(31,31,31); -gauss_function = gaussmf(-15:1:15, [5, 0]); -gauss_function = gauss_function ./ sum(gauss_function); -for z=1:31 - for y=1:31 - for x=1:31 - gau_kernel(x,y,z) = gauss_function(x) * ... - gauss_function(y) * ... - gauss_function(z); - end - end -end -%% Spline kernel -spline_function = zeros(length(-2:0.2:2),1); -for x=-10:1:10 - dist=abs(x/5); - if dist < 1 - spline_function(x+11) = 2/3 - dist*dist + 0.5*dist*dist*dist; - elseif dist < 2 - spline_function(x+11) = -(dist-2)*(dist-2)*(dist-2)/6; - end -end -spline_function = spline_function / sum(spline_function); -spl_kernel=zeros(length(spline_function), ... - length(spline_function), ... - length(spline_function)); -for z=1:length(spline_function) - for y=1:length(spline_function) - for x=1:length(spline_function) - spl_kernel(x, y, z) = spline_function(x) * ... - spline_function(y) * spline_function(z); - end - end -end -%% Loop over dimension -convolution_kernel ={med_kernel, lin_kernel, gau_kernel, spl_kernel}; -for i=1:2 - %% Load the input data - input_image = load_untouch_nii(input_image_name{i}); - input_data = input_image.img; - %% Loop over the convolution type - for c=1:4 - output_data = convn(input_data, convolution_kernel{c}, 'same'); - output_norm = convn(ones(size(input_data)), convolution_kernel{c}, 'same'); - output_data = output_data ./ output_norm; - input_matrix(1,:)=input_image.hdr.hist.srow_x; - input_matrix(2,:)=input_image.hdr.hist.srow_y; - input_matrix(3,:)=input_image.hdr.hist.srow_z; - convolved_nii=make_nii(output_data,... - [input_image.hdr.dime.pixdim(2),... - input_image.hdr.dime.pixdim(3),... - input_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - convolved_nii.hdr.dime.pixdim(1)=input_image.hdr.dime.pixdim(1); - convolved_nii.hdr.hist.quatern_b=input_image.hdr.hist.quatern_b; - convolved_nii.hdr.hist.quatern_c=input_image.hdr.hist.quatern_c; - convolved_nii.hdr.hist.quatern_d=input_image.hdr.hist.quatern_d; - convolved_nii.hdr.hist.qoffset_x=input_image.hdr.hist.qoffset_x; - convolved_nii.hdr.hist.qoffset_y=input_image.hdr.hist.qoffset_y; - convolved_nii.hdr.hist.qoffset_z=input_image.hdr.hist.qoffset_z; - convolved_nii.hdr.hist.srow_x=input_matrix(1,:); - convolved_nii.hdr.hist.srow_y=input_matrix(2,:); - convolved_nii.hdr.hist.srow_z=input_matrix(3,:); - convolved_nii.hdr.hist=input_image.hdr.hist; - save_nii(convolved_nii, [output_path,'/convolution', ... - int2str(i+1), 'D_', convolution_type{c}, '.nii.gz']); - end -end diff --git a/reg-test/matlab_tests/getLinearElasticityGradient_test.m b/reg-test/matlab_tests/getLinearElasticityGradient_test.m deleted file mode 100644 index 0e18e644..00000000 --- a/reg-test/matlab_tests/getLinearElasticityGradient_test.m +++ /dev/null @@ -1,484 +0,0 @@ -function getLinearElasticityGradient_test(grid2D_name, ... - def2D_name, grid3D_name, def3D_name, output_path) -%% -grid_name = {grid2D_name, grid3D_name}; -defField_name = {def2D_name, def3D_name}; - -for i=1:2 - %Read the grid image - grid_image = load_untouch_nii(grid_name{i}); - grid_data = grid_image.img; - orientation = zeros(3,3); - orientation(1:3,1) = grid_image.hdr.hist.srow_x(1:3); - orientation(1:3,2) = grid_image.hdr.hist.srow_y(1:3); - orientation(1:3,3) = grid_image.hdr.hist.srow_z(1:3); - orientation = inv(orientation); - grid_dim=[grid_image.hdr.dime.dim(2), ... - grid_image.hdr.dime.dim(3), ... - grid_image.hdr.dime.dim(4) ... - ]; - - grad_data = zeros(size(grid_data)); - - % Precompute the basis values - basis = getBSplineCoefficient(0); - first = getBSplineCoefficientFirstOrder(0); - % Compute the value at the control point position only - for x=2:grid_dim(1)-1 - for y=2:grid_dim(2)-1 - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:3 - for b=1:3 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for a=1:3 - for b=1:3 - if x+a-2<=grid_dim(1) && x+a-2>0 && ... - y+b-2<=grid_dim(2) && y+b-2>0 - gradient(1) = - 2 * jacobian(1,1) * ... - first(-a+4) * basis(-b+4); - gradient(2) = - 2 * jacobian(2,2) * ... - basis(-a+4) * first(-b+4); - grad_data(x+a-2,y+b-2,1,1,1:2) = ... - squeeze(grad_data(x+a-2,y+b-2,1,1,1:2)) + ... - orientation(1:2,1:2) \ ... - [gradient(1), gradient(2)]' ... - ./ prod(grid_dim); - end - end - end - else - for z=2:grid_dim(3)-1 - jacobian = zeros(3,3); - for a=1:3 - for b=1:3 - for c=1:3 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - end - end - end - jacobian = orientation(:,:) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:3 - for b=1:3 - for c=1:3 - if x+a-2<=grid_dim(1) && x+a-2>0 && ... - y+b-2<=grid_dim(2) && y+b-2>0 && ... - z+c-2<=grid_dim(3) && z+c-2>0 - gradient(1) = - 2 * ... - jacobian(1,1) * first(-a+4) * basis(-b+4) * basis(-c+4); - gradient(2) = - 2 * ... - jacobian(2,2) * basis(-a+4) * first(-b+4) * basis(-c+4); - gradient(3) = - 2 * ... - jacobian(3,3) * basis(-a+4) * basis(-b+4) * first(-c+4); - grad_data(x+a-2,y+b-2,z+c-2,1,1:3) = ... - squeeze(grad_data(x+a-2,y+b-2,z+c-2,1,1:3)) + ... - orientation \ ... - [gradient(1), gradient(2), gradient(3)]' ... - ./ prod(grid_dim); - end - end - end - end - end - end - end - end - clear basis first - % Save the control point gradient - gradField_nii=make_nii(grad_data,... - [grid_image.hdr.dime.pixdim(2),... - grid_image.hdr.dime.pixdim(3),... - grid_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - gradField_nii.hdr.dime.pixdim(1)=grid_image.hdr.dime.pixdim(1); - gradField_nii.hdr.hist.quatern_b=grid_image.hdr.hist.quatern_b; - gradField_nii.hdr.hist.quatern_c=grid_image.hdr.hist.quatern_c; - gradField_nii.hdr.hist.quatern_d=grid_image.hdr.hist.quatern_d; - gradField_nii.hdr.hist.qoffset_x=grid_image.hdr.hist.qoffset_x; - gradField_nii.hdr.hist.qoffset_y=grid_image.hdr.hist.qoffset_y; - gradField_nii.hdr.hist.qoffset_z=grid_image.hdr.hist.qoffset_z; - gradField_nii.hdr.hist=grid_image.hdr.hist; - gradField_nii.hdr.hist.srow_x=grid_image.hdr.hist.srow_x; - gradField_nii.hdr.hist.srow_y=grid_image.hdr.hist.srow_y; - gradField_nii.hdr.hist.srow_z=grid_image.hdr.hist.srow_z; - filename_nii=[output_path,'/le_grad_spline_approx', ... - int2str(i+1), 'D.nii.gz']; - save_nii(gradField_nii, filename_nii); - fprintf('%s has been saved\n', filename_nii); - - % Read the def image - def_image = load_untouch_nii(defField_name{i}); - def_dim=[def_image.hdr.dime.dim(2), ... - def_image.hdr.dime.dim(3), ... - def_image.hdr.dime.dim(4) ... - ]; - spacing = grid_image.hdr.dime.pixdim(2) / def_image.hdr.dime.pixdim(2); - % reset to gradient data - grad_data = zeros(size(grid_data)); - % Compute the value from all voxel position - for x=0:def_dim(1)-1 - pre_x = floor(x/spacing); - norm_x = x/spacing - pre_x; - basis_x = getBSplineCoefficient(norm_x); - first_x = getBSplineCoefficientFirstOrder(norm_x); - for y=0:def_dim(2)-1 - pre_y = floor(y/spacing); - norm_y = y/spacing - pre_y; - basis_y = getBSplineCoefficient(norm_y); - first_y = getBSplineCoefficientFirstOrder(norm_y); - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:4 - for b=1:4 - jacobian(1,1)=jacobian(1,1) + ... - first_x(a) * basis_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis_x(a) * first_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first_x(a) * basis_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis_x(a) * first_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for b=1:4 - for a=1:4 - gradient(1) = - 2 * jacobian(1,1) * ... - first_x(-a+5) * basis_y(-b+5); - gradient(2) = - 2 * jacobian(2,2) * ... - basis_x(-a+5) * first_y(-b+5); - grad_data(pre_x+a,pre_y+b,1,1,1:2) = ... - squeeze(grad_data(pre_x+a,pre_y+b,1,1,1:2)) + ... - orientation(1:2,1:2) \ ... - [gradient(1), gradient(2)]' ... - ./ prod(def_dim); - end - end - else - for z=0:def_dim(3)-1 - pre_z = floor(z/spacing); - norm_z = z/spacing - pre_z; - basis_z = getBSplineCoefficient(norm_z); - first_z = getBSplineCoefficientFirstOrder(norm_z); - jacobian = zeros(3,3); - for a=1:4 - for b=1:4 - for c=1:4 - jacobian(1,1)=jacobian(1,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - end - end - end - jacobian = orientation(:,:) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:4 - for b=1:4 - for c=1:4 - gradient(1) = - 2 * ... - jacobian(1,1) * ... - first_x(-a+5) * basis_y(-b+5) * basis_z(-c+5); - gradient(2) = - 2 * ... - jacobian(2,2) * ... - basis_x(-a+5) * first_y(-b+5) * basis_z(-c+5); - gradient(3) = - 2 * ... - jacobian(3,3) * ... - basis_x(-a+5) * basis_y(-b+5) * first_z(-c+5); - grad_data(pre_x+a,pre_y+b,pre_z+c,1,1:3) = ... - squeeze(grad_data(pre_x+a,pre_y+b,pre_z+c,1,1:3)) + ... - orientation \ ... - [gradient(1), gradient(2), gradient(3)]' ... - ./ prod(def_dim); - end - end - end - end - end - end - end - % Save the control point gradient - clear gradField_nii - gradField_nii=make_nii(grad_data,... - [grid_image.hdr.dime.pixdim(2),... - grid_image.hdr.dime.pixdim(3),... - grid_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - gradField_nii.hdr.dime.pixdim(1)=grid_image.hdr.dime.pixdim(1); - gradField_nii.hdr.hist.quatern_b=grid_image.hdr.hist.quatern_b; - gradField_nii.hdr.hist.quatern_c=grid_image.hdr.hist.quatern_c; - gradField_nii.hdr.hist.quatern_d=grid_image.hdr.hist.quatern_d; - gradField_nii.hdr.hist.qoffset_x=grid_image.hdr.hist.qoffset_x; - gradField_nii.hdr.hist.qoffset_y=grid_image.hdr.hist.qoffset_y; - gradField_nii.hdr.hist.qoffset_z=grid_image.hdr.hist.qoffset_z; - gradField_nii.hdr.hist=grid_image.hdr.hist; - gradField_nii.hdr.hist.srow_x=grid_image.hdr.hist.srow_x; - gradField_nii.hdr.hist.srow_y=grid_image.hdr.hist.srow_y; - gradField_nii.hdr.hist.srow_z=grid_image.hdr.hist.srow_z; - filename_nii=[output_path,'/le_grad_spline_dense', ... - int2str(i+1), 'D.nii.gz']; - save_nii(gradField_nii, filename_nii); - fprintf('%s has been saved\n', filename_nii); - clear grid_image; - - % Gradient from deformation field - def_data = def_image.img; - grad_data = zeros(size(def_data)); - orientation(1:3,1) = def_image.hdr.hist.srow_x(1:3); - orientation(1:3,2) = def_image.hdr.hist.srow_y(1:3); - orientation(1:3,3) = def_image.hdr.hist.srow_z(1:3); - orientation = inv(orientation); - basis=[1,0]; - first=[-1,1]; - for x=1:def_dim(1) - if x==def_dim(1) - X=x-1; - else - X=x; - end - for y=1:def_dim(2) - if y==def_dim(2) - Y=y-1; - else - Y=y; - end - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:2 - for b=1:2 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for b=1:2 - for a=1:2 - gradient(1) = - 2 * jacobian(1,1) * ... - first(-a+3) * basis(-b+3); - gradient(2) = - 2 * jacobian(2,2) * ... - basis(-a+3) * first(-b+3); - grad_data(X+a-1,Y+b-1,1,1,1:2) = ... - squeeze(grad_data(X+a-1,Y+b-1,1,1,1:2)) + ... - orientation(1:2,1:2) \ ... - [gradient(1), gradient(2)]' ... - ./ prod(def_dim); - end - end - else - for z=1:def_dim(3) - if z==def_dim(3) - Z=z-1; - else - Z=z; - end - jacobian = zeros(3,3); - for a=1:2 - for b=1:2 - for c=1:2 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - end - end - end - jacobian = orientation(:,:) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:2 - for b=1:2 - for c=1:2 - gradient(1) = - 2 * ... - jacobian(1,1) * ... - first(-a+3) * basis(-b+3) * basis(-c+3); - gradient(2) = - 2 * ... - jacobian(2,2) * ... - basis(-a+3) * first(-b+3) * basis(-c+3); - gradient(3) = - 2 * ... - jacobian(3,3) * ... - basis(-a+3) * basis(-b+3) * first(-c+3); - grad_data(X+a-1,Y+b-1,Z+c-1,1,1:3) = ... - squeeze(grad_data(X+a-1,Y+b-1,Z+c-1,1,1:3)) + ... - orientation \ ... - [gradient(1), gradient(2), gradient(3)]' ... - ./ prod(def_dim); - end - end - end - end - end - end - end - clear gradField_nii - gradField_nii=make_nii(grad_data,... - [def_image.hdr.dime.pixdim(2),... - def_image.hdr.dime.pixdim(3),... - def_image.hdr.dime.pixdim(4)],... - [], ... - 16 ... - ); - gradField_nii.hdr.dime.pixdim(1)=def_image.hdr.dime.pixdim(1); - gradField_nii.hdr.hist.quatern_b=def_image.hdr.hist.quatern_b; - gradField_nii.hdr.hist.quatern_c=def_image.hdr.hist.quatern_c; - gradField_nii.hdr.hist.quatern_d=def_image.hdr.hist.quatern_d; - gradField_nii.hdr.hist.qoffset_x=def_image.hdr.hist.qoffset_x; - gradField_nii.hdr.hist.qoffset_y=def_image.hdr.hist.qoffset_y; - gradField_nii.hdr.hist.qoffset_z=def_image.hdr.hist.qoffset_z; - gradField_nii.hdr.hist=def_image.hdr.hist; - gradField_nii.hdr.hist.srow_x=def_image.hdr.hist.srow_x; - gradField_nii.hdr.hist.srow_y=def_image.hdr.hist.srow_y; - gradField_nii.hdr.hist.srow_z=def_image.hdr.hist.srow_z; - filename_nii=[output_path,'/le_grad_field_dense', ... - int2str(i+1), 'D.nii.gz']; - save_nii(gradField_nii, filename_nii); - fprintf('%s has been saved\n', filename_nii); -end - -return - -function R = polarDecomposition(F) -%% Polar decomposition of a given matrix -C = F'*F; -[Q0, lambdasquare] = eig(C); -lambda = sqrt(diag((lambdasquare))); -Uinv = repmat(1./lambda',size(F,1),1).*Q0*Q0'; -R = F*Uinv; - -function basis = getBSplineCoefficient(dist) -%% Given a normalise position return the 4 corresponding basis values -basis(1) = (1-dist)*(1-dist)*(1-dist)/6; -basis(2) = (3*dist*dist*dist - 6*dist*dist + 4)/6.0; -basis(3) = (-3*dist*dist*dist + 3*dist*dist + 3*dist + 1)/6; -basis(4) = dist*dist*dist/6; - -function first = getBSplineCoefficientFirstOrder(dist) -%% Given a normalise position return the 4 corresponding basis values -first(4)= dist * dist / 2; -first(1)= dist - 0.5 - first(4); -first(3)= 1 + first(1) - 2*first(4); -first(2)= - first(1) - first(3) - first(4); diff --git a/reg-test/matlab_tests/getLinearElasticityValue_test.m b/reg-test/matlab_tests/getLinearElasticityValue_test.m deleted file mode 100644 index 2a119dd2..00000000 --- a/reg-test/matlab_tests/getLinearElasticityValue_test.m +++ /dev/null @@ -1,354 +0,0 @@ -function getLinearElasticityValue_test(grid2D_name, ... - def2D_name, grid3D_name, def3D_name, output_path) -%% -grid_name = {grid2D_name, grid3D_name}; -defField_name = {def2D_name, def3D_name}; - -for i=1:2 - %Read the grid image - grid_image = load_untouch_nii(grid_name{i}); - grid_data = grid_image.img; - orientation = zeros(3,3); - orientation(1:3,1) = grid_image.hdr.hist.srow_x(1:3); - orientation(1:3,2) = grid_image.hdr.hist.srow_y(1:3); - orientation(1:3,3) = grid_image.hdr.hist.srow_z(1:3); - orientation = inv(orientation); - grid_dim=[grid_image.hdr.dime.dim(2), ... - grid_image.hdr.dime.dim(3), ... - grid_image.hdr.dime.dim(4) ... - ]; - - constraint_approx = 0; - % Precompute the basis values - basis = getBSplineCoefficient(0); - first = getBSplineCoefficientFirstOrder(0); - % Compute the value at the control point position only - for x=2:grid_dim(1)-1 - for y=2:grid_dim(2)-1 - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:3 - for b=1:3 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * ... - grid_data(x+a-2, y+b-2, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for a=1:2 - for b=1:2 - constraint_approx = constraint_approx + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - else - for z=2:grid_dim(3)-1 - jacobian = zeros(3,3); - for a=1:3 - for b=1:3 - for c=1:3 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first(a) * basis(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis(a) * first(b) * basis(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis(a) * basis(b) * first(c) * ... - grid_data(x+a-2, y+b-2, z+c-2, 1, 3); - end - end - end - jacobian = orientation * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:3 - for b=1:3 - constraint_approx = constraint_approx + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - end - end - end - end - dlmwrite([output_path,'/le_spline_approx',num2str(i+1),'D.txt'], ... - constraint_approx/ numel(grid_data), ... - 'precision','%.6f','delimiter',' '); - - %Read the deformation field image - def_image = load_untouch_nii(defField_name{i}); - def_data = def_image.img; - def_dim=[def_image.hdr.dime.dim(2), ... - def_image.hdr.dime.dim(3), ... - def_image.hdr.dime.dim(4) ... - ]; - spacing = grid_image.hdr.dime.pixdim(2) / def_image.hdr.dime.pixdim(2); - constraint_dense = 0; - - % Compute the value at all voxel position - for x=0:def_dim(1)-1 - pre_x = floor(x/spacing); - norm_x = x/spacing - pre_x; - basis_x = getBSplineCoefficient(norm_x); - first_x = getBSplineCoefficientFirstOrder(norm_x); - for y=0:def_dim(2)-1 - pre_y = floor(y/spacing); - norm_y = y/spacing - pre_y; - basis_y = getBSplineCoefficient(norm_y); - first_y = getBSplineCoefficientFirstOrder(norm_y); - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:4 - for b=1:4 - jacobian(1,1)=jacobian(1,1) + ... - first_x(a) * basis_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis_x(a) * first_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first_x(a) * basis_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis_x(a) * first_y(b) * ... - grid_data(pre_x+a, pre_y+b, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for a=1:2 - for b=1:2 - constraint_dense = constraint_dense + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - else - for z=0:def_dim(3)-1 - pre_z = floor(z/spacing); - norm_z = z/spacing - pre_z; - basis_z = getBSplineCoefficient(norm_z); - first_z = getBSplineCoefficientFirstOrder(norm_z); - jacobian = zeros(3,3); - for a=1:4 - for b=1:4 - for c=1:4 - jacobian(1,1)=jacobian(1,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first_x(a) * basis_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis_x(a) * first_y(b) * basis_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis_x(a) * basis_y(b) * first_z(c) * ... - grid_data(pre_x+a, pre_y+b, pre_z+c, 1, 3); - end - end - end - jacobian = orientation * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:3 - for b=1:3 - constraint_dense = constraint_dense + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - end - end - end - end - dlmwrite([output_path,'/le_spline_dense',num2str(i+1),'D.txt'], ... - constraint_dense/ numel(def_data), ... - 'precision','%.6f','delimiter',' '); - clear grid_image; - - orientation(1:3,1) = def_image.hdr.hist.srow_x(1:3); - orientation(1:3,2) = def_image.hdr.hist.srow_y(1:3); - orientation(1:3,3) = def_image.hdr.hist.srow_z(1:3); - orientation = inv(orientation); - basis=[1,0]; - first=[-1,1]; - constraint_dense=0; - for x=1:def_dim(1) - if x==def_dim(1) - X=x-1; - else - X=x; - end - for y=1:def_dim(2) - if y==def_dim(2) - Y=y-1; - else - Y=y; - end - if (i+1)==2 - jacobian = zeros(2,2); - for a=1:2 - for b=1:2 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 1); - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * ... - def_data(X+a-1, Y+b-1, 1, 1, 2); - end - end - jacobian = orientation(1:2,1:2) * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(2); - for a=1:2 - for b=1:2 - constraint_dense = constraint_dense + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - else - for z=1:def_dim(3) - if z==def_dim(3) - Z=z-1; - else - Z=z; - end - jacobian = zeros(3,3); - for a=1:2 - for b=1:2 - for c=1:2 - jacobian(1,1)=jacobian(1,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - jacobian(1,2)=jacobian(1,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - jacobian(1,3)=jacobian(1,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 1); - - jacobian(2,1)=jacobian(2,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - jacobian(2,2)=jacobian(2,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - jacobian(2,3)=jacobian(2,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 2); - - jacobian(3,1)=jacobian(3,1) + ... - first(a) * basis(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - jacobian(3,2)=jacobian(3,2) + ... - basis(a) * first(b) * basis(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - jacobian(3,3)=jacobian(3,3) + ... - basis(a) * basis(b) * first(c) * ... - def_data(X+a-1, Y+b-1, Z+c-1, 1, 3); - end - end - end - jacobian = orientation * jacobian'; - rotation = polarDecomposition(jacobian); - jacobian = (rotation) \ jacobian; - jacobian = jacobian - eye(3); - for a=1:3 - for b=1:3 - constraint_dense = constraint_dense + ... - (0.5*(jacobian(a,b)+jacobian(b,a)))^2; - end - end - end - end - end - end - - dlmwrite([output_path,'/le_field_dense',num2str(i+1),'D.txt'], ... - constraint_dense/ numel(def_data), ... - 'precision','%.6f','delimiter',' '); -end - -return - -function R = polarDecomposition(F) -%% Polar decomposition of a given matrix -C = F'*F; -[Q0, lambdasquare] = eig(C); -lambda = sqrt(diag((lambdasquare))); -Uinv = repmat(1./lambda',size(F,1),1).*Q0*Q0'; -R = F*Uinv; - -function basis = getBSplineCoefficient(dist) -%% Given a normalise position return the 4 corresponding basis values -basis(1) = (1-dist)*(1-dist)*(1-dist)/6; -basis(2) = (3*dist*dist*dist - 6*dist*dist + 4)/6.0; -basis(3) = (-3*dist*dist*dist + 3*dist*dist + 3*dist + 1)/6; -basis(4) = dist*dist*dist/6; - -function first = getBSplineCoefficientFirstOrder(dist) -%% Given a normalise position return the 4 corresponding basis values -first(4)= dist * dist / 2; -first(1)= dist - 0.5 - first(4); -first(3)= 1 + first(1) - 2*first(4); -first(2)= - first(1) - first(3) - first(4); \ No newline at end of file diff --git a/reg-test/matlab_tests/imageGradient_test.m b/reg-test/matlab_tests/imageGradient_test.m deleted file mode 100644 index 59aca39b..00000000 --- a/reg-test/matlab_tests/imageGradient_test.m +++ /dev/null @@ -1,89 +0,0 @@ -%% COMPUTE GRADIENT -function imageGradient_test(img2D, img3D, output_path) -imgCell={img2D img3D}; -for img=1:length(imgCell) - % Read the nifti file - current = load_untouch_nii(imgCell{img}); - % Define the input image dim - inDimVect = current.hdr.dime.dim; - if inDimVect(4) > 1 - imgDim = 3; - else - imgDim = 2; - end - % Define the padded image dim - padDimVect = inDimVect; - padDimVect(2) = padDimVect(2) + 2; - padDimVect(3) = padDimVect(3) + 2; - if imgDim > 2 - padDimVect(4) = padDimVect(4) + 2; - end - % pad the input image with zeros - currentImgnD = zeros(padDimVect(2:end)); - if imgDim > 2 - currentImgnD(2:end-1,2:end-1,2:end-1,:) = current.img; - else - currentImgnD(2:end-1,2:end-1,:,:) = current.img; - end - % Define the kernel to use - if imgDim == 2 - convKernelX = [1 0 -1]/2; - convKernelY = convKernelX'; - else - convKernelX = [1 0 -1]/2; - convKernelY = convKernelX'; - convKernelZ(1,1,1) = 1/2; - convKernelZ(1,1,2) = 0; - convKernelZ(1,1,3)= -1/2; - end - % Create an image to save the gradient - sizeImgGrad = [size(currentImgnD),imgDim]; - imgGradient = zeros(sizeImgGrad); - % Iterate over the time points - for ii=1:inDimVect(5) - currentImg=currentImgnD(:,:,:,ii); - % Convolution of the x axis - imgGradientX = single(convn(double(currentImg),convKernelX,'valid')); - imgGradientX(isnan(imgGradientX))=0; - % Convolution of the y axis - imgGradientY = single(convn(double(currentImg),convKernelY,'valid')); - imgGradientY(isnan(imgGradientY))=0; - % Convolution of the z axis - if imgDim == 3 - imgGradientZ = single(convn(double(currentImg),convKernelZ,'valid')); - imgGradientZ(isnan(imgGradientZ))=0; - end - % - if imgDim == 2 - % - imgGradient(2:end-1,:,1,ii,1)=imgGradientY; - imgGradient(:,2:end-1,1,ii,2)=imgGradientX; - else - % - imgGradient(2:end-1,:,:,ii,1)=imgGradientY; - imgGradient(:,2:end-1,:,ii,2)=imgGradientX; - imgGradient(:,:,2:end-1,ii,3)=imgGradientZ; - end - end - %% SAVE - % The floating and warped image should have the same datatype ! - if imgDim > 2 - gradient_nii = make_nii(imgGradient(2:end-1,2:end-1,2:end-1,:),... - [current.hdr.dime.pixdim(2),... - current.hdr.dime.pixdim(3),... - current.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float - else - gradient_nii = make_nii(imgGradient(2:end-1,2:end-1,:,:),... - [current.hdr.dime.pixdim(2),... - current.hdr.dime.pixdim(3),... - current.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float - end - % - save_nii(gradient_nii, ... - [output_path,'/expectedImageGradient',num2str(imgDim),'D.nii.gz']); -end -end \ No newline at end of file diff --git a/reg-test/matlab_tests/interpolation_test.m b/reg-test/matlab_tests/interpolation_test.m deleted file mode 100644 index 1e1eb6ab..00000000 --- a/reg-test/matlab_tests/interpolation_test.m +++ /dev/null @@ -1,92 +0,0 @@ -function interpolation_test(img2D_name, img3D_name, def2D_name, def3D_name, output_path) -%% -%% Create a warped image from an affine deformation field according to an interpolation order -%% -inputImg_name= {img2D_name, img3D_name}; -inputDef_name= {def2D_name, def3D_name}; - -for d=2:3 - % Read the input images - inputImage = load_untouch_nii(inputImg_name{d-1}); % read the Nifti file - inputData = single(inputImage.img); - HMatrix = eye(4,4,'single'); - HMatrix(1,:) = inputImage.hdr.hist.srow_x; - HMatrix(2,:) = inputImage.hdr.hist.srow_y; - HMatrix(3,:) = inputImage.hdr.hist.srow_z; - worldToVoxelMatrix = single(inv(double(HMatrix))); - inputDef = load_untouch_nii(inputDef_name{d-1}); % read the Nifti file - inputDefData = single(inputDef.img); - - for type={'nearest', 'linear', 'cubic'} - - expectedWarpedImage = zeros(inputDef.hdr.dime.dim(2),... - inputDef.hdr.dime.dim(3),inputDef.hdr.dime.dim(4),... - 'single'); - - if d==2 - [X,Y] = ndgrid((0:size(inputData,1)-1),... - (0:size(inputData,2)-1)); - X=single(X); - Y=single(Y); - Z=''; - elseif d==3 - [X,Y,Z] = ndgrid((0:size(inputData,1)-1),... - (0:size(inputData,2)-1),... - (0:size(inputData,3)-1)); - X=single(X); - Y=single(Y); - Z=single(Z); - end - for kk=1:size(expectedWarpedImage,3) - for jj=1:size(expectedWarpedImage,2) - for ii=1:size(expectedWarpedImage,1) - if d==2 - currentWorldPosition = [... - inputDefData(ii,jj,kk,1,1) ... - inputDefData(ii,jj,kk,1,2) 0 1]; - elseif d==3 - currentWorldPosition = [... - inputDefData(ii,jj,kk,1,1) ... - inputDefData(ii,jj,kk,1,2) ... - inputDefData(ii,jj,kk,1,3) ... - 1]; - end - correspondingVoxelPosition = single(... - double(worldToVoxelMatrix) * ... - double(currentWorldPosition)'); - if d==2 - intensityValue = interpn(... - double(X),... - double(Y),... - double(inputData),... - double(correspondingVoxelPosition(1)),... - double(correspondingVoxelPosition(2)),... - type{1}, -999999); - elseif d==3 - intensityValue = interpn(... - double(X),... - double(Y),... - double(Z),... - double(inputData),... - double(correspondingVoxelPosition(1)),... - double(correspondingVoxelPosition(2)),... - double(correspondingVoxelPosition(3)),... - type{1}, -999999); - end - expectedWarpedImage(ii,jj,kk)=single(intensityValue); - end - end - end - expectedWarpedImage(expectedWarpedImage(:,:,:)==single(-999999)) = NaN; - % The floating and warped image should have the same datatype ! - expectedWarpedImage_nii = make_nii(expectedWarpedImage,... - [inputDef.hdr.dime.pixdim(2),... - inputDef.hdr.dime.pixdim(3),... - inputDef.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float - % - save_nii(expectedWarpedImage_nii,... - [output_path,'/warped_', type{1}, num2str(d), 'D.nii.gz']); - end -end \ No newline at end of file diff --git a/reg-test/matlab_tests/matrix_operation_test.m b/reg-test/matlab_tests/matrix_operation_test.m deleted file mode 100644 index 127444c5..00000000 --- a/reg-test/matlab_tests/matrix_operation_test.m +++ /dev/null @@ -1,41 +0,0 @@ -function matrix_operation_test(output_path) -%% -%% mat44 operation tests -%% -m=4; -M1 = rand(m,m,'single')+ eye(4,4,'single'); -M1(end,:)=[0 0 0 1]; -M2 = rand(m,m,'single')+ eye(4,4,'single'); -dlmwrite([output_path,'/inputMatrix1.txt'],M1,'precision','%.6f','delimiter',' '); -dlmwrite([output_path,'/inputMatrix2.txt'],M2,'precision','%.6f','delimiter',' '); -%% READ THE FILES THAT WE HAVE JUST WRITTEN -fileID = fopen([output_path,'/inputMatrix1.txt'],'r'); -formatSpec = '%f'; -sizeM1 = [m m]; -M1 = fscanf(fileID,formatSpec,sizeM1); -fclose(fileID); -M1=single(M1'); -fileID2 = fopen([output_path,'/inputMatrix2.txt'],'r'); -formatSpec = '%f'; -sizeM2 = [m m]; -M2 = fscanf(fileID2,formatSpec,sizeM2); -fclose(fileID2); -M2=single(M2'); -%% 1. Multiplication -expectedMul = single(double(M1)*double(M2)); -dlmwrite([output_path,'/expectedMulMatrix.txt'],expectedMul,'precision','%.6f','delimiter',' '); -%% 2. Addition -expectedAdd = single(double(M1)+double(M2)); -dlmwrite([output_path,'/expectedAddMatrix.txt'],expectedAdd,'precision','%.6f','delimiter',' '); -%% 3. Subtraction -expectedSub = single(double(M1)-double(M2)); -dlmwrite([output_path,'/expectedSubMatrix.txt'],expectedSub,'precision','%.6f','delimiter',' '); -%% 4. Exp -expectedExp = single(expm(double(M1))); -dlmwrite([output_path,'/expectedExpMatrix.txt'],expectedExp,'precision','%.6f','delimiter',' '); -%% 5. Log -expectedLog = single(logm(double(M1))); -dlmwrite([output_path,'/expectedLogMatrix.txt'],expectedLog,'precision','%.6f','delimiter',' '); -%% 6. Inv -expectedInv = single(inv(double(M1))); -dlmwrite([output_path,'/expectedInvMatrix.txt'],expectedInv,'precision','%.6f','delimiter',' '); diff --git a/reg-test/matlab_tests/mindDescriptor_test.m b/reg-test/matlab_tests/mindDescriptor_test.m deleted file mode 100644 index b8b0f623..00000000 --- a/reg-test/matlab_tests/mindDescriptor_test.m +++ /dev/null @@ -1,172 +0,0 @@ -function [expectedMIND2DDescriptorImage_nii, expectedMIND3DDescriptorImage_nii] = ... - mindDescriptor_test(img2D, img3D, output_path,rescaleImg, mask2D, mask3D) -% -p=1; -% -%% 2D -%convUniformKernel=ones(2*p+1,2*p+1); -convKernel = fspecial('gaussian', 2*p+1, 0.5); -%% -refImg2D = load_untouch_nii(img2D); % read the Nifti file -RSampling = [-1 1 0 0;0 0 -1 1]; -refImg2DImg = single(refImg2D.img); -% -if (nargin < 5) - inputMask2D = ones(size(refImg2DImg)); -else - inputMask2D = mask2D; -end -% -idZeros=find(inputMask2D==0); -refImg2DImg(idZeros) = 0;%NaN -% -if(rescaleImg) - %% TO BE Consitent with NiftyReg - image rescaling - minrefImg2D = double(min(refImg2DImg(:))); - maxrefImg2D = double(max(refImg2DImg(:))); - refImg2DImg = single((double(refImg2DImg)-minrefImg2D)./(maxrefImg2D-minrefImg2D)); - %refImg2DPrime = (refImg2DPrime-minrefImg2D)./(maxrefImg2D-minrefImg2D); - %% -end -% -Dp_array = zeros([size(refImg2DImg) size(RSampling,2)],'single'); -Vp_array = zeros([size(refImg2DImg) size(RSampling,2)],'single'); -% -for id=1:size(RSampling,2) - %% Let's translate the image by rx, ry pixel - rx=RSampling(1,id); - ry=RSampling(2,id); - idNaN = find(isnan(refImg2DImg)); - refImg2DImg(idNaN)=-999; - refImg2DPrimeImg = imtranslate(refImg2DImg, [ry, rx], 'FillValues', 0);%NaN - id999 = find(refImg2DImg==-999); - refImg2DImg(id999)=NaN; - id999 = find(refImg2DPrimeImg==-999); - refImg2DPrimeImg(id999)=NaN; - diffImg = single((double(refImg2DImg)-double(refImg2DPrimeImg))); - diffImg = single(double(diffImg).^2); - %% Have to correct the borders by hand - maskImg = zeros(size(diffImg));%2*p+1-1 - maskImg(diffImg > -1) = 1; - %inputMask2DT = imtranslate(inputMask2D, [ry, rx], 'FillValues', 0); - maskImg=maskImg.*inputMask2D; - %diffImgPadded = padarray(diffImg,[1 1],'circular'); - diffImg(isnan(diffImg))=0; - diffImg=diffImg.*maskImg; - imgConv = single(conv2(double(diffImg),convKernel,'same')); - maskConv = single(conv2(double(maskImg),convKernel,'same')); - imgConv = single(double(imgConv)./double(maskConv)); - imgConv(maskImg==0)=NaN; - Dp_array(:,:,1,id) = imgConv; - %% - Vp_array(:,:,id) = Dp_array(:,:,1,id); -end -Vp_image = mean(Vp_array,3); -idZeros=find(Vp_image==0); -Vp_image(idZeros)=eps; -%% -for id=1:size(RSampling,2) - MIND2D_descriptor(:,:,1,id) = single(exp(double(-Dp_array(:,:,1,id))./double(Vp_image))); -end -%% Normalise MIND max = 1 -maxMind=max(MIND2D_descriptor,[],4); -% -for id=1:size(RSampling,2) - MIND2D_descriptor(:,:,1,id)=single(double(MIND2D_descriptor(:,:,1,id))./double(maxMind)); -end -%% SAVE -% The floating and warped image should have the same datatype ! -expectedMIND2DDescriptorImage_nii = make_nii(MIND2D_descriptor,... - [refImg2D.hdr.dime.pixdim(2),... - refImg2D.hdr.dime.pixdim(3),... - refImg2D.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float -% -save_nii(expectedMIND2DDescriptorImage_nii, [output_path,'/expectedMINDDescriptor2D.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3D -convKernel = fGaussian3D(2*p+1, 0.5); -%% -refImg3D = load_untouch_nii(img3D); % read the Nifti file -RSampling = [-1 1 0 0 0 0;0 0 -1 1 0 0;0 0 0 0 -1 1]; -refImg3DImg = single(refImg3D.img); -% -if (nargin < 6) - inputMask3D = ones(size(refImg3DImg)); -else - inputMask3D = mask3D; -end -% -idZeros=find(inputMask3D==0); -refImg3DImg(idZeros) = 0;%NaN -% -if(rescaleImg) - %% TO BE Consitent with NiftyReg - image rescaling - minrefImg3D = double(min(refImg3DImg(:))); - maxrefImg3D = double(max(refImg3DImg(:))); - refImg3DImg = single((double(refImg3DImg)-minrefImg3D)./(maxrefImg3D-minrefImg3D)); - %refImg3DPrime = (refImg3DPrime-minrefImg3D)./(maxrefImg3D-minrefImg3D); - %% -end -% -Dp_array = zeros([size(refImg3D.img) size(RSampling,2)],'single'); -Vp_array = zeros([size(refImg3D.img) size(RSampling,2)],'single'); -% -for id=1:size(RSampling,2) - %% Let's translate the image by rx, ry pixel - rx=RSampling(1,id); - ry=RSampling(2,id); - rz=RSampling(3,id); - idNaN = find(isnan(refImg3DImg)); - refImg3DImg(idNaN)=-999; - refImg3DPrimeImg = imtranslate(refImg3DImg, [ry, rx, rz], 'FillValues', 0);%NaN - id999 = find(refImg3DImg==-999); - refImg3DImg(id999)=NaN; - id999 = find(refImg3DPrimeImg==-999); - refImg3DPrimeImg(id999)=NaN; - diffImg = single(double(refImg3DImg) - double(refImg3DPrimeImg)); - diffImg = single(double(diffImg).^2); - %% Have to correct the borders by hand - maskImg = zeros(size(diffImg));%2*p+1-1 - maskImg(diffImg > -1) = 1; - %inputMask3DT = imtranslate(inputMask3D, [ry, rx, rz], 'FillValues', 0); - maskImg=maskImg.*inputMask3D; - %diffImgPadded = padarray(diffImg,[1 1],'circular'); - diffImg(isnan(diffImg))=0; - diffImg=diffImg.*maskImg; - imgConv = single(convn(double(diffImg),convKernel,'same')); - maskConv = single(convn(double(maskImg),convKernel,'same')); - imgConv = single(double(imgConv)./double(maskConv)); - imgConv(maskImg==0)=NaN; - Dp_array(:,:,:,id) = imgConv; - %% - Vp_array(:,:,:,id) = Dp_array(:,:,:,id); -end -Vp_image = mean(Vp_array,4); -idZeros=find(Vp_image==0); -Vp_image(idZeros)=eps; -%% -for id=1:size(RSampling,2) - MIND3D_descriptor(:,:,:,id) = single(exp(double(-Dp_array(:,:,:,id))./double(Vp_image))); -end -%% Normalise MIND max = 1 -maxMind=max(MIND3D_descriptor,[],4); -%max1=mean(mind,3); -for id=1:size(RSampling,2) - MIND3D_descriptor(:,:,:,id)=single(double(MIND3D_descriptor(:,:,:,id))./double(maxMind)); -end -%% SAVE -% The floating and warped image should have the same datatype ! -expectedMIND3DDescriptorImage_nii = make_nii(MIND3D_descriptor,... - [refImg3D.hdr.dime.pixdim(2),... - refImg3D.hdr.dime.pixdim(3),... - refImg3D.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float -% -save_nii(expectedMIND3DDescriptorImage_nii, [output_path,'/expectedMINDDescriptor3D.nii.gz']); -end diff --git a/reg-test/matlab_tests/mindsscDescriptor_test.m b/reg-test/matlab_tests/mindsscDescriptor_test.m deleted file mode 100644 index 8d51d3dd..00000000 --- a/reg-test/matlab_tests/mindsscDescriptor_test.m +++ /dev/null @@ -1,209 +0,0 @@ -% -% mindDescriptor_test('/home/bpresles/OneDriveBusiness/NiftyReg/refImg2D.nii.gz', ... -% '/home/bpresles/OneDriveBusiness/NiftyReg/refImg3D.nii.gz', './test-mind'); -% -function [expectedMIND2DDescriptorImage_nii, expectedMIND3DDescriptorImage_nii] = ... - mindsscDescriptor_test(img2D, img3D, output_path,rescaleImg, mask2D, mask3D) -% -p=1; -% -%% 2D -%convUniformKernel=ones(2*p+1,2*p+1); -convKernel = fspecial('gaussian', 2*p+1, 0.5); -%% -refImg2D = load_untouch_nii(img2D); % read the Nifti file -RSampling = [+1 +1;+1 -1]; -tx=[-1,+0,-1,+0]; -ty=[+0,-1,+0,+1]; -lengthDescriptor=4; -refImg2DImg = single(refImg2D.img); -% -if (nargin < 5) - inputMask2D = ones(size(refImg2DImg)); -else - inputMask2D = mask2D; -end -% -idZeros=find(inputMask2D==0); -refImg2DImg(idZeros) = NaN; -% -if(rescaleImg) - %% TO BE Consitent with NiftyReg - image rescaling - minrefImg2D = double(min(refImg2DImg(:))); - maxrefImg2D = double(max(refImg2DImg(:))); - refImg2DImg = single((double(refImg2DImg)-minrefImg2D)./(maxrefImg2D-minrefImg2D)); - %refImg2DPrime = (refImg2DPrime-minrefImg2D)./(maxrefImg2D-minrefImg2D); - %% -end -% -Dp_array = zeros([size(refImg2DImg) lengthDescriptor],'single'); -Vp_array = zeros([size(refImg2DImg) lengthDescriptor],'single'); -% -store_id=1; -% -for id=1:size(RSampling,2) - %% Let's translate the image by rx, ry pixel - rx=RSampling(1,id); - ry=RSampling(2,id); - idNaN = find(isnan(refImg2DImg)); - refImg2DImg(idNaN)=-999; - refImg2DPrimeImg = imtranslate(refImg2DImg, [ry, rx], 'FillValues', 0); %NaN - id999 = find(refImg2DImg==-999); - refImg2DImg(id999)=NaN; - id999 = find(refImg2DPrimeImg==-999); - refImg2DPrimeImg(id999)=NaN; - diffImg = single((double(refImg2DImg)-double(refImg2DPrimeImg))); - diffImg = single(double(diffImg).^2); - %% Have to correct the borders by hand - maskImg = zeros(size(diffImg));%2*p+1-1 - maskImg(diffImg > -1) = 1; - %inputMask2DT = imtranslate(inputMask2D, [ry, rx], 'FillValues', 0); - maskImg=maskImg.*inputMask2D; - %diffImgPadded = padarray(diffImg,[1 1],'circular'); - diffImg(isnan(diffImg))=0; - diffImg=diffImg.*maskImg; - imgConv = single(conv2(double(diffImg),convKernel,'same')); - maskConv = single(conv2(double(maskImg),convKernel,'same')); - imgConv = single(double(imgConv)./double(maskConv)); - imgConv(maskImg==0)=NaN; - for idtr = 1:2 - idNaN = find(isnan(imgConv)); - imgConv(idNaN)=-999; - imgConvPrime = imtranslate(imgConv, [ty(store_id), tx(store_id)], 'FillValues', 0);%NaN - id999 = find(imgConv==-999); - imgConv(id999)=NaN; - id999 = find(imgConvPrime==-999); - imgConvPrime(id999)=NaN; - %%%%%%%% - Dp_array(:,:,1,store_id) = imgConvPrime; - Vp_array(:,:,store_id) = Dp_array(:,:,1,store_id); - store_id = store_id +1; - end -end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -Vp_image = mean(Vp_array,3); -idZeros=find(Vp_image==0); -Vp_image(idZeros)=eps; -%% -for id=1:lengthDescriptor - MIND2D_descriptor(:,:,1,id) = single(exp(double(-Dp_array(:,:,1,id))./double(Vp_image))); -end -%% Normalise MIND max = 1 -maxMind=max(MIND2D_descriptor,[],4); -% -for id=1:lengthDescriptor - MIND2D_descriptor(:,:,1,id)=single(double(MIND2D_descriptor(:,:,1,id))./double(maxMind)); -end -%% SAVE -% The floating and warped image should have the same datatype ! -expectedMIND2DDescriptorImage_nii = make_nii(MIND2D_descriptor,... - [refImg2D.hdr.dime.pixdim(2),... - refImg2D.hdr.dime.pixdim(3),... - refImg2D.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float -% -save_nii(expectedMIND2DDescriptorImage_nii, [output_path,'/expectedMINDSSCDescriptor2D.nii.gz']); -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% 3D -convKernel = fGaussian3D(2*p+1, 0.5); -%% -refImg3D = load_untouch_nii(img3D); % read the Nifti file -RSampling = [+1 +1 -1 0 +1 0;+1 -1 0 -1 0 +1;0 0 +1 +1 +1 +1]; -tx=[-1,+0,-1,+0,+0,+1,+0,+0,+0,-1,+0,+0]; -ty=[+0,-1,+0,+1,+0,+0,+0,+1,+0,+0,+0,-1]; -tz=[+0,+0,+0,+0,-1,+0,-1,+0,-1,+0,-1,+0]; -lengthDescriptor=12; -refImg3DImg = single(refImg3D.img); -% -if (nargin < 6) - inputMask3D = ones(size(refImg3DImg)); -else - inputMask3D = mask3D; -end -% -idZeros=find(inputMask3D==0); -refImg3DImg(idZeros) = NaN; -% -if(rescaleImg) - %% TO BE Consitent with NiftyReg - image rescaling - minrefImg3D = double(min(refImg3DImg(:))); - maxrefImg3D = double(max(refImg3DImg(:))); - refImg3DImg = single((double(refImg3DImg)-minrefImg3D)./(maxrefImg3D-minrefImg3D)); - %refImg3DPrime = (refImg3DPrime-minrefImg3D)./(maxrefImg3D-minrefImg3D); - %% -end -% -Dp_array = zeros([size(refImg3D.img) lengthDescriptor],'single'); -Vp_array = zeros([size(refImg3D.img) lengthDescriptor],'single'); -% -store_id=1; -% -for id=1:size(RSampling,2) - %% Let's translate the image by rx, ry pixel - rx=RSampling(1,id); - ry=RSampling(2,id); - rz=RSampling(3,id); - idNaN = find(isnan(refImg3DImg)); - refImg3DImg(idNaN)=-999; - refImg3DPrimeImg = imtranslate(refImg3DImg, [ry, rx, rz], 'FillValues', 0);%NaN - id999 = find(refImg3DImg==-999); - refImg3DImg(id999)=NaN; - id999 = find(refImg3DPrimeImg==-999); - refImg3DPrimeImg(id999)=NaN; - diffImg = single(double(refImg3DImg) - double(refImg3DPrimeImg)); - diffImg = single(double(diffImg).^2); - %% Have to correct the borders by hand - maskImg = zeros(size(diffImg));%2*p+1-1 - maskImg(diffImg > -1) = 1; - %inputMask3DT = imtranslate(inputMask3D, [ry, rx, rz], 'FillValues', 0); - maskImg=maskImg.*inputMask3D; - %diffImgPadded = padarray(diffImg,[1 1],'circular'); - diffImg(isnan(diffImg))=0; - diffImg=diffImg.*maskImg; - imgConv = single(convn(double(diffImg),convKernel,'same')); - maskConv = single(convn(double(maskImg),convKernel,'same')); - imgConv = single(double(imgConv)./double(maskConv)); - imgConv(maskImg==0)=NaN; - for idtr = 1:2 - idNaN = find(isnan(imgConv)); - imgConv(idNaN)=-999; - imgConvPrime = imtranslate(imgConv, [ty(store_id), tx(store_id), tz(store_id)], 'FillValues', 0);%NaN - id999 = find(imgConv==-999); - imgConv(id999)=NaN; - id999 = find(imgConvPrime==-999); - imgConvPrime(id999)=NaN; - %%%%%%%% - Dp_array(:,:,:,store_id) = imgConvPrime; - Vp_array(:,:,:,store_id) = Dp_array(:,:,:,store_id); - store_id = store_id +1; - end -end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -Vp_image = mean(Vp_array,4); -idZeros=find(Vp_image==0); -Vp_image(idZeros)=eps; -%% -for id=1:lengthDescriptor - MIND3D_descriptor(:,:,:,id) = single(exp(double(-Dp_array(:,:,:,id))./double(Vp_image))); -end -%% Normalise MIND max = 1 -maxMind=max(MIND3D_descriptor,[],4); -%max1=mean(mind,3); -for id=1:lengthDescriptor - MIND3D_descriptor(:,:,:,id)=single(double(MIND3D_descriptor(:,:,:,id))./double(maxMind)); -end -%% SAVE -% The floating and warped image should have the same datatype ! -expectedMIND3DDescriptorImage_nii = make_nii(MIND3D_descriptor,... - [refImg3D.hdr.dime.pixdim(2),... - refImg3D.hdr.dime.pixdim(3),... - refImg3D.hdr.dime.pixdim(4)],... - [],... - 16); % 16 is float -% -save_nii(expectedMIND3DDescriptorImage_nii, [output_path,'/expectedMINDSSCDescriptor3D.nii.gz']); -end diff --git a/reg-test/matlab_tests/svd_test.m b/reg-test/matlab_tests/svd_test.m deleted file mode 100644 index e6b509f9..00000000 --- a/reg-test/matlab_tests/svd_test.m +++ /dev/null @@ -1,31 +0,0 @@ -function svd_test(output_path) -%% -%% SVD decomposition -%% -maxSizeMatrix=10;%arbitrary value -m=randi(maxSizeMatrix); -n=randi(maxSizeMatrix); -% -A=single(double(rand(m,n,'single'))+ double(eye(m,n,'single'))); -% -%% Save to file now -dlmwrite([output_path,'/inputSVDMatrix.txt'],A,'precision','%.6f','delimiter',' '); -%% READ THE FILE THAT WE HAVE JUST WRITTEN -fileID = fopen([output_path,'/inputSVDMatrix.txt'],'r'); -formatSpec = '%f'; -sizeA = [n m]; -A = fscanf(fileID,formatSpec,sizeA); -fclose(fileID); -A=single(A'); -[U,S,V] = svd(double(A),'econ'); -U=single(U); -S=single(S); -V=single(V); -%% DEBUG -%A,U,S,V -%% DEBUG -%% Save to file now -dlmwrite([output_path,'/inputSVDMatrix.txt'],A,'precision','%.6f','delimiter',' '); -dlmwrite([output_path,'/expectedUMatrix.txt'],U,'precision','%.6f','delimiter',' '); -dlmwrite([output_path,'/expectedSMatrix.txt'],S,'precision','%.6f','delimiter',' '); -dlmwrite([output_path,'/expectedVMatrix.txt'],V,'precision','%.6f','delimiter',' '); \ No newline at end of file diff --git a/reg-test/reg_test_affine_deformation_field.cpp b/reg-test/reg_test_affine_deformation_field.cpp index 62f99157..2812ef52 100644 --- a/reg-test/reg_test_affine_deformation_field.cpp +++ b/reg-test/reg_test_affine_deformation_field.cpp @@ -1,47 +1,151 @@ -#include "_reg_ReadWriteImage.h" #include "_reg_ReadWriteMatrix.h" -#include "_reg_globalTrans.h" #include "_reg_tools.h" #include "Kernel.h" #include "AffineDeformationFieldKernel.h" #include "Platform.h" +#include + #include "AladinContent.h" #ifdef _USE_CUDA #include "CUDAAladinContent.h" #endif - #ifdef _USE_OPENCL #include "CLAladinContent.h" #endif -#define EPS 0.000001 #define EPS_SINGLE 0.0001 -void test(AladinContent *con, int platformCode) { - - Platform *platform = new Platform(platformCode); - - Kernel *affineDeformKernel = platform->createKernel(AffineDeformationFieldKernel::getName(), con); - affineDeformKernel->castTo()->calculate(); - - delete affineDeformKernel; - delete platform; -} - -int main(int argc, char **argv) -{ - if (argc != 5) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return EXIT_FAILURE; +TEST_CASE("Affine deformation field", "[Affine]") { + SECTION("2D") { + // Create a reference 2D image + int dim[8]= {2, 2, 2, 1, 1, 1, 1, 1}; + nifti_image *reference = nifti_make_new_nim( + dim, + NIFTI_TYPE_FLOAT32, + true); + reg_checkAndCorrectDimension(reference); + + // Generate the different use cases + typedef std::tuple test_data; + std::vector test_use_cases; + // Identity use case + auto *identity = new mat44; + reg_mat44_eye(identity); + // Test order [0,0] [1,0] [0,1] [1,1] + float identity_result_x[4] = {0, 1, 0, 1}; + float identity_result_y[4] = {0, 0, 1, 1}; + test_use_cases.emplace_back(test_data( + "- identity", + identity, + identity_result_x, + identity_result_y) + ); + // Translation + auto *translation = new mat44; + reg_mat44_eye(translation); + translation->m[0][3] = -0.5; + translation->m[1][3] = 1.5; + // Test order [0,0] [1,0] [0,1] [1,1] + float translation_result_x[4] = {-0.5, .5, -0.5, .5}; + float translation_result_y[4] = {1.5, 1.5, 2.5, 2.5}; + test_use_cases.emplace_back(test_data( + "- translation", + translation, + translation_result_x, + translation_result_y) + ); + + + for(auto && test_use_case: test_use_cases) { + + std::string test_name; + mat44 *test_mat; + float *test_res_x; + float *test_res_y; + std::tie(test_name, test_mat, test_res_x, test_res_y) = test_use_case; + + SECTION(test_name) { + typedef std::tuple content_desc; + std::vector listContent; + // Compute the transformation field + listContent.push_back(content_desc( + new AladinContent( + reference, + nullptr, + nullptr, + test_mat, + sizeof(float)), + "-- CPU", + 0)); +#ifdef _USE_CUDA + listContent.push_back(content_desc( + new CudaAladinContent( + reference, + nullptr, + nullptr, + test_mat, + sizeof(float)), + "-- CUDA", + 1)); +#endif +#ifdef _USE_OPENCL + listContent.push_back(content_desc( + new ClAladinContent( + reference, + nullptr, + nullptr, + test_mat, + sizeof(float)), + "-- OpenCL", + 2)); +#endif + for (auto &&content: listContent) { + + AladinContent *con; + std::string desc; + int plat_value; + std::tie(con, desc, plat_value) = content; + + SECTION(desc) { + auto *platform = new Platform(plat_value); + + Kernel *affineDeformKernel = platform->createKernel( + AffineDeformationFieldKernel::getName(), + con); + affineDeformKernel->castTo()->calculate(); + + nifti_image *defField = + con->getCurrentDeformationField(); + + // Check all values + auto *defFieldPtrX = static_cast(defField->data); + auto *defFieldPtrY = &defFieldPtrX[defField->nx * + defField->ny]; + for (int i = 0; i < defField->nx*defField->ny; ++i) { + REQUIRE(fabs( + defFieldPtrX[i] - test_res_x[i]) < + EPS_SINGLE); + REQUIRE(fabs( + defFieldPtrY[i] - test_res_y[i]) < + EPS_SINGLE); + } + delete affineDeformKernel; + delete platform; + } + delete con; + } + listContent.clear(); + } + delete test_mat; + } + test_use_cases.clear(); + nifti_image_free(reference); } +} - char *inputRefImageName = argv[1]; - char *inputMatFileName = argv[2]; - char *inputDefImageName = argv[3]; - int platformCode = atoi(argv[4]); - +/* // Read the input reference image nifti_image *referenceImage = reg_io_ReadImageFile(inputRefImageName); if (referenceImage == NULL) { @@ -127,4 +231,5 @@ int main(int argc, char **argv) return EXIT_SUCCESS; } + */ diff --git a/reg-test/target_2D.nii.gz b/reg-test/target_2D.nii.gz deleted file mode 100755 index 713891705e3c20a423ec15aa386c9985f30edbfb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27076 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 x@~b|jzo&lk_telH8;_Fv5?c;NiU0RVf?DE|Ne

W~dPyX?Mfws=wNBGWv zX}drnCJl@PT2PrZ7#wJkWzrD4IJ@697#srJ2qq0F3*e2IG}JDV>vs(f2Ji6&IKUoX z;ILh$7Smb;6lj5E(&P|(G=wZ{&p8kPyp4G+0tUbXnf5~Z5CjCMUBBf6U|hlXXizYGk6q|4o04fQ6t&kb6oq1* z2SdU4$1p&Ye|C?P;4}h^~@4?9dbbS9^(&7K- zb^Py2ZR_FrcTq#g!oe_3j`Qa)8ESEI{Ik%>a&r8C_y#iau=fU_Xn;$g-S4@po^HSh P2pO}}U?dX!52ybFsv*mj diff --git a/reg-test/matlab_tests/NIfTI_20140122/UseANALYZE.pdf b/reg-test/matlab_tests/NIfTI_20140122/UseANALYZE.pdf deleted file mode 100644 index 4bcdf2182147abe1fbc322c76b91206559cde731..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91343 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