pax_global_header00006660000000000000000000000064125746226740014531gustar00rootroot0000000000000052 comment=4ec4e79f7e7cf41ec61e24865096316efbf90bb3 libminc-libminc-2-3-00/000077500000000000000000000000001257462267400146415ustar00rootroot00000000000000libminc-libminc-2-3-00/.gitignore000066400000000000000000000003641257462267400166340ustar00rootroot00000000000000*~ INSTALL *.o *.lo *.la Makefile .deps .dirstamp .libs aclocal.m4 autom4te.cache/ configure config.h config.log config.status libtool stamp-h1 volume_io/Testing/example_modify volume_io/Testing/example_tags volume_io/Testing/example_volume_io libminc-libminc-2-3-00/.gitmodules000066400000000000000000000000001257462267400170040ustar00rootroot00000000000000libminc-libminc-2-3-00/AUTHORS000066400000000000000000000010401257462267400157040ustar00rootroot00000000000000Peter Neelin designed, coded, and maintained MINC (1992-2002). David MacDonald designed, coded, and maintained VolumeIO (1991-1998). Current maintenance is done on the minc-development mailing list. http://www.bic.mni.mcgill.ca/mailman/listinfo/minc-development MINC Contributors (in alphabetical order) Leila Baghdadi Vladimir Fonov Colin Holmes Andrew Janke David Leonard (original minccalc parser) Claude Lepage Ilana Leppert (dcm2mnc DTI additions) Jason Lerch Sean McBride (many warning fixes) Steven Robbins John Sled Robert Vincent libminc-libminc-2-3-00/CMakeLists.txt000066400000000000000000000365371257462267400174170ustar00rootroot00000000000000# CMakeFiles.txt for the libminc library # # Andrew Janke - a.janke@gmail.com # Vladimir S. FONOV - vladimir.fonov@gmail.com PROJECT(LIBMINC) SET(LIBMINC_PACKAGE_VERSION_MAJOR 2) SET(LIBMINC_PACKAGE_VERSION_MINOR 3) SET(LIBMINC_PACKAGE_VERSION_PATCH 00) SET(LIBMINC_SOVERSION "4.0.0") SET(LIBMINC_PACKAGE "libminc") SET(LIBMINC_PACKAGE_BUGREPORT "a.janke@gmail.com") SET(LIBMINC_PACKAGE_NAME "libminc") SET(LIBMINC_PACKAGE_VERSION "${LIBMINC_PACKAGE_VERSION_MAJOR}.${LIBMINC_PACKAGE_VERSION_MINOR}.${LIBMINC_PACKAGE_VERSION_PATCH}") SET(LIBMINC_PACKAGE_STRING "${LIBMINC_PACKAGE_NAME} ${LIBMINC_PACKAGE_VERSION}") INCLUDE(CTest) ENABLE_TESTING() CMAKE_MINIMUM_REQUIRED(VERSION 2.6) IF(MINC_TOOLKIT_BUILD) SET(LIBMINC_EXTERNALLY_CONFIGURED ON) ENDIF(MINC_TOOLKIT_BUILD) SET(LIBMINC_BUILD_V2 ON) IF(NOT LIBMINC_EXTERNALLY_CONFIGURED) # Allowing User to configure parameters SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules") SET(CPACK_GENERATOR TGZ) SET(CPACK_PACKAGE_VERSION_MAJOR ${LIBMINC_PACKAGE_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${LIBMINC_PACKAGE_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${LIBMINC_PACKAGE_VERSION_PATCH}) SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Medical Imaging library core IO") OPTION(LIBMINC_BUILD_SHARED_LIBS "Build libminc with shared libraries." OFF) OPTION(LIBMINC_MINC1_SUPPORT "Support minc1 file format, requires NETCDF" OFF) OPTION(LIBMINC_BUILD_EZMINC_EXAMPLES "Build EZminc examples" OFF) SET (LIBMINC_EXPORTED_TARGETS "LIBMINC-targets") SET (LIBMINC_INSTALL_BIN_DIR bin) SET (LIBMINC_INSTALL_LIB_DIR lib${LIB_SUFFIX}) SET (LIBMINC_INSTALL_INCLUDE_DIR include) SET (LIBMINC_INSTALL_DATA_DIR share) IF(LIBMINC_MINC1_SUPPORT) FIND_PACKAGE(NETCDF REQUIRED) ENDIF(LIBMINC_MINC1_SUPPORT) # external packages FIND_PACKAGE(ZLIB REQUIRED) FIND_PACKAGE(HDF5 REQUIRED) SET(HAVE_ZLIB ON) ELSE(NOT LIBMINC_EXTERNALLY_CONFIGURED) #TODO: set paths for HDF5 etc ENDIF(NOT LIBMINC_EXTERNALLY_CONFIGURED) # Configure libminc IF(LIBMINC_BUILD_SHARED_LIBS) SET(LIBRARY_TYPE SHARED) SET(LIBRARY_INSTALL LIBRARY) ELSE(LIBMINC_BUILD_SHARED_LIBS) SET(LIBRARY_TYPE STATIC) SET(LIBRARY_INSTALL ARCHIVE) ENDIF(LIBMINC_BUILD_SHARED_LIBS) IF(CMAKE_BUILD_TYPE MATCHES Debug) SET(DEBUG "1") ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) # add for building relocatable library IF(UNIX) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") ENDIF(UNIX) # check for prereqs INCLUDE(CheckFunctionExists) CHECK_FUNCTION_EXISTS(mkstemp HAVE_MKSTEMP) CHECK_FUNCTION_EXISTS(tmpnam HAVE_TMPNAM) CHECK_FUNCTION_EXISTS(tempnam HAVE_TEMPNAM) CHECK_FUNCTION_EXISTS(strerror HAVE_STRERROR) CHECK_FUNCTION_EXISTS(popen HAVE_POPEN) CHECK_FUNCTION_EXISTS(fork HAVE_WORKING_FORK) CHECK_FUNCTION_EXISTS(vfork HAVE_WORKING_VFORK) CHECK_FUNCTION_EXISTS(fdopen HAVE_FDOPEN) CHECK_FUNCTION_EXISTS(strdup HAVE_STRDUP) CHECK_FUNCTION_EXISTS(getpwnam HAVE_GETPWNAM) CHECK_FUNCTION_EXISTS(select HAVE_SELECT) CHECK_FUNCTION_EXISTS(strerror HAVE_STRERROR) CHECK_FUNCTION_EXISTS(sysconf HAVE_SYSCONF) CHECK_FUNCTION_EXISTS(system HAVE_SYSTEM) CHECK_FUNCTION_EXISTS(srand48 HAVE_SRAND48) CHECK_FUNCTION_EXISTS(drand48 HAVE_DRAND48) CHECK_FUNCTION_EXISTS(sleep HAVE_SLEEP) CHECK_FUNCTION_EXISTS(gettimeofday HAVE_GETTIMEOFDAY) INCLUDE(CheckLibraryExists) CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) IF(HAVE_CLOCK_GETTIME) SET(RT_LIBRARY "rt") ENDIF(HAVE_CLOCK_GETTIME) INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILES(float.h HAVE_FLOAT_H) CHECK_INCLUDE_FILES(sys/dir.h HAVE_SYS_DIR_H) CHECK_INCLUDE_FILES(sys/ndir.h HAVE_SYS_NDIR_H) CHECK_INCLUDE_FILES(sys/stat.h HAVE_SYS_STAT_H) CHECK_INCLUDE_FILES(sys/types.h HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILES(sys/wait.h HAVE_SYS_WAIT_H) CHECK_INCLUDE_FILES(sys/time.h HAVE_SYS_TIME_H) CHECK_INCLUDE_FILES(values.h HAVE_VALUES_H) CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILES(dirent.h HAVE_DIRENT_H) CHECK_INCLUDE_FILES(memory.h HAVE_MEMORY_H) CHECK_INCLUDE_FILES(stdlib.h HAVE_STDLIB_H) CHECK_INCLUDE_FILES(fcntl.h HAVE_FCNTL_H) CHECK_INCLUDE_FILES(dlfcn.h HAVE_DLFCN_H) CHECK_INCLUDE_FILES(vfork.h HAVE_VFORK_H) CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILES(string.h HAVE_STRING_H) CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILES(pwd.h HAVE_PWD_H) ADD_DEFINITIONS(-DHAVE_CONFIG_H) ADD_DEFINITIONS(-DMINC2=1) # aliases SET(VERSION "${LIBMINC_EXTERNAL_LIB_PREFIX}${LIBMINC_PACKAGE_VERSION}") SET(HAVE_MINC1 ${LIBMINC_MINC1_SUPPORT}) SET(HAVE_MINC2 ON) SET(MINC2 "1") IF(LIBMINC_MINC1_SUPPORT) ADD_DEFINITIONS(-DHAVE_MINC1=1) ENDIF(LIBMINC_MINC1_SUPPORT) IF(LIBMINC_BUILD_EZMINC) set(LIBMINC_INCLUDE_DIRS_CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/ezminc ) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/ezminc ) ENDIF(LIBMINC_BUILD_EZMINC) # config files for build CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) # others #CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/epm-header.in ${CMAKE_CURRENT_BINARY_DIR}/epm-header) # set the master INCLUDE directories INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libsrc2 ${CMAKE_CURRENT_SOURCE_DIR}/libsrc ${CMAKE_CURRENT_SOURCE_DIR}/volume_io/Include ${CMAKE_CURRENT_SOURCE_DIR}/ezminc ${CMAKE_CURRENT_SOURCE_DIR}/nifti ${HDF5_INCLUDE_DIR} ) SET(minc_common_SRCS libsrc/time_stamp.c libsrc/read_file_names.c libsrc/ParseArgv.c libsrc/restructure.c ) SET(minc_common_HEADERS libsrc/time_stamp.h libsrc/ParseArgv.h libsrc/read_file_names.h libsrc/restructure.h volume_io/Include/volume_io.h nifti/nifti1.h nifti/nifti1_io.h nifti/znzlib.h ) # some variables SET(minc1_LIB_SRCS libsrc/dim_conversion.c libsrc/image_conversion.c libsrc/minc_convenience.c libsrc/minc_error.c libsrc/nd_loop.c libsrc/netcdf_convenience.c libsrc/value_conversion.c libsrc/voxel_loop.c libsrc/hdf_convenience.c libsrc/minc_compat.c libsrc/minc_simple.c libsrc/minc_format_convert.c ) SET(minc1_HEADERS libsrc/minc.h libsrc/voxel_loop.h libsrc/nd_loop.h libsrc/minc_compat.h libsrc/minc_simple.h libsrc/minc_format_convert.h ) SET(minc2_LIB_SRCS libsrc2/convert.c libsrc2/datatype.c libsrc2/dimension.c libsrc2/free.c libsrc2/grpattr.c libsrc2/hyper.c libsrc2/label.c libsrc2/m2util.c libsrc2/record.c libsrc2/slice.c libsrc2/valid.c libsrc2/volprops.c libsrc2/volume.c libsrc2/minc2_error.c ) SET(minc2_HEADERS libsrc2/minc2.h libsrc2/minc2_defs.h libsrc2/minc2_structs.h libsrc2/minc2_api.h libsrc2/minc2_error.h ) # volume_io2 SET(volume_io_LIB_SRCS volume_io/Geometry/colour.c volume_io/Geometry/colour_def.c volume_io/Geometry/gaussian.c volume_io/Geometry/inverse.c volume_io/Geometry/newton.c volume_io/Geometry/points.c volume_io/Geometry/splines.c volume_io/Geometry/tensors.c volume_io/Geometry/transforms.c volume_io/MNI_formats/gen_xf_io.c volume_io/MNI_formats/gen_xfs.c volume_io/MNI_formats/grid_transforms.c volume_io/MNI_formats/mni_io.c volume_io/MNI_formats/tag_points.c volume_io/MNI_formats/thin_plate_spline.c volume_io/Prog_utils/alloc.c volume_io/Prog_utils/alloc_check.c volume_io/Prog_utils/arrays.c volume_io/Prog_utils/files.c volume_io/Prog_utils/print.c volume_io/Prog_utils/progress.c volume_io/Prog_utils/string.c volume_io/Prog_utils/time.c volume_io/Volumes/evaluate.c volume_io/Volumes/get_hyperslab.c volume_io/Volumes/input_free.c volume_io/Volumes/input_mnc.c volume_io/Volumes/input_volume.c volume_io/Volumes/multidim_arrays.c volume_io/Volumes/output_mnc.c volume_io/Volumes/output_volume.c volume_io/Volumes/set_hyperslab.c volume_io/Volumes/volume_cache.c volume_io/Volumes/volumes.c volume_io/Volumes/input_mnc2.c volume_io/Volumes/output_mnc2.c volume_io/Volumes/input_mgh.c volume_io/Volumes/input_nifti.c nifti/nifti1_io.c nifti/znzlib.c ) SET(volume_io_HEADERS volume_io/Include/volume_io/alloc.h volume_io/Include/volume_io/arrays.h volume_io/Include/volume_io/basic.h volume_io/Include/volume_io/def_math.h volume_io/Include/volume_io/files.h volume_io/Include/volume_io/geom_structs.h volume_io/Include/volume_io/geometry.h volume_io/Include/volume_io/multidim.h volume_io/Include/volume_io/progress.h volume_io/Include/volume_io/string_funcs.h volume_io/Include/volume_io/system_dependent.h volume_io/Include/volume_io/transforms.h volume_io/Include/volume_io/vol_io_prototypes.h volume_io/Include/volume_io/volume.h volume_io/Include/volume_io/volume_cache.h ) SET(LIBMINC_LIBRARY ${LIBMINC_EXTERNAL_LIB_PREFIX}minc2) # a hack for APPLE IF(LIBMINC_BUILD_SHARED_LIBS) SET(LIBMINC_LIBRARY_STATIC ${LIBMINC_EXTERNAL_LIB_PREFIX}minc2_static) ELSE(LIBMINC_BUILD_SHARED_LIBS) SET(LIBMINC_LIBRARY_STATIC ${LIBMINC_LIBRARY}) ENDIF(LIBMINC_BUILD_SHARED_LIBS) SET(LIBMINC_LIBRARIES ${LIBMINC_LIBRARY} ${HDF5_LIBRARY} ${ZLIB_LIBRARY} ${RT_LIBRARY}) SET(LIBMINC_STATIC_LIBRARIES ${LIBMINC_LIBRARY_STATIC} ${HDF5_LIBRARY} ${ZLIB_LIBRARY} ${RT_LIBRARY}) IF(UNIX) SET(LIBMINC_LIBRARIES ${LIBMINC_LIBRARIES} m dl) SET(LIBMINC_STATIC_LIBRARIES ${LIBMINC_STATIC_LIBRARIES} m dl) ENDIF(UNIX) SET(minc_LIB_SRCS ${minc2_LIB_SRCS} ${minc_common_SRCS}) SET(minc_HEADERS ${minc2_HEADERS} ${minc_common_HEADERS}) IF(LIBMINC_MINC1_SUPPORT) SET(minc_HEADERS ${minc_HEADERS} ${minc1_HEADERS}) SET(minc_LIB_SRCS ${minc_LIB_SRCS} ${minc1_LIB_SRCS}) ENDIF(LIBMINC_MINC1_SUPPORT) # Keep this variable for compatibility SET(VOLUME_IO_LIBRARY ${LIBMINC_EXTERNAL_LIB_PREFIX}minc2) ADD_LIBRARY(${LIBMINC_LIBRARY} ${LIBRARY_TYPE} ${minc_LIB_SRCS} ${minc_HEADERS} ${volume_io_LIB_SRCS} ${volume_io_HEADERS} ) TARGET_LINK_LIBRARIES(${LIBMINC_LIBRARY} ${HDF5_LIBRARY} ${ZLIB_LIBRARY} ${RT_LIBRARY}) # IF(LIBMINC_MINC1_SUPPORT) INCLUDE_DIRECTORIES(${NETCDF_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(${LIBMINC_LIBRARY} ${NETCDF_LIBRARY}) ENDIF(LIBMINC_MINC1_SUPPORT) EXPORT(TARGETS ${LIBMINC_LIBRARY} FILE "${LIBMINC_EXPORTED_TARGETS}.cmake") IF(UNIX) TARGET_LINK_LIBRARIES(${LIBMINC_LIBRARY} m dl ) IF(LIBMINC_BUILD_SHARED_LIBS) ADD_LIBRARY(${LIBMINC_LIBRARY_STATIC} STATIC ${minc_LIB_SRCS} ${minc_HEADERS} ${volume_io_LIB_SRCS} ${volume_io_HEADERS} ) TARGET_LINK_LIBRARIES(${LIBMINC_LIBRARY_STATIC} ${HDF5_LIBRARY} ${ZLIB_LIBRARY} ${RT_LIBRARY} m dl ) IF(LIBMINC_MINC1_SUPPORT) TARGET_LINK_LIBRARIES(${LIBMINC_LIBRARY} ${NETCDF_LIBRARY}) ENDIF(LIBMINC_MINC1_SUPPORT) ENDIF(LIBMINC_BUILD_SHARED_LIBS) ENDIF(UNIX) SET_TARGET_PROPERTIES(${LIBMINC_LIBRARY} PROPERTIES SOVERSION ${LIBMINC_SOVERSION}) IF(LIBMINC_MINC1_SUPPORT) SET(LIBMINC_LIBRARIES ${LIBMINC_LIBRARIES} ${NETCDF_LIBRARY} ) SET(LIBMINC_STATIC_LIBRARIES ${LIBMINC_STATIC_LIBRARIES} ${NETCDF_LIBRARY} ) ENDIF(LIBMINC_MINC1_SUPPORT) IF( LIBMINC_INSTALL_LIB_DIR ) INSTALL( TARGETS ${LIBMINC_LIBRARY} EXPORT ${LIBMINC_EXPORTED_TARGETS} LIBRARY DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries ARCHIVE DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries RUNTIME DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries ) ENDIF( LIBMINC_INSTALL_LIB_DIR ) IF(LIBMINC_INSTALL_INCLUDE_DIR AND NOT LIBMINC_INSTALL_NO_DEVELOPMENT) INSTALL(FILES ${minc_HEADERS} DESTINATION ${LIBMINC_INSTALL_INCLUDE_DIR} COMPONENT headers ) INSTALL(FILES ${volume_io_HEADERS} DESTINATION ${LIBMINC_INSTALL_INCLUDE_DIR}/volume_io COMPONENT headers) ENDIF(LIBMINC_INSTALL_INCLUDE_DIR AND NOT LIBMINC_INSTALL_NO_DEVELOPMENT) IF(LIBMINC_MINC1_SUPPORT) ADD_SUBDIRECTORY( ezminc ) ENDIF(LIBMINC_MINC1_SUPPORT) # config for the build directory SET(LIBMINC_USE_FILE_CONFIG ${CMAKE_CURRENT_BINARY_DIR}/UseLIBMINC.cmake) SET(LIBMINC_INCLUDE_DIRS_CONFIG ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/libsrc2 ${CMAKE_CURRENT_SOURCE_DIR}/volume_io/Include ${CMAKE_CURRENT_SOURCE_DIR}/nifti ) IF(LIBMINC_MINC1_SUPPORT) SET(LIBMINC_INCLUDE_DIRS_CONFIG ${LIBMINC_INCLUDE_DIRS_CONFIG} ${CMAKE_CURRENT_SOURCE_DIR}/libsrc ${CMAKE_CURRENT_SOURCE_DIR}/ezminc ) ENDIF(LIBMINC_MINC1_SUPPORT) SET(EZMINC_LIBRARIES minc_io ${LIBMINC_LIBRARIES}) SET(LIBMINC_LIBRARY_DIRS_CONFIG ${CMAKE_CURRENT_BINARY_DIR}) SET(LIBMINC_LIBRARIES_CONFIG ${LIBMINC_LIBRARIES}) SET(LIBMINC_STATIC_LIBRARIES_CONFIG ${LIBMINC_STATIC_LIBRARIES}) IF(LIBMINC_MINC1_SUPPORT) SET(LIBMINC_LIBRARY_DIRS_CONFIG ${LIBMINC_LIBRARY_DIRS_CONFIG} ${CMAKE_CURRENT_BINARY_DIR}/ezminc) ENDIF(LIBMINC_MINC1_SUPPORT) configure_file(LIBMINCConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/LIBMINCConfig.cmake @ONLY ) configure_file(UseLIBMINC.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/UseLIBMINC.cmake @ONLY) # config for install dir SET(LIBMINC_USE_FILE_CONFIG "${CMAKE_INSTALL_PREFIX}/${LIBMINC_INSTALL_LIB_DIR}/Use${LIBMINC_EXTERNAL_LIB_PREFIX}LIBMINC.cmake") SET(LIBMINC_INCLUDE_DIRS_CONFIG ${CMAKE_INSTALL_PREFIX}/${LIBMINC_INSTALL_INCLUDE_DIR} ) SET(LIBMINC_LIBRARY_DIRS_CONFIG ${CMAKE_INSTALL_PREFIX}/${LIBMINC_INSTALL_LIB_DIR}) SET(LIBMINC_STATIC_LIBRARIES_CONFIG "") SET(VOLUME_IO_LIBRARY_STATIC "") #fix for superbuild install IF(SUPERBUILD_STAGING_PREFIX) STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" LIBMINC_INCLUDE_DIRS_CONFIG "${LIBMINC_INCLUDE_DIRS_CONFIG}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" LIBMINC_LIBRARY_DIRS_CONFIG "${LIBMINC_LIBRARY_DIRS_CONFIG}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" LIBMINC_LIBRARIES_CONFIG "${LIBMINC_LIBRARIES_CONFIG}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" EZMINC_LIBRARIES "${EZMINC_LIBRARIES}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" NETCDF_INCLUDE_DIR "${NETCDF_INCLUDE_DIR}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" HDF5_INCLUDE_DIR "${HDF5_INCLUDE_DIR}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" NETCDF_LIBRARY "${NETCDF_LIBRARY}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" HDF5_LIBRARY "${HDF5_LIBRARY}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" ZLIB_LIBRARY "${ZLIB_LIBRARY}") STRING(REPLACE "${SUPERBUILD_STAGING_PREFIX}/" "" ZLIB_LIBRARIES "${ZLIB_LIBRARIES}") ENDIF(SUPERBUILD_STAGING_PREFIX) configure_file(LIBMINCConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${LIBMINC_EXTERNAL_LIB_PREFIX}LIBMINCConfig.cmake @ONLY ) configure_file(UseLIBMINC.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Use${LIBMINC_EXTERNAL_LIB_PREFIX}LIBMINC.cmake @ONLY) IF(LIBMINC_INSTALL_LIB_DIR AND NOT LIBMINC_INSTALL_NO_DEVELOPMENT) INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Use${LIBMINC_EXTERNAL_LIB_PREFIX}LIBMINC.cmake ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${LIBMINC_EXTERNAL_LIB_PREFIX}LIBMINCConfig.cmake DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT Development) ENDIF(LIBMINC_INSTALL_LIB_DIR AND NOT LIBMINC_INSTALL_NO_DEVELOPMENT) # testing IF(BUILD_TESTING) ADD_SUBDIRECTORY(testdir) ENDIF(BUILD_TESTING) IF(NOT LIBMINC_EXTERNALLY_CONFIGURED) INCLUDE(CPack) ENDIF(NOT LIBMINC_EXTERNALLY_CONFIGURED) libminc-libminc-2-3-00/COPYING000066400000000000000000000007521257462267400157000ustar00rootroot00000000000000Copyright the MINC developers, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. libminc-libminc-2-3-00/CTestConfig.cmake000066400000000000000000000010231257462267400200070ustar00rootroot00000000000000## This file should be placed in the root directory of your project. ## Then modify the CMakeLists.txt file in the root directory of your ## project to incorporate the testing dashboard. ## # The following are required to uses Dart and the Cdash dashboard ## ENABLE_TESTING() ## INCLUDE(CTest) set(CTEST_PROJECT_NAME "libminc") set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=libminc") set(CTEST_DROP_SITE_CDASH TRUE) libminc-libminc-2-3-00/ChangeLog000066400000000000000000001210031257462267400164100ustar00rootroot00000000000000-- Release of minc 2.3.00 -- 2015-09-11 Robert D. Vincent * Remove autoconf/automake support. 2015-08-24 Robert D. Vincent * libsrc/netcdf_convenience.c: Changed the logic in miopen() to explicitly differentiate netCDF vs. HDF5 files. This was needed because ncopen() in netCDF 4.4 successfully opens HDF5 files, so the old logic no longer worked. 2015-08-13 Robert D. Vincent * volume_io/Volumes/input_nifti.c: Fix loading of dynamic scans, handle NIfTI units. 2015-06-11 Robert D. Vincent * libsrc/minc.h, libsrc2/minc2_defs.h: Increase MI_MAX_NUM_ICV from 32 to 1000. This should improve the behavior of voxel_loop programs when used with a large number of input files. 2015-05-28 Robert D. Vincent * libsrc/ParseArgv.c: Added ParseLong() function to handle numeric command-line parameters. This eliminates the questionable support for octal values. We now support only decimal (with optional leading zeros) and hex (with a leading "0x"). 2015-04-23 Robert D. Vincent * Support for loading FreeSurfer and NIfTI-1 format files in volume_io. (volume_io/Volumes/{input_mgh.c, input_nifti.c, input_volume.c}) * Added set_volume_n_dimensions() function. (volume_io/Volumes/volumes.c) (volume_io/Volumes/Include/volume_io/vol_io_prototypes.h) * Incorporated public NIfTI-1 library code in new "nifti" subdirectory. (nifti/*) 2015-04-20 Robert D. Vincent * Allow the current_realtime_seconds() function to return fractional seconds. (volume_io/Prog_utils/time.c) 2015-04-20 Robert D. Vincent * Fix some warnings through use of "const" where appropriate. (libsrc/ParseArgv.c, libsrc/ParseArgv.h, libsrc/image_conversion.c) (libsrc/minc.h) * Added range checks to catch edge cases in ICV code. (libsrc/image_conversion.c, libsrc/dim_conversion.c) 2014-08-05 Vladimir S. FONOV * Merged changes from Steven M. Robbins 2014-04-12 Andrew L Janke * Updated README.release for CMake build instructions * Release handover to Matthijs van Eede 2014-04-09 Vladimir S Fonov * Incorporated multiple bug fixes from Sean McBride * Added some info in NEWS 2013-11-28 Vladimir S Fonov * Removed autoconf/automake files * MINC2 file format now can store long attributes, but uses HDF5 1.8 features * MINC2 API can now read MINC1 files, by creating a temporary MINC2 file first, beware of huge files * This is a pre-release of minc 2.3.00 * volume_io can now read and write files in MINC2 format using MINC2 API * MINC2 library can be compiled without netcdf library 2013-07-09 Vladimir S Fonov * Lots of bug fixes in minc2 * Joining of volume_io into main library 2012-09-25 Andrew L Janke + Vladimir S Fonov * split libminc from minc-tools * started build of minc2 only for ITK -- Release of minc 2.2.00 -- 2012-09-24 Andrew L Janke * added make_mincmorph_kernel.pl * merged changed from develop branch * added -size arguments to mincresample 2012-08-03 Andrew L Janke * progs/mincheader/mincheader: fixed to handle files with spaces 2012-06-21 Andrew L Janke * added mincblob.man1 and updated configure.ac and Makefile.am 2012-05-16 Andrew L Janke * added patches from Jordi GutiƩrrez Hermoso * libsrc/hdf_convenience.c: added default switch case * libsrc/ParseArgv.c: added intptr_t casts * conversion/dcm2mnc/dicom_to_minc.c: added initialisation of tmp_offset 2012-04-10 Andrew L Janke * libsrc2/grpattr.c: added a history patch from Leila 2012-03-14 Andrew L Janke * added progs/mincsample 2012-03-14 Andrew L Janke * added progs/mincmorph -- Release of minc 2.1.10 -- 2012-02-22 Andrew L Janke * added a patch from Steve Robbins that factors references to PATH_MAX out to fix a build problem on hurd 2011-12-20 Andrew L Janke * libsrc/netcdf_convenience.c: changed execute_decompress_command() to always decompress the whole file as the approach used for netCDF files for header_only does not work with HDF5 MINC2 files * merged Vlad's ezminc branch 2011-06-29 Andrew L Janke * configure.in: changed typo from enable to disable minc2 2011-05-30 Andrew L Janke * progs/minccalc: added imax() and imin() operators 2011-04-29 Andrew L Janke * libsrc2/volume.c: added minc_version global * libsrc/netcdf_convenience.c: added minc_version global 2011-02-17 Andrew L Janke * libsrc2/test/datatype-test.c: fixed a HDF5 bug in error output 2011-02-14 Andrew L Janke * progs/mincview/mincview: converted from csh to bash and changed from xv to display 2011-02-09 Andrew L Janke * libsrc/minc_compat.h: Added cplusplus extern to header 2011-01-19 Jim Nikelski * Fixed x86_64 ecattominc bug which resulted in a segmentation fault on 64-bit platforms. Change primarily required changing the type of the "dirblock" IO buffer from long to int32_t, reflecting the actual storage type in the ecat file. 2010-11-23 Claude Lepage * Fixed bug in dicom_to_minc.c for segmentation fault on undefined sequence (initialization of gi_ptr->cur_size) * Free some memory after usage 2010-11-23 Ilana Leppert * Added b-matrix field for Siemens diffusion scans (version >= VB only). * Made changes to ordering of slices: e.g. a descending acquisition now has negative slice step. This was an issue with MOSAIC, in which the ordering of the slices in the MOSAIC image is ascending, even though the acquisition is descending (version >= VA25 and >= VB11). -- Release of minc 2.1.00 -- 2010-07-27 Andrew L Janke * conversion/micropet/upet2mnc.c: error bug squashed (John Cupitt) 2010-07-06 Claude Lepage * Improved convergence and accuracy for application of non-linear transformation (especially for 2-d slices) 2010-06-28 Andrew L Janke * Added progs/xfm/xfm2def.c and progs/xfm/xfm2def.man1 2010-05-23 Andrew L Janke * changed all calls to H5Acreate2 to the H5Acreate macro 2010-05-18 Steve M. Robbins * libsrc/hdf_convenience.c: specify version 2 of H5Gopen, H5Acreate. * minc.h: ensure hdf5.h included before netcdf.h to avoid build error with netcdf 4.1.1 and openmpi 1.4.1. 2010-03-22 Andrew L Janke * added pod2man for manpage generation from scripts * shifted minchistory man to POD * conversion/vff2mnc/vff2mnc.c: added error checking for fgets * set default volume_io caching to none * added checks for outfiles in minccalc * removed outdated GETTING_STARTED file 2010-03-02 Andrew L Janke * Added minccmp (minccmp.c and minccmp.man1) * libsrc/hdf_convenience.c: removed spurious debug output * libsrc/minc.h: replaced MAX_NC_OPEN with 32 * libsrc/voxel_loop.c: replaced MAX_NC_OPEN with MI_MAX_NUM_ICV 2010-02-03 Andrew L Janke * progs/mincpik/mincik: added -text_size option (Thanks Mishkin) * removed all non-quoted string barewords (hash refs) * added range calculation for anot bar if not defined 2009-11-13 Andrew L Janke * progs/mincpik/mincpik: added -sagittal_offset_perc option 2009-11-06 Claude Lepage * volume_io/Volumes/output_mnc.c: fix output buffers for a slice as only first buffer would be written out 2009-08-11 Andrew L Janke * progs/mincpik/mincpik: Added taking first time point for 4D files 2009-07-08 Andrew L Janke * Added minimum cmake version to CMakeLists.txt * Added libtoolize/glibtoolize logic in autogen.sh (thanks Sean) 2009-06-03 Claude Lepage * Fixed bug in multidim_array_is_alloced for correct check of memory allocation of image data. Return volume=NULL when memory allocation fails. -- Release of minc 2.0.18 -- 2009-04-29 Claude Lepage * Smarter utilization of buffer in input_mnc.c and output_mnc.c 2009-04-29 Andrew L Janke * progs/mincinfo/mincinfo.c: fixed a stack smash 2009-04-21 Claude Lepage * Fixed bug with chunking for internal file compression using hdf5 (now makes the code faster for large files) -- Release of minc 2.0.17 -- 2009-01-20 Andrew L Janke * Updated version in CMakeLists.txt * Note that with the bugfix in ParseArgv all other MINC programs that link against this will have to be rebuilt. * CMakeLists.txt: updated version Warning cleanups below * conversion/dcm2mnc/minc_file.c: fixed printf type * conversion/dcm2mnc/siemens_to_dicom.c: fixed printf type * conversion/ecattominc/machine_indep.c: added string.h and fixed 2 fprintf missing format args * conversion/micropet/upet2mnc.c: fixed two fprintf format args * conversion/minctoecat/ecat_write.c: added string.h * conversion/minctoecat/minctoecat.c: added missing argument to fprintf * conversion/nifti1/mnc2nii.c: fixed incorrect printf type * progs/mincview/invert_raw_image.c: added fwrite checking 2009-01-03 Steve M. Robbins * testdir/run_test_progs.sh: New. Test that programs like mincheader and mincdiff work properly. * configure.in: Bump to version 2.0.17. * libsrc/ParseArgv.c (ParseArgv): Change ARGV_CONSTANT to treat src and dst and pointer to integer, as documented. Reverts change 6.9 of this file. * testdir/test_arg_parse.c: New. Test program for argument parsing (ParseArgv.[ch]). * testdir/run_test_arg_parse.sh: New. Test driver for above. * testdir/Makefile.am (TESTS): Add run_test_arg_parse.sh. * libsrc2/Makefile.am: Remove; the only content was SUBDIRS = test. * Makefile.am: Change SUBDIRS from libsrc2 to libsrc2/test. * configure.in: Remove libsrc2/Makefile output. * testdir/xfmconcat_01.sh: * testdir/xfmconcat_02.sh: Add -clobber to output-generating commands. * libsrc2/test/testminctools.sh: Add "set -e" to exit on any error. * configure.in: Remove AC_PROG_RANLIB, obsoleted by use of AC_PROG_LIBTOOL. 2008-10-12 Steve M. Robbins * conversion/dcm2mnc/dcm2mnc.man1: * conversion/ecattominc/ecattominc.man1: * conversion/micropet/upet2mnc.man1: * conversion/minctoecat/minctoecat.man1: * conversion/nifti1/mnc2nii.man1: * conversion/nifti1/nii2mnc.man1: * conversion/vff2mnc/vff2mnc.man1: * progs/mincgen/mincgen.man1: Fix errors, mainly removing trailing TAB characters. -- Release of minc 2.0.16 -- 2008-09-04 Andrew L Janke * progs/xfm/xfmconcat.c: Added -verbose and -clobber options * progs/xfm/xfmconcat.man1: added -verbose and -clobber * progs/xfm/xfminvert.man1: added -verbose and -clobber 2008-08-13 Andrew L Janke * progs/rawtominc/rawtominc.c: changed to an enum for modalities to get around an esoteric bug with use of ParseArgv and strings passed by reference on 64 bit architectures 2008-08-11 Andrew L Janke * conversion/Acr_Nema: Many changes from Claude * conversion/dcm2mnc: Many changes for 64 bit and "new" DICOM types 2008-04-07 Andrew L Janke * conversion/ecattominc/ecattominc.c: added config.h * Makefile.am: removed minc_globdef.h * libsrc/minc_basic.h: rewrote error code to avoid global vars (Claude) * libsrc/minc_error.h: changes for error code (Claude) * libsrc/minc_error.c: changes for error code (Claude) 2008-03-08 Andrew L Janke * progs/mincpik/mincpik: fixed a bug with bar width from Mike Ferreira 2008-03-07 Andrew L Janke * progs/mincpik/mincpik: fixed two bugs with lookup triplanars 2008-02-28 Andrew L Janke * progs/mincpik/mincpik: added the -anot_bar option -- Release of minc 2.0.15 -- 2008-02-15 Andrew L Janke * Removed all fortran code (the subdirectory anyhow) * removed get_image_offset.c as it will not work with MINC2 * updated configure.in and Makefile.am to suit * changed --enable-minc2 to --disable-minc2 2008-01-24 Andrew L Janke * AUTHORS: added Claude Lepage * progs/xfm/xfmconcat.c: Added a history patch from Mishkin Derakhshan * conversion/nifti1/nii2mnc.c: Added a patch from Claude for patient names 2008-01-17 Andrew L Janke * libsrc/hdf_convenience.c (hdf_path_from_name): added NC_NAT to switch 2008-01-17 Steve M. Robbins * testdir/test_speed.c (nctypename): Return "unknown" if no switch case used. * libsrc2/test/vector_dimension-test.c (create_test_file): * libsrc2/test/hyper-test-2.c (create_test_file): * conversion/dcm2mnc/dicom_to_minc.c (copy_element_properties): Change return type from int to void; no callers require a return value. * testdir/icv_fillvalue.c (main): * conversion/micropet/upet2mnc.c (main): Return 0 at end of function. 2008-01-17 Andrew L Janke * removed all rcsid's as they are not used * removed a bunch of ^L's that crept in somehow * removed old and out of date BUGS file 2008-01-13 Steve M. Robbins * progs/mincreshape/mincreshape.h: * progs/mincreshape/mincreshape.c: * progs/mincreshape/copy_data.c: Move static function declarations from header file to .c files that define the function. Avoids compiler warnings. * progs/mincresample/mincresample.h: * progs/mincresample/mincresample.c: * progs/mincresample/resample_volumes.c: Move static function declarations from header file to .c files that define the function. Avoids compiler warnings. * progs/minccalc/minccalc.c (constant,constant2): Remove unushed variables. * libsrc/voxel_loop.c (get_output_filename): Remove unused function. * conversion/nifti1/nifti1_local.h: * conversion/nifti1/mnc2nii.c: Move definition of dimnames to .c file where it is used. Avoids compiler warning. * conversion/dcm2mnc/dicom_read.c: Don't declare or define convert_numa3_coordinate(), since the caller is #if 0'd out. 2008-01-12 Steve M. Robbins * libsrc2/hyper.c (mirw_hyperslab_raw): * libsrc/hdf_convenience.c (hdf_varget): Use hsize_t rather than hssize_t for 3rd argument to H5Sselect_hyperslab(). * libsrc/voxel_loop.c: * progs/mincinfo/mincinfo.c: * progs/mincmath/mincmath.c: * progs/rawtominc/rawtominc.c: * testdir/icv.c: * testdir/icv_dim.c: * testdir/icv_dim1.c: * testdir/icv_fillvalue.c: * testdir/icv_range.c: * testdir/minc_types.c: Add braces around static initializers. 2008-01-11 Andrew L Janke * removed outdated TODO file * merged WHATSNEW-2.0 into NEWS/README 2008-01-09 Andrew L Janke * replaced mincedit with a complete rewrite in sh 2008-01-04 Andrew L Janke * mincheader: updated scripting style and replaced tmpdir code 2008-01-02 Steve M. Robbins * testdir/run_tests.sh: * testdir/run_test2.sh: Quote the value assigned to variable tests, so that the scripts run. 2007-12-18 Jonathan Harlap * dcm2mnc: Restored dicom fields in minc headers. (conversion/dcm2mnc/minc_file.c) * dcm2mnc: Restored old name of acquisition comments attribute. (conversion/dcm2mnc/minc_file.c) 2007-12-06 Claude Lepage * Freed more memory in miicv_free (libsrc/image_conversion.c) * Added cubic interpolation in mincresample for x-y slices * Fixed seg fault bug with null history string (libsrc/minc_convenience.c) * Changed hard-coded strings for MIxspace (y,z) (libsrc2/volume.c and libsrc2/dimension.c) * Made global variables static in minccompress to avoid conflict with libz 2007-12-03 Andrew L Janke * changed all global variables to static in progs directory to avoid possible linker errors in the future. 2007-10-23 Andrew L Janke * Added most things needed for a CMake build * nii2mnc mnc2nii: Fixed a linking bug -- Release of minc 2.0.14 -- 2007-09-13 Andrew L Janke * Added a few more free's for memory thanks to Claude 2007-08-24 Andrew L Janke * added xfmflip and man page * fixed a bug in the build of minccalc * updated nifti library for nii2mnc 2007-08-08 Claude Lepage * Increased size of MI_MAX_VAR_BUFFER_SIZE and fix chunking for internal file compression using hdf5 * Modified mincconvert to use default chunking 2007-05-18 Andrew L Janke * Fixed up small problems with build process * replaced csh scripts with sh. (checks fail if no tcsh) * added libsrc2 to the INCLUDES. why this was not in before beats me 2006-09-01 Jonathan Harlap * conversion/Acr_nema - Fixed a bug causing dump_acr_nema to skip all elements with element number 0x0010 2006-05-31 Jonathan Harlap * Makefile.am - modified to build and install image_filters/extract and image_filters/byte_swap if the ACR NEMA tools are intstalled, as they are required by dicom_to_minc.pl 2006-05-18 Bert Vincent * progs/various... added config.h to source files that need it for proper operation with MINC 2.0 2006-04-19 Claude Lepage * fix duplicate instances of solve_linear_system and scaled_maximal_pivoting_gaussian_elimination in volume_io/Geometry/gaussian.c and libsrc.2. Make static. This is to avoid linker problems with minc2. 2006-04-09 Bert Vincent * conversion/dcm2mnc, conversion/Acr_nema - added general support for Siemens DTI sequences, inserting attributes according to Jennifer Campbell's conventions. 2006-03-27 Bert Vincent * conversion/nifti1/mnc2nii.c - Set time and vector dimensions "properly" for some versions of Analyze and NIfTI libraries. Also set the intent_code field to NIFTI_INTENT_VECTOR if the vector_dimension is set. Also fixed an issue with file names. 2006-03-10 Bert Vincent * conversion/Acr_nema/element.c - Modify acr_dump_element_list() to use helper function maybe_print_as_string() to print data with unknown VR as either ASCII or a string of hex bytes. 2006-02-28 Bert Vincent * libsrc/hdf_convenience.c - Modify the hdf_vardef() function to avoid errors in HDF5 1.6.5 2006-02-19 Steve M. Robbins * testdir/create_grid_xfm.c (main): Initialize variable mio before using. 2006-02-09 Bert Vincent * conversion/dcm2mnc/dicom_to_minc.c, conversion/dcm2mnc/siemens_to_dicom.c - Deal more correctly with some odd cases in .IMA files. 2006-02-08 Bert Vincent * progs/rawtominc/rawtominc.c, conversion/nifti1/nii2mnc.c, conversion/micropet/upet2mnc.c: Change fopen() parameter to "rb" to force correct operation on DOS/Windows. * libsrc2/volume.c: Allow signed as well as unsigned base types for labels. 2005-12-15 Bert Vincent * libsrc2/volume.c - set units field unconditionally in _miget_file_dimension() 2005-12-13 Bert Vincent * conversion/dcm2mnc/dicom_to_minc.c: Ignore DICOM protocol errors. This change was necessitated by images from a Philips Intera scanner version 'NT 10.4.1\\PIIM V2.1.4.1 MIMIT MCS' that appears to set the DICOM length field incorrectly. 2005-12-03 Bert Vincent * conversion/micropet/upet2mnc.c - fix handling of single-frame data * progs/mincgen/main.c - make "-o" option imply "-b" 2005-11-22 Bert Vincent * progs/mincgen/genlib.c - fix free() issue * progs/mincgen/load.c - #include config.h 2005-11-11 Bert Vincent * conversion/dcm2mnc/dcm2mnc.h - fix definition of IMA_MAGIC_SIZE * conversion/dcm2mnc/dcm2mnc.c - fix test for is_ima_file() 2005-11-04 Bert Vincent * conversion/dcm2mnc/dicom_to_minc.h - further relaxation of COORDINATE_EPSILON, now set to 0.005 * conversion/dcm2mnc/minc_file.c - combine cloned regular/irregular dimension checking code into new function, check_regular() * conversion/dcm2mnc/dcm2mnc.c - update version to 2.0.07 2005-10-26 Bert Vincent * conversion/dcm2mnc/dicom_to_minc.h - set COORDINATE_EPSILON to a fixed value of 0.0001 rather than 100*FLT_EPSILON to allow for more slop in coordinates. * conversion/nifti1/mnc2nii.c - handle missing value for spacetype. 2005-09-16 Bert Vincent * libsrc2/conversion/dcm2mnc/dicom_read.c - again change the handling of the slice thickness / slice spacing issue to accomodate Andrew Janke's Philips data. If both slice thickness and slice spacing are set, we select the maximum. For some reason I had been choosing the minimum, but logically the opposite seems more reasonable (and is certainly correct in Andrew's case). 2005-09-14 Bert Vincent * libsrc2/slice.c - in function mirw_slice_minmax(), properly reorient access for slice minimum and maximum if dimension order has been altered. * libsrc2/hyper.c - normalize data correctly in miget_real_value_hyperslab(). * progs/mincinfo/mincinfo.c - include config.h 2005-08-26 Bert Vincent * Implemented --enable-hdf5 option in ./configure script so that we can selectively enable or disable MINC2 support. This required changing the way we treat the MINC2 symbol, and being careful to include config.h in all files that call netCDF functions. * libsrc2/grpattr.c - Fixed issue in micreate_group() function - it would not function properly when a group already existed in the file. * conversion/dcm2mnc - Ported changes from MINC 1 branch. 2005-07-29 Bert Vincent * progs/mincstats/mincstats.c: Add warning when mask file specified with no mask range option. 2005-07-28 Bert Vincent * conversion/nifti1/mnc2nii.c: Implement fix suggested by Hyun-Pil Kim to set unused dimension lengths to 1 rather than zero. 2005-07-25 Bert Vincent * progs/mincstats/mincstats.c: Fix calculation of percent threshold to correctly account for non-zero histogram floor. 2005-07-15 Andrew Janke * added -auto_range option to mincpik (thanks to Jon Harlap) 2005-07-15 Bert Vincent * libsrc/hdf_convenience.c: Two minor fixes. First, when emulating 'signtype' attributes in MINC 2 files, comparisons with MI_SIGNED and MI_UNSIGNED should NOT depend on a properly- null-terminated attribute value. Second, suppress HDF5 errors in hdf_attdel() * progs/mincresample/mincresample.c * progs/mincresample/mincresample.h * progs/mincresample/mincresample.man1 * progs/mincresample/resample_volumes.c Support windowed sinc interpolation, as ported from 1.X branch. * progs/mincconcat/mincconcat.c: * progs/mincconcat/mincconcat.man1: Add support for -filestarts option, as ported from 1.X branch. 2005-07-04 Steve M. Robbins * Makefile.am: * progs/minchistory/minchistory.man1: New manual page for minchistory. 2005-07-03 Steve M. Robbins * configure.in: Check for sysconf(). * volume_io/Prog_utils/time.c (get_clock_ticks_per_second): Use POSIX sysconf() function, if available. 2005-06-22 Bert Vincent * Minor fix to ncgenyy.l to avoid compiler complaint on ia64. 2005-05-20 Bert Vincent * Update Makefile.msvc-win32 to build converters. * Declare restructure_array() in hyper.c to be MNCAPI so that nii2mnc can link to it. * Get rid of warnings in mincgen build. Affected files are progs/mincgen/ncgenyy.l and progs/mincgen/ncgentab.y * Replace direct usage of H5Fis_hdf5() function to avoid annoying error messages for nonexistant files. Instead there is now a function named hdf_access() that returns a boolean value TRUE if the file can be accessed and is in HDF5 format. This change affected the files libsrc/hdf_convenience.h, libsrc/hdf_convenience.c, and libsrc/netcdf_convenience.c * Remove and/or conditionalize some test code for memory-mapped files that should not have been checked in. Affected files are libsrc/netcdf_convenience.c, libsrc/hdf_convenience.c, and progs/mincstats/mincstats.c 2005-05-19 Bert Vincent * Fix volume.c and m2util.c to be compatible with HDF5 1.6.3 and later. * Port nifti converter changes from MINC 1.X branch * Port dcm2mnc converter changes from MINC 1.X branch * Port build changes for dcm2mnc and ACR/NEMA library from MINC 1.X branch 2005-04-18 Bert Vincent * Move volume_io headers into Include/volume_io subdirectory. 2005-03-17 Andrew Janke * removed ':' from temporary filenames for windows compatibility 2005-03-11 Bert Vincent * Improve nii2mnc's support for functional (time-varying) data, and support qform as well as sform transforms in the header. 2005-01-28 Bert Vincent * Incorporate NIfTI-1 converters, nii2mnc and mnc2nii * Incorporate upet2mnc, converter for Concorde microPET data. * Copied fix for mincmakescalar to warn user if the vector_dimension is not the last dimension in the file. * Modify Leila's vector_dimension-test code to create its own data file, to avoid having to carry around a multi-megabyte test file as part of the distrbution. 2005-01-19 Bert Vincent * Incorporate Anthonin Reilhac's changes to ecattominc * Add ecattominc, mnc2nii, and nii2mnc to the automake files * Modify mincdump to print long attributes of type NC_BYTE as strings if all of the characters are printable. 2005-01-04 Bert Vincent * Adapt minc_simple.c to use Leila's revision of the restructure_array() parameters. This seems to work properly again. 2004-12-14 Bert Vincent * Got rid of lots of C99-related warnings * Added new biModalT algorithms to mincstats 2004-12-15 Andrew Janke * added epm-header.in, removed mni_minc.epm.header 2004-12-07 Andrew Janke * Squashed yet another bug with the BiModalT code aaargh! This should now replicate volume_stats even closer! 2004-10-18 Andrew Janke * Fixed bug in mincstats -BimodalT code to exactly replicate (within reason) volume_stats * Changed default # of int histogram bins from 10000 to 65536 2004-08-11 Bert Vincent * Fix minc.h for netCDF 3.5.1 2004-08-03 Bert Vincent * Added new test cases to dimension-test.c * Fix bug(s) in volume.c * Correctly implement some of the conversion functions 2004-06-21 Bert Vincent * Updated mincgen man page * "Improve" Doxygen documentation 2004-06-16 Bert Vincent * Fixes and improvements for mincgen/mincdump * Emulate a vector_dimension for MINC 2.0 files with a compound datatype. * Move libsrc2 (MINC 2.0 API) files under the MINC hierarchy in CVS * Fix ordering of world coordinate values in miconvert_world_to_voxel and miconvert_voxel_to_world 2004-06-11 Bert Vincent * Fix issue with minc_modify_header's new -sappend and -dappend options. * Add "mincgen" based upon "ncgen" for "mincedit" * Fix nasty minccalc bug. Minccalc would fail to work properly on any file with a vector_dimension of length greater than 1. 2004-06-09 Bert Vincent * Add netcdf and HDF5 versions to the -version list. 2004-06-08 Bert Vincent * Avoid printing HDF5 errors on excessively long attributes. Still have to decide how best to deal with this situations - truncate, drop, or somehow convert the attribute into a dataset? Right now we will drop excessively large attributes from HDF5 files, which is probably bad. * Fix bug (mentioned in previous entries) which caused mincdump (as derived from ncdump) to print zero-length attributes as an erroneous string of length one instead of as an empty string. * Eliminate bogus "not implemented yet" message from volume_io 2004-06-07 Bert Vincent * Fix setting of length in hdf_vardef/hdf_dimdef * Handle zero-length character strings properly. There is actually a minor bug in "ncdump" which makes zero-length strings appear to have length one (the bogus character will appear to match the first character in the preceding string). 2004-06-04 Bert Vincent * Changed volume_io/Volumes/volume_cache.c to increase both the cache size and the default cached volume size (i.e. the size which turns on caching). 2004-06-01 Bert Vincent * Fixed endian-ness issues in MINC 2.0 format. 2004-05-25 Bert Vincent * Added -dappend, -sappend to minc_modify_header 2004-05-20 Bert Vincent * Revised man pages * Added -2 option to minclookup 2004-04-30 Bert Vincent * Further reduce compiler issues for IRIX MIPSpro compiler. * Tagged version 2.0.06 2004-04-29 Bert Vincent * Fix compiler compatibility problem against IRIX MIPSpro compiler in netcdf_convenience.c 2004-04-22 Bert Vincent * Expanded test cases 2004-04-15 Bert Vincent * Add -DMINC2, #ifdef MINC2 to make most MINC2 additions and changes optional. * Expand minc2_uguide.tex 2004-04-08 Bert Vincent * Add mincdump command * Minor changes to increase portability, esp. to Windows. 2004-03-25 Bert Vincent * Add support for -compress and -chunk options to mincconvert * Some small library fixes * Fix handling of irregular dimension variables in emulation library. 2004-02-27 Bert Vincent * Fix dimorder handling 2004-02-18 Bert Vincent * Fixed handling of "rootvariable" emulation in hdf_convenience.c and netcdf_convenience.c * Fixed behavior of some of the netCDF emulation attribute functions in hdf_convenience.c and minc_compat.c 2004-02-17 Bert Vincent * Fixed mincconvert - added ncendef() * Actually implemented MINC_COMPRESS MINC 1: 2004-03-24 Bert Vincent * Minor fix to miappend_history() in libsrc/minc_convenience.c 2004-03-23 Bert Vincent * Modify configure.in, libsrc/netcdf_convenience.c, volume_io/Prog_utils/files.c, and volume_io/Prog_utils/time.c to make MINC more portable, especially to Windows compilers. 2003-02-02 Bert Vincent * Added -version flag for all executables (or at least all "C" language executables). Implemented in ParseArgv() to make it universal, and applications can override their version number by adding a "ARGV_VERINFO" record to their argTable[]. * Created miget_version() and miappend_history() functions. 2003-12-05 Andrew L. Janke * Changes to mincpik: added -depth option (as per a diff supplied by Jonathan HARLAP); while at it, did a bit of clean-up including replacing home-grown tempdir cleanup with File::Temp; Added -clobber option so that mincpik more closely matches the other minc tools. 2003-11-23 Steve M. Robbins * configure.in: Set version to 1.3. Check for headers sys/stat.h, sys/wait.h, unistd.h; and for functions fork, system, and popen. * libsrc/netcdf_convenience.c: Use above checks for conditional inclusion of headers. (execute_decompress_command): Use fork or system to decompress file, if facility available. 2003-11-21 Steve ROBBINS * volume_io/Include/basic.h: Include for M_PI definition. Build problem reported by Maxime Descoteaux . 2003-11-14 Steve M. Robbins * Makefile.am (libvolume_io_la_LDFLAGS): (libminc_la_LDFLAGS): Update version-info. * progs/rawtominc/rawtominc.c: Include for declaration of swab(). Cast void pointer "image" to unsigned char before adding integer size; otherwise IRIX CC fails. * progs/mincview/invert_raw_image.c (main): * progs/minc_modify_header/minc_modify_header.c (main): Specify return type. * libsrc/voxel_loop.c: * libsrc/value_conversion.c: * libsrc/dim_conversion.c: Include for declaration of fabs(). 2003-11-13 Steve M. Robbins * progs/minchistory: * progs/mincpik: New. Perl scripts moved here from Andrew Janke's "minc_dev" tool set. * Makefile.am (dist_bin_SCRIPTS): Install minchistory and mincpik. * README: Mention minchistory, mincpik. Correct distribution URL. * mni_minc.epm.header: Update license, version. * Makefile.am (EXTRA_DIST): Distribute README.binary_packaging. * configure.in: Run autoupdate; change AM_CONFIG_HEADER to AC_CONFIG_HEADERS. * testdir/Makefile.am (INCLUDES): Add volume_io/Include. 2003-10-31 Bert Vincent * progs/rawtominc/rawtominc.c: Add options -dimorder and -swap_bytes. 2003-06-01 Steve M. Robbins * testdir/Makefile.am (script_tests): * Makefile.am (SUBDIRS): Process volume_io before testdir, because tests may link against volume_io. * volume_io/MNI_formats/gen_xf_io.c (output_one_transform): Update *volume_count after writing a grid transform (thanks, Peter Neelin). * testdir/create_grid_xfm.c: New. Utility to create grid transformation. * testdir/test_xfm.c: Copied from volume_io/Testing/test-xfm.c. * testdir/xfmconcat_01.sh: * testdir/xfmconcat_02.sh: New. Test concatenation of grid transformations. 2003-03-17 Bert Vincent * configure.in: added check for mkstemp(), tempnam(), and tmpnam() * libsrc/netcdf_convenience.c: added definition of micreate_tempfile() * libsrc/minc.h: added declaration of micreate_tempfile() * volume_io/Prog_utils/files.c: replaced uses of tmpnam() with micreate_tempfile(). * volume_io/Volumes/volume_cache.c: replaced use of tmpnam() with micreate_tempfile() 2003-02-14 Jason Lerch * configure.in: Set version to 1.1.1 * mni_minc.epm.header: added. * README.binary_packaging: added. 2003-01-17 Steve M. Robbins * configure.in: Set version to 1.2. 2003-01-17 Steve M. Robbins * MINC-1-1 tagged. * README.release: * INSTALL.minc: new. * AUTHORS: * GETTING_STARTED: * INSTALL: * README: Tune up for release. 2003-01-10 Steve M. Robbins * testdir/Makefile.am (INCLUDES): * volume_io/Testing/Makefile.am (INCLUDES): Set includes for test files. * Makefile.am (EXTRA_DIST, dist-hook): Distribute fortran subdirectory, sans the CVS files. * progs/mincstats/mincstats.man1: Document change of -max_bins to -int_max_bins. 2003-01-09 Steve M. Robbins * progs/rawtominc/rawtominc.man1: Document -skip option. * volume_io/Makefile.am: New. * volume_io/Documentation/Makefile.am: New. * volume_io/Testing/check_xfm.sh: * volume_io/Testing/test-xfm.c: * volume_io/Testing/t1.xfm: * volume_io/Testing/t2.xfm: * volume_io/Testing/t3.xfm: * volume_io/Testing/t3_grid_0.mnc: * volume_io/Testing/Makefile.am: New. * volume_io/Testing/test-xfm.c: Allow tolerance specified on command line. Exit with nonzero status if point out of tolerance. * Makefile.am (dist_man3_MANS): Distribute and install ParseArgv(3). (EXTRA_DIST): Distribute get_image_offset.c (not built). (noinst_HEADERS): Remove vax_conversions.h (not used). * doc/Makefile: Removed. * doc/Makefile.am: New. * testdir/run_tests.csh: Use -e flag to stop on error. Look for expected output files in $srcdir. * testdir/Makefile: Removed. * testdir/Makefile.am: New. * configure.in: * Makefile.am: Adjust for testdir/Makefile. 2003-01-07 Steve M. Robbins * autogen.sh: New. * Makefile.am: New. * configure.in: Updated to use automake, libtool. * AUTHORS: New. * LICENCE: Renamed to ... * COPYING: ... this. Makes automake happier. * progs/coordinates/voxeltoworld.man1: * progs/minccopy/minccopy.man1: * progs/mincdiff/mincdiff.man1: * progs/mincedit/mincedit.man1: * progs/mincextract/mincextract.man1: * progs/minctoraw/minctoraw.man1: * progs/mincview/invert_raw_image.man1: * progs/mincview/mincview.man1: * progs/mincwindow/mincwindow.man1: * progs/xfm/transformtags.man1: * progs/xfm/xfmconcat.man1: * progs/xfm/xfminvert.man1: New. 2002-12-11 Steve M. Robbins * progs/minccalc/gram.y: Insert missing semicolon at end of "exprlist" production. 2002-12-05 Steve Robbins * volume_io/Include/vol_io_prototypes.h: Add prototype for get_volume_translation(). 2002-11-05 Jason Lerch * progs/mincresample/mincresample.{c,h}: changed the setting of the interpolation type to go through an enum rather than directly to a function pointer, as that is the behaviour that ParseArgv expects and also makes mincresample 64 bit safe. 2002-10-30 Jason Lerch * libsrc/ParseArgv: added the ARGV_LONG argument type. * progs/mincresample/mincresample.c: changed the parsing of the nelements arguments to be ARGV_LONG. 2002-09-05 Andrew Janke * progs/mincstats/mincstats.c: Change command line option "-max_bins" to "-int_max_bins", to avoid clash with option "-max". 2002-09-03 Steve M. Robbins * CHANGES: Renamed ... * NEWS: ... to this. NEWS is the place to document important user-visible changes. The ChangeLog is the place for more detailed notes. See http://www.gnu.org/prep/standards.html. * volume_io/MNI_formats/gen_xfs.c (transform_or_invert_point): Do not flip inverse_flag when transform inverted. * volume_io/Testing/test-xfm.c: New. Regression tests for General_transforms. 2002-08-22 Steve M. Robbins * volume_io/Documentation/volume_io.tex (section{Volume Input}): Clarify notion of vector volume and vector-to-scalar conversion. * progs/minccalc/eval.c: * progs/minccalc/gram.y: * progs/minccalc/lex.l: * progs/minccalc/minccalc.man1: * progs/minccalc/node.c: * progs/minccalc/node.h: Add support for tan, asin, acos, and atan, courtesy of Andrew Janke. 2002-08-04 Peter Neelin * progs/rawtominc/rawtominc.c: Add slightly modified code from Colin Holmes to support -skip option. 2002-04-08 John Sled * progs/mincstats/mincstats.c: Do not call fclose() on NULL file pointer. 2002-02-22 Steve M. Robbins * libsrc/ParseArgv.h: Declare code with "C" linkage when included by C++ compiler. libminc-libminc-2-3-00/INSTALL000066400000000000000000000021441257462267400156730ustar00rootroot00000000000000 libminc is now compiled with cmake ---------------------------------- Shared or Static libraries? --------------------------- By default, only a static version (i.e. libminc.a) will be produced. A shared version of the library may be enabled using the configure flag "--enabled-shared". See also "--disable-static". Finding NetCDF/HDF5 ------------------- The NetCDF and HDF libraries must be built and installed before you can build MINC. Teaching MINC to find the NetCDF library depends on where the latter is installed. There are two possibilities here: 1. NetCDF and HDF5 may be installed in a location where the compiler can find it. If so, you have nothing to do. 2. Third-party libraries are commonly installed with headers in /usr/local/include and libraries in /usr/local/include. If this is the case, you should pass the argument "--with-build-path=/usr/local" to configure as such: ./configure --with-build-path=/usr/local 3. Use NetCDF 4.X and HDF5 1.8.X for minc-2.1.X. Compiling --------- Now that things are configured all you need to do is run: ccmake and then make install libminc-libminc-2-3-00/LIBMINCConfig.cmake.in000066400000000000000000000023251257462267400204550ustar00rootroot00000000000000# LIBMINC CMake configuration file get_filename_component(LIBMINC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH ) get_filename_component(LIBMINC_INSTALL_PREFIX "${LIBMINC_CMAKE_DIR}" PATH) set(HAVE_MINC2 1) set(HAVE_EZMINC @LIBMINC_MINC1_SUPPORT@) set(HAVE_MINC1 @LIBMINC_MINC1_SUPPORT@) set(NETCDF_INCLUDE_DIR "@NETCDF_INCLUDE_DIR@") set(HDF5_INCLUDE_DIR "@HDF5_INCLUDE_DIR@") set(NETCDF_LIBRARY "@NETCDF_LIBRARY@") set(HDF5_LIBRARY "@HDF5_LIBRARY@") set(ZLIB_LIBRARY "@ZLIB_LIBRARY@") set(ZLIB_LIBRARIES "@ZLIB_LIBRARY@") set(LIBMINC_INCLUDE_DIRS "@LIBMINC_INCLUDE_DIRS_CONFIG@") set(LIBMINC_LIBRARY_DIRS "@LIBMINC_LIBRARY_DIRS_CONFIG@") set(LIBMINC_USE_FILE "@LIBMINC_USE_FILE_CONFIG@") set(LIBMINC_LIBRARIES "@LIBMINC_LIBRARIES_CONFIG@") set(EZMINC_LIBRARIES "@EZMINC_LIBRARIES@") set(VOLUME_IO_LIBRARIES "@VOLUME_IO_LIBRARY@") set(LIBMINC_STATIC_LIBRARIES "@LIBMINC_STATIC_LIBRARIES_CONFIG@") set(VOLUME_IO_STATIC_LIBRARIES "@VOLUME_IO_LIBRARY_STATIC@") set( LIBMINC_FOUND 1 ) # VF: disable for now #if( NOT LIBMINC_TARGETS_IMPORTED AND NOT LIBMINC_BINARY_DIR ) # set( LIBMINC_TARGETS_IMPORTED 1 ) # include( "${LIBMINC_CMAKE_DIR}/@LIBMINC_EXPORTED_TARGETS@.cmake" ) #endif() libminc-libminc-2-3-00/NEWS000066400000000000000000000760101257462267400153440ustar00rootroot00000000000000New in Release 2.3.00 --------------------- * Removed interpretation of leading zero as specifying an octal constant on the command line (affects almost all minc-tools). * Volume_IO can now load NIfTI-1 and MGZ/MGH volumetric data. * many bugfixes for minc 2.0 code * Using features of HDF5 version 1.8, which allows permits attributes of unlitied size * some API functions signature have changed * MINC2 API can now open MINC1 files by converting it and storing in a temporary file * LIBMINC can be compiled without netcdf library, without MINC1 support * Volume_IO can work directly with MINC2 files if MINC1 API is not available * build system is CMAKE only New in Release 2.2.00 --------------------- * added mincmorph, mincblob and mincsample * many bugfixes for minc 2.0 code * last release before split of libminc from minc-tools New in Release 2.1.10 --------------------- * added imin() and imax() operators to minccalc * added a minc_version global to files created with minc * Fixed a few HDF5 error output bugs * mincview is now coded in sh, not csh also shifted from xv to display * Fixed bug in dicom_to_minc.c for segmentation fault on undefined sequence (initialization of gi_ptr->cur_size) * Fixed some memory leaks (thanks Jim Nikelski) * Added b-matrix field for Siemens diffusion scans (version >= VB only). (thanks to Ilana Leppert) * Made changes to ordering of slices: e.g. a descending acquisition now has negative slice step. This was an issue with MOSAIC, in which the ordering of the slices in the MOSAIC image is ascending, even though the acquisition is descending (version >= VA25 and >= VB11). (thanks to Ilana Leppert) New in Release 2.1.00 --------------------- * Improved convergence and accuracy for application of non-linear transformation (especially for 2-d slices) - Claude * Set default volume_io caching to none * Added pod2man for man page generation of help for perl scripts * Added minccmp and xfm2def man man pages * Added checks for outfiles to minccalc * Added taking first time point for 4D files, -test_size and -sagittal_offset_perc to mincpik * ported some HDF calls to 1.8.x (must now use 1.8.X; 1.6.X no longer supported from now on) * Added libtoolize/glibtoolize logic in autogen.sh (thanks Sean) * Fixed bug in multidim_array_is_alloced for correct check of memory allocation of image data. Return volume=NULL when memory allocation fails. * Fixed a bug in upet2mnc on 64 bit platforms (thanks John Cupitt) New in Release 2.0.18 --------------------- * Fixed bug with chunking for internal file compression using hdf5 (now makes the code faster for large files) * Smarter utilization of buffer in input_mnc.c and output_mnc.c New in Release 2.0.17 --------------------- * Fix for argument handling on 64 bit systems, previously the order of arguments could cause some arguments to be missed. * New tests for argument parsing, mincheader and mincdiff * Cosmetic fixes for a lot of manpages * Fixed a bunch of warnings for cleaner Debian and Ubuntu builds New in Release 2.0.16 --------------------- * new option for mincpik (-anot_bar) and two bugs fixed for lookup triplanars * updates to dcm2mnc for 64bit machines thanks to Claude * 64bit bug fixes for rawtominc * added -clobber and -verbose options to xfmconcat New in Release 2.0.15 --------------------- * MINC2 is now built by default (or disabled with --disable-minc2). * Began to aggregate and update all docs for a clean(er) 2.1 release * Added cubic interpoation in mincresample for x-y slices * Fixed a seg-fault with null history strings * Changed all global variables to static to avoid linking problems with other libraries (eg: libz) * new version of mincedit/mincheader in sh instead of csh * Many fixes to remove compile warnings * removed all rcsid's in files given that we have a -version argument * Added preservation of "patient name" in nii2mnc (where patient name is the descriptor field in a nifti/analyze file) * xfmconcat now records history in the output file * removed all fortran build bits. This code was both out of date and not ever built * removed get_image_offset as with a MINC2 file this would not work as it was it wouldn't work that well with MINC1 let along MINC2 * changed --enable-minc2 to --disable-minc2 in configure.ac (come what may....) New in Release 2.0.14 --------------------- * Added files needed for a CMake build of MINC * Fixed a linking problem with nii2mnc mnc2nii New in Release 2.0.13 --------------------- * Fixed a few small build errors for make check * Changes to ensure there is a clean ITK minc build * added xfmflip * Fixed buffering and chunking for fast internal file compression * updated nii2mnc and mnc2nii with the latest version of niftilib New in Release 2.0.12 --------------------- * Fixed a bug causing dump_acr_nema to skip all elements with element number 0x0010. New in Release 2.0.11 --------------------- * Many small fixes * fix to mincresample * Small fix to mincconvert * -like flag added to rawtominc * Added additional tests for minc2 files. New in Release 2.0.10 --------------------- * Many small fixes to dcm2mnc * Fix mincgen HDF5 support * Fix upet2mnc handling of single-frame files * Fix mincgen -o option to imply -b * Fix duplicate symbols of solve_linear_system and scaled_maximal_pivoting_gaussian_elimination in volume_io/Geometry/gaussian.c and libsrc.2. New in Release 2.0.09 --------------------- This release is primarily a bug fix release for the core MINC libraries, as well as an opportunity to port library changes from the 1.X series. One of the most important changes involves support for HDF5 versions subsequent to version 1.6.2. Previous releases of MINC 2 would not work properly with HDF5 1.6.3 or later, but these issues should now be fixed. Additional major changes include: New converters added in this release: * minctoecat, as contributed by Anthonin Reilhac. * dcm2mnc mincresample now supports "windowed sinc" interpolation. See the man page for more details. mincconcat now has a "-filestarts" option for greater flexibility in combining files. This is especially useful for concatenating functional runs. * mincresample now supports the sinc interpolant * mincconcat has new -filestarts option * Many updates and fixes to upet2mnc (Concorde microPET conversion) * Fixes to the MINC 2.0 library (libminc2) courtesy of Leila Baghdadi * Some progress on documentation * Improved nii2mnc, Analyze and NIfTI-1 conversion utility * Fix MINC 2 code to work with HDF5 1.6.4 as well as 1.6.2. There is a problem with the "make check" self-test code when using HDF5 1.6.3, but this problem should not affect normal operation of the code. New in Release 2.0.08 --------------------- Several major changes are incorporated into this release. First, the "simplified" MINC interface is now included in the release. This interface, which is still under development, is intended to provide easier access for software that does not need to use MINC's more advanced features. Second, we are beginning to include conversion programs with MINC. For this release, we are including converters for the new NIfTI-1 format, created as a successor to Analyze. FSL 3.2 and other popular fMRI packages now support this format. For PET users we are also including the ECAT to MINC format converter, with updates from Anthonin Reilhac Third, some new tool features have been added and bugs have been fixed. * The mincstats bimodal threshold calculation has been fixed and improved. Mincstats now supports multiple algorithms for calculating the bimodal threshold. The default is still the "Otsu '97" algorithm, where the code for this has been corrected to give more reasonable results. In addition we have implemented the "Kittler & Illingworth '86", the "Kapur et al. '85", and a simple "mean of means" algorithms as options. * mincmakescalar now warns the user if an attempt is made to convert a file that has a vector_dimension that is NOT the fastest-varying dimension in the image. Previously the program would simply copy the input file to the output unmodified, without warning the user. In addition to these changes, many bugs and issues with the MINC 2.0 format and interface have been fixed. * Added conversion subdirectory with PRELIMINARY converters for ECAT, Concorde microPET, and NIfTI-1 data files. * Simplified MINC programming interface. * Many bug fixes * See WHATSNEW-2.0 for additional details New in Release 2.0.07 --------------------- * Added automatic creation of "ident" attribute in all MINC files. * Added -sappend and -dappend arguments to minc_modify_header * Most man pages updated * Fixed endian-ness issues which could affect interoperability in MINC 2.0 format * Changed volume_io caching defaults New in Release 2.0.06 --------------------- * No functional changes - this release is intended only to address compilation issues found with the SGI IRIX MIPSpro compiler. New in Release 2.0.05 --------------------- This version represents several weeks of effort cleaning up the existing code and trying to put things into better shape for a release. In particular, the existing test suites ("make check") now run for both MINC 1.0 and MINC 2.0 format files. One functional change is that the MINC 2.0 tools should now create MINC 2.0 format files by default if all of the input files are themselves in MINC 2.0 format. In other words, you only have to specify the "-2" option to force a MINC 2.0 file if one or more of your input files are MINC 1.0 format. The environment variable "MINC_FORCE_V2" should force all output to MINC 2.0 even if MINC 1.0 files are present at the input. There is additional documentation on the new "2.0" MINC programming interface. This is still very much a work in progress so your feedback is very much appreciated, both as to the form and content of these documents: libsrc2/doc/minc_20.tex - Interface reference libsrc2/doc/minc2_uguide.tex - Programmer's guide This distribution will build its libraries as "libminc2.a" and "libvolume_io2.a", rather than the previous "libminc.a" and "libvolume_io.a". This decission was intended to make it easier to keep both MINC1 and MINC2 libraries installed simultaneously, but this may prove to be unwieldy or unnecessary. We welcome suggestions from beta testers. * More bugs fixed * "make check" expanded to test MINC 2.0 format. * "mincdump" added to the distribution. * Almost all MINC 2.0 specific changes are now surrounded by "#ifdef MINC2" New in Release 2.0.04 --------------------- * More bugs fixed * mincconvert now takes a -compress and -chunk option to force the output file to take on a desired structure. * User's guide to MINC 2.0 library is now in doc/minc2_uguide.tex. It's a work in progress but it should contain some useful information. New in Release 2.0.03 --------------------- * Actually implemented MINC_COMPRESS * Many bugs fixed New in Release 2.0.02 --------------------- * Several bug fixes * Even newer error handling New in Release 2.0.01 --------------------- * Support for HDF5 "MINC 2.0" format files. * Revised error message handling. Some new, unwanted messages are probably present. * Tentative .mincrc and environment variable support. * New programming interface. New in Release 1.3 ------------------ * Build fixes. New in Release 1.2 ------------------ * New tools: mincpik, minchistory. * Rawtominc has new options -dimorder and -swap_bytes. * Ability to easily create binary packages; see README.binary_packaging. New in Release 1.1 ------------------ * All MINC programs now ship with a man page. * Rawtominc has new "-skip" option, to allow skipping header information. * Mincstats option "-max_bins" renamed to "-int_max_bins", to avoid clash with "-max". * Minccalc has new functions: tan, asin, acos, and atan. ************************************************************************ Wed Jan 16 08:43:28 EST 2002 *** RELEASE OF MINC 1.0 Tue Jan 15 12:55:21 EST 2002 - Modified libsrc/Makefile to install ParseArgv man pages. Tue Jan 15 10:40:49 EST 2002 - Updated README to include minccalc and mincstats. Mon Jan 14 16:26:53 - Moved nd_loop, voxel_loop, ParseArgv and time_stamp to the libsrc directory for inclusion in the minc library. The header files will continue to be separate from minc.h Mon Jan 14 15:02:39 EST 2002 - Modified voxel_loop so that input buffers have a minimum size (1K voxels). This prevents large output images from forcing buffers to contain only one voxel when the requested memory is not large enough. Wed Jan 9 08:58:27 EST 2002 - Modified mincstats to only print a blank line after the histogram info if -quiet is not specified. Fri Dec 14 12:12:42 EST 2001 - Removed rcsid variables from volume_io .h files. Mon Dec 10 09:11:58 EST 2001 - Sped up mincstats by only doing centre-of-mass summing when it is needed (thanks to Andrew Janke for the suggestion). Thu Dec 6 16:55:50 EST 2001 - extensive modifications to mincstats to get it working properly and to add support for multiple volume and mask ranges. Thu Dec 6 09:14:22 EST 2001 - Corrected return from mivar_exists in minc library to use MI_RETURN so that ncopts is properly restored. Tue Dec 4 12:24:29 EST 2001 - No code changes, but checked in lex.c and gram.c in progs/minccalc. This fixes an interaction problem between make and CVS. When exporting the minc tree the file dates are set to the commit time. If the generated .c files were previously committed at the same time as the generating files (.l and .y), then later makes on the exported files might try to rebuild the .c files. Since these file dates tend to end up in the distribution, the distribution will not build cleanly without bison and flex being installed. Wed Nov 28 10:36:13 EST 2001 - Removed limit on number of icv's that can exist at one time. The definition of MI_MAX_NUM_ICV remains, but it is no longer enforced. - Added function set_minc_input_user_real_range to allow users to set the scaling for input to integers. This forces the Volume real range to a particular pair of values, rather than using the full range of the file. - Added get_info_voxel_index to voxel_loop to allow users to get the full multi-dimensional file index of the current voxel. - Allow arg_string in voxel_loop to be NULL. Tue Nov 13 16:04:36 EST 2001 - Modified icv normalization handling in minc library. When the icv type is floating point, normalization is always done (scaling to real values), regardless of the normalization setting. When the file type is floating point, rescaling of internal integers are done using the slice real range (image-max/min). Thus image-max/min are only completely ignored for integer to integer conversion. This fixes some problems that appeared when converting from int to float or float to int with mincrehape. Tue Nov 13 09:17:17 EST 2001 - Added functions miget_image_range and mivar_exists to minc_convenience.c. - In mincreshape, set output valid range correctly for conversion from int to float types. Wed Oct 31 14:40:30 EST 2001 - Fixed mincinfo printing of sign for default output. Change to miget_datatype had messed this up. - Changed names of macros _R and _P in volume_io/Include/geometry.h to avoid clashes with macro in ctype.h. Wed Oct 17 14:33:00 EDT 2001 - Modified miset_valid_range to write out valid_range as double in all cases except float. Unfortunately, writing out values in a type that matched the type of the image variable caused problems with programs linked against old minc libraries. Tue Sep 18 11:47:13 EDT 2001 - In library function miset_valid_range, changed output type of valid_range from byte to short when image type is byte. Tue Sep 18 11:34:16 EDT 2001 - Always create image variable last in order to allow images > 2GB on 64-bit machines (offset to variable must be < 2GB, but size can be greater). This also fixes a compatibility problem between NetCDF 3.x and 2.3, in which 3.x can create files that cause 2.3-linked programs to crash (when NC_NOFILL is set). Putting the image variable at the end ensures that data is always written to the end of the file, assuming that the image data will always be completely written. Mon Aug 20 09:20:04 EDT 2001 - more fixes to valid_range handling functions - added function miattget_with_sign Thu Aug 16 12:47:32 EDT 2001 - added library functions to handle reading and writing of valid_range and reading of type and sign, as well as setting of default range values for a given type. These routines properly handle type differences between valid_range and the image variable. For the case of a float image and a double valid_range attribute, valid data can go out of range and appear invalid through rounding in the conversion from double to float and back. Writing of the valid_range attribute now follows the NetCDF convention of having the same type for variables and valid_range. Modified volume_io, voxel_loop and programs to use these functions. - modified minctoraw so that user must specify either -normalize or -nonormalize Mon Aug 13 09:44:13 EDT 2001 - Changed use of floorf to floor in macro SCALAR_ROUND of node.h in minccalc. - Added invocation of ranlib when installing minc and volume_io libraries. ************************************************************************ Fri Aug 10 08:49:54 EDT 2001 *** RELEASE OF MINC 0.8 Fri Aug 10 08:49:34 EDT 2001 - Fixed Makefiles in doc directories so that they build properly Tue Apr 24 14:18:07 EDT 2001 - Added minccalc from Andrew Janke to progs directory. Tue Apr 24 12:21:42 EDT 2001 - Fixed bug in execute_decompress_command in libsrc/netcdf_convenience.c that caused multiple stdio buffer flushing when opening compressed files (particularly with mincinfo). - Moved volume_io documentation build from doc directory to volume_io/Documentation, and added install-docs target to top-level Makefile. Tue Apr 24 09:43:38 EDT 2001 - Replaced NC_NAT by MI_ORIGINAL_TYPE since NC_NAT is not defined for NetCDF 3.1-3.4. Because NC_NAT and NC_UNSPECIFIED are defined through enums, they cannot be detected by the preprocessor. So we just define MI_ORIGINAL_TYPE to the same value ((nc_type) 0). Mon Apr 23 09:20:36 EDT 2001 - Fixed volume_io/Volumes/output_mnc.c to pass cdfid to mivarput* instead of minc_icv id (patch from Steve Robbins). Problems showed up when used NetCDF 3.x Tue Apr 17 15:00:48 EDT 2001 - Modified to build with NetCDF 3.x. Changed NC_LONG to NC_INT, as well as corresponding longs to ints. Replaced NC_UNSPECIFIED with NC_NAT (not-a-type). Changed volume_io to use UNSIGNED_INT instead of UNSIGNED_LONG. Added appropriate definitions to support both of these with both NetCDF 2.x and 3.x. Changed error handling to be independent of NetCDF library apart from use of ncopts and ncerr. Uses old style NetCDF2 error handling still, but no longer makes use of NetCDF internals. Removed all prototype wrappers to only support ANSI C. Changed fortran build - no longer generate .c from .src with m4 since fortran support mechanism under NetCDF has changed. Keeping irix5 .c file for backwards compatibility. Only build when BUILD_FORTRAN is set to "yes". Mon Apr 2 11:35:29 EDT 2001 - Added -keep_real_range option to mincresample for resampling labels. Fri Nov 3 11:44:38 EST 2000 - Modified -dinsert option to minc_modify_header to allow multiple values. Tue Sep 19 11:37:41 EDT 2000 - Added option for user-defined allocation function to voxel_loop code - Added LICENSE file to top-level directory Wed Sep 13 11:38:49 EDT 2000 - added bzip support (patches from Steve Robbins) - modified TMPDIR to look in /var/tmp, /usr/tmp, /tmp in scripts mincdiff, mincheader, mincview and mincedit (Steve Robbins) - converted mincheader and mincdiff to Bourne shell (Steve Robbins) Fri Jul 7 09:34:17 EDT 2000 - added -filelist option to mincaverage, mincmath, mincconcat to read input file names from a file. This gets around command-line length limits. Wed Apr 26 15:36:04 EDT 2000 - modified mincinfo to handle multiple files (patch from Steve Robbins) Wed Apr 5 09:02:55 EDT 2000 - fixed bug in mincresample to properly handle valid fillvalues in slices containing no original data. ************************************************************************ Tue Mar 21 10:03:15 EST 2000 *** RELEASE OF MINC 0.7 Wed Feb 2 13:43:43 EST 2000 - modified miexpand_file in library so that fclose is not called with a NULL fp when the file does not exist. This would cause a seg fault with newer versions of glibc. Thu Jan 20 15:36:04 EST 2000 - Re-arranged headers in volume_io so that only volume_io.h needs to be on the search path. Other header files are installed in the volume_io subdirectory and found by volume_io.h. (Patch from Steve Robbins.) Tue Oct 19 14:45:27 1999 - Completed conversion from RCS to CVS Fri Oct 15 15:25:56 EST 1999 - Moved the Acr_nema libraries and conversion programs out of the minc package. Fri Nov 13 11:02:51 EST 1998 - Modified acrnema library to support asynchronous transfers in the client routines (allow sending of next message before receiving reply to previous one). Wed Nov 11 11:28:34 EST 1998 - Modifications to acrnema library: Added functions acr_find_group and acr_group_steal_element. Increased default buffer size to 64KB. Thu Feb 19 09:06:38 EDT 1998 - Fixes to internals of mincreshape that had not yet shown symptoms, but appeared with insure++. Thu Aug 13 15:35:15 EDT 1998 - Modified mincconcat so that the coordinate variable corresponding to the concatenation dimension is always created, subscripted with by the dimension (like an irregularly-spaced dimension). This is required by some programs that want all of the time points (in particular) to be stored even for a regularly-spaced dimension. Mon Jun 22 10:06:29 EDT 1998 - Fixed bug in rawtominc in handling of default types and signs. This bug showed up when options -short and -scan_range were used with data containing negative values. Wed Apr 22 16:23:06 EDT 1998 - Fixed a bug in fortran test program minc_ftest.f in which an insufficient number of arguments were being passed to miclos. Mon Mar 23 15:17:49 EST 1998 - Moved some functions in the acr-nema library from one file to another. Tue Mar 17 12:05:43 EST 1998 - Modified acrnema library so that default maximum length for client connections is 1MB to fix problems with servers that do not handle maximum length 0 (unlimited). Tue Mar 10 12:07:28 EST 1998 - Fixed more bugs in acrnema library to prevent core dumps under Linux when a protocol error occurs while reading a message. Also fixed handling of last fragment bit for DICOM command PDV's. Re-organized code to use watchpoints more consistently in dicom_network.c Fri Feb 20 12:29:18 EST 1998 - Fixed bug in acrnema library (dicom_client_routines) that sometimes caused write errors under irix 5.3. Thu Feb 19 10:05:34 EST 1998 - Minor bug fixes. Wed Feb 18 15:29:28 EST 1998 - Minor bug fixes in libacrnema. Mon Sep 29 08:24:20 EDT 1997 - Modified rawtominc so that its argument error messages are more explicit. Wed Sep 17 09:24:32 EDT 1997 - Modified gcomserver to do conversions to dicom and retransmit data to a dicom server. ************************************************************************ Fri Sep 12 13:24:48 EDT 1997 *** RELEASE OF MINC 0.6 Thu Sep 11 10:54:41 EDT 1997 - Copied in new version of volume_io. The version released with minc 0.5 was missing function set_volume_translation. It has been re-instated. Thu Sep 11 09:46:51 EDT 1997 - Modified gcomserver to have project file syntax that allows different things to be done to the data. The old syntax is still supported. Wed Sep 10 15:31:27 EDT 1997 - Small modification to dicomserver (siemens_dicom_read.c) to set default direction cosines properly if they are absent from the dicom data. Mon Sep 08 17:52:21 EDT 1997 - Added dicom client routines to acr_nema library. - Added new status for connection timeout to acr_nema library. Tue Sep 02 18:52:12 EDT 1997 - Fixed acr_nema library (element.c) padding of UI-type elements (pad with NUL instead of blank). ************************************************************************ Thu Aug 21 09:22:18 EDT 1997 *** RELEASE OF MINC 0.5 Wed Aug 13 19:30:00 EDT 1997 - Changes to acr-nema library: Fixed bug that was causing the loss of PDU items on read in dicom_network.c. Added function acr_group_remove_element to group.c. Wed Aug 13 12:09:37 EDT 1997 - Copied in new release of volume_io Wed Aug 13 11:44:38 EDT 1997 - Fixed initialization bug in mincresample that caused it to always crash under Linux Tue Aug 12 11:44:38 EDT 1997 - Added new program mincmakevector for assembling a list of scalar files into one vector file. Thu Aug 7 11:44:38 EDT 1997 - Added new program mincmakescalar for converting vector volumes to scalar by various schemes. Fri Jun 20 09:59:47 EDT 1997 - Fixed bug in voxel_loop that affected mincconcat when concatenating 4D (or greater) files. Basically, when there are no output files and accumulation is used, then an outer loop over files is done (process each file in order). With 4D or greater input files, the first volume of each file would be handled correctly, but subsequent volumes would only have the last slice read in. Tue Jun 3 10:59:29 EDT 1997 - Really corrected dimension width suffix added by mincconcat. Changed to _width to -width (fix of Aug 27, 1996 was not complete) ************************************************************************ Wed May 7 16:05:05 EDT 1997 *** RELEASE OF MINC 0.4 Wed May 7 15:09:58 EDT 1997 - Changed distribution script to use gzip instead of compress. Thu Apr 24 13:48:51 EDT 1997 - Added mincmath options: -maximum, -minimum, -abs, -illegal_value and -count_valid. Wed Apr 23 10:58:02 EDT 1997 - Added code to volume_io/Prog_utils/files.c to handle missing strerror (on SunOS). Modified configure.in and Make_machine_specific.in to define constant in this case (through GLOBAL_CDEFINES). - Fixed progs install so that directories with more than one binary will get them all installed under linux (bash seems to have a problem with my Bourne shell code). - Added dependencies for install targets. They were removed at one point, but it seems safer to have them in. They'll probably disappear at a later point, and perhaps I'll make a note of the reason...! - Added options -maximum, -minimum, -abs, -illegal_value and -count_valid to mincmath. Fixed handling of invalid or uninitialized data for cumulative operations. Mon Apr 21 13:33:55 EDT 1997 - Fixed icv calculation of scale so that values are never re-scaled from their internal "real" value to an external floating-point type (or the other way around). Previously, this re-scaling could occur if normalization was turned off and a valid range was set in the file. Thu Apr 17 14:45:18 EDT 1997 - Added option of setting BUILD_FORTRAN to "no" for configure. - Moved building of volume_io documentations to docs directory. - Added explicit path to run_tests script Thu Apr 10 15:23:32 EDT 1997 - Fixed icv handling of invalid data when the scale factor is zero. When this happens fillvalue checking is turned on (for both input and output) regardless of the user setting. This fixes problems when mincmath is writing out a uniform image that contains invalid data as well. - Removed redefinition of NULL from library header file and added casts to pointer types in places where it matters. Mon Mar 10 15:23:32 EDT 1997 - Updated ACR-NEMA library to handle dicom messages. Wed Jan 15 11:26:28 EST 1997 - Fixed small bug in test program minc_types.c so that handling of FLT_MAX is done properly and output agrees with original output and Tue Jan 14 14:47:40 EST 1997 - Added Fortran wrappers miopn, micre and miclos Tue Jan 7 14:47:40 EST 1997 - Added -origin option to rawtominc. Tue Dec 10 09:30:21 EST 1996 - Modified top-level Makefile - added targets libs, programs, test and docs - default make no longer does make in docs directory Tue Aug 27 13:08:18 EDT 1996 - Corrected dimension width suffix added by mincconcat. Changed to _width to -width. Wed Jul 10 13:08:18 EDT 1996 - Modified minclookup: - added output type options - added -lut_string - added handling of duplicate first or last lookup table entries - added man page Wed Jun 19 14:26:33 EDT 1996 - Catch errors in opening file specified with -input for rawtominc Tue Jan 16 08:30:08 EST 1996 - Added -invert option to mincmath Wed Dec 13 08:47:26 EST 1995 - Added -check_dimensions and -nocheck_dimensions options to mincmath. - Improved tmp dir cleanup in mincview when an error occurs. - Small changes to minc_modify_header man page. Tue Dec 12 14:19:07 EST 1995 - Added convert_origin_to_start routines to Proglib to handle conversion of a point to a start value given 3 direction cosines - Modified mincresample: - modified argument parsing so that only one pass is done - changed default to transform input sampling when -transformation is specified and added options -tfm_input_sampling (to get above behaviour) and -use_input_sampling (to get old behaviour). - added -origin option to specify a coordinate instead of dimension start values. - added -standard_sampling option. - added -invert_transformation option. - added -spacetype, -talairach and -units options. Mon Nov 20 14:24:47 EST 1995 - Added -weights option to mincaverage. Thu Nov 16 13:11:16 EST 1995 - Include math.h in rawtominc, mincwindow, mincinfo, mincconcat and Acr_nema library to get declaration of strtod under SunOs. Wed Oct 4 19:05:25 EDT 1995 - Fixed default minimum for signed long. Fri Sep 29 12:59:06 EDT 1995 - Modified micopy_all_atts in library to handle MI_ERROR being passed as a variable id. - Fixed handling of image-min/max in mincconcat when the variables are not present in the input files. Wed Aug 2 13:41:36 - Added -prefix option to gyrotominc. Fri Jun 16 08:28:39 EDT 1995 - Modified mincview so that file name appears in xv window frame. Tue Jun 13 16:44:28 EDT 1995 - Improved test for ANSI compiler in configure script. Check that (signed char *) is handled properly (since the Solaris compiler doesn't handle it from what I've been told). - Improved configuration for fortran compilation. Set FORTRAN_SUBDIR and FORTRAN_OBJ from configure only if f77 is found and the system is irix. Mon Jun 12 13:32:53 EDT 1995 - Added MI_LABEL modality to minc.h - Modified miexpand_file and miopen to try adding compression extensions to filenames if the first open fails. libminc-libminc-2-3-00/README000066400000000000000000000061211257462267400155210ustar00rootroot00000000000000MINC - Medical Image NetCDF or MINC isn't netCDF INTRODUCTION ------------ The MINC file format is a highly flexible medical image file format built on the HDF5 generalized data format. The format is simple, self-describing, extensible, portable and N-dimensional, with programming interfaces for both low-level data access and high-level volume manipulation. On top of the libraries is a suite of generic image-file manipulation tools. The format, libraries and tools are designed for use in a medical-imaging research environment : they are simple and powerful and make no attempt to provide a pretty interface to users. Minc tools are hosted in https://github.com/BIC-MNI/minc-tools Additional tools are available from the Montreal Neurological Institute (MNI). http://packages.bic.mni.mcgill.ca/tgz SUPPORT FOR HDF5 "MINC 2.0" FORMAT FILES ---------------------------------------- This change requires that HDF5 must be installed before MINC can be built. You can obtain HDF5 from, current version is 1.8.11: http://www.hdfgroup.org/ You should NOT need to become an HDF5 expert to use MINC 2.0. However, two tools included with HDF5 may prove useful, "h5dump" and "h5ls". h5dump is roughly equivalent to netCDF's ncdump utility. There is no exact netCDF tool analogous to h5ls. h5ls is useful for exploring and extracting bits of the HDF5 hierarchy. 3. TENTATIVE ".mincrc" AND ENVIRONMENT VARIABLE SUPPORT This is implemented as a way to control certain behaviors of the library which are not readily available through other means. These variables may be defined either in your environment, or in the .mincrc file in your home directory. The value in the environment should override the one in .mincrc. Here's what exists so far: # Force output files to MINC 2.0 format, regardless of the "-2" option. # MINC_FORCE_V2 = {1, 0} # Desired ZLIB compression level. Zero implies no compression, a value # of 4 gives a good tradeoff of compression and performance. # MINC_COMPRESS = {0..9} # Desired HDF5 chunking dimension. This controls the size of the # "hypercube" used by HDF5 to store the file. If set to zero, the # file will not be stored in a chunked format, unless compression is # enabled. The chunk size will automatically be reduced if it exceeds # any actual dimension of the volume. If chunking is enabled # automatically because of compression, the default chunk dimension is # 32. # MINC_CHUNKING = {0..N} # Log file - path to the desirned output file for messages. The default # is the standard error (stderr) stream. To redirect to standard output, # set this variable to "stdout" or "-". Otherwise the variable is taken # to be the path to the desired log file. If the path name begins with # a '+' character, the log file should be appended rather than recreated. # MINC_LOGFILE = [+]|stdout|- # Logging level. Not really useful yet, but intended to allow setting # of 'verbosity' of messages. A value of zero would inhibit all but # fatal messages, a value of 4 would allow debugging messages. # MINC_LOGLEVEL = 0-4 DOCUMENTATION ------------- http://en.wikibooks.org/wiki/MINC libminc-libminc-2-3-00/README.release000066400000000000000000000027071257462267400171460ustar00rootroot00000000000000 # how to use git and the master and develop branches http://nvie.com/posts/a-successful-git-branching-model/ # libtool and libraries We are using libtool to (possibly) generate a shared library for libminc with a three-component version string. CURRENT[:REVISION[:AGE]] # Release procedure * Update NEWS from Changelog, add break mark in Changelog * Update the LIBMINC_SOVERSION in CMakeLists.txt according to the following rules: 0. Each library's version should be updated according to these rules INDEPENDENTLY! 1. If the library source code has changed at all since the last update, then increment REVISION (`C:R:A' becomes `C:r+1:A'). 2. If any interfaces have been added, removed, or changed since the last update, increment CURRENT, and set REVISION to 0. 3. If any interfaces have been added since the last public release, then increment AGE. 4. If any interfaces have been removed since the last public release, then set AGE to 0. * ccmake, build, and install. * Update Documentation (AUTHORS, COPYING, NEWS, README) Authors should be sourced from recent commits * Commit. * Run CPack to make .tar.gz * Test build from .tar.gz on another system. * Copy tar file to distribution site. * Tag the release branch as "libminc-x-y-z" (MINC x.y.z). * Update the version number in CMakeLists.txt for the next release in order to avoid the problem of multiple releases with the same version number * Commit. libminc-libminc-2-3-00/UseLIBMINC.cmake.in000066400000000000000000000005271257462267400200060ustar00rootroot00000000000000INCLUDE_DIRECTORIES(${LIBMINC_INCLUDE_DIRS}) IF(HAVE_MINC1) ADD_DEFINITIONS( -DHAVE_MINC1=1) INCLUDE_DIRECTORIES(${NETCDF_INCLUDE_DIR}) ENDIF(HAVE_MINC1) IF(HAVE_MINC2) SET(MINC2 "1") ADD_DEFINITIONS( -DMINC2=1 -DHAVE_MINC2=1) INCLUDE_DIRECTORIES(${HDF5_INCLUDE_DIR}) ENDIF(HAVE_MINC2) LINK_DIRECTORIES(${LIBMINC_LIBRARY_DIRS}) libminc-libminc-2-3-00/cmake-modules/000077500000000000000000000000001257462267400173675ustar00rootroot00000000000000libminc-libminc-2-3-00/cmake-modules/BuildHDF5.cmake000066400000000000000000000012441257462267400220400ustar00rootroot00000000000000macro(build_hdf5 install_prefix) ExternalProject_Add(HDF5 SOURCE_DIR HDF5 URL "http://www.hdfgroup.org/ftp/HDF5/releases/hdf5-1.8.7/src/hdf5-1.8.7.tar.gz" URL_MD5 "37711d4bcb72997e93d495f97c76c33a" BUILD_IN_SOURCE 1 INSTALL_DIR "${install_prefix}" BUILD_COMMAND make INSTALL_COMMAND make install CONFIGURE_COMMAND ./configure --prefix=${install_prefix} --with-pic --disable-shared --disable-cxx --disable-f77 --disable-f90 --disable-examples --disable-hl --disable-docs ) SET(HDF5_INCLUDE_DIR ${install_prefix}/include ) SET(HDF5_LIBRARY ${install_prefix}/lib/libhdf5.a ) endmacro(build_hdf5)libminc-libminc-2-3-00/cmake-modules/BuildNETCDF.cmake000066400000000000000000000012241257462267400223130ustar00rootroot00000000000000 macro(build_netcdf install_prefix) ExternalProject_Add(NETCDF SOURCE_DIR NETCDF URL "ftp://ftp.unidata.ucar.edu/pub/netcdf/netcdf-4.0.1.tar.gz" URL_MD5 "a251453c5477599f050fa4e593295186" BUILD_IN_SOURCE 1 INSTALL_DIR "${install_prefix}" BUILD_COMMAND make INSTALL_COMMAND make install CONFIGURE_COMMAND ./configure --prefix=${install_prefix} --with-pic --disable-netcdf4 --disable-hdf4 --disable-dap --disable-shared --disable-cxx --disable-f77 --disable-f90 --disable-examples --enable-v2 --disable-docs ) SET(NETCDF_LIBRARY ${install_prefix}/lib/libnetcdf.a ) SET(NETCDF_INCLUDE_DIR ${install_prefix}/include ) endmacro(build_netcdf)libminc-libminc-2-3-00/cmake-modules/FindHDF5.cmake000066400000000000000000000043651257462267400216700ustar00rootroot00000000000000# # this module look for HDF5 (http://hdf.ncsa.uiuc.edu) support # it will define the following values # # HDF5_INCLUDE_DIR = where hdf5.h can be found # HDF5_LIBRARY = the library to link against (hdf5 etc) # HDF5_FOUND = set to true after finding the library # IF(EXISTS ${PROJECT_CMAKE}/Hdf5Config.cmake) INCLUDE(${PROJECT_CMAKE}/Hdf5Config.cmake) ENDIF(EXISTS ${PROJECT_CMAKE}/Hdf5Config.cmake) IF(Hdf5_INCLUDE_DIRS) FIND_PATH(HDF5_INCLUDE_DIR hdf5.h ${Hdf5_INCLUDE_DIRS}) FIND_LIBRARY(HDF5_LIBRARY hdf5 ${Hdf5_LIBRARY_DIRS}) ELSE(Hdf5_INCLUDE_DIRS) SET(TRIAL_LIBRARY_PATHS $ENV{HDF5_HOME}/lib /usr/apps/lib /usr/lib /usr/local/lib /opt/lib /sw/lib ) SET(TRIAL_INCLUDE_PATHS $ENV{HDF5_HOME}/include /usr/apps/include /usr/include /opt/include /usr/local/include /sw/include ) IF($ENV{HDF5_DIR} MATCHES "hdf") MESSAGE(STATUS "Using environment variable HDF5_DIR.") SET(TRIAL_LIBRARY_PATHS $ENV{HDF5_DIR}/lib ${TRIAL_LIBRARY_PATHS} ) SET(TRIAL_INCLUDE_PATHS $ENV{HDF5_DIR}/include ${TRIAL_INCLUDE_PATHS} ) ENDIF($ENV{HDF5_DIR} MATCHES "hdf") FIND_LIBRARY(HDF5_LIBRARY hdf5 ${TRIAL_LIBRARY_PATHS}) FIND_PATH(HDF5_INCLUDE_DIR hdf5.h ${TRIAL_INCLUDE_PATHS} ) ENDIF(Hdf5_INCLUDE_DIRS) ## ----------------------------------------------------------------------------- ## Assign status of the search IF(HDF5_INCLUDE_DIR AND HDF5_LIBRARY) SET(HDF5_FOUND 1 CACHE BOOL "Found hdf5 library") ELSE(HDF5_INCLUDE_DIR AND HDF5_LIBRARY) SET(HDF5_FOUND 0 CACHE BOOL "Not fount hdf5 library") ENDIF(HDF5_INCLUDE_DIR AND HDF5_LIBRARY) ## ----------------------------------------------------------------------------- ## Feedback IF (HDF5_FOUND) IF (NOT HDF5_FIND_QUIETLY) MESSAGE (STATUS "Found components for HDF5") MESSAGE (STATUS "HDF5 library : ${HDF5_LIBRARY}") MESSAGE (STATUS "HDF5 headers : ${HDF5_INCLUDE_DIR}") ENDIF (NOT HDF5_FIND_QUIETLY) ELSE (HDF5_FOUND) IF (HDF5_FIND_REQUIRED) MESSAGE (FATAL_ERROR "Could not find HDF5!") ENDIF (HDF5_FIND_REQUIRED) ENDIF (HDF5_FOUND) ## ----------------------------------------------------------------------------- ## Variables marked as advanced MARK_AS_ADVANCED( HDF5_INCLUDE_DIR HDF5_LIBRARY HDF5_FOUND ) libminc-libminc-2-3-00/cmake-modules/FindNETCDF.cmake000066400000000000000000000012771257462267400221440ustar00rootroot00000000000000# FindNetCDF.cmake module FIND_PATH(NETCDF_INCLUDE_DIR netcdf.h /usr/include /usr/local/include /usr/local/bic/include) FIND_LIBRARY(NETCDF_LIBRARY NAMES netcdf PATHS /usr/lib /usr/local/lib /usr/local/bic/lib) IF (NETCDF_INCLUDE_DIR AND NETCDF_LIBRARY) SET(NETCDF_FOUND TRUE) ENDIF (NETCDF_INCLUDE_DIR AND NETCDF_LIBRARY) IF (NETCDF_FOUND) IF (NOT NETCDF_FIND_QUIETLY) MESSAGE(STATUS "Found NetCDF headers: ${NETCDF_INCLUDE_DIR}") MESSAGE(STATUS "Found NetCDF library: ${NETCDF_LIBRARY}") ENDIF (NOT NETCDF_FIND_QUIETLY) ELSE (NETCDF_FOUND) IF (NETCDF_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Cound not find NetCDF") ENDIF (NETCDF_FIND_REQUIRED) ENDIF (NETCDF_FOUND) libminc-libminc-2-3-00/config.h.cmake000066400000000000000000000034101257462267400173340ustar00rootroot00000000000000/* various defines */ #ifndef MINC2 #define MINC2 @MINC2@ #endif #cmakedefine DEBUG 1 #define MINC_PACKAGE_NAME "@LIBMINC_PACKAGE_NAME@" #define MINC_PACKAGE_BUGREPORT "@LIBMINC_PACKAGE_BUGREPORT@" #define MINC_PACKAGE_VERSION "@LIBMINC_PACKAGE_VERSION@" #define MINC_VERSION "@LIBMINC_PACKAGE_VERSION@" #define MINC_PACKAGE_STRING "@LIBMINC_PACKAGE_STRING@" #cmakedefine HAVE_MINC1 1 #cmakedefine HAVE_MINC2 1 #ifndef H5Acreate_vers #define H5Acreate_vers 2 #endif //H5Acreate_vers #cmakedefine HAVE_MKSTEMP 1 #cmakedefine HAVE_STRERROR 1 #cmakedefine HAVE_FLOAT_H 1 #cmakedefine HAVE_DIRENT_H 1 #cmakedefine HAVE_DLFCN_H 1 #cmakedefine HAVE_FCNTL_H 1 #cmakedefine HAVE_FORK 1 #cmakedefine HAVE_GETPWNAM 1 #cmakedefine HAVE_INT16_T 1 #cmakedefine HAVE_INT32_T 1 #cmakedefine HAVE_INTTYPES_H 1 #cmakedefine HAVE_MEMORY_H 1 #cmakedefine HAVE_MKSTEMP 1 #cmakedefine HAVE_NDIR_H 1 #cmakedefine HAVE_POPEN 1 #cmakedefine HAVE_PWD_H 1 #cmakedefine HAVE_SELECT 1 #cmakedefine HAVE_STDINT_H 1 #cmakedefine HAVE_STDLIB_H 1 #cmakedefine HAVE_STRDUP 1 #cmakedefine HAVE_SYSCONF 1 #cmakedefine HAVE_SYSTEM 1 #cmakedefine HAVE_SYS_DIR_H 1 #cmakedefine HAVE_SYS_NDIR_H 1 #cmakedefine HAVE_SYS_STAT_H 1 #cmakedefine HAVE_SYS_TIME_H 1 #cmakedefine HAVE_SYS_TYPES_H 1 #cmakedefine HAVE_SYS_WAIT_H 1 #cmakedefine HAVE_TEMPNAM 1 #cmakedefine HAVE_TMPNAM 1 #cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_VALUES_H 1 #cmakedefine HAVE_VFORK 1 #cmakedefine HAVE_VFORK_H 1 #cmakedefine HAVE_WORKING_FORK 1 #cmakedefine HAVE_WORKING_VFORK 1 #cmakedefine HAVE_ZLIB 1 #cmakedefine HAVE_STRINGS_H 1 #cmakedefine HAVE_STRING_H 1 #cmakedefine HAVE_SRAND48 1 #cmakedefine HAVE_DRAND48 1 #cmakedefine HAVE_SLEEP 1 #cmakedefine HAVE_CLOCK_GETTIME 1 #cmakedefine HAVE_GETTIMEOFDAY 1 libminc-libminc-2-3-00/doc/000077500000000000000000000000001257462267400154065ustar00rootroot00000000000000libminc-libminc-2-3-00/doc/README.md000066400000000000000000000001351257462267400166640ustar00rootroot00000000000000# LibMINC documentation LibMINC documentation resides at http://en.wikibooks.org/wiki/MINC libminc-libminc-2-3-00/ezminc/000077500000000000000000000000001257462267400161265ustar00rootroot00000000000000libminc-libminc-2-3-00/ezminc/CMakeLists.txt000066400000000000000000000024761257462267400206770ustar00rootroot00000000000000IF(LIBMINC_BUILD_V2) ADD_DEFINITIONS( -DMINC2 ) ENDIF(LIBMINC_BUILD_V2) SET( MINC_IO_HEADERS minc_io_exceptions.h minc_io_fixed_vector.h minc_io_simple_volume.h minc_1_rw.h minc_1_simple.h minc_1_simple_rw.h minc_io_4d_volume.h ) SET( MINC_IO_SRC minc_1_rw.cpp minc_1_simple_rw.cpp ) INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}) ADD_LIBRARY( minc_io ${LIBRARY_TYPE} ${MINC_IO_HEADERS} ${MINC_IO_SRC}) TARGET_LINK_LIBRARIES(minc_io ${LIBMINC_LIBRARIES}) SET_TARGET_PROPERTIES(minc_io PROPERTIES SOVERSION ${LIBMINC_SOVERSION}) IF(LIBMINC_INSTALL_LIB_DIR) INSTALL(TARGETS minc_io EXPORT ${LIBMINC_EXPORTED_TARGETS} LIBRARY DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries ARCHIVE DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries RUNTIME DESTINATION ${LIBMINC_INSTALL_LIB_DIR} COMPONENT libraries ) ENDIF(LIBMINC_INSTALL_LIB_DIR) IF(LIBMINC_INSTALL_INCLUDE_DIR) INSTALL(FILES ${MINC_IO_HEADERS} DESTINATION ${LIBMINC_INSTALL_INCLUDE_DIR}) ENDIF(LIBMINC_INSTALL_INCLUDE_DIR) IF(LIBMINC_BUILD_EZMINC_EXAMPLES) ADD_SUBDIRECTORY(examples) ENDIF(LIBMINC_BUILD_EZMINC_EXAMPLES) IF(BUILD_TESTING) ADD_SUBDIRECTORY(tests) ENDIF(BUILD_TESTING) libminc-libminc-2-3-00/ezminc/examples/000077500000000000000000000000001257462267400177445ustar00rootroot00000000000000libminc-libminc-2-3-00/ezminc/examples/CMakeLists.txt000066400000000000000000000003471257462267400225100ustar00rootroot00000000000000LINK_LIBRARIES(minc_io) ADD_EXECUTABLE(trilinear_resample trilinear_resample.cpp) ADD_EXECUTABLE(volume_avg volume_avg.cpp) ADD_EXECUTABLE(volume_msq_dist volume_msq_dist.cpp) ADD_EXECUTABLE(create_grid_file create_grid_file.cpp) libminc-libminc-2-3-00/ezminc/examples/Example_CMakeLists.txt000066400000000000000000000004421257462267400241570ustar00rootroot00000000000000# Example CMakeLists.txt to work with minc&EZminc PROJECT(EXAMPLE) cmake_minimum_required(VERSION 2.8) FIND_PACKAGE(LIBMINC REQUIRED) INCLUDE( ${LIBMINC_USE_FILE} ) ADD_EXECUTABLE(trilinear_resample trilinear_resample.cpp) target_link_libraries(trilinear_resample ${EZMINC_LIBRARIES}) libminc-libminc-2-3-00/ezminc/examples/create_grid_file.cpp000066400000000000000000000061471257462267400237270ustar00rootroot00000000000000#include #include #include #include #include #include // for get_opt_long #include #include #include using namespace std; using namespace minc; static void show_usage(const char *name) { std::cerr << "Usage: "<" << std::endl << "Optional parameters:" << std::endl << "\t--verbose be verbose" << std::endl << "\t--clobber clobber the output files" << std::endl << "\t--version print version" << std::endl ; } int main (int argc, char **argv) { int clobber=0; int verbose=0; // read the arguments static struct option long_options[] = { {"verbose", no_argument, &verbose, 1}, {"quiet", no_argument, &verbose, 0}, {"clobber", no_argument, &clobber, 1}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0} }; int c; for (;;) { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "vt:m:", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 0: break; case 'v': cout << "Version: 1.0" << endl; return 0; case '?': /* getopt_long already printed an error message. */ default: show_usage(argv[0]); return 1; } } if ((argc - optind) < 1) { show_usage(argv[0]); return 1; } std::string output =argv[optind]; if(!clobber && !access (output.c_str(), F_OK)) { std::cerr << output.c_str () << " Exists!" << std::endl; return 1; } try { minc_info output_info_grid; const size_t nx=20,ny=20,nz=20; const double step=10; const double start_x=-100,start_y=-100,start_z=-100; const double amp=2.0; // amplitude of the harmonic deformation // describe minc volume output_info_grid.push_back( dim_info(3,0,1,dim_info::DIM_VEC,false)); output_info_grid.push_back( dim_info(nx,start_x,step,dim_info::DIM_X,false)); output_info_grid.push_back( dim_info(ny,start_y,step,dim_info::DIM_Y,false)); output_info_grid.push_back( dim_info(nz,start_z,step,dim_info::DIM_Z,false)); //allocate volume minc::minc_grid_volume grid(nx,ny,nz); //assign coordinates grid.start()=IDX(start_x,start_y,start_z); grid.step()=IDX(step,step,step); for(size_t x=0;x pos=grid.voxel_to_world(IDX(x,y,z)); fixed_vec<3,float> def; def[0]=amp*sin(M_PI*pos[0]/100.0); def[1]=amp*sin(2*M_PI*pos[1]/100.0); def[2]=amp*cos(3*M_PI*pos[2]/100.0); grid.set(x,y,z,def); } // minc_1_writer wrt_vec; wrt_vec.open(output.c_str(),output_info_grid,3,NC_FLOAT); save_simple_volume(wrt_vec,grid); } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg() << std::endl; return 1; } return 0; } libminc-libminc-2-3-00/ezminc/examples/trilinear_resample.cpp000066400000000000000000000073431257462267400243400ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : trilinear_resample @DESCRIPTION: an example of using trilinear resampling algorithm @COPYRIGHT : Copyright 2011 Vladimir Fonov, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_1_rw.h" #include #include "minc_1_simple.h" #include "minc_1_simple_rw.h" #include #include #include using namespace minc; static void show_usage (const char * prog) { std::cerr<<"Usage: "< [--step --verbose ]"< input_vol,output_vol; load_simple_volume(rdr,input_vol); new_info=rdr.info(); minc::fixed_vec<3,float> old_step; minc::fixed_vec<3,float> old_start,new_start; minc::fixed_vec<3,int> new_len; for(int i=1;i<4;i++) { old_step[i-1]=new_info[rdr.map_space(i)].step; old_start[i-1]=new_info[rdr.map_space(i)].start; float len=(new_info[rdr.map_space(i)].length)*old_step[i-1]; new_info[rdr.map_space(i)].start-=old_step[i-1]; new_info[rdr.map_space(i)].step=_step; new_info[rdr.map_space(i)].start+=_step/2; new_start[i-1]=new_info[rdr.map_space(i)].start; new_len[i-1]=new_info[rdr.map_space(i)].length=ceil(fabs(len/_step)); } output_vol.resize(new_len); for(int z=0;z cc=IDX(x*_step,y*_step,z*_step); cc+=new_start; cc/=old_step; cc-=old_start; output_vol.set(x,y,z,input_vol.interpolate(cc[0],cc[1],cc[2])); } minc_1_writer wrt; wrt.open(argv[optind+1],new_info,2,NC_FLOAT); save_simple_volume(wrt,output_vol); //wrt.close(); } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< #include #include "minc_1_simple.h" #include "minc_1_simple_rw.h" #include #include using namespace minc; static void show_usage(const char *name) { std::cerr << "Usage: "< .... " << std::endl << "\tn should be more than 1"<< std::endl << "Optional parameters:" << std::endl << "\t--verbose be verbose" << std::endl << "\t--clobber clobber the output files" << std::endl << "\t--sd "<< std::endl; } int main(int argc,char **argv) { int clobber=0; int verbose=0; std::string sd_f; static struct option long_options[] = { {"verbose", no_argument, &verbose, 1}, {"quiet", no_argument, &verbose, 0}, {"clobber", no_argument, &clobber, 1}, {"sd", required_argument, 0, 's'}, {0, 0, 0, 0} }; int c; for (;;) { /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "s", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 0: break; case 's': sd_f=optarg; break; case '?': /* getopt_long already printed an error message. */ default: show_usage(argv[0]); return 1; } } if ((argc - optind) < 3) { show_usage(argv[0]); return 1; } std::string output=argv[argc-1]; //last argument is output file... maybe we should make it a parameter instead? argc-=optind; if(!clobber && !access (output.c_str(), F_OK)) { std::cerr << output.c_str () << " Exists!" << std::endl; return 1; } try { minc_1_reader rdr1; rdr1.open(argv[optind]); minc_float_volume _avg; load_simple_volume(rdr1,_avg); minc_float_volume _sd(_avg); minc_float_volume _tmp(_avg); for(size_t i=0;i<_avg.c_buf_size();i++) { _sd.c_buf()[i]=_avg.c_buf()[i]*_avg.c_buf()[i]; } for(int i=1;i<(argc-1);i++) { minc_1_reader rdr2; rdr2.open(argv[optind+i]); if(!is_same(rdr1,rdr2)) { return 1; } load_simple_volume(rdr2,_tmp); _avg+=_tmp; _tmp*=_tmp; _sd+=_tmp; } _avg/=(float)(argc-1); for(size_t i=0;i<_avg.c_buf_size();i++) { _sd.c_buf()[i]=sqrt(_sd.c_buf()[i]/(argc-1) - _avg.c_buf()[i]*_avg.c_buf()[i] ); } minc_1_writer wrt; wrt.open(output.c_str(),rdr1.info(),2,NC_FLOAT); save_simple_volume(wrt,_avg); if(!sd_f.empty()) { minc_1_writer wrt2; wrt2.open(sd_f.c_str(),rdr1.info(),2,NC_FLOAT); save_simple_volume(wrt2,_sd); } } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< #include "minc_1_simple.h" using namespace minc; int main(int argc,char **argv) { try { if(argc<4) { std::cerr<<"Usage: "< "<0) size*=rdr1.ndim(i); } for(int i=0;i<5;i++) { if(rdr1.nspacing(i)!=rdr2.nspacing(i) || rdr1.nspacing(i)!=rdr_m.nspacing(i)) { std::cerr<<"Different step size! "< buffer1(size),buffer2(size); std::vector mask(size); load_standard_volume(rdr1,&buffer1[0]); load_standard_volume(rdr2,&buffer2[0]); load_standard_volume(rdr_m,&mask[0]); double avg=0; int cnt=0; for(unsigned long i=0;i #include #include #include //minc stuff #include #include #include #include "minc_1_rw.h" namespace minc { dim_info::dim_info(int l, double sta, double spa,dimensions d, bool hd): length(l),step(spa),start(sta),have_dir_cos(hd),dim(d) { switch(dim) { case dim_info::DIM_X:name=MIxspace;break; case dim_info::DIM_Y:name=MIyspace;break; case dim_info::DIM_Z:name=MIzspace;break; case dim_info::DIM_TIME:name=MItime;break; case dim_info::DIM_VEC:name=MIvector_dimension;break; default: REPORT_ERROR("Unknown Dimension!"); } } minc_1_base::minc_1_base(): _slab_len(0), _icvid(MI_ERROR), _cur(MAX_VAR_DIMS,0), _slab(MAX_VAR_DIMS,1), _slice_dimensions(0), _last(false), _positive_directions(true), _datatype(MI_ORIGINAL_TYPE), _io_datatype(MI_ORIGINAL_TYPE), _ndims(0), _is_signed(false), _mincid(MI_ERROR), _imgid(MI_ERROR), _icmax(-1), _icmin(-1), _dims(3,0), _map_to_std(5,-1), _minc2(false) { _icvid=miicv_create(); } //! destructor, closes minc file minc_1_base::~minc_1_base() { close(); } void minc_1_base::close(void) { if(_icvid!=MI_ERROR) { miicv_free(_icvid); _icvid=MI_ERROR; } if(_mincid!=MI_ERROR) miclose(_mincid); _mincid=MI_ERROR; } std::string minc_1_base::history(void) const { nc_type datatype; int att_length; if ((ncattinq(_mincid, NC_GLOBAL, MIhistory, &datatype,&att_length) == MI_ERROR) || (datatype != NC_CHAR)) { return ""; } char* str = new char[att_length+1]; str[0] = '\0'; miattgetstr(_mincid, NC_GLOBAL, MIhistory, att_length+1,str); std::string r(str); delete [] str; return r; } //code from mincinfo int minc_1_base::var_number(void) const { int nvars; if(ncinquire(_mincid, NULL, &nvars, NULL, NULL)!=MI_ERROR) return nvars; return 0; } std::string minc_1_base::var_name(int no) const { char name[MAX_NC_NAME]; if(ncvarinq(_mincid, no, name, NULL, NULL, NULL, NULL)!=MI_ERROR) return name; return ""; } std::vector minc_1_base::var_value_double(int varid) const { nc_type var_type; int vardims; int dims[MAX_VAR_DIMS]; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; if(ncvarinq(_mincid, varid, NULL, &var_type, &vardims, dims, NULL)!=MI_ERROR && var_type==NC_DOUBLE ) { int _var_length=1; for(int i=0;i r(_var_length); if(ncvarget(_mincid, varid, start,count,&r[0])!=MI_ERROR) return r; else return std::vector(0); } else { return std::vector(0); } } std::vector minc_1_base::var_value_double(const char *var_name) const { int varid=var_id(var_name); if(varid!=MI_ERROR) return var_value_double(varid); else return std::vector(0); } int minc_1_base::att_number(const char *var_name) const { int varid; if (*var_name=='\0') { varid = NC_GLOBAL; } else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return 0; } return att_number(varid); } int minc_1_base::var_id(const char *var_name) const { return ncvarid(_mincid, var_name); } long minc_1_base::var_length(const char *var_name) const { int varid=var_id(var_name); if(varid!=MI_ERROR) return var_length(varid); else return 0; } long minc_1_base::var_length(int var_id) const { int vardims; if(ncvarinq(_mincid, var_id, NULL, NULL, &vardims, NULL, NULL)!=MI_ERROR) { if(vardims==0) return 1; int *dims=new int[vardims]; if(ncvarinq(_mincid, var_id, NULL, NULL, NULL, dims, NULL)!=MI_ERROR) { long varlength=1; if(ncdiminq(_mincid,dims[0],NULL,&varlength)!=MI_ERROR) { delete[] dims; return varlength; } delete[] dims; return 1; } else { delete[] dims; return 1; } } return 0; } //! get the number of attributes associated with variable int minc_1_base::att_number(int var_no) const { int natts; if(ncvarinq(_mincid, var_no, NULL, NULL, NULL, NULL, &natts)!=MI_ERROR) return natts; return 0; } std::string minc_1_base::att_name(const char *var_name,int no) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return ""; } return att_name(varid,no); } std::string minc_1_base::att_name(int varid,int no) const { char name[MAX_NC_NAME]; if(ncattname(_mincid, varid, no, name)==MI_ERROR) return ""; return name; } std::string minc_1_base::att_value_string(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return ""; } return att_value_string(varid,att_name); } std::string minc_1_base::att_value_string(int varid,const char *att_name) const { int att_length; nc_type datatype; //TODO: make this handle other (double?) data types correctly if ((ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) || (datatype != NC_CHAR)) { //ncopts=op; return ""; } char* str = new char[att_length+1]; str[0] = '\0'; miattgetstr(_mincid, varid, att_name, att_length+1, str); //ncopts=op; std::string r(str); delete [] str; return r; } std::vector minc_1_base::att_value_double(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return std::vector(0); } return att_value_double(varid,att_name); } std::vector minc_1_base::att_value_short(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return std::vector(0); } return att_value_short(varid,att_name); } std::vector minc_1_base::att_value_byte(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return std::vector(0); } return att_value_byte(varid,att_name); } std::vector minc_1_base::att_value_int(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return std::vector(0); } return att_value_int(varid,att_name); } std::vector minc_1_base::att_value_int(int varid,const char *att_name) const { int att_length; nc_type datatype; if ((ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) || (datatype != NC_INT)) { //ncopts=op; return std::vector(0); } std::vector r(att_length); miattget(_mincid, varid, att_name, NC_INT, att_length,&r[0], NULL) ; //ncopts=op; return r; } std::vector minc_1_base::att_value_double(int varid,const char *att_name) const { int att_length; nc_type datatype; //TODO: make this handle other (double?) data types correctly if ((ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) || (datatype != NC_DOUBLE)) { //ncopts=op; return std::vector(0); } std::vector r(att_length); miattget(_mincid, varid, att_name, NC_DOUBLE, att_length,&r[0], NULL) ; //ncopts=op; return r; } std::vector minc_1_base::att_value_short(int varid,const char *att_name) const { int att_length; nc_type datatype; //TODO: make this handle other (double?) data types correctly if ((ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) || (datatype != NC_SHORT)) { //ncopts=op; return std::vector(0); } std::vector r(att_length); miattget(_mincid, varid, att_name, NC_SHORT, att_length,&r[0], NULL) ; //ncopts=op; return r; } std::vector minc_1_base::att_value_byte(int varid,const char *att_name) const { int att_length; nc_type datatype; //TODO: make this handle other (double?) data types correctly if ((ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) || (datatype != NC_BYTE)) { //ncopts=op; return std::vector(0); } std::vector r(att_length); miattget(_mincid, varid, att_name, NC_BYTE, att_length,&r[0], NULL) ; //ncopts=op; return r; } nc_type minc_1_base::att_type(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return MI_ORIGINAL_TYPE; } return att_type(varid,att_name); } nc_type minc_1_base::att_type(int varid,const char *att_name) const { int att_length; nc_type datatype; if(ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) return MI_ORIGINAL_TYPE; return datatype; } int minc_1_base::att_length(const char *var_name,const char *att_name) const { int varid; if (*var_name=='\0') varid = NC_GLOBAL; else { if((varid = ncvarid(_mincid, var_name))==MI_ERROR) return 0; } return att_length(varid,att_name); } int minc_1_base::att_length(int varid,const char *att_name) const { int att_length; nc_type datatype; if(ncattinq(_mincid, varid, att_name, &datatype,&att_length) == MI_ERROR) return 0; return att_length; } minc_1_reader::minc_1_reader(const minc_1_reader& that): minc_1_base(that), _metadate_only(false), _have_temp_file(false), _read_prepared(false) { } minc_1_reader::minc_1_reader():_metadate_only(false),_have_temp_file(false),_read_prepared(false) { } //based on the code from mincextract void minc_1_reader::open(const char *path,bool positive_directions/*=true*/,bool metadate_only/*=false*/,bool rw/*=false*/) { #ifndef WIN32 ncopts = 0; #endif _metadate_only=metadate_only; _read_prepared=false; _positive_directions=positive_directions; //ncopts = 0; // Open the file // Expand file if(_metadate_only) { int created_tempfile; char * tempfile = miexpand_file(path, NULL, true, &created_tempfile); if (tempfile == NULL) REPORT_ERROR("Error expanding minc file"); _tempfile=tempfile; path=_tempfile.c_str(); _have_temp_file=created_tempfile; if(created_tempfile) free(tempfile); } _mincid = miopen(path, rw?NC_WRITE:NC_NOWRITE); if(_mincid == MI_ERROR) REPORT_ERROR("Can't open minc file for reading!"); #ifdef MINC2 if (MI2_ISH5OBJ(_mincid)) { _minc2 = true; } #endif /* Inquire about the image variable */ _imgid = ncvarid(_mincid, MIimage); if(_imgid == MI_ERROR) REPORT_ERROR("Can't get Image ID"); ncvarinq(_mincid, _imgid, NULL, NULL, &_ndims, mdims, NULL); //get image data type... not used for now miget_datatype(_mincid, _imgid, &_datatype, &_is_signed); //dir_cos.SetIdentity(); miget_image_range(_mincid, _image_range); //go through dimensions , calculating parameters for reshaping into ZYX array if needed _info.resize(_ndims); _world_matrix.resize(_ndims*4,0.0); _voxel_matrix.resize(_ndims*4,0); //_dir_cos.resize(_ndims*_ndims,0.0); for(int i=_ndims-1;i>=0;i--) { //_world_matrix[i*(_ndims+1)]=1.0; //_voxel_matrix[i*(_ndims+1)]=1.0; char dimname[MAX_NC_NAME]; long dimlength; //get dimensions info ncdiminq(_mincid, mdims[i], dimname, &dimlength); _info[i].name=dimname; _info[i].length=dimlength; _info[i].have_dir_cos=false; int axis=-1; if(!strcmp(dimname,MIxspace)) { _dims[0]=dimlength;axis=0; _info[i].dim=dim_info::DIM_X; _map_to_std[1]=i; } else if(!strcmp(dimname,MIyspace)) { _dims[1]=dimlength;axis=1; _info[i].dim=dim_info::DIM_Y; _map_to_std[2]=i; } else if(!strcmp(dimname,MIzspace)) { _dims[2]=dimlength;axis=2; _info[i].dim=dim_info::DIM_Z; _map_to_std[3]=i; } else if(!strcmp(dimname,MIvector_dimension)) { axis=-1; _info[i].dim=dim_info::DIM_VEC; _map_to_std[0]=i; } else if(!strcmp(dimname,MItime)) { axis=3; _info[i].dim=dim_info::DIM_TIME; _map_to_std[4]=i; } else { _info[i].dim=dim_info::DIM_UNKNOWN; REPORT_ERROR ("Unknown dimension"); } if(_info[i].dim!=dim_info::DIM_VEC) { //ncopts = 0; int dimid = ncvarid(_mincid, dimname); //ncopts = NC_VERBOSE | NC_FATAL; if (dimid == MI_ERROR) continue; // Get dimension attributes //ncopts = 0; miattget1(_mincid, dimid, MIstep, NC_DOUBLE, &_info[i].step); if(_info[i].step == 0.0) _info[i].step = 1.0; miattget1(_mincid, dimid, MIstart, NC_DOUBLE, &_info[i].start); if(_positive_directions && _info[i].step<0.0) { _info[i].start+=_info[i].step*(dimlength-1); _info[i].step=-_info[i].step; } if(miattget(_mincid, dimid, MIdirection_cosines, NC_DOUBLE, 3, &_info[i].dir_cos[0], NULL)!= MI_ERROR) { _info[i].have_dir_cos=true; /* Normalize the direction cosine */ double len=sqrt(_info[i].dir_cos[0]*_info[i].dir_cos[0]+ _info[i].dir_cos[1]*_info[i].dir_cos[1]+ _info[i].dir_cos[2]*_info[i].dir_cos[2]); if(len>1e-6 && fabs(len-1.0)>1e-6) //TODO: use some epsiolon here? { for(int a=0;a<3;a++) _info[i].dir_cos[a]/=len; } } // fill voxel matrix _voxel_matrix[i*4+axis]=1; } else { //vectors don't have spatial component! _info[i].start=0; _info[i].step=0.0; _info[i].dir_cos[0]=_info[i].dir_cos[1]=_info[i].dir_cos[2]=0.0; _info[i].have_dir_cos=false; } //fill world matrix for(int a=0;a<3;a++) _world_matrix[i*4+a]=_info[i].dir_cos[a]*_info[i].step; if(axis==3) //time _world_matrix[i*4+3]=_info[i].step; else _world_matrix[i*4+3]=0.0; } //ncopts = NC_VERBOSE | NC_FATAL; // now let's find out the slice dimensions int idmax = ncvarid(_mincid, MIimagemax); _slice_dimensions=0; if(idmax != MI_ERROR) { int nmax_dims; int mmax_dims[MAX_VAR_DIMS]; ncvarinq(_mincid, _imgid, NULL, NULL, &nmax_dims, mmax_dims, NULL); if(nmax_dims>0) _slice_dimensions=_ndims-nmax_dims; } if(_slice_dimensions<=0) { if(_info[_ndims-1].dim==dim_info::DIM_VEC || _info[_ndims-1].dim==dim_info::DIM_TIME) _slice_dimensions=std::min(_ndims,3); else _slice_dimensions=std::min(_ndims,2); } std::fill(_slab.begin(),_slab.end(),1); _slab_len=1; for(size_t i=0;i<_slice_dimensions;i++) { _slab[_ndims-i-1]=_info[_ndims-i-1].length; _slab_len*=_info[_ndims-i-1].length; } } void minc_1_reader::close(void) { _have_temp_file=false; minc_1_base::close(); if(_have_temp_file) { if(remove(_tempfile.c_str())) REPORT_ERROR ("Error removing temporary file"); } } minc_1_reader::~minc_1_reader() { minc_1_reader::close(); } minc_1_writer::minc_1_writer(): _set_image_range(false),_set_slice_range(false), _calc_min_max(true),_write_prepared(false) { } minc_1_writer::minc_1_writer(const minc_1_writer&that): minc_1_base(that), _set_image_range(false), _set_slice_range(false), _calc_min_max(true), _write_prepared(false) { } void minc_1_writer::open(const char *path,const minc_info& inf,int slice_dimensions,nc_type datatype,int _s) { #ifndef WIN32 ncopts = 0; #endif _info=inf; //int mdims[MAX_VAR_DIMS]; double vrange[2]; _write_prepared=false; _mincid = micreate(path, NC_CLOBBER/*|MI2_CREATE_V2*/); //TODO: add environment variable checking #ifdef MINC2 if (MI2_ISH5OBJ(_mincid)) { //micreate might create MINC2 file if environment variable is set _minc2 = true; } #endif _ndims=_info.size(); _datatype=datatype; _slice_dimensions=slice_dimensions; _is_signed=_s; fill(_map_to_std.begin(),_map_to_std.end(),-1); for(int i=_ndims-1;i>=0;i--) { //just a precaution switch(_info[i].dim) { case dim_info::DIM_X:_info[i].name=MIxspace;_map_to_std[1]=i;break; case dim_info::DIM_Y:_info[i].name=MIyspace;_map_to_std[2]=i;break; case dim_info::DIM_Z:_info[i].name=MIzspace;_map_to_std[3]=i;break; case dim_info::DIM_TIME:_info[i].name=MItime;_map_to_std[4]=i;break; default: case dim_info::DIM_VEC:_info[i].name=MIvector_dimension;_map_to_std[0]=i;break; //default: REPORT_ERROR("Unknown Dimension!"); } mdims[i]=ncdimdef(_mincid, _info[i].name.c_str(), _info[i].length); if(_info[i].dim!=dim_info::DIM_VEC) { int dimid=micreate_std_variable(_mincid,_info[i].name.c_str(),NC_INT, 0, NULL); miattputdbl(_mincid, dimid, MIstep,_info[i].step); miattputdbl(_mincid, dimid, MIstart,_info[i].start); if(_info[i].have_dir_cos) ncattput(_mincid, dimid, MIdirection_cosines,NC_DOUBLE, 3, _info[i].dir_cos); } } _slab_len=1; for(size_t i=0;i<_slice_dimensions;i++) { _slab[_ndims-i-1]=_info[_ndims-i-1].length; _slab_len*=_info[_ndims-i-1].length; } _icmax=_icmin=MI_ERROR; //ncopts = NC_OPTS_VAL; _imgid=micreate_std_variable(_mincid, MIimage, _datatype, _ndims, mdims); _image_range[0]=DBL_MAX;_image_range[1]=-DBL_MAX; switch(_datatype) { case NC_DOUBLE: vrange[0]=-DBL_MAX;vrange[1]=DBL_MAX; _is_signed=1; break; case NC_FLOAT: vrange[0]=-FLT_MAX;vrange[1]=FLT_MAX; _is_signed=1; break; case NC_SHORT: if(_is_signed) { vrange[0]=SHRT_MIN; vrange[1]=SHRT_MAX; } else { vrange[0]=0;vrange[1]=USHRT_MAX; } break; case NC_BYTE: if(_is_signed) { vrange[0]=-128;vrange[1]=127; } else { vrange[0]=0;vrange[1]=255; } break; case NC_INT: if(_is_signed) { vrange[0]=INT_MIN;vrange[1]=INT_MAX; }else{ vrange[0]=0;vrange[1]=UINT_MAX; } break; default:break; }; miattputstr(_mincid, _imgid, MIcomplete, MI_FALSE); miattputstr(_mincid, _imgid, MIsigntype, (_is_signed?MI_SIGNED:MI_UNSIGNED)); ncattput(_mincid, _imgid, MIvalid_range, NC_DOUBLE, 2, vrange); miset_valid_range(_mincid, _imgid, vrange); } void minc_1_writer::open(const char *path,const minc_1_base& imitate) { open(path,imitate.info(),imitate.slice_dimensions(), imitate.datatype(),imitate.is_signed()); copy_headers(imitate); } void minc_1_writer::open(const char *path,const char *imitate_file) { minc_1_reader rdr; //open minc file in metadate mode rdr.open(imitate_file,false,true); open(path,rdr); //copy_headers(rdr); } void minc_1_writer::setup_write_float() { _image_range[0]=DBL_MAX;_image_range[1]=-DBL_MAX; switch(_datatype) { case NC_DOUBLE: _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; break; case NC_FLOAT: _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; break; case NC_SHORT: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; case NC_BYTE: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; case NC_INT: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; default: break; }; ncendef(_mincid); if(_datatype==NC_DOUBLE || _datatype==NC_FLOAT) { miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); miicv_setint(_icvid, MI_ICV_TYPE, NC_FLOAT); miicv_setint(_icvid, MI_ICV_DO_NORM, true); miicv_setint(_icvid, MI_ICV_USER_NORM, true); miicv_setdbl(_icvid, MI_ICV_VALID_MIN, -FLT_MAX); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, FLT_MAX); _calc_min_max=true; } else { //do something smart here? miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); miicv_setint(_icvid, MI_ICV_TYPE, NC_FLOAT); miicv_setint(_icvid, MI_ICV_DO_NORM, false); //miicv_setint(_icvid, MI_ICV_USER_NORM, false); miicv_setdbl(_icvid, MI_ICV_VALID_MIN, -FLT_MAX); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, FLT_MAX); _calc_min_max=true; } miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_FLOAT; _write_prepared=true; } void minc_1_writer::setup_write_double() { _image_range[0]=DBL_MAX;_image_range[1]=-DBL_MAX; switch(_datatype) { case NC_DOUBLE: _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; break; case NC_FLOAT: _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; break; case NC_SHORT: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; case NC_BYTE: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; case NC_INT: _set_image_range=false; _set_slice_range=true; _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, _ndims-_slice_dimensions, mdims); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, _ndims-_slice_dimensions, mdims); break; default: break; }; ncendef(_mincid); if(_datatype==NC_DOUBLE) { miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); miicv_setint(_icvid, MI_ICV_TYPE, NC_DOUBLE); miicv_setint(_icvid, MI_ICV_DO_NORM, true); miicv_setint(_icvid, MI_ICV_USER_NORM, true); miicv_setdbl(_icvid, MI_ICV_VALID_MIN, -DBL_MAX); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, DBL_MAX); _calc_min_max=true; } else if(_datatype==NC_FLOAT) { miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); miicv_setint(_icvid, MI_ICV_TYPE, NC_DOUBLE); miicv_setint(_icvid, MI_ICV_DO_NORM, true); miicv_setint(_icvid, MI_ICV_USER_NORM, true); miicv_setdbl(_icvid, MI_ICV_VALID_MIN, -DBL_MAX); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, DBL_MAX); _calc_min_max=true; } else { //do something smart here? miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); miicv_setint(_icvid, MI_ICV_TYPE, NC_DOUBLE); miicv_setint(_icvid, MI_ICV_DO_NORM, false); //miicv_setint(_icvid, MI_ICV_USER_NORM, false); miicv_setdbl(_icvid, MI_ICV_VALID_MIN, -DBL_MAX); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, DBL_MAX); _calc_min_max=true; } miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_DOUBLE; _write_prepared=true; } void minc_1_writer::setup_write_short(bool n) { _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; ncendef(_mincid); miicv_setint(_icvid, MI_ICV_TYPE, NC_SHORT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); //miicv_setstr(_icvid, MI_ICV_SIGN, true); /* Set range of values */ //TODO: set this to something sensible? miicv_setint(_icvid, MI_ICV_VALID_MIN, SHRT_MIN); miicv_setint(_icvid, MI_ICV_VALID_MAX, SHRT_MAX); /* No normalization so that pixels are scaled to the slice */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); miicv_setint(_icvid, MI_ICV_DO_RANGE, false); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_SHORT; _write_prepared=true; } void minc_1_writer::setup_write_ushort(bool n) { _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; ncendef(_mincid); miicv_setint(_icvid, MI_ICV_TYPE, NC_SHORT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ //TODO: set this to something sensible? miicv_setint(_icvid, MI_ICV_VALID_MIN, 0); miicv_setint(_icvid, MI_ICV_VALID_MAX, USHRT_MAX); /* No normalization so that pixels are scaled to the slice */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); miicv_setint(_icvid, MI_ICV_DO_RANGE, false); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_SHORT; _write_prepared=true; } void minc_1_writer::setup_write_byte(bool n) { _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; ncendef(_mincid); miicv_setint(_icvid, MI_ICV_TYPE, NC_BYTE); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ //TODO: set this to something sensible? miicv_setint(_icvid, MI_ICV_VALID_MIN, 0); miicv_setint(_icvid, MI_ICV_VALID_MAX, UCHAR_MAX); /* No normalization so that pixels are scaled to the slice */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); miicv_setint(_icvid, MI_ICV_DO_RANGE, false); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_BYTE; _write_prepared=true; } void minc_1_writer::setup_write_int(bool n) { _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; ncendef(_mincid); miicv_setint(_icvid, MI_ICV_TYPE, NC_INT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); /* Set range of values */ //TODO: set this to something sensible? miicv_setint(_icvid, MI_ICV_VALID_MIN, INT_MIN); miicv_setint(_icvid, MI_ICV_VALID_MAX, INT_MAX); /* No normalization so that pixels are scaled to the slice */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); miicv_setint(_icvid, MI_ICV_DO_RANGE, false); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_INT; _write_prepared=true; } void minc_1_writer::setup_write_uint(bool n) { _icmax=micreate_std_variable(_mincid, MIimagemax, NC_DOUBLE, 0, NULL); _icmin=micreate_std_variable(_mincid, MIimagemin, NC_DOUBLE, 0, NULL); _set_image_range=true; _set_slice_range=false; ncendef(_mincid); miicv_setint(_icvid, MI_ICV_TYPE, NC_INT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ //TODO: set this to something sensible? miicv_setint(_icvid, MI_ICV_VALID_MIN, 0); miicv_setint(_icvid, MI_ICV_VALID_MAX, UINT_MAX); /* No normalization so that pixels are scaled to the slice */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); miicv_setint(_icvid, MI_ICV_DO_RANGE, false); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_INT; _write_prepared=true; } void minc_1_reader::_setup_dimensions(void) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); if(_positive_directions) { /* We want to ensure that images have X, Y and Z dimensions in the positive direction, giving patient left on left and for drawing from bottom up. If we wanted patient right on left and drawing from top down, we would set to MI_ICV_NEGATIVE. */ miicv_setint(_icvid, MI_ICV_DO_DIM_CONV, true); //TODO: make sure to change only x,y,z conversions here //miicv_setint(_icvid, MI_ICV_XDIM_DIR, 3); //we want to convert only X,Y,Z dimensions if they are present int num=(_map_to_std[1]>=0?1:0)+(_map_to_std[2]>=0?1:0)+(_map_to_std[3]>=0?1:0); miicv_setint(_icvid, MI_ICV_NUM_IMGDIMS, num); if(_map_to_std[1]>=0) { miicv_setint(_icvid, MI_ICV_DIM_SIZE+_map_to_std[1],-1); miicv_setint(_icvid, MI_ICV_XDIM_DIR, MI_ICV_POSITIVE); } if(_map_to_std[2]>=0) { miicv_setint(_icvid, MI_ICV_DIM_SIZE+_map_to_std[2],-1); miicv_setint(_icvid, MI_ICV_YDIM_DIR, MI_ICV_POSITIVE); } if(_map_to_std[3]>=0) { miicv_setint(_icvid, MI_ICV_DIM_SIZE+_map_to_std[3],-1); miicv_setint(_icvid, MI_ICV_ZDIM_DIR, MI_ICV_POSITIVE); } } miicv_setint(_icvid, MI_ICV_DO_SCALAR, false); } void minc_1_reader::setup_read_float(void) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_FLOAT); miicv_setint(_icvid, MI_ICV_DO_NORM, true); miicv_setint(_icvid, MI_ICV_USER_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_FLOAT; _read_prepared=true; } void minc_1_reader::setup_read_double(void) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_DOUBLE); miicv_setint(_icvid, MI_ICV_DO_NORM, true); miicv_setint(_icvid, MI_ICV_USER_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_DOUBLE; _read_prepared=true; } void minc_1_reader::setup_read_short(bool n) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_SHORT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); /* Set range of values */ miicv_setdbl(_icvid, MI_ICV_VALID_MIN, _image_range[0]); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, _image_range[1]); /* Do normalization so that all pixels are on same scale */ miicv_setint(_icvid, MI_ICV_DO_NORM, true); //miicv_setint(_icvid, MI_ICV_USER_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_SHORT; _read_prepared=true; } void minc_1_reader::setup_read_ushort(bool n) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_SHORT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ miicv_setdbl(_icvid, MI_ICV_VALID_MIN, _image_range[0]); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, _image_range[1]); /* Do normalization so that all pixels are on same scale */ miicv_setint(_icvid, MI_ICV_DO_NORM, false); //miicv_setint(_icvid, MI_ICV_USER_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_SHORT; _read_prepared=true; } void minc_1_reader::setup_read_byte(bool n) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_BYTE); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ miicv_setdbl(_icvid, MI_ICV_VALID_MIN, _image_range[0]); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, _image_range[1]); /* Do normalization so that all pixels are on same scale */ miicv_setint(_icvid, MI_ICV_DO_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_BYTE; _read_prepared=true; } void minc_1_reader::setup_read_int(bool n) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_INT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_SIGNED); /* Set range of values */ miicv_setdbl(_icvid, MI_ICV_VALID_MIN, _image_range[0]); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, _image_range[1]); /* Do normalization so that all pixels are on same scale */ miicv_setint(_icvid, MI_ICV_DO_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_INT; _read_prepared=true; } void minc_1_reader::setup_read_uint(bool n) { if(_metadate_only) REPORT_ERROR("Minc file in metadate only mode!"); miicv_setint(_icvid, MI_ICV_TYPE, NC_INT); miicv_setstr(_icvid, MI_ICV_SIGN, MI_UNSIGNED); /* Set range of values */ miicv_setdbl(_icvid, MI_ICV_VALID_MIN, _image_range[0]); miicv_setdbl(_icvid, MI_ICV_VALID_MAX, _image_range[1]); /* Do normalization so that all pixels are on same scale */ miicv_setint(_icvid, MI_ICV_DO_NORM, true); /* Make sure that any out of range values are mapped to lowest value of type (for input only) */ miicv_setint(_icvid, MI_ICV_DO_FILLVALUE, true); _setup_dimensions(); miicv_attach(_icvid, _mincid, _imgid); _io_datatype=NC_INT; _read_prepared=true; } void minc_1_reader::read(void* buffer) { if(!_read_prepared) REPORT_ERROR("Not ready to read, use setup_read_XXXX"); miicv_get(_icvid, &_cur[0], &_slab[0], buffer); } void minc_1_writer::write(void* buffer) { if(!_write_prepared) REPORT_ERROR("Not ready to write, use setup_write_XXXX"); double r_min= DBL_MAX; //slab minimal value double r_max=-DBL_MAX; //slab maximal value //int irmin=0,irmax=0; if(_calc_min_max ) { if(_io_datatype==NC_FLOAT) {// float *tmp=(float*)buffer; for(int i=0;i<_slab_len;i++) { if(r_min>tmp[i]) r_min=tmp[i];//irmin=i; if(r_maxtmp[i]) r_min=tmp[i];//irmin=i; if(r_maxtmp[i]) r_min=tmp[i]; if(r_maxtmp[i]) r_min=tmp[i]; if(r_maxtmp[i]) r_min=tmp[i]; if(r_maxtmp[i]) r_min=tmp[i]; if(r_maxtmp[i]) r_min=tmp[i]; if(r_max10000) { std::cerr<r_min) _image_range[0]=r_min; if(_image_range[1] &val) { ncattput(_mincid, create_var_id(varname),attname, NC_DOUBLE, val.size(), &val[0]); } void minc_1_base::insert(const char *varname,const char *attname,const std::vector &val) { ncattput(_mincid, create_var_id(varname),attname, NC_INT, val.size(), &val[0]); } void minc_1_base::insert(const char *varname,const char *attname,const std::vector &val) { ncattput(_mincid, create_var_id(varname),attname, NC_SHORT, val.size(), &val[0]); } void minc_1_base::insert(const char *varname,const char *attname,const std::vector &val) { ncattput(_mincid, create_var_id(varname),attname, NC_BYTE, val.size(), &val[0]); } } libminc-libminc-2-3-00/ezminc/minc_1_rw.h000066400000000000000000000331451257462267400201630ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : @DESCRIPTION: Primitive C++ interface to minc files, uses MINC1 API only @COPYRIGHT : Copyright 2007 Vladimir Fonov, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC_1_RW_H #define MINC_1_RW_H #include #include #include "minc_io_exceptions.h" #ifdef USE_MINC2 #define MINC2 1 #endif extern "C" { #include } #include #include #include namespace minc { //! class for storing dimension information struct dim_info { enum dimensions {DIM_UNKNOWN=0,DIM_X,DIM_Y,DIM_Z,DIM_TIME,DIM_VEC} ; dim_info():length(0),step(0),start(0),have_dir_cos(false),dim(DIM_UNKNOWN) { dir_cos[0]=dir_cos[1]=dir_cos[2]=0.0; } dim_info(int l, double sta,double spa,dimensions d,bool hd=false); size_t length; double step,start; bool have_dir_cos; double dir_cos[3]; std::string name; dimensions dim; }; //! collection of dimensions describing a minc file typedef std::vector minc_info; //! minc file rw base class class minc_1_base { protected: int _slab_len; int _icvid; std::vector _cur,_slab; size_t _slice_dimensions; bool _last; bool _positive_directions; nc_type _datatype; nc_type _io_datatype; char _dimension_names[MAX_VAR_DIMS][MAX_NC_NAME]; std::vector _dir_cos; long vcount[MAX_VAR_DIMS]; std::vector _world_matrix; std::vector _voxel_matrix; int _ndims, mdims[MAX_VAR_DIMS]; int _is_signed; int _mincid, _imgid; int _icmax,_icmin; double _image_range[2]; std::vector _dims; std::vector _map_to_std; minc_info _info; bool _minc2; public: //! get the minc handle int mincid(void) const //this is not really a const ? { return _mincid; } //! get the data type id (NC_BYTE,NC_INT etc) nc_type datatype(void) const { return _datatype; } //! byte size of the volume elements unsigned int element_size(void) const { switch(_io_datatype) { case NC_FLOAT: return sizeof(float); case NC_DOUBLE: return sizeof(double); case NC_SHORT: return sizeof(short); case NC_BYTE: return sizeof(char); default:return 0;//maybe throw exception here? } } //! is data stored in signed format bool is_signed(void) const { return _is_signed; } //! constructor minc_1_base(); //! destructor, closes minc file virtual ~minc_1_base(); //! close the minc file virtual void close(void); //! is last slice was read? bool last(void) const { return _last; } //! go to the beginning of file void begin(void) { fill(_cur.begin(),_cur.end(),0); _last=false; } //! advance to next slice bool next_slice(void) { if(_last) return !_last; for(int i=_ndims-_slice_dimensions-1;i>=0;i--) { _cur[i]++; if(_cur[i](_info[i].length)) break; if(!i) _last=true; else _cur[i]=0; } return !_last; } //! slice length in elements int slice_len(void) const { return _slab_len; } //! number of dimensions int dim_no(void) const { return _ndims; } //! get the dimension information const dim_info& dim(unsigned int n) const { if(n>=static_cast(_ndims)) REPORT_ERROR("Dimension is not defined"); return _info[n]; } //! get the pointer to the dimension description array const minc_info& info(void) const { return _info; } //! get the number of dimensions in one slice int slice_dimensions(void) const { return _slice_dimensions; } //! get the current slice index const std::vector & current_slice(void) const { return _cur; } //! get the normalized dimensions sizes //! ( 0 - vector_dimension, 1 - x, 2- y , 3 -z , 4 - time) int ndim(int i) const { int j=_map_to_std[i]; if(j>=0) return _info[j].length; return 0; } //! get normalized dimension start coordinate (see ndim) double nstart(int i) const { int j=_map_to_std[i]; if(j>=0) return _info[j].start; return 0.0; } //! get normalized dimension spacing (see ndim) double nspacing(int i) const { int j=_map_to_std[i]; if(j>=0) return _info[j].step; return 0.0; } //! get normalized dimension direction cosine component (see ndim) double ndir_cos(int i,int j) const { int k=_map_to_std[i]; if(k>=0) return _info[k].dir_cos[j]; return 0.0; } //! check if a normalized dimension has direction cosine information bool have_dir_cos(int i) const { int k=_map_to_std[i]; if(k>=0) return _info[k].have_dir_cos; return false; } //! map file dimensions into normalized dimensions int map_space(int i) { return _map_to_std[i]; } //metadate info handling function: //! read the minc history (:history attribute) std::string history(void) const; //! retrive var id, if it exists, otherwise return MI_ERROR int var_id(const char *var_name) const; //! get variable length long var_length(const char *var_name) const; //! get variable length long var_length(int var_id) const; //! read the number of variables int var_number(void) const; //! get the variable name number no std::string var_name(int no) const; //! get the variable contents, given it's id std::vector var_value_double(int varid) const; //! get the variable contents, given it's name std::vector var_value_double(const char *var_name) const; //! get the number of attributes associated with variable int att_number(const char *var_name) const; //! get the number of attributes associated with variable int att_number(int var_no) const; //! get the attribute name, given the number std::string att_name(const char *var_name,int no) const; //! get the attribute name, given the number std::string att_name(int varid,int no) const; //! get the string attribute value , given the name std::string att_value_string(const char *var_name,const char *att_name) const; //! get the string attribute value , given variable id std::string att_value_string(int varid,const char *att_name) const; //! get the double attribute value , given the name std::vector att_value_double(const char *var_name,const char *att_name) const; //! get the int attribute value , given the name std::vector att_value_int(const char *var_name,const char *att_name) const; //! get the short attribute value , given the variable id std::vector att_value_short(const char *var_name,const char *att_name) const; //! get the byte attribute value , given the variable id std::vector att_value_byte(const char *var_name,const char *att_name) const; //! get the double attribute value , given the variable id std::vector att_value_double(int varid,const char *att_name) const; //! get the int attribute value , given the variable id std::vector att_value_int(int varid,const char *att_name) const; //! get the short attribute value , given the variable id std::vector att_value_short(int varid,const char *att_name) const; //! get the byte attribute value , given the variable id std::vector att_value_byte(int varid,const char *att_name) const; //! enquire about attribute data type nc_type att_type(const char *var_name,const char *att_name) const; //! enquire about attribute data type nc_type att_type(int varid,const char *att_name) const; //! enquire about attribute length int att_length(const char *var_name,const char *att_name) const; //! enquire about attribute length int att_length(int varid,const char *att_name) const; //! return var_id for the given name (create one, if it doesn't exists) int create_var_id(const char *varname); void insert(const char *varname,const char *attname,double val); void insert(const char *varname,const char *attname,const char* val); void insert(const char *varname,const char *attname,const std::vector &val); void insert(const char *varname,const char *attname,const std::vector &val); void insert(const char *varname,const char *attname,const std::vector &val); void insert(const char *varname,const char *attname,const std::vector &val); //! check if the file in MINC2 format bool is_minc2(void) const { return _minc2; } }; //! minc file reader class minc_1_reader:public minc_1_base { protected: bool _metadate_only; std::string _tempfile; bool _have_temp_file; bool _read_prepared; void _setup_dimensions(void); public: //! copy constructor minc_1_reader(const minc_1_reader&); //! default constructor minc_1_reader(); //! close the minc file virtual void close(void); //! destructor virtual ~minc_1_reader(); //! open a minc file //! \param path - path to existing minc file //! \param positive_directions - make all step sizes positive //! \param metadate_only - file is opened only for the purpose of reading metadata (will save memory) //! \param rw - file headers may be modified void open(const char *path,bool positive_directions=false,bool metadate_only=false,bool rw=false); //! read single slice void read(void* slice); //! setup reading in float format void setup_read_float(void); //! setup reading in double format void setup_read_double(void); //! setup reading in signed short format void setup_read_short(bool normalized=false); //! setup reading in unsigned short format void setup_read_ushort(bool normalized=false); //! setup reading in byte format void setup_read_byte(bool normalized=false); //! setup reading in int format void setup_read_int(bool normalized=false); //! setup reading in unsigned int format void setup_read_uint(bool normalized=false); }; //! minc file writer class minc_1_writer:public minc_1_base { protected: bool _set_image_range; bool _set_slice_range; bool _calc_min_max; bool _write_prepared; public: //! open minc file for writing - will overwrite existing //! \param path - path to minc file //! \param inf - information about dimensions //! \param slice_dimensions - number of dimensions per slice (used for storage) //! \param datatype - storage datatype //! \param is_signed - check if datatype will be signed or not void open(const char *path,const minc_info& inf,int slice_dimensions,nc_type datatype,int is_signed=0); //! open minc file for writing - will overwrite existing //! \param path - path to minc file //! \param imitate - all information is copied from this opened minc file void open(const char *path,const minc_1_base& imitate); //! open minc file for writing - will overwrite existing //! \param path - path to minc file //! \param imitate_file - all information is copied from this existing minc file void open(const char *path,const char *imitate_file); //! prepare for writing float array void setup_write_float(void); //! prepare for writing double array void setup_write_double(void); //! prepare for writing short array void setup_write_short(bool normalize=false); //! prepare for writing unsigned short array void setup_write_ushort(bool normalize=false); //! prepare for writing unsigned char array void setup_write_byte(bool normalize=false); //! prepare for writing int array void setup_write_int(bool normalize=false); //! prepare for writing unsigned int array void setup_write_uint(bool normalize=false); //! copy header from another minc file //! \param src - path to existing minc file void copy_headers(const minc_1_base& src); //! append a line into minc history //! \param append_history - history line to append void append_history(const char *append_history); //! default constructor minc_1_writer(); //! make a copy of another writer minc_1_writer(const minc_1_writer&); //! destructor virtual ~minc_1_writer(); //! close the minc file virtual void close(void); //!write a single slice, size of the buffer should be more or equall to slab_len void write(void* slice); }; } #endif //__PRIMITIVE_MINC_IO__ libminc-libminc-2-3-00/ezminc/minc_1_simple.h000066400000000000000000000170701257462267400210230ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : @DESCRIPTION: Simplified interator-based access to minc files, using minc_1_rw interface @COPYRIGHT : Copyright 2007 Vladimir Fonov, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC_1_SIMPLE_H #define MINC_1_SIMPLE_H #include "minc_1_rw.h" namespace minc { template class minc_input_iterator { protected: mutable minc_1_reader* _rw; std::vector _buf; std::vector _cur; bool _last; size_t _count; public: const std::vector& cur(void) const { return _cur; } minc_input_iterator(const minc_input_iterator& a):_rw(a._rw),_cur(a._cur),_last(a._last),_count(a._count) { } minc_input_iterator(minc_1_reader& rw):_rw(&rw),_last(false),_count(0) { } minc_input_iterator():_rw(NULL),_last(false),_count(0) { } void attach(minc_1_reader& rw) { _rw=&rw; _last=false; _count=0; } bool next(void) { if(_last) return false; _count++; for(size_t i=static_cast(_rw->dim_no()-1); i>static_cast(_rw->dim_no()-_rw->slice_dimensions()-1);i--) { _cur[i]++; if(_cur[i](_rw->dim(i).length)) break; if(i>static_cast(_rw->dim_no()-_rw->slice_dimensions())) _cur[i]=0; else { //move to next slice if(i==0) // the case when slice_dimensions==dim_no { _last=true; _count=0; break; } if(!_rw->next_slice()) { _last=true; break; } _rw->read(&_buf[0]); _cur=_rw->current_slice(); _count=0; break; } } return !_last; } bool last(void) { return _last; } void begin(void) { _cur.resize(MAX_VAR_DIMS,0); _buf.resize(_rw->slice_len()); _count=0; _rw->begin(); _rw->read(&_buf[0]); _cur=_rw->current_slice(); } const T& value(void) const { return _buf[_count]; } }; template class minc_output_iterator { protected: mutable minc_1_writer* _rw; std::vector _buf; std::vector _cur; bool _last; size_t _count; public: const std::vector& cur(void) const { return _cur; } minc_output_iterator(const minc_output_iterator& a):_rw(a._rw),_cur(a._cur),_last(a._last),_count(a._count) { } minc_output_iterator(minc_1_writer& rw):_rw(&rw),_last(false),_count(0) { _buf.resize(rw.slice_len()); } minc_output_iterator():_rw(NULL),_last(false),_count(0) { } void attach(minc_1_writer& rw) { _rw=&rw; _last=false; _count=0; } ~minc_output_iterator() { if(_count && !_last) _rw->write(&_buf[0]); } bool next(void) { if(_last) return false; _count++; for(int i=_rw->dim_no()-1;i>(_rw->dim_no()-_rw->slice_dimensions()-1);i--) { _cur[i]++; if(_cur[i](_rw->dim(i).length)) break; if(i>(_rw->dim_no()-_rw->slice_dimensions())) _cur[i]=0; else { //write slice into minc file _rw->write(&_buf[0]); _count=0; //move to next slice if(i==0) // the case when slice_dimensions==dim_no { _last=true; return false; } if(!_rw->next_slice()) { _last=true; break; } _cur=_rw->current_slice(); break; } } return !_last; } bool last(void) { return _last; } void begin(void) { _buf.resize(_rw->slice_len()); _cur.resize(MAX_VAR_DIMS,0); _count=0; _rw->begin(); _cur=_rw->current_slice(); } void value(const T& v) { _buf[_count]=v; } }; //! will attempt to laod the whole volume in T Z Y X V order into buffer, file should be prepared (setup_read_XXXX) template void load_standard_volume(minc_1_reader& rw, T* volume) { std::vector strides(MAX_VAR_DIMS,0); size_t str=1; for(size_t i=0;i<5;i++) { if(rw.map_space(i)<0) continue; strides[rw.map_space(i)]=str; str*=rw.ndim(i); } minc_input_iterator in(rw); for(in.begin();!in.last();in.next()) { size_t address=0; for(size_t i=0;i(rw.dim_no());i++) address+=in.cur()[i]*strides[i]; volume[address]=in.value(); } } //! will attempt to save the whole volume in T Z Y X V order from buffer, file should be prepared (setup_read_XXXX) template void save_standard_volume(minc_1_writer& rw, const T* volume) { std::vector strides(MAX_VAR_DIMS,0); size_t str=1; for(size_t i=0;i<5;i++) { if(rw.map_space(i)<0) continue; strides[rw.map_space(i)]=str; str*=rw.ndim(i); } minc_output_iterator out(rw); for(out.begin();!out.last();out.next()) { size_t address=0; for(size_t i=0;i(rw.dim_no());i++) address+=out.cur()[i]*strides[i]; out.value(volume[address]); } } //! will attempt to load the whole volume in Z Y X T V order into buffer, file should be prepared (setup_read_XXXX) template void load_non_standard_volume(minc_1_reader& rw, T* volume) { std::vector strides(MAX_VAR_DIMS,0); size_t str=1; const size_t dimorder[]={0,4,1,2,3}; for(size_t i=0;i<5;i++) { if(rw.map_space(dimorder[i])<0|| !rw.ndim(dimorder[i]) ) continue; strides[rw.map_space(dimorder[i])]=str; str*=rw.ndim(dimorder[i]); } minc_input_iterator in(rw); for(in.begin();!in.last();in.next()) { size_t address=0; for(int i=0;i void save_non_standard_volume(minc_1_writer& rw, const T* volume) { std::vector strides(MAX_VAR_DIMS,0); size_t str=1; const size_t dimorder[]={0,4,1,2,3}; for(size_t i=0;i<5;i++) { if(rw.map_space(dimorder[i])<0 || !rw.ndim(dimorder[i]) ) continue; strides[rw.map_space(dimorder[i])]=str; str*=rw.ndim(dimorder[i]); } minc_output_iterator out(rw); for(out.begin();!out.last();out.next()) { size_t address=0; for(int i=0;i #include #include "minc_1_simple_rw.h" namespace minc { const double minc_eps=1e-5; bool is_same(minc_1_reader& one,minc_1_reader& two,bool verbose) { if(one.dim_no()!=two.dim_no()) { if(verbose) std::cerr<<"Unequal number of dimensions !"<minc_eps) { if(verbose) std::cerr<<"Unequal dimension sarts"<minc_eps) { if(verbose) std::cerr<<"Unequal dimension steps"<minc_eps) { if(verbose) std::cerr<<"Unequal direction cosines"< void load_simple_volume(minc_1_reader& rw,simple_volume& vol) { if(rw.ndim(1)<=0||rw.ndim(2)<=0||rw.ndim(3)<=0||rw.ndim(4)>0) REPORT_ERROR("Need 3D minc file"); vol.resize(rw.ndim(1),rw.ndim(2),rw.ndim(3)); if(typeid(T)==typeid(unsigned char)) { rw.setup_read_byte(); load_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(int)) { rw.setup_read_int(); load_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(fixed_vec<3,float>)) { rw.setup_read_float(); load_standard_volume(rw,(float*)vol.c_buf()); } else if(typeid(T)==typeid(float)) { rw.setup_read_float(); load_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(fixed_vec<3,double>)) { rw.setup_read_double(); load_standard_volume(rw,(double*)vol.c_buf()); } else if(typeid(T)==typeid(double)) { rw.setup_read_double(); load_standard_volume(rw,vol.c_buf()); } else REPORT_ERROR("Data type not supported for minc io"); //set coordinate transfer parameters for(int i=0;i<3;i++) { vol.step()[i]=rw.nspacing(i+1); vol.start()[i]=rw.nstart(i+1); if(rw.have_dir_cos(i+1)) { for(int j=0;j<3;j++) vol.direction_cosines(i)[j]=rw.ndir_cos(i+1,j); } else { for(int j=0;j<3;j++) vol.direction_cosines(i)[j]=(i==j?1.0:0.0); //identity } } } template void save_simple_volume(minc_1_writer& rw,const simple_volume& vol) { if(typeid(T)==typeid(unsigned char)) { rw.setup_write_byte(); save_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(int)) { rw.setup_write_int(); save_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(fixed_vec<3,float>)) { rw.setup_write_float(); save_standard_volume(rw,(float*)vol.c_buf()); } else if(typeid(T)==typeid(float)) { rw.setup_write_float(); save_standard_volume(rw,vol.c_buf()); } else if(typeid(T)==typeid(fixed_vec<3,double>)) { rw.setup_write_double(); save_standard_volume(rw,(double*)vol.c_buf()); } else if(typeid(T)==typeid(double)) { rw.setup_write_double(); save_standard_volume(rw,vol.c_buf()); } else REPORT_ERROR("Data type not supported for minc io"); } template void load_4d_volume(minc_1_reader& rw,simple_4d_volume& vol) { //if(rw.ndim(1)<=0||rw.ndim(2)<=0||rw.ndim(3)<=0||rw.ndim(4)<=0) // REPORT_ERROR("Need 4D minc file"); vol.resize(rw.ndim(1),rw.ndim(2),rw.ndim(3),rw.ndim(4)>0?rw.ndim(4):1); //always assume 4 dimensions if(typeid(T)==typeid(unsigned char)) rw.setup_read_byte(); else if(typeid(T)==typeid(int)) rw.setup_read_int(); else if(typeid(T)==typeid(fixed_vec<3,float>)) rw.setup_read_float(); else if(typeid(T)==typeid(float)) rw.setup_read_float(); else if(typeid(T)==typeid(fixed_vec<3,double>)) rw.setup_read_double(); else if(typeid(T)==typeid(double)) rw.setup_read_double(); else REPORT_ERROR("Data type not supported for minc io"); std::vector strides(MAX_VAR_DIMS,0); size_t str=1; for(size_t i=0;i<5;i++) //T is a special case { if(rw.map_space(i)<0) continue; strides[rw.map_space(i)]=str; str*=rw.ndim(i); } if(rw.map_space(4)>=0) strides[rw.map_space(4)]=0; //t dimension minc_input_iterator in(rw); for(in.begin();!in.last();in.next()) { size_t address=0; size_t slice=0; for(int i=0;i0) address+=in.cur()[i]*strides[i]; else // slice=in.cur()[i]; } vol.frame(slice).c_buf()[address]=in.value(); } //set coordinate transfer parameters for(int i=0;i<3;i++) { vol.step()[i]=rw.nspacing(i+1); vol.start()[i]=rw.nstart(i+1); if(rw.have_dir_cos(i+1)) { for(int j=0;j<3;j++) vol.direction_cosines(i)[j]=rw.ndir_cos(i+1,j); } else { for(int j=0;j<3;j++) vol.direction_cosines(i)[j]=(i==j?1.0:0.0); //identity } } if(rw.ndim(4)>0) { vol.t_start()=rw.nstart(4);//T vol.t_step()=rw.nspacing(4);//T } else { vol.t_start()=0;//T vol.t_step()=0;//T } } template void save_4d_volume(minc_1_writer& rw,const simple_4d_volume& vol) { if(typeid(T)==typeid(unsigned char)) rw.setup_write_byte(); else if(typeid(T)==typeid(int)) rw.setup_write_int(); else if(typeid(T)==typeid(fixed_vec<3,float>)) rw.setup_write_float(); else if(typeid(T)==typeid(float)) rw.setup_write_float(); else if(typeid(T)==typeid(fixed_vec<3,double>)) rw.setup_write_double(); else if(typeid(T)==typeid(double)) rw.setup_write_double(); else REPORT_ERROR("Data type not supported for minc io"); std::vector strides(MAX_VAR_DIMS,0); size_t str=1; for(size_t i=0;i<4;i++)//T is a special { if(rw.map_space(i)<0) continue; strides[rw.map_space(i)]=str; str*=rw.ndim(i); } if(rw.map_space(4)>=0) strides[rw.map_space(4)]=0; //t dimension minc_output_iterator out(rw); for(out.begin();!out.last();out.next()) { size_t address=0; size_t slice=0; for(int i=0;i0) address+=out.cur()[i]*strides[i]; else // slice=out.cur()[i]; } out.value(vol.frame(slice).c_buf()[address]); } } bool is_same(minc_1_reader& one,minc_1_reader& two,bool verbose=true); template void load_minc_file(const char *file,simple_4d_volume& vol) { minc_1_reader rdr; rdr.open(file); load_4d_volume(rdr,vol); } template void generate_info(const simple_4d_volume& vol,minc_info& info) { bool have_time=vol.frames()>1||vol.t_step()!=0.0; //assume that it is 3D file otherwise bool is_vector=false; if(typeid(T)==typeid(fixed_vec<3,float>)) { is_vector=true; } info.resize(3+(is_vector?1:0)+(have_time?1:0)); if(is_vector) { info[0].dim=dim_info::DIM_VEC; info[0].length=3; info[0].step=1; } for(int i=0;i<3;i++) { int ii=i+(is_vector?1:0); info[ii].dim=dim_info::dimensions( dim_info::DIM_X+i); info[ii].length=vol.dim(i); info[ii].step =vol.step()[i]; info[ii].start =vol.start()[i]; info[ii].have_dir_cos=true; for(int j=0;j<3;j++) info[ii].dir_cos[j]=vol.direction_cosines(i)[j]; } if(have_time) { info[3+(is_vector?1:0)].dim=dim_info::DIM_TIME; info[3+(is_vector?1:0)].step=vol.t_step(); info[3+(is_vector?1:0)].start=vol.t_start(); info[3+(is_vector?1:0)].length=vol.frames(); } } template void save_minc_file(const char *file,const simple_4d_volume& vol, const char* history=NULL,const minc_1_reader* original=NULL, nc_type datatype=NC_NAT,bool is_signed=false) { minc_1_writer wrt; //convert parameters to info if(typeid(T)==typeid(unsigned char)) { if(datatype==NC_NAT) datatype=NC_BYTE; } else if(typeid(T)==typeid(int)) { if(datatype==NC_NAT) datatype=NC_INT; is_signed=true; } else if(typeid(T)==typeid(unsigned int)) { if(datatype==NC_NAT) datatype=NC_INT; is_signed=false; } else if(typeid(T)==typeid(float)) { if(datatype==NC_NAT) datatype=NC_FLOAT; is_signed=true; } else if(typeid(T)==typeid(fixed_vec<3,float>)) { if(datatype==NC_NAT) datatype=NC_FLOAT; is_signed=true; } else if(typeid(T)==typeid(double)) { if(datatype==NC_NAT) datatype=NC_DOUBLE; is_signed=true; } else if(typeid(T)==typeid(fixed_vec<3,double>)) { if(datatype==NC_NAT) datatype=NC_DOUBLE; is_signed=true; } else REPORT_ERROR("Unsupported data type!"); minc_info info; generate_info(vol,info); wrt.open(file,info,2,datatype,is_signed); if(original) { wrt.copy_headers(*original); } if(history) wrt.append_history(history); save_4d_volume(wrt,vol); } } #endif //MINC_1_SIMPLE_RW_H libminc-libminc-2-3-00/ezminc/minc_io_4d_volume.h000066400000000000000000000073461257462267400217040ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : @DESCRIPTION: Simplified 4D volume @COPYRIGHT : Copyright 2007 Vladimir Fonov, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC_IO_4D_VOLUME_H #include "minc_io_simple_volume.h" #include #include namespace minc { //! simple 4D volume - collection of 3D volumes template class simple_4d_volume { protected: enum {ndims=3}; typedef fixed_vec idx; typedef fixed_vec vect; double _start_t; double _step_t; void allocate(int n,const idx& sz) { _volumes.resize(n); for(int i=0;i volume; typedef std::vector volume_list; int dim(int i) const { return _volumes[0].dim(i); } vect voxel_to_world(const idx& iii) const { return _volumes[0].voxel_to_world(iii); } idx world_to_voxel(const vect& iii) const { return _volumes[0].world_to_voxel(iii); } void resize(int x,int y,int z,int t) { allocate(t,IDX(x,y,z)); } //! number of temporal frames size_t frames(void) const { return _volumes.size(); } T& at(int x,int y,int z,int t) { return _volumes[t].at(x,y,z); } const T& get(int x,int y,int z,int t) const { return _volumes[t].get(x,y,z); } void set(int x,int y,int z,int t,const T& v) { _volumes[t].set(x,y,z,v); } T& at(const idx& i,int t) { return _volumes[t].at(i); } const T& get(const idx& i,int t) const { return _volumes[t].get(i); } void set(const idx& i,int t,const T& v) { _volumes[t].set(i,v); } volume& frame(int t) { return _volumes[t]; } const volume& frame(int t) const { return _volumes[t]; } vect& start(void) { return _volumes[0].start(); } const vect& start(void) const { return _volumes[0].start(); } vect& step(void) { return _volumes[0].step(); } const vect& step(void) const { return _volumes[0].step(); } vect& direction_cosines(int i) { return _volumes[0].direction_cosines(i); } const vect& direction_cosines(int i) const { return _volumes[0].direction_cosines(i); } double & t_step(void) { return _step_t; } double t_step(void) const { return _step_t; } double & t_start(void) { return _start_t; } double t_start(void) const { return _start_t; } protected: volume_list _volumes; }; } #endif //MINC_IO_4D_VOLUME_H libminc-libminc-2-3-00/ezminc/minc_io_exceptions.h000066400000000000000000000032171257462267400221600ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : @DESCRIPTION: minc exceptions @COPYRIGHT : Copyright 2006 Vladimir Fonov, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC_IO_EXCEPTIONS_H #define MINC_IO_EXCEPTIONS_H #define REPORT_ERROR(MSG) throw minc::generic_error(__FILE__,__LINE__,MSG) namespace minc { class generic_error { public: const char *_file; int _line; const char *_msg; int _code; public: generic_error (const char *file__, int line__, const char *msg__ = "Error", int code__ = 0): _file (file__), _line (line__), _msg (msg__), _code (code__) { // std::cerr<<"Exception created: "<<_file<<":"<<_line<<" "<<_msg< namespace minc { //! fixed size array, which support arithmetic operations template class fixed_vec { protected: I c[dim]; public: //! default constructor, does nothing (i.e data is uninitilized) fixed_vec() {} //! constructor which sets all the elements to the same value explicit fixed_vec(I v) { for(unsigned int i=0;i=dim) REPORT_ERROR("Index out of bounds"); #endif //_INDEX_CHECK return c[i]; } //! const element access operator I operator[](int i) const { #ifdef _INDEX_CHECK if(i<0 || i>=dim) REPORT_ERROR("Index out of bounds"); #endif //_INDEX_CHECK return c[i]; } //! const element access operator I get(int i) { return (*this)[i]; } //! element writing operator void set(int i,I v) { (*this)[i]=v; } //! find a maximum of elements I max(void) const { I s=std::numeric_limits < I >::min ();; for(unsigned int i=0;is) s=c[i]; return s; } //! find a minimum of elements I min(void) const { I s=std::numeric_limits < I >::max ();; for(unsigned int i=0;i& operator *=(const fixed_vec& b) { for(unsigned int i=0;i& operator *=(const I b) { for(unsigned int i=0;i& operator +=(const fixed_vec& b) { for(unsigned int i=0;i& operator -=(const fixed_vec& b) { for(unsigned int i=0;i& operator /=(const fixed_vec& b) { for(unsigned int i=0;i& operator /=(const I b) { for(unsigned int i=0;i operator /(const I b) { fixed_vec tmp; for(unsigned int i=0;i operator *(const I b) { fixed_vec tmp; for(unsigned int i=0;i operator -(const fixed_vec& b) { fixed_vec tmp; for(unsigned int i=0;i operator +(const fixed_vec& b) { fixed_vec tmp; for(unsigned int i=0;i& operator=(const fixed_vec& b) { for(unsigned int i=0;i& operator=(const I* b) { for(unsigned int i=0;i& operator=(const I b) { for(unsigned int i=0;i& b) const { for(int i=0;i& b) const { for(int i=0;i fixed_vec operator/(const fixed_vec &l,const fixed_vec &r) { fixed_vec out=l; out/=r; return out; //this is not effecient - no return value optimisation } //! element wise multiplication template fixed_vec operator*(const fixed_vec &l,const fixed_vec &r) { fixed_vec out=l; out*=r; return out; //this is not effecient - no return value optimisation } //! element wise addition template fixed_vec operator+(const fixed_vec &l,const fixed_vec &r) { fixed_vec out=l; out+=r; return out; //this is not effecient - no return value optimisation } //! element wise subtraction template fixed_vec operator-(const fixed_vec &l,const fixed_vec &r) { fixed_vec out=l; out-=r; return out; //this is not effecient - no return value optimisation } //! devide all elements by a value template fixed_vec operator/(const fixed_vec &l,I r) { fixed_vec out=l; out/=r; return out; //this is not effecient - no return value optimisation } //! multiply all elements by a value template fixed_vec operator*(const fixed_vec &l,I r) { fixed_vec out=l; out*=r; return out; //this is not effecient - no return value optimisation } //! add a value to all elements template fixed_vec operator+(const fixed_vec &l,I r) { fixed_vec out=l; out+=r; return out; //this is not effecient - no return value optimisation } //! subtract a value from all elements template fixed_vec operator-(const fixed_vec &l,I r) { fixed_vec out=l; out-=r; return out; //this is not effecient - no return value optimisation } //! create 1d fixed_vec template fixed_vec<1,I> IDX(I i) { fixed_vec<1,I> d; d[0]=i; return d; } //! create 2d fixed_vec template fixed_vec<2,I> IDX(I i,I j) { fixed_vec<2,I> d; d[0]=i; d[1]=j; return d; } //! create 3d fixed_vec template fixed_vec<3,I> IDX(I i,I j,I k) { fixed_vec<3,I> d; d[0]=i; d[1]=j; d[2]=k; return d; } //! create 4d fixed_vec template fixed_vec<4,I> IDX(I i,I j,I k,I l) { fixed_vec<3,I> d; d[0]=i; d[1]=j; d[2]=k; d[3]=l; return d; } //!average value of a vector templateT AVG(const fixed_vec &v) { return v.sum()/d; } //!dot product of two vectors templateT dot(const fixed_vec &v1,const fixed_vec &v2) { T val=0; for(int i=0;i #include namespace minc { //! very simple 3D volume, initially created as an example but became usable template class simple_volume { public: enum {ndims=3}; typedef fixed_vec idx; typedef fixed_vec idx_i; typedef fixed_vec vect; protected: T * _vol; //! the volume itself idx _size; //! dimension sizes idx _stride; //! used internally size_t _count; //! total number of voxels bool _free_memory; //! should the array be freed vect _step,_start; //! conversion to wold coordinates vect _direction_cosines[3]; void _allocate(T* data=NULL) { _stride[0]=1; size_t total=_size[0]; for(size_t i=1;i(1.0,1.0,1.0); _start=IDX(0.0,0.0,0.0); _direction_cosines[0]=IDX(1.0,0.0,0.0); _direction_cosines[1]=IDX(0.0,1.0,0.0); _direction_cosines[2]=IDX(0.0,0.0,1.0); } public: vect& start(void) { return _start; } const vect& start(void) const { return _start; } vect& step(void) { return _step; } const vect& step(void) const { return _step; } vect& direction_cosines(int i) { return _direction_cosines[i]; } const vect& direction_cosines(int i) const { return _direction_cosines[i]; } operator T*() { return _vol; } T* c_buf() { return _vol; } const T* c_buf() const { return _vol; } size_t c_buf_size() const { return _count; } explicit simple_volume(const size_t* dims):_vol(0),_size(dims) { _allocate(); } explicit simple_volume(const int* dims):_vol(0) { _size[0]=static_cast(dims[0]); _size[1]=static_cast(dims[1]); _size[2]=static_cast(dims[2]); _allocate(); } simple_volume(const simple_volume& a,bool copy_data=true):_vol(0) { for(size_t i=0;i(sx,sy,sz); _allocate(); } explicit simple_volume(const idx& s):_vol(0) { _size=s; _allocate(); } explicit simple_volume(const idx_i& s):_vol(0) { _size=IDX(s[0],s[1],s[2]); _allocate(); } simple_volume():_vol(0),_count(0),_free_memory(false) { for(size_t i=0;i(0.0,0.0,0.0); _direction_cosines[i][i]=1.0; } } bool empty(void) const { return !_size[0]||!_vol; } void resize(size_t sx,size_t sy,size_t sz) { if( _size[0]==sx && _size[1]==sy && _size[2]==sz ) return; if(_vol && _free_memory) delete [] _vol; _size=IDX(sx,sy,sz); _allocate(); } void resize(const idx& s) { if(_size==s) return; if(_vol&&_free_memory) delete [] _vol; _size=s; _allocate(); } void resize(const idx_i& s) { resize(IDX(s[0],s[1],s[2])); } virtual ~simple_volume() { if(_vol && _free_memory) delete [] _vol; } T& operator()(size_t x,size_t y,size_t z) { return _vol[x+y*_stride[1]+z*_stride[2]]; } T& operator()(int x,int y,int z) { return _vol[(size_t)x+(size_t)y*_stride[1]+(size_t)z*_stride[2]];; } T& operator()(const idx& i) { return _vol[dot(i,_stride)]; } T& operator()(const idx_i& i) { return _vol[dot(IDX(i[0],i[1],i[2]),_stride)]; } const T& operator()(size_t x,size_t y,size_t z) const { return get(x,y,z); } const T& operator()(int x,int y,int z) const { return get(x,y,z); } const T& operator()(const idx& i) const { return get(i); } const T& operator()(const idx_i& i) const { return get(i); } const T& get(size_t x,size_t y,size_t z) const { return _vol[x+y*_stride[1]+z*_stride[2]]; } const T& get(int x,int y,int z) const { return get(static_cast(x),static_cast(y),static_cast(z)); } const T& get(const idx& i) const { return _vol[dot(i,_stride)]; } const T& get(const idx_i& i) const { return _vol[dot(IDX(i[0],i[1],i[2]),_stride)]; } const T& safe_get(size_t x,size_t y,size_t z) const { check_index(x,y,z); return _vol[x+y*_stride[1]+z*_stride[2]]; } const T& safe_get(int x,int y,int z) const { size_t xx=x<0?-x:x; size_t yy=y<0?-y:y; size_t zz=z<0?-z:z; check_index(xx,yy,zz); return _vol[xx+yy*_stride[1]+zz*_stride[2]]; } const T& safe_get(idx i) const { check_index(i); return get(i); } const T& safe_get(idx_i i) const { idx ii=IDX(i[0]<0?-i[0]:i[1],i[1]<0?-i[1]:i[1],i[2]<0?-i[2]:i[2]); check_index(ii); return get(ii); } //trilinear intrpolation double interpolate(float _x,float _y,float _z) const { if(_x<0) _x=-_x; if(_y<0) _y=-_y; if(_z<0) _z=-_z; size_t x=floor(_x); size_t y=floor(_y); size_t z=floor(_z); float dx=_x-x; float dy=_y-y; float dz=_z-z; if(x>=(_size[0]-1)) x=_size[0]*2-3-x; if(y>=(_size[1]-1)) y=_size[1]*2-3-y; if(z>=(_size[2]-1)) z=_size[2]*2-3-z; //trilinear intrpolation return (1.0-dx)*(1.0-dy)*(1.0-dz)*get(x,y,z)+ dx*(1.0-dy)*(1.0-dz)*get(x+1,y,z)+ (1.0-dx)*dy*(1.0-dz)*get(x,y+1,z)+ (1.0-dx)*(1.0-dy)*dz*get(x,y,z+1)+ dx*(1.0-dy)*dz*get(x+1,y,z+1)+ (1.0-dx)*dy*dz*get(x,y+1,z+1)+ dx*dy*(1.0-dz)*get(x+1,y+1,z)+ dx*dy*dz*get(x+1,y+1,z+1); } T set(size_t x,size_t y,size_t z, const T&v) { return _vol[x+y*_stride[1]+z*_stride[2]]=v; } T set(const idx& i, const T&v) { return _vol[dot(i,_stride)]=v; } size_t dim(size_t i) const { return _size[i]; } const size_t* dims() const { return _size.c_buf(); } const idx& size() const { return _size; } void extract_subvolume(simple_volume& dst,const idx& s, const idx& f) const { for(size_t k=s[2];k=dim(0)) ii=2*dim(0)-ii-1; if(jj>=dim(1)) jj=2*dim(1)-jj-1; if(kk>=dim(2)) kk=2*dim(2)-kk-1; } void check_index(idx& iii) { for(size_t i=0;i<3;i++) { if(iii[i]<0) iii[i]=-iii[i]; if(iii[i]>=dim(i)) iii[i]=2*dim(i)-iii[i]-1; } } bool hit(size_t ii,size_t jj,size_t kk) const { if(ii>=dim(0)) return false; if(jj>=dim(1)) return false; if(kk>=dim(2)) return false; return true; } bool hit(int ii,int jj,int kk) const { if(ii<0) return false; if(jj<0) return false; if(kk<0) return false; if(ii>=dim(0)) return false; if(jj>=dim(1)) return false; if(kk>=dim(2)) return false; return true; } bool hit(const idx iii) const { for(size_t i=0;i<3;i++) { if(iii[i]<0) return false; if(iii[i]>=dim(i)) return false; } return true; } simple_volume& operator+=(const simple_volume& a) { for(size_t i=0;i& operator+=(const T& a) { for(size_t i=0;i<_count;i++) _vol[i]+=a; return *this; } simple_volume& operator-=(const simple_volume& a) { if(_size!=a._size) REPORT_ERROR("Dimensions are different"); for(size_t i=0;i<_count;i++) _vol[i]-=a._vol[i]; return *this; } simple_volume& operator-=(const T& a) { for(size_t i=0;i<_count;i++) _vol[i]-=a; return *this; } simple_volume& operator*=(const simple_volume& a) { for(size_t i=0;i& operator*=(const T& a) { for(size_t i=0;i<_count;i++) _vol[i]*=a; return *this; } simple_volume& operator/=(const simple_volume& a) { if(_size!=a._size()) REPORT_ERROR("Dimensions are different"); for(size_t i=0;i<_count;i++) _vol[i]/=a._vol[i]; return *this; } simple_volume& operator/=(const T& a) { for(size_t i=0;i<_count;i++) _vol[i]/=a; return *this; } simple_volume& operator=(const simple_volume&a) { resize(a.dim(0),a.dim(1),a.dim(2)); memmove(_vol, a._vol, _count*sizeof(T)); _step=a._step; _start=a._start; for(size_t i=0;i&a, double w) { if(_size!=a._size) REPORT_ERROR("Dimensions are different"); for(size_t i=0;i<_count;i++) _vol[i]+=a._vol[i]*w; } vect voxel_to_world(const idx& iii) const { vect ret=IDX(0.0,0.0,0.0); for(size_t i=0;i(0.0,0.0,0.0); for(size_t i=0;i(s[0],s[1],s[2]); allocate(array); } }; //! remove (unpad) or add padding as needed, volume will be centered templatevoid pad_volume(const simple_volume &src,simple_volume &dst, const T& fill) { fixed_vec<3,size_t> sz1=src.size(); fixed_vec<3,size_t> sz2=dst.size(); fixed_vec<3,size_t> d=sz2-sz1; fixed_vec<3,size_t> i; d/=2;//offset for( i[2]=0;i[2] j= i-d; if( src.hit(j)) dst.set(i,src.get(j)); else dst.set(i,fill); } } templatevoid volume_min_max(const simple_volume& v,T &_min,T &_max) { _min=_max=v.c_buf()[0]; for(size_t i=0;i_max) _max=v.c_buf()[i]; else if(v.c_buf()[i]<_min) _min=v.c_buf()[i]; } } template void volume_min_max(const simple_volume& v,const simple_volume& mask,T &_min,T &_max) { if(v.size()!=mask.size()) REPORT_ERROR("Volume size mismatch"); _min=1e10; _max=-1e10; for(size_t i=0;i_max) _max=v.c_buf()[i]; else if(v.c_buf()[i]<_min) _min=v.c_buf()[i]; } } } typedef simple_volume minc_float_volume; typedef simple_volume > minc_grid_volume; typedef simple_volume minc_byte_volume; } //minc #endif // MINC_IO_SIMPLE_VOLUME_H libminc-libminc-2-3-00/ezminc/tests/000077500000000000000000000000001257462267400172705ustar00rootroot00000000000000libminc-libminc-2-3-00/ezminc/tests/CMakeLists.txt000066400000000000000000000006331257462267400220320ustar00rootroot00000000000000LINK_LIBRARIES(minc_io ${LIBMINC_LIBRARIES}) ADD_EXECUTABLE(ezminc_rw_test ezminc_rw_test.cpp) #ADD_EXECUTABLE(ezminc_stats ezminc_stats.cpp) ADD_TEST(ezminc_rw_test ezminc_rw_test ${CMAKE_CURRENT_BINARY_DIR}) ADD_EXECUTABLE(ezminc_rw_test2 minc_rw_test2.cpp) IF(MINC_TEST_ENVIRONMENT) set_tests_properties( ezminc_rw_test PROPERTIES ENVIRONMENT "${MINC_TEST_ENVIRONMENT}") ENDIF(MINC_TEST_ENVIRONMENT) libminc-libminc-2-3-00/ezminc/tests/ezminc_rw_test.cpp000066400000000000000000000135501257462267400230340ustar00rootroot00000000000000#include #include #include #include #include #include "minc_1_rw.h" #include "minc_1_simple.h" using namespace minc; template void make_rw_test(const char * filename,int slice_dim,nc_type datatype,bool is_signed,double max_diff=0.0) { std::string history="History "; // generate info minc_info info(3); int volume=1.0; for(int i=0;i<3;i++) { info[i].dim=dim_info::dimensions( dim_info::DIM_X+i); info[i].length=10+i; info[i].step =i+0.1; info[i].start =i-5.0; info[i].have_dir_cos=true; for(int j=0;j<3;j++) info[i].dir_cos[j]=(i==j?1.0:0.0); volume*=info[i].length; } //fill the buffer std::vector buffer(volume); if(typeid(TPixel)==typeid(float) || typeid(TPixel)==typeid(double)) for(int i=0;i(random())*100.0/RAND_MAX; else for(int i=0;i(random()); std::vector double_attr(10); std::vector int_attr(10); std::vector short_attr(10); std::string string_attr="Test string attribuite"; for(int i=0;i<10;i++) { double_attr[i]=i+0.1; int_attr[i]=i*100; short_attr[i]=i; } //now let's write volume minc_1_writer wrt; wrt.open(filename,info,slice_dim,datatype,is_signed); //atributes wrt.append_history(history.c_str()); wrt.insert("patient","double_attr",double_attr); wrt.insert("patient","int_attr",int_attr); wrt.insert("patient","short_attr",short_attr); wrt.insert("patient","string_attr",string_attr.c_str()); if(typeid(TPixel)==typeid(unsigned char)) wrt.setup_write_byte(); else if(typeid(TPixel)==typeid(int)) wrt.setup_write_int(); else if(typeid(TPixel)==typeid(float)) wrt.setup_write_float(); else if(typeid(TPixel)==typeid(double)) wrt.setup_write_double(); else REPORT_ERROR("Data type not supported for minc io"); //write volume save_standard_volume(wrt,&buffer[0]); wrt.close(); //reading volume minc_1_reader rdr; rdr.open(filename); if(history!=rdr.history()) REPORT_ERROR("Mismatched history"); std::vector double_attr_rd=rdr.att_value_double("patient","double_attr"); std::vector int_attr_rd=rdr.att_value_int("patient","int_attr"); std::vector short_attr_rd=rdr.att_value_short("patient","short_attr"); std::string string_attr_rd=rdr.att_value_string("patient","string_attr"); if(string_attr_rd!=string_attr) REPORT_ERROR("Mismatched string attribute"); for(int i=0;i<10;i++) { if(double_attr_rd[i]!=double_attr[i]) REPORT_ERROR("Mismatched double attribute"); if(int_attr_rd[i]!=int_attr[i]) REPORT_ERROR("Mismatched int attribute"); if(short_attr_rd[i]!=short_attr[i]) REPORT_ERROR("Mismatched short attribute"); } // let's compare info for(int i=0;i<3;i++) { if(rdr.info()[i].dim!=info[i].dim) REPORT_ERROR("Mismatched dimension"); if(rdr.info()[i].length!=info[i].length) REPORT_ERROR("Mismatched dimension length"); if(rdr.info()[i].step!=info[i].step) REPORT_ERROR("Mismatched step"); if(rdr.info()[i].start!=info[i].start) REPORT_ERROR("Mismatched step"); if(rdr.info()[i].have_dir_cos!=info[i].have_dir_cos) REPORT_ERROR("Mismatched have direction cosines"); for(int j=0;j<3;j++) if(rdr.info()[i].dir_cos[j]!=info[i].dir_cos[j]) REPORT_ERROR("Mismatched direction cosines"); } if(typeid(TPixel)==typeid(unsigned char)) rdr.setup_read_byte(); else if(typeid(TPixel)==typeid(int)) rdr.setup_read_int(); else if(typeid(TPixel)==typeid(float)) rdr.setup_read_float(); else if(typeid(TPixel)==typeid(double)) rdr.setup_read_double(); else REPORT_ERROR("Data type not supported for minc io"); std::vector in_buffer(volume); load_standard_volume(rdr,&in_buffer[0]); rdr.close(); if(max_diff==0.0) { for(int i=0;imax_diff) { std::cerr<<"Expected:"<1) { chdir(argv[1]); } //no rounding expected make_rw_test("EZminc_byte_2.mnc",2,NC_BYTE,false); make_rw_test("EZminc_byte_3.mnc",3,NC_BYTE,false); make_rw_test("EZminc_int_2.mnc",2,NC_INT,true); make_rw_test("EZminc_int_3.mnc",3,NC_INT,true); make_rw_test("EZminc_float_2.mnc",2,NC_FLOAT,true); make_rw_test("EZminc_float_3.mnc",3,NC_FLOAT,true); make_rw_test("EZminc_double_2.mnc",2,NC_DOUBLE,true); make_rw_test("EZminc_double_3.mnc",3,NC_DOUBLE,true); // some rounding expected make_rw_test("EZminc_float_2_short.mnc",2,NC_SHORT,true,0.1); make_rw_test("EZminc_float_3_short.mnc",3,NC_SHORT,true,0.1); make_rw_test("EZminc_double_2_short.mnc",2,NC_SHORT,true,0.1); make_rw_test("EZminc_double_3_short.mnc",3,NC_SHORT,true,0.1); make_rw_test("EZminc_float_2_byte.mnc",2,NC_BYTE,false,0.5); make_rw_test("EZminc_float_3_byte.mnc",3,NC_BYTE,false,0.5); make_rw_test("EZminc_double_2_byte.mnc",2,NC_BYTE,false,0.5); make_rw_test("EZminc_double_3_byte.mnc",3,NC_BYTE,false,0.5); } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< #include #include #include #include #include "minc_1_rw.h" #include "minc_1_simple.h" using namespace minc; template double calc_stats(const char * filename) { //reading volume minc_1_reader rdr; rdr.open(filename); int volume=1.0; for(int i=0;i<3;i++) { volume*=rdr.info()[i].length; } if(typeid(TPixel)==typeid(unsigned char)) rdr.setup_read_byte(); else if(typeid(TPixel)==typeid(int)) rdr.setup_read_int(); else if(typeid(TPixel)==typeid(float)) rdr.setup_read_float(); else if(typeid(TPixel)==typeid(double)) rdr.setup_read_double(); else REPORT_ERROR("Data type not supported for minc io"); std::vector in_buffer(volume); load_standard_volume(rdr,&in_buffer[0]); rdr.close(); double mean=0.0; for(int i=0;i(argv[1])<(argv[1])<(argv[1])<(argv[1])< #include "minc_1_simple.h" #include "minc_io_simple_volume.h" using namespace minc; int main(int argc,char **argv) { try { if(argc<3) { std::cerr<<"Usage: "< "< vol; vol.resize(rdr.ndim(1),rdr.ndim(2),rdr.ndim(3)); load_non_standard_volume(rdr,vol.c_buf()); //rdr.close(); for(size_t z=0;z(wrt,vol.c_buf()); } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< using namespace minc; int main(int argc,char **argv) { try { if(argc<3) { std::cerr<<"Usage: "< "<0 )) { if(rdr.datatype()==NC_FLOAT || rdr.datatype()==NC_SHORT) { std::cout<<"Reading float volume"< vol; load_4d_volume(rdr,vol); save_minc_file(argv[2],vol,"test",&rdr,rdr.datatype(),rdr.is_signed()); } else if(rdr.datatype()==NC_BYTE) { std::cout<<"Reading byte volume"< vol; load_4d_volume(rdr,vol); save_minc_file(argv[2],vol,"test",&rdr,rdr.datatype(),rdr.is_signed()); } } else if((rdr.dim_no()==4|| rdr.dim_no()==5)&&rdr.ndim(0)==3) { //we are dealing with vectors std::cout<<"Reading vector volume"< > vol; load_4d_volume(rdr,vol); save_minc_file(argv[2],vol,"test",&rdr,rdr.datatype(),rdr.is_signed()); } } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< #include "minc_1_simple.h" using namespace minc; int main(int argc,char **argv) { try { if(argc<3) { std::cerr<<"Usage: "< "< in(rdr); minc_output_iterator out(wrt); float c=0.0; for(in.begin(),out.begin();!in.last();in.next(),out.next()) { out.value(in.value()+c); c++; } } catch (const minc::generic_error & err) { std::cerr << "Got an error at:" << err.file () << ":" << err.line () << std::endl; std::cerr << err.msg()< #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_INTTYPES_H #include #endif #ifdef HAVE_MINC1 #include "minc.h" #endif #ifdef HAVE_MINC2 #include #endif #include #include #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* * Default table of argument descriptors. These are normally available * in every application. */ static ArgvInfo defaultTable[] = { {"-help", ARGV_HELP, (char *) NULL, (char *) NULL, "Print summary of command-line options and abort"}, {"-version", ARGV_VERSION, (char *) NULL, (char *) NULL, "Print version number of program and exit"}, {NULL, ARGV_END, (char *) NULL, (char *) NULL, (char *) NULL} }; /* * Forward declarations for functions defined in this file: */ static void PrintUsage(ArgvInfo *argTable, int flags); static void PrintVersion(ArgvInfo *argTable); /* * ParseLong * * Quick replacement for strtol which eliminates the undesirable property * of interpreting numbers with leading '0' characters as octal, while * retaining "0x" as indicating a hexidecimal number. */ long int ParseLong(const char *argPtr, char **endPtr) { const char *tmpPtr = argPtr; int base = 10; /* Default to decimal. */ /* Skip sign if present. */ if (tmpPtr[0] == '+' || tmpPtr[0] == '-') tmpPtr++; /* If '0x' or '0X', treat this as hex. */ if (tmpPtr[0] == '0' && (tmpPtr[1] == 'x' || tmpPtr[1] == 'X')) base = 16; return strtol(argPtr, endPtr, base); } /* *---------------------------------------------------------------------- * * ParseArgv -- * * Process an argv array according to a table of expected * command-line options. See the manual page for more details. * * Results: * The return value is a Boolean value with non-zero indicating an * error. * If an error occurs then an error message is printed on stderr. * Under normal conditions, both *argcPtr and *argv are modified * to return the arguments that couldn't be processed here (they * didn't match the option table, or followed an ARGV_REST * argument). * * Side effects: * *---------------------------------------------------------------------- */ int ParseArgv(argcPtr, argv, argTable, flags) int *argcPtr; /* Number of arguments in argv. Modified * to hold # args left in argv at end. */ char **argv; /* Array of arguments. Modified to hold * those that couldn't be processed here. */ ArgvInfo *argTable; /* Array of option descriptions */ int flags; /* Or'ed combination of various flag bits, * such as ARGV_NO_DEFAULTS. */ { ArgvInfo *infoPtr; /* Pointer to the current entry in the * table of argument descriptions. */ ArgvInfo *matchPtr; /* Descriptor that matches current argument. */ char *curArg; /* Current argument */ char c; /* Second character of current arg (used for * quick check for matching; use 2nd char. * because first char. will almost always * be '-'). */ int srcIndex; /* Location from which to read next argument * from argv. */ int dstIndex; /* Index into argv to which next unused * argument should be copied (never greater * than srcIndex). */ int argc; /* # arguments in argv still to process. */ size_t length; /* Number of characters in current argument. */ uintptr_t nargs; /* Number of following arguments to get. */ uintptr_t i; /* Macro to optionally print errors */ #define FPRINTF if (!(flags&ARGV_NO_PRINT)) (void) fprintf if (flags & ARGV_DONT_SKIP_FIRST_ARG) { srcIndex = dstIndex = 0; argc = *argcPtr; } else { srcIndex = dstIndex = 1; argc = *argcPtr-1; } while (argc > 0) { curArg = argv[srcIndex]; srcIndex++; argc--; c = curArg[1]; length = strlen(curArg); /* * Loop throught the argument descriptors searching for one with * the matching key string. If found, leave a pointer to it in * matchPtr. */ matchPtr = NULL; for (i = 0; i < 2; i++) { if (i == 0) { infoPtr = argTable; } else { infoPtr = defaultTable; } for (; infoPtr->type != ARGV_END; infoPtr++) { if (infoPtr->key == NULL) { continue; } if ((infoPtr->key[1] != c) || (strncmp(infoPtr->key, curArg, length) != 0)) { continue; } if (infoPtr->key[length] == 0) { matchPtr = infoPtr; goto gotMatch; } if (flags & ARGV_NO_ABBREV) { continue; } if (matchPtr != NULL) { FPRINTF(stderr, "ambiguous option \"%s\"\n", curArg); return TRUE; } matchPtr = infoPtr; } } if (matchPtr == NULL) { /* * Unrecognized argument. Just copy it down, unless the caller * prefers an error to be registered. */ if (flags & ARGV_NO_LEFTOVERS) { FPRINTF(stderr, "unrecognized argument \"%s\"\n", curArg); } argv[dstIndex] = curArg; dstIndex++; continue; } /* * Take the appropriate action based on the option type */ gotMatch: infoPtr = matchPtr; switch (infoPtr->type) { case ARGV_CONSTANT: *((int *) infoPtr->dst) = (intptr_t) infoPtr->src; break; case ARGV_INT: nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (i=0; idst)+i) = ParseLong(argv[srcIndex], &endPtr); if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { FPRINTF(stderr, "expected integer argument for \"%s\" but got \"%s\"", infoPtr->key, argv[srcIndex]); return TRUE; } srcIndex++; argc--; } } break; case ARGV_LONG: nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (i=0; idst)+i) = ParseLong(argv[srcIndex], &endPtr); if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { FPRINTF(stderr, "expected integer argument for \"%s\" but got \"%s\"", infoPtr->key, argv[srcIndex]); return TRUE; } srcIndex++; argc--; } } break; case ARGV_STRING: nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (i=0; idst)+i) = argv[srcIndex]; srcIndex++; argc--; } } break; case ARGV_REST: *((int *) infoPtr->dst) = dstIndex; goto argsDone; case ARGV_FLOAT: nargs = (uintptr_t) infoPtr->src; if (nargs<1) nargs=1; for (i=0; idst)+i) = strtod(argv[srcIndex], &endPtr); if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) { FPRINTF(stderr, "expected floating-point argument for \"%s\" but got\"%s\"\n", infoPtr->key, argv[srcIndex]); return TRUE; } srcIndex++; argc--; } } break; case ARGV_FUNC: { int (*handlerProc)() = (int (*)())(uintptr_t)infoPtr->src; if ((*handlerProc)(infoPtr->dst, infoPtr->key, argv[srcIndex])) { srcIndex += 1; argc -= 1; } break; } case ARGV_GENFUNC: { int (*handlerProc)() = (int (*)())(uintptr_t)infoPtr->src; argc = (*handlerProc)(infoPtr->dst, infoPtr->key, argc, argv+srcIndex); if (argc < 0) { return TRUE; } break; } case ARGV_HELP: PrintUsage (argTable, flags); return TRUE; case ARGV_VERSION: PrintVersion(argTable); return FALSE; default: FPRINTF(stderr, "bad argument type %d in ArgvInfo", infoPtr->type); return TRUE; } } /* * If we broke out of the loop because of an OPT_REST argument, * copy the remaining arguments down. */ argsDone: while (argc) { argv[dstIndex] = argv[srcIndex]; srcIndex++; dstIndex++; argc--; } argv[dstIndex] = (char *) NULL; *argcPtr = dstIndex; return FALSE; missingArg: FPRINTF(stderr, "\"%s\" option requires an additional argument\n", curArg); return TRUE; } /* *---------------------------------------------------------------------- * * PrintUsage -- * * Generate a help string describing command-line options. * * argTable: Array of command-specific argument descriptions. * * flags: If the ARGV_NO_DEFAULTS bit is set in this word, then don't * generate information for default options. * * Results: * Prints on stderr (unless ARGV_NO_PRINT is specified in flags) * a help string describing all the options in argTable, plus all those * in the default table unless ARGV_NO_DEFAULTS is * specified in flags. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void PrintUsage(ArgvInfo *argTable, int flags) { ArgvInfo *infoPtr; int width, i, numSpaces; intptr_t j, nargs; /* Macro to optionally print errors */ #define FPRINTF if (!(flags&ARGV_NO_PRINT)) (void) fprintf /* * First, compute the width of the widest option key, so that we * can make everything line up. */ width = 4; for (i = 0; i < 2; i++) { for (infoPtr = i ? defaultTable : argTable; infoPtr->type != ARGV_END; infoPtr++) { int length; if (infoPtr->key == NULL) { continue; } length = (int) strlen(infoPtr->key); if (length > width) { width = length; } } } FPRINTF(stderr, "Command-specific options:"); for (i = 0; ; i++) { for (infoPtr = i ? defaultTable : argTable; infoPtr->type != ARGV_END; infoPtr++) { if (infoPtr->type == ARGV_VERINFO) { continue; } if ((infoPtr->type == ARGV_HELP) && (infoPtr->key == NULL)) { FPRINTF(stderr, "\n%s", infoPtr->help); continue; } FPRINTF(stderr, "\n %s:", infoPtr->key); /* Write out padding after the key, followed by the help text. */ numSpaces = width + 1 - strlen(infoPtr->key); FPRINTF(stderr, "%*s %s", numSpaces, "", infoPtr->help); switch (infoPtr->type) { case ARGV_INT: { FPRINTF(stderr, "\n\t\tDefault value:"); nargs = (intptr_t) infoPtr->src; if (nargs<1) nargs=1; for (j=0; jdst)+j)); } break; } case ARGV_FLOAT: { FPRINTF(stderr, "\n\t\tDefault value:"); nargs = (intptr_t) infoPtr->src; if (nargs<1) nargs=1; for (j=0; jdst)+j)); } break; } case ARGV_STRING: { char *string; nargs = (intptr_t) infoPtr->src; if (nargs<1) nargs=1; string = *((char **) infoPtr->dst); if ((nargs==1) && (string == NULL)) break; for (j=0; jdst)+j); if (string != NULL) { FPRINTF(stderr, " \"%s\"", string); } else { FPRINTF(stderr, " "); /* Don't print null strings. */ } } break; } default: { break; } } } if ((flags & ARGV_NO_DEFAULTS) || (i > 0)) { break; } FPRINTF(stderr, "\nGeneric options for all commands:"); } FPRINTF(stderr, "\n"); } static void PrintVersion(ArgvInfo *argTable) { const char *versionStr = MINC_VERSION; for ( ; argTable->type != ARGV_END; argTable++) { if (argTable->type == ARGV_VERINFO) { /* Version information found? */ if (argTable->src != NULL) { versionStr = argTable->src; break; } } } printf("program: %s\n", versionStr); #ifdef HAVE_MINC1 { printf("libminc: %s\n", miget_version()); printf("netcdf : %s\n", nc_inq_libvers()); } #endif #ifdef HAVE_MINC2 { unsigned int major, minor, release; H5get_libversion(&major, &minor, &release); printf("HDF5 : %d.%d.%d\n", major, minor, release); } #endif exit(0); } libminc-libminc-2-3-00/libsrc/ParseArgv.h000066400000000000000000000046761257462267400201770ustar00rootroot00000000000000/* * ParseArgv.h -- * * Declarations for Tk-related things that are visible * outside of the Tk module itself. * * Copyright 1989-1992 Regents of the University of California. * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies. The University of California * makes no representations about the suitability of this * software for any purpose. It is provided "as is" without * express or implied warranty. * * This file has been modified to be used only for argv parsing without * reference to tk, tcl or X11. Base on tk.h from tk2.3 * $Header: /private-cvsroot/minc/libsrc/ParseArgv.h,v 6.6 2004-10-15 13:44:52 bert Exp $ SPRITE (Berkeley) */ #ifdef HAVE_MINC1 #include "minc.h" #else #define MNCAPI #endif /* * Structure used to specify how to handle argv options. */ typedef struct { const char *key; /* The key string that flags the option in the * argv array. */ int type; /* Indicates option type; see below. */ const char *src; /* Value to be used in setting dst; usage * depends on type. */ void *dst; /* Address of value to be modified; usage * depends on type. */ const char *help; /* Documentation message describing this option. */ } ArgvInfo; /* * Legal values for the type field of a ArgvInfo: see the user * documentation for details. */ #define ARGV_CONSTANT 15 #define ARGV_INT 16 #define ARGV_STRING 17 #define ARGV_LONG 100 #define ARGV_REST 19 #define ARGV_FLOAT 20 #define ARGV_FUNC 21 #define ARGV_GENFUNC 22 #define ARGV_HELP 23 #define ARGV_VERSION 24 #define ARGV_VERINFO 25 #define ARGV_END 27 /* * Flag bits for passing to ParseArgv: */ #define ARGV_NO_DEFAULTS 0x1 #define ARGV_NO_LEFTOVERS 0x2 #define ARGV_NO_ABBREV 0x4 #define ARGV_DONT_SKIP_FIRST_ARG 0x8 #define ARGV_NO_PRINT 0x10 /* bert- value was 0x16, which was bogus */ /* *-------------------------------------------------------------- * * Exported procedures and variables. * *-------------------------------------------------------------- */ #if defined(__cplusplus) extern "C" { #endif int MNCAPI ParseArgv(int *argcPtr, char **argv, ArgvInfo *argTable, int flags); long int MNCAPI ParseLong(const char *argPtr, char **endPtr); #if defined(__cplusplus) } #endif libminc-libminc-2-3-00/libsrc/ParseArgv.man3000066400000000000000000000406171257462267400206010ustar00rootroot00000000000000'\" '\" Copyright 1990-1992 Regents of the University of California '\" Permission to use, copy, modify, and distribute this '\" documentation for any purpose and without fee is hereby '\" granted, provided that this notice appears in all copies. '\" The University of California makes no representations about '\" the suitability of this material for any purpose. It is '\" provided "as is" without express or implied warranty. '\" '\" Modified to be used without tk or tcl (Peter Neelin, November 30,1992) '\" Based on tk 2.3 file : '\" $Header: /private-cvsroot/minc/libsrc/ParseArgv.man3,v 6.1 2002-01-14 21:28:26 neelin Exp $ SPRITE (Berkeley) '\" .\" The definitions below are for supplemental macros used in Sprite .\" manual entries. .\" .\" .HS name section [date [version]] .\" Replacement for .TH in other man pages. See below for valid .\" section names. .\" .\" .AP type name in/out [indent] .\" Start paragraph describing an argument to a library procedure. .\" type is type of argument (int, etc.), in/out is either "in", "out", .\" or "in/out" to describe whether procedure reads or modifies arg, .\" and indent is equivalent to second arg of .IP (shouldn't ever be .\" needed; use .AS below instead) .\" .\" .AS [type [name]] .\" Give maximum sizes of arguments for setting tab stops. Type and .\" name are examples of largest possible arguments that will be passed .\" to .AP later. If args are omitted, default tab stops are used. .\" .\" .BS .\" Start box enclosure. From here until next .BE, everything will be .\" enclosed in one large box. .\" .\" .BE .\" End of box enclosure. .\" .\" .VS .\" Begin vertical sidebar, for use in marking newly-changed parts .\" of man pages. .\" .\" .VE .\" End of vertical sidebar. .\" .\" .DS .\" Begin an indented unfilled display. .\" .\" .DE .\" End of indented unfilled display. .\" '\" # Heading for Sprite man pages .de HS .if '\\$2'cmds' .TH \\$1 1 \\$3 \\$4 .if '\\$2'lib' .TH \\$1 3 \\$3 \\$4 .if '\\$2'tcl' .TH \\$1 3 \\$3 \\$4 .if '\\$2'tk' .TH \\$1 3 \\$3 \\$4 .if t .wh -1.3i ^B .nr ^l \\n(.l .ad b .. '\" # Start an argument description .de AP .ie !"\\$4"" .TP \\$4 .el \{\ . ie !"\\$2"" .TP \\n()Cu . el .TP 15 .\} .ie !"\\$3"" \{\ .ta \\n()Au \\n()Bu \&\\$1 \\fI\\$2\\fP (\\$3) .\".b .\} .el \{\ .br .ie !"\\$2"" \{\ \&\\$1 \\fI\\$2\\fP .\} .el \{\ \&\\fI\\$1\\fP .\} .\} .. '\" # define tabbing values for .AP .de AS .nr )A 10n .if !"\\$1"" .nr )A \\w'\\$1'u+3n .nr )B \\n()Au+15n .\" .if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n .nr )C \\n()Bu+\\w'(in/out)'u+2n .. '\" # BS - start boxed text '\" # ^y = starting y location '\" # ^b = 1 .de BS .br .mk ^y .nr ^b 1u .if n .nf .if n .ti 0 .if n \l'\\n(.lu\(ul' .if n .fi .. '\" # BE - end boxed text (draw box now) .de BE .nf .ti 0 .mk ^t .ie n \l'\\n(^lu\(ul' .el \{\ .\" Draw four-sided box normally, but don't draw top of .\" box if the box started on an earlier page. .ie !\\n(^b-1 \{\ \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .el \}\ \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul' .\} .\} .fi .br .nr ^b 0 .. '\" # VS - start vertical sidebar '\" # ^Y = starting y location '\" # ^v = 1 (for troff; for nroff this doesn't matter) .de VS .mk ^Y .ie n 'mc \s12\(br\s0 .el .nr ^v 1u .. '\" # VE - end of vertical sidebar .de VE .ie n 'mc .el \{\ .ev 2 .nf .ti 0 .mk ^t \h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n' .sp -1 .fi .ev .\} .nr ^v 0 .. '\" # Special macro to handle page bottom: finish off current '\" # box/sidebar if in box/sidebar mode, then invoked standard '\" # page bottom macro. .de ^B .ev 2 'ti 0 'nf .mk ^t .if \\n(^b \{\ .\" Draw three-sided box if this is the box's first page, .\" draw two sides but no top otherwise. .ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c .\} .if \\n(^v \{\ .nr ^x \\n(^tu+1v-\\n(^Yu \kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c .\} .bp 'fi .ev .if \\n(^b \{\ .mk ^y .nr ^b 2 .\} .if \\n(^v \{\ .mk ^Y .\} .. '\" # DS - begin display .de DS .RS .nf .sp .. '\" # DE - end display .de DE .fi .RE .sp .5 .. .HS ParseArgv tk .BS .SH NAME ParseArgv \- process command-line options .SH SYNOPSIS \fB#include \fR .sp int .br \fBParseArgv\fR(\fIargcPtr, argv, argTable, flags\fR) .SH ARGUMENTS .AS ArgvInfo *argTable .AP int argcPtr in/out Pointer to number of arguments in argv; gets modified to hold number of unprocessed arguments that remain after the call. .AP char **argv in/out Command line arguments passed to main program. Modified to hold unprocessed arguments that remain after the call. .AP ArgvInfo *argTable in Array of argument descriptors, terminated by element with type ARGV_END. .AP int flags in If non-zero, then it specifies one or more flags that control the parsing of arguments. Different flags may be OR'ed together. .na The flags currently defined are ARGV_DONT_SKIP_FIRST_ARG, ARGV_NO_ABBREV, ARGV_NO_LEFTOVERS, ARGV_NO_DEFAULTS and ARGV_NO_PRINT. .ad .BE .SH DESCRIPTION .PP \fBParseArgv\fR processes an array of command-line arguments according to a table describing the kinds of arguments that are expected. Each of the arguments in \fIargv\fR is processed in turn: if it matches one of the entries in \fIargTable\fR, the argument is processed according to that entry and discarded. The arguments that do not match anything in \fIargTable\fR are copied down to the beginning of \fIargv\fR (retaining their original order) and returned to the caller. At the end of the call \fBParseArgv\fR sets \fI*argcPtr\fR to hold the number of arguments that are left in \fIargv\fR, and \fIargv[*argcPtr]\fR will hold the value NULL. Normally, \fBParseArgv\fR assumes that \fIargv[0]\fR is a command name, so it is treated like an argument that doesn't match \fIargTable\fR and returned to the caller; however, if the ARGV_DONT_SKIP_FIRST_ARG bit is set in \fIflags\fR then \fIargv[0]\fR will be processed just like the other elements of \fIargv\fR. .PP \fBParseArgv\fR normally returns the value FALSE (0). If an error occurs while parsing the arguments, then TRUE (1) is returned and \fBParseArgv\fR will print an error message on stderr. In the event of an error return, \fI*argvPtr\fR will not have been modified, but \fIargv\fR could have been partially modified. The possible causes of errors are explained below. .PP The \fIargTable\fR array specifies the kinds of arguments that are expected; each of its entries has the following structure: .DS .ta 2c \fBtypedef struct\fR { \fBchar\fR *\fIkey\fR; \fBint\fR \fItype\fR; \fBchar\fR *\fIsrc\fR; \fBchar\fR *\fIdst\fR; \fBchar\fR *\fIhelp\fR; \fB} ArgvInfo;\fR .DE .LP The \fIkey\fR field is a string such as ``\-display'' or ``\-bg'' that is compared with the values in \fIargv\fR. \fIType\fR indicates how to process an argument that matches \fIkey\fR (more on this below). \fISrc\fR and \fIdst\fR are additional values used in processing the argument. Their exact usage depends on \fItype\fR, but typically \fIsrc\fR indicates a value and \fIdst\fR indicates where to store the value. The \fBchar *\fR declarations for \fIsrc\fR and \fIdst\fR are placeholders: the actual types may be different. Lastly, \fIhelp\fR is a string giving a brief description of this option; this string is printed when users ask for help about command-line options. .PP When processing an argument in \fIargv\fR, \fBParseArgv\fR compares the argument to each of the \fIkey\fR's in \fIargTable\fR. \fBParseArgv\fR selects the first specifier whose \fIkey\fR matches the argument exactly, if such a specifier exists. Otherwise \fBParseArgv\fR selects a specifier for which the argument is a unique abbreviation. If the argument is a unique abbreviation for more than one specifier, then an error is returned. If there is no matching entry in \fIargTable\fR, then the argument is skipped and returned to the caller. .PP Once a matching argument specifier is found, \fBParseArgv\fR processes the argument according to the \fItype\fR field of the specifier. The argument that matched \fIkey\fR is called ``the matching argument'' in the descriptions below. As part of the processing, \fBParseArgv\fR may also use the next argument in \fIargv\fR after the matching argument, which is called ``the following argument''. The legal values for \fItype\fR, and the processing that they cause, are as follows: .TP \fBARGV_END\fR Marks the end of the table. The last entry in \fIargTable\fR must have this type; all of its other fields are ignored and it will never match any arguments. .TP \fBARGV_CONSTANT\fR \fISrc\fR is treated as an integer and \fIdst\fR is treated as a pointer to an integer. \fISrc\fR is stored at \fI*dst\fR. The matching argument is discarded. .TP \fBARGV_INT\fR The following argument must contain an integer string in the format accepted by \fBstrtol\fR (e.g. ``0'' and ``0x'' prefixes may be used to specify octal or hexadecimal numbers, respectively). \fIDst\fR is treated as a pointer to an integer; the following argument is converted to an integer value and stored at \fI*dst\fR. \fISrc\fR is treated as an integer count: if its value is greater than 1, then that many arguments are processed and \fIDst\fR is treated as an array pointer. The matching and following arguments are discarded from \fIargv\fR. .TP \fBARGV_FLOAT\fR The following argument must contain a floating-point number in the format accepted by \fBstrtol\fR. \fIDst\fR is treated as the address of an double-precision floating point value; the following argument is converted to a double-precision value and stored at \fI*dst\fR. \fISrc\fR is treated as an integer count: if its value is greater than 1, then that many arguments are processed and \fIDst\fR is treated as an array pointer. The matching and following arguments are discarded from \fIargv\fR. .TP \fBARGV_STRING\fR In this form, \fIdst\fR is treated as a pointer to a (char *); \fBParseArgv\fR stores at \fI*dst\fR a pointer to the following argument, and discards the matching and following arguments from \fIargv\fR. \fISrc\fR is treated as an integer count: if its value is greater than 1, then that many arguments are processed and \fIDst\fR is treated as an array pointer. .TP \fBARGV_HELP\fR When this kind of option is encountered, \fBParseArgv\fR uses the \fIhelp\fR fields of \fIargTable\fR to format a message describing all the valid arguments. The message is written on stderr and \fBParseArgv\fR returns TRUE. When this happens, the caller normally aborts. If the \fIkey\fR field of a ARGV_HELP specifier is NULL, then the specifier will never match any arguments; in this case the specifier simply provides extra documentation, which will be included when some other ARGV_HELP entry causes help information to be returned. .TP \fBARGV_REST\fR This option is used by programs or commands that allow the last several of their options to be the name and/or options for some other program. If a \fBARGV_REST\fR argument is found, then \fBParseArgv\fR doesn't process any of the remaining arguments; it returns them all at the beginning of \fIargv\fR (along with any other unprocessed arguments). In addition, \fBParseArgv\fR treats \fIdst\fR as the address of an integer value, and stores at \fI*dst\fR the index of the first of the \fBARGV_REST\fR options in the returned \fIargv\fR. This allows the program to distinguish the \fBARGV_REST\fR options from other unprocessed options that preceeded the \fBARGV_REST\fR. .TP \fBARGV_FUNC\fR For this kind of argument, \fIsrc\fR is treated as the address of a procedure, which is invoked to process the following argument. The procedure should have the following structure: .DS .ta 1c 2c 3c 4c 5c 6c \fBint\fI func(dst, key, nextArg) \fBchar\fR *\fIdst\fR; \fBchar\fR *\fIkey\fR; \fBchar\fR *\fInextArg\fR; { } .DE .IP The \fIdst\fR and \fIkey\fR parameters will contain the corresponding fields from the \fIargTable\fR entry, and \fInextArg\fR will point to the following argument from \fIargv\fR (or NULL if there aren't any more arguments left in \fIargv\fR). If \fIfunc\fR uses \fInextArg\fR (so that \fBParseArgv\fR should discard it), then it should return 1. Otherwise it should return 0 and \fBTkParseArgv\fR will process the following argument in the normal fashion. In either event the matching argument is discarded. .TP \fBARGV_GENFUNC\fR This form provides a more general procedural escape. It treats \fIsrc\fR as the address of a procedure, and passes that procedure all of the remaining arguments. The procedure should have the following form: .DS .ta 1c 2c 3c 4c 5c 6c \fBint\fI genfunc(dst, key, argc, argv) \fBchar\fR *\fIdst\fR; \fBchar\fR *\fIkey\fR; \fBint\fR \fIargc\fR; \fBchar\fR **\fIargv\fR; { } .DE .IP The \fIdst\fR and \fIkey\fR parameters will contain the corresponding fields from the \fIargTable\fR entry. \fIArgc\fR and \fIargv\fR refer to all of the options after the matching one. \fIGenfunc\fR should behave in a fashion similar to \fBParseArgv\fR: parse as many of the remaining arguments as it can, then return any that are left by compacting them to the beginning of \fIargv\fR (starting at \fIargv\fR[0]). \fIGenfunc\fR should return a count of how many arguments are left in \fIargv\fR; \fBParseArgv\fR will process them. If \fIgenfunc\fR encounters an error then it should print an error message on stderr, and return -1; when this happens \fBParseArgv\fR will abort its processing and return TRUE. .SH "FLAGS" .IP \fBARGV_DONT_SKIP_FIRST_ARG\fR \fBParseArgv\fR normally treats \fIargv[0]\fR as a program or command name, and returns it to the caller just as if it hadn't matched \fIargTable\fR. If this flag is given, then \fIargv[0]\fR is not given special treatment. .IP \fBARGV_NO_ABBREV\fR Normally, \fBParseArgv\fR accepts unique abbreviations for \fIkey\fR values in \fIargTable\fR. If this flag is given then only exact matches will be acceptable. .IP \fBARGV_NO_LEFTOVERS\fR Normally, \fBParseArgv\fR returns unrecognized arguments to the caller. If this bit is set in \fIflags\fR then \fBParseArgv\fR will return an error if it encounters any argument that doesn't match \fIargTable\fR. The only exception to this rule is \fIargv[0]\fR, which will be returned to the caller with no errors as long as ARGV_DONT_SKIP_FIRST_ARG isn't specified. .IP \fBARGV_NO_DEFAULTS\fR Normally, \fBParseArgv\fR searches an internal table of standard argument specifiers in addition to \fIargTable\fR. If this bit is set in \fIflags\fR, then \fBParseArgv\fR will use only \fIargTable\fR and not its default table. .IP \fBARGV_NO_PRINT\fR Normally, \fBParseArgv\fR prints error message on stderr. If this bit is set in \fIflags\fR, then \fBParseArgv\fR will not print any error messages. .SH EXAMPLE .PP Here is an example definition of an \fIargTable\fR and some sample command lines that use the options. Note the effect on \fIargc\fR and \fIargv\fR; arguments processed by \fBParseArgv\fR are eliminated from \fIargv\fR, and \fIargc\fR is updated to reflect reduced number of arguments. .DS L \fC/* * Define and set default values for globals. */ int debugFlag = 0; int numReps = 100; char defaultFileName[] = "out"; char *fileName = defaultFileName; Boolean exec = FALSE; /* * Define option descriptions. */ ArgvInfo argTable[] = { {"-X", ARGV_CONSTANT, (char *) 1, (char *) &debugFlag, "Turn on debugging printfs"}, {"-N", ARGV_INT, (char *) NULL, (char *) &numReps, "Number of repetitions"}, {"-of", ARGV_STRING, (char *) NULL, (char *) &fileName, "Name of file for output"}, {"x", ARGV_REST, (char *) NULL, (char *) &exec, "File to exec, followed by any arguments (must be last argument)."}, {(char *) NULL, ARGV_END, (char *) NULL, (char *) NULL, (char *) NULL} }; main(argc, argv) int argc; char *argv[]; { \&... if (ParseArgv(&argc, argv, argTable, 0)) { exit(1); } /* * Remainder of the program. */ }\fR .DE .PP Note that default values can be assigned to variables named in \fIargTable\fR: the variables will only be overwritten if the particular arguments are present in \fIargv\fR. Here are some example command lines and their effects. .DS \fCprog -N 200 infile # just sets the numReps variable to 200 prog -of out200 infile # sets fileName to reference "out200" prog -XN 10 infile # sets the debug flag, also sets numReps\fR .DE In all of the above examples, \fIargc\fR will be set by \fBParseArgv\fR to 2, \fIargv\fR[0] will be ``prog'', \fIargv\fR[1] will be ``infile'', and \fIargv\fR[2] will be NULL. .SH KEYWORDS arguments, command line, options libminc-libminc-2-3-00/libsrc/config.h.msvc-win32000066400000000000000000000016631257462267400214520ustar00rootroot00000000000000/* Win32 config.h for MINC * * Author: Bert Vincent * * $Header: /private-cvsroot/minc/libsrc/config.h.msvc-win32,v 6.3 2005-07-18 22:19:01 bert Exp $ */ #ifndef _CONFIG_H_ #define _CONFIG_H_ 1 #define HAVE_FCNTL_H 1 #define HAVE_FLOAT_H 1 #define HAVE_MEMORY_H 1 #define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 #define HAVE_STRING_H 1 #define HAVE_SYS_STAT_H 1 #define HAVE_SYS_TYPES_H 1 #define HAVE_TEMPNAM 1 #define HAVE_TMPNAM 1 #define STDC_HEADERS 1 #define VERSION "2.0.09" #define sleep(x) _sleep(x) #define snprintf _snprintf #define strncasecmp(x,y,z) strnicmp(x,y,z) #define tempnam(x,y) _tempnam(x,y) int __stdcall gethostname(char *name, int namelen); /* Quick implmentation of rint() for Intel, since thoughtless Microsoft * programmers failed to provide us with one... */ __inline double rint(double dblvar) { __asm fld dblvar __asm frndint __asm fst dblvar return dblvar; } #endif /* _CONFIG_H_ not defined */ libminc-libminc-2-3-00/libsrc/dim_conversion.c000066400000000000000000001156011257462267400213050ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : dim_conversion.c @DESCRIPTION: File of functions to do dimension conversion for image conversion variables (icv). These variables allow conversion of netcdf variables (the MINC image variable, in particular) to a form more convenient for a program. @METHOD : Routines included in this file : public : miicv_attach private : MI_icv_get_dim MI_get_dim_flip MI_get_dim_scale MI_get_dim_bufsize_step MI_icv_get_dim_conversion MI_icv_dimconvert MI_icv_dimconv_init @CREATED : September 9, 1992. (Peter Neelin) @MODIFIED : * $Log: dim_conversion.c,v $ * Revision 6.6 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.5 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.4 2004/10/15 13:44:52 bert * Minor changes for Windows compatibility * * Revision 6.3 2003/11/14 16:52:24 stever * More last-minute fixes. * * Revision 6.2 2003/09/18 16:16:15 bert * Use standard labs() and fabs() instead of our private macros * * Revision 6.1 1999/10/19 14:45:07 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/10 18:14:50 neelin * Fixed handling of invalid data when icv scale is zero. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.3 1995/02/08 19:14:44 neelin * More changes for irix 5 lint. * * Revision 2.2 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.1 1994/11/02 09:42:37 neelin * Fixed conversion of vector to scalar (old code simply returned the first * component of the vector - now averaging is done properly). * * Revision 2.0 94/09/28 10:37:52 neelin * Release of minc version 0.2 * * Revision 1.11 94/09/28 10:37:35 neelin * Pre-release * * Revision 1.10 94/07/05 15:31:07 neelin * Assume that MIstep is positive if it is not found (for flipping dimensions). * * Revision 1.9 94/03/16 10:30:00 neelin * Changed default MI_ICV_STEP: if not found use 1.0 (and 0.0 for start). * * Revision 1.8 93/08/11 12:06:03 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include #include /* Private functions */ PRIVATE int MI_icv_get_dim(mi_icv_type *icvp, int cdfid, int varid); PRIVATE int MI_get_dim_flip(mi_icv_type *icvp, int cdfid, int dimvid[], int subsc[]); PRIVATE int MI_get_dim_scale(mi_icv_type *icvp, int cdfid, int dimvid[]); PRIVATE int MI_get_dim_bufsize_step(mi_icv_type *icvp, int subsc[]); PRIVATE int MI_icv_get_dim_conversion(mi_icv_type *icvp, int subsc[]); PRIVATE int MI_icv_dimconvert(int operation, mi_icv_type *icvp, long start[], long count[], void *values, long bufstart[], long bufcount[], void *buffer); PRIVATE int MI_icv_dimconv_init(int operation, mi_icv_type *icvp, mi_icv_dimconv_type *dcp, long start[], long count[], void *values, long bufstart[], long bufcount[], void *buffer); /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_attach @INPUT : icvid - icv id cdfid - cdf file id varid - cdf variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Attaches an open cdf file and variable to an image conversion variable for subsequent access through miicvget and miicvput. File must be in data mode. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 9, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_attach(int icvid, int cdfid, int varid) { mi_icv_type *icvp; /* Pointer to icv structure */ long size_diff, user_dim_size; int idim; MI_SAVE_ROUTINE_NAME("miicv_attach"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN_ERROR(MI_ERROR); /* Call routine to set variables for everything except dimension conversion */ {MI_CHK_ERR(miicv_ndattach(icvid, cdfid, varid))} /* Check to see if we need to worry about dimension conversion */ if (!icvp->user_do_dimconv) { MI_RETURN(MI_NOERROR); } /* Reset cdfid and varid in icv structure in case something goes wrong in dimension conversion calculations */ icvp->cdfid = MI_ERROR; icvp->varid = MI_ERROR; /* Get dimensions info */ {MI_CHK_ERR(MI_icv_get_dim(icvp, cdfid, varid))} /* Set the do_dimconvert field of icv structure We do dimension conversion if any dimension needs flipping, scaling or offset, or if we have to convert from vector to scalar. */ icvp->do_dimconvert = (icvp->user_do_scalar && icvp->var_is_vector); for (idim=0; idimuser_num_imgdims; idim++) { if (icvp->derv_dim_flip[idim] || (icvp->derv_dim_scale[idim] != 1) || (icvp->derv_dim_off[idim] != 0)) icvp->do_dimconvert = TRUE; } icvp->dimconvert_func = MI_icv_dimconvert; /* Check if we have to zero user's buffer on GETs */ icvp->derv_do_zero = FALSE; for (idim=0; idimuser_num_imgdims; idim++) { user_dim_size = ((icvp->user_dim_size[idim]<=0) ? icvp->var_dim_size[idim] : icvp->user_dim_size[idim]); if (icvp->derv_dim_grow[idim]) size_diff = user_dim_size - icvp->var_dim_size[idim] * icvp->derv_dim_scale[idim]; else size_diff = user_dim_size - 1 - (icvp->var_dim_size[idim] - 1) / icvp->derv_dim_scale[idim]; if ((icvp->derv_dim_off[idim]!=0) || (size_diff!=0)) icvp->derv_do_zero = TRUE; } /* Set the cdfid and varid fields */ icvp->cdfid = cdfid; icvp->varid = varid; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_dim @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets dimension info for the icv @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_dim(mi_icv_type *icvp, int cdfid, int varid) /* ARGSUSED */ { int oldncopts; /* For saving value of ncopts */ char dimname[MAX_NC_NAME]; /* Dimensions name */ int idim; /* Looping counter for fastest image dims */ int subsc[MI_MAX_IMGDIMS]; /* Subscripts for fastest image dims */ int dimvid[MI_MAX_IMGDIMS]; /* Variable ids for dimensions */ MI_SAVE_ROUTINE_NAME("MI_icv_get_dim"); /* Check that the variable has at least icvp->user_num_imgdims dimensions */ if (icvp->var_ndims < icvp->user_num_imgdims) { MI_LOG_PKG_ERROR2(MI_ERR_TOOFEWDIMS, "Variable has too few dimensions to be an image"); MI_RETURN_ERROR(MI_ERROR); } /* Check the first dimensions of the variable */ MI_CHK_ERR(ncdiminq(cdfid, icvp->var_dim[icvp->var_ndims-1], dimname, &(icvp->var_vector_size))) icvp->var_is_vector = STRINGS_EQUAL(dimname, MIvector_dimension); /* Check that the variable has at least icvp->user_num_imgdims+1 dimensions if it is a vector field */ if (icvp->var_is_vector && (icvp->var_ndims < icvp->user_num_imgdims+1)) { MI_LOG_PKG_ERROR2(MI_ERR_TOOFEWDIMS, "Variable has too few dimensions to be an image"); MI_RETURN_ERROR(MI_ERROR); } /* Check for dimension flipping and get dimension sizes */ /* Get subscripts for first icvp->user_num_imgdims dimensions */ subsc[0] = (icvp->var_is_vector) ? icvp->var_ndims-2 : icvp->var_ndims-1; for (idim=1; idim < icvp->user_num_imgdims; idim++) subsc[idim]=subsc[idim-1]-1; /* Get dimension variable ids */ for (idim=0; idim < icvp->user_num_imgdims; idim++) { {MI_CHK_ERR(ncdiminq(cdfid, icvp->var_dim[subsc[idim]], dimname, &(icvp->var_dim_size[idim])))}; oldncopts = ncopts; ncopts = 0; dimvid[idim] = ncvarid(cdfid, dimname); ncopts = oldncopts; } /* Check for flipping */ {MI_CHK_ERR(MI_get_dim_flip(icvp, cdfid, dimvid, subsc))} /* Check for scaling of dimension */ {MI_CHK_ERR(MI_get_dim_scale(icvp, cdfid, dimvid))} /* Check for variable buffer size increments */ {MI_CHK_ERR(MI_get_dim_bufsize_step(icvp, subsc))} /* Get information for dimension conversion */ {MI_CHK_ERR(MI_icv_get_dim_conversion(icvp, subsc))} MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_dim_flip @INPUT : icvp - icv pointer cdfid - cdf file id dimvid - variable id subsc - array of dimension subscripts for fastest varying dimensions @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Checks for flipping of icv. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 1, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_get_dim_flip(mi_icv_type *icvp, int cdfid, int dimvid[], int subsc[]) { int oldncopts; /* For saving value of ncopts */ char dimname[MAX_NC_NAME]; /* Dimensions name */ int dim_dir; /* Desired direction for current dimension */ double dimstep; /* Dimension step size (and direction) */ int idim; MI_SAVE_ROUTINE_NAME("MI_get_dim_flip"); /* Loop through fast dimensions */ for (idim=0; idim < icvp->user_num_imgdims; idim++) { /* Get name of the dimension */ {MI_CHK_ERR(ncdiminq(cdfid, icvp->var_dim[subsc[idim]], dimname, NULL))} /* Should we look for dimension flipping? */ icvp->derv_dim_flip[idim]=FALSE; if (STRINGS_EQUAL(dimname, MIxspace) || STRINGS_EQUAL(dimname, MIxfrequency)) dim_dir = icvp->user_xdim_dir; else if (STRINGS_EQUAL(dimname, MIyspace) || STRINGS_EQUAL(dimname, MIyfrequency)) dim_dir = icvp->user_ydim_dir; else if (STRINGS_EQUAL(dimname, MIzspace) || STRINGS_EQUAL(dimname, MIzfrequency)) dim_dir = icvp->user_zdim_dir; else dim_dir = MI_ICV_ANYDIR; /* Look for variable corresponding to dimension */ if (dim_dir != MI_ICV_ANYDIR) { /* Look for flipping? */ /* Get the value of the MIstep attribute to determine whether flipping is needed. Assume that direction is positive if no step is provided. */ dimstep = 1.0; if (dimvid[idim] != MI_ERROR) { /* if dimension exists */ oldncopts = ncopts; ncopts = 0; (void) miattget1(cdfid, dimvid[idim], MIstep, NC_DOUBLE, &dimstep); ncopts = oldncopts; } /* if dimension exists */ if (dim_dir == MI_ICV_POSITIVE) icvp->derv_dim_flip[idim] = (dimstep<0.0); else if (dim_dir == MI_ICV_NEGATIVE) icvp->derv_dim_flip[idim] = (dimstep>0.0); } /* if look for flipping */ } /* for each dimension */ MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_dim_scale @INPUT : icvp - icv pointer cdfid - cdf file id dimvid - dimension variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Checks for scaling of images @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 1, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_get_dim_scale(mi_icv_type *icvp, int cdfid, int dimvid[]) { int oldncopts; /* For saving value of ncopts */ int min_grow, dim_grow; int min_scale, dim_scale; double dimstep, dimstart; int idim; long user_dim_size; MI_SAVE_ROUTINE_NAME("MI_get_dim_scale"); /* Loop through dimensions, calculating scale and looking for smallest one. For each dimension, check to see if we need to shrink or grow the image. (This is get-oriented: grow is TRUE if the variable dimension has to be expanded to fit the user's dimensions). */ for (idim=0; idim < icvp->user_num_imgdims; idim++) { /* Check to see if we user wants resize */ if (icvp->user_dim_size[idim] <= 0) { icvp->derv_dim_grow[idim]=TRUE; icvp->derv_dim_scale[idim]=1; } else { /* Check for growing or shrinking */ icvp->derv_dim_grow[idim] = (icvp->var_dim_size[idim] <= icvp->user_dim_size[idim]); /* If growing */ if (icvp->derv_dim_grow[idim]) { /* Get scale so that whole image fits in user array */ icvp->derv_dim_scale[idim] = icvp->user_dim_size[idim] / icvp->var_dim_size[idim]; } /* Otherwise, shrinking. Things are complicated by the fact that the external variable must fit completely in the user's array */ else { icvp->derv_dim_scale[idim] = 1 + (icvp->var_dim_size[idim] - 1) / icvp->user_dim_size[idim]; } } /* if user wants resizing */ /* Check for smallest scale */ if (idim==0) { min_grow = icvp->derv_dim_grow[idim]; min_scale = icvp->derv_dim_scale[idim]; } else { dim_grow = icvp->derv_dim_grow[idim]; dim_scale = icvp->derv_dim_scale[idim]; /* Check for one of three conditions : (1) smallest so far is growing, but this dim is shrinking (2) both are growing and this dim has smaller scale (3) both are shrinking and this dim has larger scale */ if ((min_grow && !dim_grow) || ((min_grow && dim_grow) && (min_scale > dim_scale)) || ((!min_grow && !dim_grow) && (min_scale < dim_scale))) { min_grow = dim_grow; min_scale = dim_scale; } } /* if not first dim */ } /* for each dimension, get scale */ /* Loop through dimensions, resetting scale if needed, setting offset and pixel step and start */ for (idim=0; idim < icvp->user_num_imgdims; idim++) { /* Check for aspect ratio */ if (icvp->user_keep_aspect) { icvp->derv_dim_grow[idim] = min_grow; icvp->derv_dim_scale[idim] = min_scale; } /* Get user's buffer size */ user_dim_size = ((icvp->user_dim_size[idim]<=0) ? icvp->var_dim_size[idim] : icvp->user_dim_size[idim]); /* Set offset of variable into user's image */ /* If growing */ if (icvp->derv_dim_grow[idim]) { /* Calculate remainder and split it in half */ icvp->derv_dim_off[idim] = ( user_dim_size - icvp->var_dim_size[idim] * icvp->derv_dim_scale[idim] ) / 2; } /* Otherwise, shrinking. Things are complicated by the fact that the external variable must fit completely in the user's array */ else { /* Calculate remainder and split it in half */ icvp->derv_dim_off[idim] = ( user_dim_size - 1 - (icvp->var_dim_size[idim] - 1) / icvp->derv_dim_scale[idim] ) / 2 ; } /* Get pixel step and start for variable and calculate for user. Look for them in the dimension variable (if MIstep is not there, then use defaults step = 1.0, start = 0.0 */ oldncopts = ncopts; ncopts = 0; dimstep = 1.0; (void) miattget1(cdfid, dimvid[idim], MIstep, NC_DOUBLE, &dimstep); /* Flip dimstep if needed */ if (icvp->derv_dim_flip[idim]) dimstep *= (-1); /* Get step size for user's image */ icvp->derv_dim_step[idim] = icvp->derv_dim_grow[idim] ? dimstep / icvp->derv_dim_scale[idim] : dimstep * icvp->derv_dim_scale[idim]; /* Get start position for user's image - if no MIstart for dimension, then assume 0.0 */ dimstart = 0.0; (void) miattget1(cdfid, dimvid[idim], MIstart, NC_DOUBLE, &dimstart); /* Flip dimstart if needed */ if (icvp->derv_dim_flip[idim]) dimstart -= dimstep * (icvp->var_dim_size[idim]-1); /* Calculate start position */ icvp->derv_dim_start[idim] = dimstart + (icvp->derv_dim_step[idim] - dimstep) / 2.0 - icvp->derv_dim_off[idim] * icvp->derv_dim_step[idim]; ncopts = oldncopts; } /* for each dimension */ MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_dim_bufsize_step @INPUT : icvp - icv pointer subsc - array of dimension subscripts for fastest varying dimensions @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets the variables giving variable buffer size @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 3, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_get_dim_bufsize_step(mi_icv_type *icvp, int subsc[]) { int idim; MI_SAVE_ROUTINE_NAME("MI_get_dim_bufsize_step"); /* Set default buffer size step */ for (idim=0; idim < MAX_VAR_DIMS; idim++) icvp->derv_bufsize_step[idim]=1; /* Check for converting vector to scalar */ icvp->derv_do_bufsize_step = (icvp->var_is_vector && icvp->user_do_scalar); if (icvp->derv_do_bufsize_step) icvp->derv_bufsize_step[icvp->var_ndims-1] = icvp->var_vector_size; /* Check each dimension to see if we need to worry about the variable buffer size. This only occurs if we are shrinking the dimension from the variable buffer to the user buffer. */ for (idim=0; idim < icvp->user_num_imgdims; idim++) { if (!icvp->derv_dim_grow[idim]) icvp->derv_bufsize_step[subsc[idim]]=icvp->derv_dim_scale[idim]; if (icvp->derv_bufsize_step[subsc[idim]] != 1) icvp->derv_do_bufsize_step = TRUE; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_dim_conversion @INPUT : icvp - icv pointer subsc - array of dimension subscripts for fastest varying dimensions @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets the variables for dimensions converions @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 8, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_dim_conversion(mi_icv_type *icvp, int subsc[]) /* ARGSUSED */ { int idim; MI_SAVE_ROUTINE_NAME("MI_icv_get_dim_conversion"); /* Find out whether we need to compress variable or user buffer */ icvp->derv_var_compress = (icvp->var_is_vector && icvp->user_do_scalar); icvp->derv_usr_compress = FALSE; for (idim=0; idimuser_num_imgdims; idim++) { if (icvp->derv_dim_scale[idim]!=1) { if (icvp->derv_dim_grow[idim]) icvp->derv_usr_compress = TRUE; else icvp->derv_var_compress = TRUE; } } /* Get the fastest varying dimension in user's buffer */ icvp->derv_dimconv_fastdim = icvp->var_ndims-1; if (icvp->var_is_vector && icvp->user_do_scalar) icvp->derv_dimconv_fastdim--; /* Find out how many pixels to compress/expand for variable and user buffers and allocate arrays */ if (icvp->var_is_vector && icvp->user_do_scalar) icvp->derv_var_pix_num=icvp->var_vector_size; else icvp->derv_var_pix_num=1; icvp->derv_usr_pix_num=1; for (idim=0; idimuser_num_imgdims; idim++) { if (icvp->derv_dim_grow[idim]) icvp->derv_usr_pix_num *= icvp->derv_dim_scale[idim]; else icvp->derv_var_pix_num *= MIN(icvp->var_dim_size[idim], icvp->derv_dim_scale[idim]); } icvp->derv_var_pix_off = MALLOC(icvp->derv_var_pix_num, long); icvp->derv_usr_pix_off = MALLOC(icvp->derv_usr_pix_num, long); if ((icvp->derv_var_pix_off == NULL) || (icvp->derv_usr_pix_off == NULL)) { MI_LOG_SYS_ERROR1("MI_icv_get_dim_conversion"); MI_RETURN_ERROR(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_dimconvert @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvp - icv structure pointer start - start passed by user count - count passed by user values - pointer to user's data area (for put) bufstart - start of variable buffer bufcount - count of variable buffer buffer - pointer to variable buffer (for get) @OUTPUT : values - pointer to user's data area (for get) buffer - pointer to variable buffer (for put) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Converts values and dimensions from an input buffer to the user's buffer. Called by MI_var_action. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 27, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_dimconvert(int operation, mi_icv_type *icvp, long start[], long count[], void *values, long bufstart[], long bufcount[], void *buffer) { mi_icv_dimconv_type dim_conv_struct; mi_icv_dimconv_type *dcp; double sum0, sum1; /* Counters for averaging values */ double dvalue; /* Pixel value */ long counter[MAX_VAR_DIMS]; /* Dimension loop counter */ void *ptr, *iptr, *optr; /* Pointers for looping through fastest dim */ void *ivecptr[MAX_VAR_DIMS]; /* Pointers to start of each dimension */ void *ovecptr[MAX_VAR_DIMS]; long *end; /* Pointer to array of dimension ends */ int fastdim; /* Dimension that varies fastest */ long ipix; /* Buffer subscript */ int idim; /* Dimension subscript */ int notmodified; /* First dimension not reset */ int out_of_range; /* Flag indicating one pixel of sum out of range */ double dmin, dmax, epsilon; /* Range limits */ MI_SAVE_ROUTINE_NAME("MI_icv_dimconvert"); /* Initialize variables */ dcp = &dim_conv_struct; {MI_CHK_ERR(MI_icv_dimconv_init(operation, icvp, dcp, start, count, values, bufstart, bufcount, buffer))} /* Initialize local variables */ iptr = dcp->istart; optr = dcp->ostart; end = dcp->end; fastdim = icvp->derv_dimconv_fastdim; dmax = icvp->fill_valid_max; dmin = icvp->fill_valid_min; epsilon = (dmax - dmin) * FILLVALUE_EPSILON; epsilon = fabs(epsilon); dmax += epsilon; dmin -= epsilon; /* Initialize counters */ for (idim=0; idim<=fastdim; idim++) { counter[idim] = 0; ivecptr[idim] = iptr; ovecptr[idim] = optr; } /* Loop through data */ while (counter[0] < end[0]) { /* Compress data by averaging if needed */ if (!dcp->do_compress) { {MI_TO_DOUBLE(dvalue, dcp->intype, dcp->insign, iptr)} out_of_range = (icvp->do_fillvalue && ((dvalue < dmin) || (dvalue > dmax))); } else { sum1 = 0.0; sum0 = 0.0; out_of_range=FALSE; for (ipix=0; ipixin_pix_num; ipix++) { ptr=(void *) ((char *)iptr + dcp->in_pix_off[ipix]); /* Check if we are outside the buffer. If we are looking before the buffer, then we need to add in the previous result to do averaging properly. If we are looking after the buffer, then break. */ if (ptrin_pix_first) { /* Get the output value and re-scale it */ {MI_TO_DOUBLE(dvalue, dcp->outtype, dcp->outsign, optr)} if (icvp->do_scale) { dvalue = ((icvp->scale==0.0) ? 0.0 : (dvalue - icvp->offset) / icvp->scale); } } else if (ptr>dcp->in_pix_last) { continue; } else { {MI_TO_DOUBLE(dvalue, dcp->intype, dcp->insign, ptr)} } /* Add in the value, checking for range if needed */ if (icvp->do_fillvalue && ((dvalue < dmin) || (dvalue > dmax))) { out_of_range = TRUE; } else { sum1 += dvalue; sum0++; } } /* Foreach pixel to compress */ /* Average values */ if (sum0!=0.0) dvalue = sum1/sum0; else dvalue = 0.0; } /* If compress */ /* Check for out of range values and scale result */ if (out_of_range) { dvalue = icvp->user_fillvalue; } else if (icvp->do_scale) { dvalue = icvp->scale * dvalue + icvp->offset; } /* Expand data if needed */ if (!dcp->do_expand) { {MI_FROM_DOUBLE(dvalue, dcp->outtype, dcp->outsign, optr)} } else { for (ipix=0; ipixout_pix_num; ipix++) { ptr=(void *) ((char *)optr + dcp->out_pix_off[ipix]); /* Check if we are outside the buffer. */ if ((ptr>=dcp->out_pix_first) && (ptr<=dcp->out_pix_last)) { {MI_FROM_DOUBLE(dvalue, dcp->outtype, dcp->outsign, ptr)} } } /* Foreach pixel to expand */ } /* if expand */ /* Increment the counter and the pointers */ if ((++counter[fastdim]) < end[fastdim]) { optr = (void *) ((char *) optr + dcp->ostep[fastdim]); iptr = (void *) ((char *) iptr + dcp->istep[fastdim]); } else { /* If we reach the end of fastdim, then reset the counter and increment the next dimension down - keep going as needed. The vectors ovecptr and ivecptr give the starting values of optr and iptr for that dimension. */ idim = fastdim; while ((idim>0) && (counter[idim] >= end[idim])) { counter[idim] = 0; idim--; counter[idim]++; ovecptr[idim] = (void *)((char *)ovecptr[idim]+dcp->ostep[idim]); ivecptr[idim] = (void *)((char *)ivecptr[idim]+dcp->istep[idim]); } notmodified = idim; /* Copy the starting index up the vector */ for (idim=notmodified+1; idim<=fastdim; idim++) { ovecptr[idim]=ovecptr[notmodified]; ivecptr[idim]=ivecptr[notmodified]; } optr = ovecptr[fastdim]; iptr = ivecptr[fastdim]; } /* if at end of row */ } /* while more pixels to process */ MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_dimconv_init @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvp - icv structure pointer dcp - dimconvert structure pointer start - start passed by user count - count passed by user values - pointer to user's data area (for put) bufstart - start of variable buffer bufcount - count of variable buffer buffer - pointer to variable buffer (for get) @OUTPUT : @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets up stuff for MI_icv_dimconvert. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 4, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_dimconv_init(int operation, mi_icv_type *icvp, mi_icv_dimconv_type *dcp, long start[], long count[], void *values, long bufstart[], long bufcount[], void *buffer) /* ARGSUSED */ { long buffer_len, values_len; /* Buffer lengths, offsets and indices */ long buffer_off, values_off; long buffer_index, values_index; int imgdim_high, imgdim_low; /* Range of subscripts of image dimensions */ int scale, offset; /* Dimension scale and offset */ int idim, jdim; int fastdim; /* Variables for calculating pixel offsets for compress/expand */ long var_dcount[MI_MAX_IMGDIMS+1], var_dend[MI_MAX_IMGDIMS+1]; long usr_dcount[MI_MAX_IMGDIMS+1], usr_dend[MI_MAX_IMGDIMS+1]; long pixcount; int var_fd, usr_fd, dshift; long ipix; MI_SAVE_ROUTINE_NAME("MI_icv_dimconv_init"); /* Check to see if any compression or expansion needs to be done. Work it out for a GET and then swap if a PUT. */ if (operation==MI_PRIV_GET) { dcp->do_compress = icvp->derv_var_compress; dcp->do_expand = icvp->derv_usr_compress; } else { dcp->do_expand = icvp->derv_var_compress; dcp->do_compress = icvp->derv_usr_compress; } fastdim = icvp->derv_dimconv_fastdim; /* Get the indices of high and low image dimensions */ imgdim_high=icvp->var_ndims-1; if (icvp->var_is_vector) imgdim_high--; imgdim_low = imgdim_high - icvp->user_num_imgdims + 1; /* Get the buffer sizes */ buffer_len = icvp->var_typelen; values_len = icvp->user_typelen; for (idim=0; idim < icvp->var_ndims; idim++) { buffer_len *= bufcount[idim]; if (idim<=fastdim) values_len *= icvp->derv_icv_count[idim]; } /* Calculate step size for variable and user buffers. This does not allow for growing or shrinking pixels. That correction is done below. */ if (icvp->var_is_vector && icvp->user_do_scalar) { dcp->buf_step[fastdim+1] = icvp->var_typelen; dcp->buf_step[fastdim] = dcp->buf_step[fastdim+1] * bufcount[fastdim+1]; } else { dcp->buf_step[fastdim] = icvp->var_typelen; } dcp->usr_step[fastdim] = icvp->user_typelen; for (idim=fastdim-1; idim>=0; idim--) { dcp->buf_step[idim] = dcp->buf_step[idim+1] * bufcount[idim+1]; dcp->usr_step[idim] = dcp->usr_step[idim+1] * icvp->derv_icv_count[idim+1]; } /* Set sign of user steps for flipping, if needed */ for (idim=imgdim_low; idim <= imgdim_high; idim++) { if (icvp->derv_dim_flip[imgdim_high-idim]) dcp->usr_step[idim] *= (-1); } /* Get the pointers to the start of buffers and the number of pixels in each dimension (count a pixel as one expansion/compression - one time through the loop below) */ buffer_off = 0; values_off = 0; for (idim=0; idim <= fastdim; idim++) { if ((idim < imgdim_low) || (idim > imgdim_high)) { dcp->end[idim] = bufcount[idim]; buffer_index = 0; values_index = bufstart[idim] - icvp->derv_icv_start[idim]; } else { jdim = imgdim_high - idim; scale = icvp->derv_dim_scale[jdim]; offset = icvp->derv_dim_off[jdim]; if (icvp->derv_dim_grow[jdim]) { dcp->end[idim] = bufcount[idim]; buffer_index = 0; if (!icvp->derv_dim_flip[jdim]) values_index = bufstart[idim]*scale - icvp->derv_icv_start[idim] + offset; else values_index = (icvp->var_dim_size[jdim] - bufstart[idim])*scale - 1 - icvp->derv_icv_start[idim] + offset; } else { dcp->end[idim] = (bufcount[idim] - 1 + bufstart[idim]%scale) / scale + 1; buffer_index = -(bufstart[idim] % scale); if (!icvp->derv_dim_flip[jdim]) values_index = bufstart[idim]/scale - icvp->derv_icv_start[idim] + offset; else values_index = (icvp->var_dim_size[jdim] - bufstart[idim] - 1)/scale - icvp->derv_icv_start[idim] + offset; } } /* Force these offsets to stay within the presumed limits of the * allocated memory. Before implementing this change it was * possible for miicv_get() or miicv_put() to write outside the * "values" buffer, leading to heap corruption. I overload the * "pixcount" variable since it is never really used elsewhere * (bert). */ pixcount = buffer_index * labs(dcp->buf_step[idim]); if (buffer_off + pixcount < buffer_len) { buffer_off += pixcount; } pixcount = values_index * labs(dcp->usr_step[idim]); if (values_off + pixcount < values_len) { values_off += pixcount; } } /* Disallow negative offsets to avoid illegal accesses (bert). */ if (buffer_off < 0) { buffer_off = 0; } if (values_off < 0) { values_off = 0; } /* Calculate arrays of offsets for compress/expand. */ if (dcp->do_compress || dcp->do_expand) { /* Initialize counters */ var_fd = icvp->user_num_imgdims-1; usr_fd = icvp->user_num_imgdims-1; if (icvp->var_is_vector && icvp->user_do_scalar) { var_fd++; var_dcount[var_fd]=0; var_dend[var_fd]=icvp->var_vector_size; } for (jdim=0; jdimuser_num_imgdims; jdim++) { idim=icvp->user_num_imgdims - jdim - 1; var_dcount[idim] = 0; usr_dcount[idim] = 0; var_dend[idim] = (icvp->derv_dim_grow[jdim] ? 1 : MIN(icvp->var_dim_size[jdim], icvp->derv_dim_scale[jdim])); usr_dend[idim] = (icvp->derv_dim_grow[jdim] ? icvp->derv_dim_scale[jdim] : 1); } /* Loop through variable buffer pixels */ pixcount=0; dshift = imgdim_low; for (ipix=0; ipixderv_var_pix_num; ipix++) { icvp->derv_var_pix_off[ipix] = pixcount; pixcount += dcp->buf_step[var_fd+dshift]; if ((++var_dcount[var_fd]) >= var_dend[var_fd]) { idim=var_fd; while ((idim>0) && (var_dcount[idim]>=var_dend[idim])) { var_dcount[idim]=0; idim--; var_dcount[idim]++; } for (idim=0, pixcount=0; idim<=var_fd; idim++) { pixcount += var_dcount[idim] * dcp->buf_step[idim+dshift]; } } } /* Loop through user buffer pixels */ pixcount=0; dshift = imgdim_low; for (ipix=0; ipixderv_usr_pix_num; ipix++) { icvp->derv_usr_pix_off[ipix] = pixcount; pixcount += dcp->usr_step[usr_fd+dshift]; if ((++usr_dcount[usr_fd]) >= usr_dend[usr_fd]) { idim=usr_fd; while ((idim>0) && (usr_dcount[idim]>=usr_dend[idim])) { usr_dcount[idim]=0; idim--; usr_dcount[idim]++; } for (idim=0, pixcount=0; idim<=var_fd; idim++) { pixcount += usr_dcount[idim] * dcp->usr_step[idim+dshift]; } } } /* Correct buffer steps for compress/expand */ for (idim=imgdim_low; idim <= imgdim_high; idim++) { jdim = imgdim_high-idim; if (icvp->derv_dim_grow[jdim]) dcp->usr_step[idim] *= icvp->derv_dim_scale[jdim]; else dcp->buf_step[idim] *= icvp->derv_dim_scale[jdim]; } } /* if compress/expand */ /* Set input and output variables */ if (operation==MI_PRIV_GET) { /* For a GET */ dcp->in_pix_num = icvp->derv_var_pix_num; dcp->in_pix_off = icvp->derv_var_pix_off; dcp->in_pix_first = buffer; dcp->in_pix_last = (void *) ((char *)buffer + buffer_len - 1); dcp->out_pix_num = icvp->derv_usr_pix_num; dcp->out_pix_off = icvp->derv_usr_pix_off; dcp->out_pix_first = values; dcp->out_pix_last = (void *) ((char *)values + values_len - 1); dcp->intype = icvp->var_type; dcp->insign = icvp->var_sign; dcp->outtype = icvp->user_type; dcp->outsign = icvp->user_sign; dcp->istep = dcp->buf_step; dcp->ostep = dcp->usr_step; dcp->istart = (void *) ((char *) buffer + buffer_off); dcp->ostart = (void *) ((char *) values + values_off); } /* if GET */ else { /* For a PUT */ dcp->out_pix_num = icvp->derv_var_pix_num; dcp->out_pix_off = icvp->derv_var_pix_off; dcp->out_pix_first = buffer; dcp->out_pix_last = (void *) ((char *)buffer + buffer_len - 1); dcp->in_pix_num = icvp->derv_usr_pix_num; dcp->in_pix_off = icvp->derv_usr_pix_off; dcp->in_pix_first = values; dcp->in_pix_last = (void *) ((char *)values + values_len - 1); dcp->outtype = icvp->var_type; dcp->outsign = icvp->var_sign; dcp->intype = icvp->user_type; dcp->insign = icvp->user_sign; dcp->ostep = dcp->buf_step; dcp->istep = dcp->usr_step; dcp->ostart = (void *) ((char *) buffer + buffer_off); dcp->istart = (void *) ((char *) values + values_off); } /* if PUT */ MI_RETURN(MI_NOERROR); } libminc-libminc-2-3-00/libsrc/hdf_convenience.c000066400000000000000000001627301257462267400214110ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #if MINC2 /* Ignore this file if not MINC2 */ /* #define NC_FILL_INT 1 */ #include "minc_private.h" #include "hdf_convenience.h" #define MI2_STD_DIM_COUNT 9 #define MI2_DIMORDER "dimorder" #define MI2_LENGTH "length" #define MI2_CLASS "class" /* So we build with 1.8.4 */ #ifndef H5F_LIBVER_18 #define H5F_LIBVER_18 H5F_LIBVER_LATEST #endif /************************************************************************ * Structures for files, variables, and dimensions. ************************************************************************/ struct m2_var { char name[NC_MAX_NAME]; char path[NC_MAX_NAME]; int id; int ndims; int is_cmpd; /* Is compound? */ hsize_t *dims; hid_t dset_id; hid_t ftyp_id; /* File type */ hid_t mtyp_id; /* Memory type */ hid_t fspc_id; }; struct m2_dim { struct m2_dim *link; int id; long length; int is_fake; /* TRUE if "emulated" vector dimension. */ char name[NC_MAX_NAME]; }; static struct m2_file { struct m2_file *link; hid_t fd; int wr_ok; /* non-zero if write OK */ int resolution; /* Resolution setting. */ int nvars; int ndims; struct m2_var *vars[NC_MAX_VARS]; struct m2_dim *dims[NC_MAX_DIMS]; hid_t grp_id; /* Root group ID */ int comp_type; /* Compression type */ int comp_param; /* Compression parameter */ int chunk_type; /* Chunking enabled */ int chunk_param; /* Chunk length */ } *_m2_list; static struct m2_file * hdf_id_check(int fd) { struct m2_file *curr; for (curr = _m2_list; curr != NULL; curr = curr->link) { if (fd == curr->fd) { return (curr); } } return (NULL); } static struct m2_file * hdf_id_add(int fd) { struct m2_file *new; new = (struct m2_file *) malloc(sizeof (struct m2_file)); if (new != NULL) { new->fd = fd; new->resolution = 0; new->nvars = 0; new->ndims = 0; new->link =_m2_list; new->grp_id = H5Gopen1(fd, MI2_GRPNAME); new->comp_type = MI2_COMP_UNKNOWN; new->comp_param = 0; new->chunk_type = MI2_CHUNK_UNKNOWN; new->chunk_param = 0; _m2_list = new; } else { milog_message(MI_MSG_OUTOFMEM, sizeof(struct m2_file)); exit(-1); } return (new); } static int hdf_id_del(int fd) { struct m2_file *curr, *prev; int i; for (prev = NULL, curr = _m2_list; curr != NULL; prev = curr, curr = curr->link) { if (fd == curr->fd) { /* Unlink it from the global list. */ if (prev == NULL) { _m2_list = curr->link; } else { prev->link = curr->link; } /* Delete the variable list. */ for (i = 0; i < curr->nvars; i++) { struct m2_var *tmp = curr->vars[i]; if (tmp->dims != NULL) { free(tmp->dims); } /* Close the HDF5 handles we were holding open. */ H5Dclose(tmp->dset_id); H5Tclose(tmp->ftyp_id); H5Tclose(tmp->mtyp_id); H5Sclose(tmp->fspc_id); free(tmp); } /* Delete the dimension list. */ for (i = 0; i < curr->ndims; i++) { struct m2_dim *tmp = curr->dims[i]; free(tmp); } H5Gclose(curr->grp_id); free(curr); return (MI_NOERROR); } } return (MI_ERROR); } static struct m2_var * hdf_var_byname(struct m2_file *file, const char *name) { int i; for (i = 0; i < file->nvars; i++) { if (!strcmp(file->vars[i]->name, name)) { return (file->vars[i]); } } return (NULL); } static struct m2_var * hdf_var_byid(struct m2_file *file, int varid) { if (varid >= 0 && varid < file->nvars) { return (file->vars[varid]); } return (NULL); } static struct m2_var * hdf_var_add(struct m2_file *file, const char *name, const char *path, int ndims, hsize_t *dims) { struct m2_var *new; if (file->nvars >= NC_MAX_VARS) { return (NULL); } new = (struct m2_var *) malloc(sizeof(struct m2_var)); if (new != NULL) { new->id = file->nvars++; strncpy(new->name, name, NC_MAX_NAME - 1); strncpy(new->path, path, NC_MAX_NAME - 1); new->is_cmpd = 0; new->dset_id = H5Dopen1(file->fd, path); new->ftyp_id = H5Dget_type(new->dset_id); new->mtyp_id = H5Tget_native_type(new->ftyp_id, H5T_DIR_ASCEND); new->fspc_id = H5Dget_space(new->dset_id); new->ndims = ndims; if (ndims != 0) { new->dims = (hsize_t *) malloc(sizeof (hsize_t) * ndims); if (new->dims != NULL) { int i; for (i = 0; i < ndims; i++) { new->dims[i] = dims[i]; } } else { milog_message(MI_MSG_OUTOFMEM, sizeof(hsize_t) * ndims); } } else { new->dims = NULL; } file->vars[new->id] = new; } else { milog_message(MI_MSG_OUTOFMEM, sizeof (struct m2_var)); exit(-1); } return (new); } /** Find a dimension by name. */ static struct m2_dim * hdf_dim_byname(struct m2_file *file, const char *name) { int i; for (i = 0; i < file->ndims; i++) { if (!strcmp(file->dims[i]->name, name)) { return (file->dims[i]); } } return (NULL); } /** Find a dimension by ID number. */ static struct m2_dim * hdf_dim_byid(struct m2_file *file, int dimid) { if (dimid >= 0 && dimid < file->ndims) { return (file->dims[dimid]); } return (NULL); } static struct m2_dim * hdf_dim_add(struct m2_file *file, const char *name, long length) { struct m2_dim *new; if (file->ndims >= NC_MAX_DIMS) { return (NULL); } new = (struct m2_dim *) malloc(sizeof(struct m2_dim)); if (new != NULL) { new->id = file->ndims++; new->length = length; new->is_fake = 0; strncpy(new->name, name, NC_MAX_NAME - 1); file->dims[new->id] = new; } else { milog_message(MI_MSG_OUTOFMEM, sizeof(struct m2_dim)); exit(-1); } return (new); } /************************************************************************ * Other helper functions ************************************************************************/ static int hdf_is_dimension_name(struct m2_file *file, const char *varnm) { static char *dimnms[MI2_STD_DIM_COUNT] = { MIxspace, MIyspace, MIzspace, MItime, MIxfrequency, MIyfrequency, MIzfrequency, MItfrequency, MIvector_dimension, }; int i; if (hdf_dim_byname(file, varnm) != NULL) { return (1); } for (i = 0; i < MI2_STD_DIM_COUNT; i++) { if (!strcmp(varnm, dimnms[i])) { return (1); } } return (0); /* Not a dimension (yet?) */ } /** Find the path of a variable, given its name. The variable * may not yet exist in the file. */ static hid_t hdf_path_from_name(struct m2_file *file, const char *varnm, char *varpath) { if (!strcmp(varnm, MIimage) || !strcmp(varnm, MIimagemax) || !strcmp(varnm, MIimagemin)) { sprintf(varpath, "/minc-2.0/image/%d/", file->resolution); } else if (hdf_is_dimension_name(file, varnm)) { strcpy(varpath, "/minc-2.0/dimensions/"); } else { strcpy(varpath, "/minc-2.0/info/"); } strcat(varpath, varnm); return (MI_NOERROR); } /* map NetCDF types onto HDF types */ static hid_t nc_to_hdf5_type(nc_type dtype, int is_signed) { switch (dtype) { case NC_CHAR: return (is_signed ? H5T_STD_I8LE : H5T_STD_U8LE); case NC_BYTE: return (is_signed ? H5T_STD_I8LE : H5T_STD_U8LE); case NC_SHORT: return (is_signed ? H5T_STD_I16LE : H5T_STD_U16LE); case NC_INT: return (is_signed ? H5T_STD_I32LE : H5T_STD_U32LE); case NC_FLOAT: return (H5T_IEEE_F32LE); case NC_DOUBLE: return (H5T_IEEE_F64LE); case NC_NAT: return (H5T_NO_CLASS); } return (-1); } static void hdf_get_diminfo(hid_t dst_id, int *ndims, hsize_t dims[]) { hid_t spc_id; spc_id = H5Dget_space(dst_id); if (spc_id < 0) { milog_message(MI_MSG_SNH); } else { *ndims = H5Sget_simple_extent_ndims(spc_id); if (*ndims > 0) { H5Sget_simple_extent_dims(spc_id, dims, NULL); } } } static int hdf_size(hid_t spc_id, hid_t typ_id) { int typ_size = H5Tget_size(typ_id); int spc_size = H5Sget_simple_extent_npoints(spc_id); if (typ_size <= 0 || spc_size <= 0) { milog_message(MI_MSG_SNH); return (-1); } return (typ_size * spc_size); } /** Given a numeric variable ID, get the text name of a MINC/HDF5 variable. * Equivalent of ncvarname() */ int hdf_varname(int fd, int varid, char *varnm) { struct m2_file *file; struct m2_var *var; /* Emulate rootvariable. */ if (varid == MI_ROOTVARIABLE_ID) { strcpy(varnm, MIrootvariable); return (MI_NOERROR); } if ((file = hdf_id_check(fd)) != NULL && (var = hdf_var_byid(file, varid)) != NULL) { strcpy(varnm, var->name); return (MI_NOERROR); } return (MI_ERROR); } /** Given a text name, get the numeric ID of a MINC/HDF5 variable. * Equivalent of ncvarid(). */ int hdf_varid(int fd, const char *varnm) { struct m2_file *file; struct m2_var *var; if (!strcmp(varnm, MIrootvariable)) { return (MI_ROOTVARIABLE_ID); } file = hdf_id_check(fd); if (file != NULL) { var = hdf_var_byname(file, varnm); if (var != NULL) { return (var->id); } } return (MI_ERROR); } /** Given a variable ID and attribute number return the attribute's text * name. The variable ID may be NC_GLOBAL. Equivalent to ncattname(). */ int hdf_attname(int fd, int varid, int attnum, char *name) { hid_t loc_id; hid_t att_id; int status; struct m2_file *file; struct m2_var *var; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (varid == NC_GLOBAL) { var = NULL; loc_id = file->grp_id; } else { if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } loc_id = var->dset_id; } H5E_BEGIN_TRY { att_id = H5Aopen_idx(loc_id, attnum); } H5E_END_TRY; if (att_id >= 0) { status = H5Aget_name(att_id, MAX_NC_NAME, name); H5Aclose(att_id); } else { /* See if this is the magic emulated signtype attribute. */ if (var != NULL && !strcmp(var->name, MIimage) && attnum == H5Aget_num_attrs(loc_id)) { strcpy(name, MIsigntype); status = MI_NOERROR; } else { status = MI_ERROR; } } return (status); } /** This function provides emulation for the "ncattinq()" call which * is part of netCDF. Unlike a number of netCDF functions, this * function is expected to return 1 on success, rather than zero. */ int hdf_attinq(int fd, int varid, const char *attnm, nc_type *type_ptr, int *length_ptr) { hid_t att_id = -1; hid_t typ_id = -1; hid_t spc_id = -1; hid_t loc_id = -1; int status = MI_ERROR; size_t typ_size; H5T_class_t typ_class; struct m2_file *file; struct m2_var *var; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (varid == NC_GLOBAL) { var = NULL; loc_id = file->grp_id; } else { if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } loc_id = var->dset_id; } /* Special case - emulate the signtype attribute. */ if (!strcmp(attnm, MIsigntype)) { if (var != NULL && H5Tget_class(var->ftyp_id) == H5T_INTEGER) { if (type_ptr != NULL) { *type_ptr = NC_CHAR; } if (length_ptr != NULL) { /* Signed and unsigned are the same length, */ *length_ptr = sizeof(MI_UNSIGNED); } return (1); /* 1 -> success here */ } else { return (MI_ERROR); } } else { H5E_BEGIN_TRY { att_id = H5Aopen_name(loc_id, attnm); } H5E_END_TRY; if (att_id < 0) goto cleanup; if ((spc_id = H5Aget_space(att_id)) < 0) goto cleanup; if ((typ_id = H5Aget_type(att_id)) < 0) goto cleanup; typ_class = H5Tget_class(typ_id); typ_size = H5Tget_size(typ_id); if (type_ptr != NULL) { if (typ_class == H5T_INTEGER) { if (typ_size == 1) *type_ptr = NC_BYTE; else if (typ_size == 2) *type_ptr = NC_SHORT; else if (typ_size == 4) *type_ptr = NC_INT; else { milog_message(MI_MSG_INTSIZE, typ_size); } } else if (typ_class == H5T_FLOAT) { if (typ_size == 4) { *type_ptr = NC_FLOAT; } else if (typ_size == 8) { *type_ptr = NC_DOUBLE; } else { milog_message(MI_MSG_FLTSIZE, typ_size); } } else if (typ_class == H5T_STRING) { *type_ptr = NC_CHAR; } else { milog_message(MI_MSG_TYPECLASS, typ_class); } } if (length_ptr != NULL) { if (typ_class == H5T_STRING) { *length_ptr = typ_size; } else { *length_ptr = H5Sget_simple_extent_npoints(spc_id); } } status = 1; /* 1 -> success here */ cleanup: if (typ_id >= 0) H5Tclose(typ_id); if (spc_id >= 0) H5Sclose(spc_id); if (att_id >= 0) H5Aclose(att_id); } return (status); } static int hdf_put_dimorder(struct m2_file *file, int dst_id, int ndims, const int *dims_ptr) { int i; hid_t att_id; hid_t spc_id; hid_t typ_id; char str_buf[NC_MAX_NAME]; /* Don't bother */ if (ndims == 0) { return (MI_NOERROR); } str_buf[0] = '\0'; for (i = 0; i < ndims; i++) { struct m2_dim *dim = hdf_dim_byid(file, dims_ptr[i]); if (dim != NULL) { strcat(str_buf, dim->name); } if (i != ndims - 1) { strcat(str_buf, ","); } } typ_id = H5Tcopy(H5T_C_S1); H5Tset_size(typ_id, strlen(str_buf) + 1); spc_id = H5Screate(H5S_SCALAR); att_id = H5Acreate2(dst_id, MI2_DIMORDER, typ_id, spc_id, H5P_DEFAULT, H5P_DEFAULT); if (att_id >= 0) { H5Awrite(att_id, typ_id, str_buf); } H5Aclose(att_id); H5Sclose(spc_id); H5Tclose(typ_id); return (MI_NOERROR); } static int hdf_get_dimorder(struct m2_file *file, int dst_id, int ndims, int *dims_ptr) { char *str_ptr; char *tmp_ptr; hid_t att_id; hid_t typ_id; char buf[NC_MAX_NAME]; int status; struct m2_dim *dim; int n; int done; int length; /* Don't bother */ if (ndims == 0) { return (MI_NOERROR); } att_id = H5Aopen_name(dst_id, MI2_DIMORDER); if (att_id < 0) { return (MI_ERROR); } typ_id = H5Aget_type(att_id); length = H5Tget_size(typ_id); if (length > NC_MAX_NAME) { return (MI_ERROR); } status = H5Aread(att_id, typ_id, buf); if (status < 0) { return (MI_ERROR); } H5Tclose(typ_id); H5Aclose(att_id); buf[length] = '\0'; /* Make certain string is terminated. */ str_ptr = &buf[0]; n = 0; done = 0; while (!done && n < ndims) { tmp_ptr = str_ptr; while (*tmp_ptr != ',' && *tmp_ptr != '\0') { tmp_ptr++; } if (*tmp_ptr == '\0') { /* Real end of string? */ done = 1; /* We're finished after this iteration. */ } else { *tmp_ptr++ = '\0'; /* Terminate the string. */ } dim = hdf_dim_byname(file, str_ptr); if (dim != NULL) { dims_ptr[n++] = dim->id; } str_ptr = tmp_ptr; } #ifndef NO_EMULATE_VECTOR_DIMENSION typ_id = H5Dget_type(dst_id); if (typ_id >= 0) { if (H5Tget_class(typ_id) == H5T_COMPOUND) { dim = hdf_dim_byname(file, MIvector_dimension); if (dim != NULL) { dims_ptr[n++] = dim->id; } } H5Tclose(typ_id); } #endif /* NO_EMULATE_VECTOR_DIMENSION */ return (MI_NOERROR); } int hdf_inquire(int fd, int *ndims_ptr, int *nvars_ptr, int *natts_ptr, int *unlimdim_ptr) { struct m2_file *file; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (ndims_ptr != NULL) { *ndims_ptr = file->ndims; } if (unlimdim_ptr != NULL) { /* We don't support unlimited dimensions. */ *unlimdim_ptr = -1; } if (nvars_ptr != NULL) { *nvars_ptr = file->nvars; } if (natts_ptr != NULL) { *natts_ptr = H5Aget_num_attrs(file->grp_id); } return (MI_NOERROR); } int hdf_varinq(int fd, int varid, char *varnm_ptr, nc_type *type_ptr, int *ndims_ptr, int *dims_ptr, int *natts_ptr) { hid_t dst_id; hid_t typ_id; size_t size; H5T_class_t class; int ndims; struct m2_file *file; struct m2_var *var; /* Emulate rootvariable */ if (varid == MI_ROOTVARIABLE_ID) { if (varnm_ptr != NULL) { strcpy(varnm_ptr, MIrootvariable); } if (type_ptr != NULL) { *type_ptr = NC_INT; } if (ndims_ptr != NULL) { *ndims_ptr = 0; } if (natts_ptr != NULL) { *natts_ptr = 0; } return (MI_NOERROR); } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } dst_id = var->dset_id; typ_id = var->ftyp_id; class = H5Tget_class(typ_id); size = H5Tget_size(typ_id); if (type_ptr != NULL) { #ifndef NO_EMULATE_VECTOR_DIMENSION if (class == H5T_COMPOUND) { hid_t subtype_id = H5Tget_member_type(typ_id, 0); class = H5Tget_class(subtype_id); size = H5Tget_size(subtype_id); H5Tclose(subtype_id); } #endif /* NO_EMULATE_VECTOR_DIMENSION */ if (class == H5T_INTEGER) { if (size == 1) *type_ptr = NC_BYTE; else if (size == 2) *type_ptr = NC_SHORT; else if (size == 4) *type_ptr = NC_INT; else { milog_message(MI_MSG_INTSIZE, size); exit(-1); } } else if (class == H5T_FLOAT) { if (size == 4) { *type_ptr = NC_FLOAT; } else if (size == 8) { *type_ptr = NC_DOUBLE; } else { milog_message(MI_MSG_FLTSIZE, size); exit(-1); } } else if (class == H5T_STRING) { *type_ptr = NC_CHAR; } else { milog_message(MI_MSG_TYPECLASS, class); exit(-1); } } if (class == H5T_STRING) { ndims = 0; } else { ndims = var->ndims; } if (dims_ptr != NULL && ndims != 0) { hdf_get_dimorder(file, dst_id, ndims, dims_ptr); } if (ndims_ptr != NULL) { *ndims_ptr = ndims; } if (natts_ptr != NULL) { int natts = H5Aget_num_attrs(dst_id); /* Emulate the signtype attribute for the image variable. */ if (!strcmp(var->name, MIimage) && H5Tget_class(var->ftyp_id) == H5T_INTEGER) { natts++; } *natts_ptr = natts; } if (varnm_ptr != NULL) { strcpy(varnm_ptr, var->name); } return (MI_NOERROR); } int hdf_dimrename(int fd, int dimid, const char *new_name) { milog_message(MI_MSG_NOTIMPL, "dimrename"); return (MI_NOERROR); } int hdf_dimid(int fd, const char *dimnm) { struct m2_file *file; struct m2_dim *dim; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((dim = hdf_dim_byname(file, dimnm)) == NULL) { return (MI_ERROR); } return (dim->id); } int hdf_diminq(int fd, int dimid, char *dimnm_ptr, long *len_ptr) { struct m2_file *file; struct m2_dim *dim; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((dim = hdf_dim_byid(file, dimid)) == NULL) { return (MI_ERROR); } /* Copy the dimension name, if appropriate. */ if (dimnm_ptr != NULL) { strcpy(dimnm_ptr, dim->name); } if (len_ptr != NULL) { *len_ptr = dim->length; } return (MI_NOERROR); } static void hdf_set_length(hid_t dst_id, const char *dimnm, unsigned long length) { hid_t att_id; hid_t aspc_id; aspc_id = H5Screate(H5S_SCALAR); if (aspc_id >= 0) { H5E_BEGIN_TRY { H5Adelete(dst_id, MI2_LENGTH); /* Create the attribute anew. */ att_id = H5Acreate2(dst_id, MI2_LENGTH, H5T_STD_U32LE, aspc_id, H5P_DEFAULT, H5P_DEFAULT); } H5E_END_TRY; if (att_id >= 0) { H5Awrite(att_id, H5T_NATIVE_LONG, (void *) &length); H5Aclose(att_id); } H5Sclose(aspc_id); } } int hdf_dimdef(int fd, const char *dimnm, long length) { int status = MI_ERROR; struct m2_file *file; struct m2_dim *dim; if ((file = hdf_id_check(fd)) != NULL && (dim = hdf_dim_add(file, dimnm, length)) != NULL) { struct m2_var *var = hdf_var_byname(file, dimnm); if (var != NULL) { hdf_set_length(var->dset_id, dimnm, length); } status = dim->id; } return (status); } /** Like hdf_attinq, this function must return one for success, not * zero! */ int hdf_attget(int fd, int varid, const char *attnm, void *value) { hid_t att_id; hid_t ftyp_id; hid_t mtyp_id; hid_t loc_id; int status = MI_ERROR; struct m2_file *file; struct m2_var *var; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (varid == NC_GLOBAL) { var = NULL; loc_id = file->grp_id; } else { if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } loc_id = var->dset_id; } /* Special case - emulate the signtype attribute. */ if (!strcmp(attnm, MIsigntype)) { if (H5Tget_class(var->ftyp_id) == H5T_INTEGER) { if (H5Tget_sign(var->ftyp_id) == H5T_SGN_NONE) { strcpy((char *) value, MI_UNSIGNED); } else { strcpy((char *) value, MI_SIGNED); } status = 1; /* 1 -> success */ } } else if (!strcmp(attnm, MI_FillValue)) { hid_t plist_id = H5Dget_create_plist(loc_id); if (plist_id >= 0) { if (H5Pget_fill_value(plist_id, var->mtyp_id, value) >= 0) { status = MI_NOERROR; } H5Pclose(plist_id); } } else { H5E_BEGIN_TRY { att_id = H5Aopen_name(loc_id, attnm); } H5E_END_TRY; if (att_id >= 0) { if ((ftyp_id = H5Aget_type(att_id)) >= 0) { mtyp_id = H5Tget_native_type(ftyp_id, H5T_DIR_ASCEND); if (mtyp_id >= 0) { if (H5Aread(att_id, mtyp_id, value) >= 0) { status = 1; /* 1 -> success */ } H5Tclose(mtyp_id); } H5Tclose(ftyp_id); } H5Aclose(att_id); } } return (status); } int hdf_attput(int fd, int varid, const char *attnm, nc_type val_typ, int val_len, const void *val_ptr) { hid_t att_id = -1; hid_t mtyp_id = -1; /* Memory type */ hid_t ftyp_id = -1; /* File type */ hid_t spc_id = -1; hid_t loc_id; int status = MI_ERROR; struct m2_file *file; struct m2_var *var; /* Ignore deprecated variables. */ if (varid == MI_ROOTVARIABLE_ID) { return (MI_NOERROR); /* Pretend all is OK. */ } /* Ignore deprecated attributes. */ if (!strcmp(attnm, MIparent) || !strcmp(attnm, MIchildren) || !strcmp(attnm, MIimagemin) || !strcmp(attnm, MIimagemax) || !strcmp(attnm, MI_FillValue)) { return (MI_NOERROR); /* Pretend we created it. */ } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (varid == NC_GLOBAL) { var = NULL; loc_id = file->grp_id; } else { if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } loc_id = var->dset_id; } if (!strcmp(attnm, MIsigntype)) { /* Emulate 'signtype' */ int new_signed; /* Need to recreate dataset with new type. Sigh. */ if (!strncmp(val_ptr, MI_SIGNED, 8)) { new_signed = 1; } else if (!strncmp(val_ptr, MI_UNSIGNED, 8)) { new_signed = 0; } else { return (MI_ERROR); } if ((H5Tget_sign(var->ftyp_id) == H5T_SGN_NONE && new_signed) || (H5Tget_sign(var->ftyp_id) == H5T_SGN_2 && !new_signed)) { hid_t new_type_id; hid_t new_dset_id; hid_t new_plst_id; char temp[128]; unsigned int i; sprintf(temp, "junkXXXX"); new_type_id = H5Tcopy(var->ftyp_id); if (new_type_id < 0) { milog_message(MI_MSG_SNH); } if (H5Tset_sign(new_type_id, (new_signed) ? H5T_SGN_2 : H5T_SGN_NONE) < 0) { milog_message(MI_MSG_SNH); } new_plst_id = H5Dget_create_plist(var->dset_id); H5Pset_attr_phase_change( new_plst_id , 0 , 0); new_dset_id = H5Dcreate2(file->grp_id, temp, new_type_id, var->fspc_id, H5P_DEFAULT, new_plst_id, H5P_DEFAULT); /* Iterate over all attributes, copying from old to new. */ i = 0; H5Aiterate1(var->dset_id, &i, hdf_copy_attr, (void *) &new_dset_id); H5Dclose(var->dset_id); H5Tclose(var->ftyp_id); H5Tclose(var->mtyp_id); H5Tclose(new_type_id); H5Pclose(new_plst_id); H5Sclose(var->fspc_id); if (H5Gunlink(fd, var->path) < 0) { milog_message(MI_MSG_SNH); } if (H5Gmove2(file->grp_id, temp, fd, var->path) < 0) { milog_message(MI_MSG_SNH); } var->dset_id = new_dset_id; var->ftyp_id = H5Dget_type(var->dset_id); var->mtyp_id = H5Tget_native_type(var->ftyp_id, H5T_DIR_ASCEND); var->fspc_id = H5Dget_space(var->dset_id); } return (MI_NOERROR); } if (val_typ == NC_CHAR) { ftyp_id = H5Tcopy(H5T_C_S1); H5Tset_size(ftyp_id, val_len); mtyp_id = H5Tcopy(ftyp_id); spc_id = H5Screate(H5S_SCALAR); } else { switch (val_typ) { case NC_BYTE: mtyp_id = H5T_NATIVE_UCHAR; ftyp_id = H5T_STD_U8LE; break; case NC_SHORT: mtyp_id = H5T_NATIVE_USHORT; ftyp_id = H5T_STD_U16LE; break; case NC_INT: mtyp_id = H5T_NATIVE_UINT; ftyp_id = H5T_STD_U32LE; break; case NC_FLOAT: mtyp_id = H5T_NATIVE_FLOAT; ftyp_id = H5T_IEEE_F32LE; break; case NC_DOUBLE: mtyp_id = H5T_NATIVE_DOUBLE; ftyp_id = H5T_IEEE_F64LE; break; default: milog_message(MI_MSG_BADTYPE, val_typ); return (MI_ERROR); } mtyp_id = H5Tcopy(mtyp_id); ftyp_id = H5Tcopy(ftyp_id); if (val_len == 1) { spc_id = H5Screate(H5S_SCALAR); } else { hsize_t temp_size = val_len; spc_id = H5Screate_simple(1, &temp_size, NULL); } } /* If the attribute already exists, delete it. It is not possible * to change the size of an existing attribute. */ H5E_BEGIN_TRY { H5Adelete(loc_id, attnm); /* Create the attribute anew. */ } H5E_END_TRY; att_id = H5Acreate2(loc_id, attnm, ftyp_id, spc_id, H5P_DEFAULT, H5P_DEFAULT); if (att_id < 0) goto cleanup; /* Save the value. */ status = H5Awrite(att_id, mtyp_id, val_ptr); if (status >= 0) { status = MI_NOERROR; } cleanup: if (spc_id >= 0) H5Sclose(spc_id); if (ftyp_id >= 0) H5Tclose(ftyp_id); if (mtyp_id >= 0) H5Tclose(mtyp_id); if (att_id >= 0) H5Aclose(att_id); return (status); } /** This function simply sweeps through all of the dimensions defined * in a file, and if a dimension variable has not yet been saved to the * file, it creates one here. */ static void hdf_dim_commit(int fd) { struct m2_file *file; struct m2_dim *dim; int i; if ((file = hdf_id_check(fd)) != NULL && file->wr_ok) { for (i = 0; i < file->ndims; i++) { if ((dim = hdf_dim_byid(file, i)) != NULL && !dim->is_fake) { if (hdf_var_byname(file, dim->name) == NULL) { hdf_vardef(fd, dim->name, NC_INT, 0, NULL); } } } } } /** This function is called when the "definition" phase of NetCDF file * creation is completed. The HDF5 library really doesn't need this, * since the mode doesn't exist in HDF5. But this is a convenient * time to make certain all of the dimension variables have actually * been defined. */ void hdf_enddef(int fd) { hdf_dim_commit(fd); /* Make sure all dimensions were saved. */ } /** This function provides the HDF5 emulation of the function ncvardef */ int hdf_vardef(int fd, const char *varnm, nc_type vartype, int ndims, const int *dimids) { int dst_id = -1; int typ_id = -1; int spc_id = -1; int prp_id = -1; int status = MI_ERROR; int i; long length; hsize_t dims[MAX_NC_DIMS]; hsize_t chkdims[MAX_NC_DIMS]; char varpath[NC_MAX_NAME]; struct m2_file *file; struct m2_var *var; struct m2_dim *dim; int chunk_length; int comp_level; /* Ignore deprecated variables */ if (!strcmp(varnm, MIrootvariable)) { return (MI_ROOTVARIABLE_ID); } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (hdf_path_from_name(file, varnm, varpath) < 0) { return (MI_ERROR); } prp_id = H5Pcreate(H5P_DATASET_CREATE); H5Pset_attr_phase_change (prp_id, 0, 0); if (prp_id < 0) { goto cleanup; } if (ndims == 0) { spc_id = H5Screate(H5S_SCALAR); /* For any scalar datasets, use compact dataset layout to * minimize file overhead. */ /*VF: unfortunately this limits attribute size!*/ H5Pset_layout( prp_id, H5D_CONTIGUOUS ); } else { for (i = 0; i < ndims; i++) { status = hdf_diminq(fd, dimids[i], NULL, &length); if (status < 0) { return (status); } dims[i] = length; } spc_id = H5Screate_simple(ndims, dims, NULL); if (file->comp_type == MI2_COMP_UNKNOWN) { comp_level = miget_cfg_int(MICFG_COMPRESS); } else { if (file->comp_type == MI2_COMP_ZLIB) { comp_level = file->comp_param; } else { comp_level = 0; } } if (comp_level != 0) { /* If compression is specified, chunking must be enabled. */ if (file->chunk_type == MI2_CHUNK_UNKNOWN) { /* read from minc config file or environment variable */ chunk_length = miget_cfg_int(MICFG_CHUNKING); } else if( file->chunk_type == MI2_CHUNK_OFF ) { chunk_length = 0; } else { chunk_length = file->chunk_param; if( chunk_length < MI2_CHUNK_MIN_SIZE ) { chunk_length = MI2_CHUNK_MIN_SIZE; fprintf(stdout, "Warning: using chunk size of %d\n", MI2_CHUNK_MIN_SIZE ); } } /* If nothing good was found for the chunking, use chunking that matches the hyperslab that fits into the work buffer. This seems to be the optimal chunking performance-wise. */ if (chunk_length <= 0 ) { chunk_length = 0; } /* This mimics the way the buffer is filled in MI_var_loop so that a good (optimal) chunking can be set from the dimensions filling up the buffer. This seems pretty optimal in terms of speed and gives a very good compression ratio, often better than gzip. */ hsize_t val = 1; int unit_size = nctypelen( vartype ); for( i = ndims-1; i >= 0; i-- ) { if( MI_MAX_VAR_BUFFER_SIZE > dims[i] * val * unit_size ) { chkdims[i] = dims[i]; } else { chkdims[i] = MIN( dims[i], (hsize_t)( MI_MAX_VAR_BUFFER_SIZE / ( val * unit_size ) ) ); } val *= chkdims[i]; } for (i = 0; i < ndims; i++) { if( chunk_length >= MI2_CHUNK_MIN_SIZE ) { if( chkdims[i] > (hsize_t)chunk_length ) { chkdims[i] = chunk_length; } } } H5Pset_deflate(prp_id, comp_level); H5Pset_chunk(prp_id, ndims, chkdims); } } if (spc_id < 0) { goto cleanup; } typ_id = H5Tcopy(nc_to_hdf5_type(vartype, TRUE)); if (typ_id < 0) { goto cleanup; } H5E_BEGIN_TRY { dst_id = H5Dcreate2(fd, varpath, typ_id, spc_id, H5P_DEFAULT, prp_id, H5P_DEFAULT); } H5E_END_TRY; if (dst_id < 0) { milog_message(MI_MSG_OPENDSET, varnm); goto cleanup; } /* If this is a dimension variable, we have to define the length * attribute now. */ if ((dim = hdf_dim_byname(file, varnm)) != NULL) { hdf_set_length(dst_id, varnm, dim->length); } /* Add the dimension order information. */ hdf_put_dimorder(file, dst_id, ndims, dimids); /* bert - Closing the dataset here is necessary for HDF5 1.6.5. * Without this we get nasty errors caused by re-opening the * dataset in hdf_var_add(). The conclusion seems to be that * HDF5 1.6.5 does not allow re-opening a newly created dataset. */ H5Dclose(dst_id); dst_id = -1; /* Add the variable to the internal table. */ var = hdf_var_add(file, varnm, varpath, ndims, dims); if (var == NULL) { goto cleanup; } status = var->id; cleanup: if (prp_id >= 0) { H5Pclose(prp_id); } if (dst_id >= 0) { H5Dclose(dst_id); } if (typ_id >= 0) { H5Tclose(typ_id); } if (spc_id >= 0) { H5Sclose(spc_id); } return (status); } /** */ int hdf_var_declare(int fd, char *varnm, char *varpath, int ndims, hsize_t *sizes) { struct m2_file *file; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } hdf_var_add(file, varnm, varpath, ndims, sizes); return (MI_NOERROR); } int hdf_varget(int fd, int varid, const long *start_ptr, const long *length_ptr, void *val_ptr) { int status = MI_ERROR; int dst_id = -1; int typ_id = -1; int fspc_id = -1; int mspc_id = -1; int i; int ndims; hsize_t fstart[MAX_VAR_DIMS]; hsize_t count[MAX_VAR_DIMS]; struct m2_file *file; struct m2_var *var; // fprintf(stderr, "HDF varget\n"); /* Emulate the obsolete "rootvariable" */ if (varid == MI_ROOTVARIABLE_ID) { *((int *)val_ptr) = 0; return (MI_NOERROR); } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } dst_id = var->dset_id; typ_id = var->mtyp_id; fspc_id = var->fspc_id; ndims = var->ndims; #ifndef NO_EMULATE_VECTOR_DIMENSION /* If it is a compound variable, we cannot actually pick and choose the * length of the hyperslab along the emulated vector_dimension, so we * just reduce the dimensionality by 1 */ if (var->is_cmpd) { struct m2_dim *dim = hdf_dim_byname(file, MIvector_dimension); if (dim != NULL && length_ptr[ndims - 1] != dim->length) { fprintf(stderr, "ERROR: can't read subset of emulated vector dimension\n"); return (MI_ERROR); } ndims--; } #endif /* NO_EMULATE_VECTOR_DIMENSION */ if (ndims == 0) { mspc_id = H5Screate(H5S_SCALAR); } else { for (i = 0; i < ndims; i++) { fstart[i] = start_ptr[i]; count[i] = length_ptr[i]; } status = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, fstart, NULL, count, NULL); if (status < 0) { milog_message(MI_MSG_SNH); goto cleanup; } mspc_id = H5Screate_simple(ndims, count, 0); if (mspc_id < 0) { milog_message(MI_MSG_SNH); goto cleanup; } } status = H5Dread(dst_id, typ_id, mspc_id, fspc_id, H5P_DEFAULT, val_ptr); if (status < 0) { milog_message(MI_MSG_READDSET, var->path); } cleanup: // fprintf(stderr, "cleanup - dst_id: %d fspc_id: %d mspc_id: %d\n", dst_id, fspc_id, mspc_id); // I think this should be needed but am not sure with HDF 1.8.x // Andrew Janke - 3/3/2010 // if(fspc_id >= 0) // H5Sclose(fspc_id); if (mspc_id >= 0) H5Sclose(mspc_id); // fprintf(stderr, "cleanup - done\n"); return (status); } int hdf_varputg(int fd, int varid, const long *start, const long *edges, const long *stride, const long *map, const void *value) { int status = MI_ERROR; /* Assume guilty */ int maxidim; /* maximum dimensional index */ int idim; hsize_t *mystart = NULL; hsize_t *myedges; hsize_t *iocount; /* count vector */ hsize_t *stop; /* stop indexes */ hsize_t *length; /* edge lengths in bytes */ ptrdiff_t *mystride; ptrdiff_t *mymap; struct m2_var *varp; struct m2_file *file; int dst_id = -1; int typ_id = -1; int fspc_id = -1; int mspc_id = -1; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((varp = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } dst_id = varp->dset_id; typ_id = varp->mtyp_id; fspc_id = varp->fspc_id; mspc_id = H5Scopy(fspc_id); maxidim = (int) varp->ndims - 1; if (maxidim < 0) { /* * The variable is a scalar! */ milog_message(MI_MSG_SNH); goto cleanup; } /* * Verify stride argument. */ if (stride != NULL) { for (idim = 0; idim <= maxidim; idim++) { /* cast needed for braindead systems with signed size_t */ if (stride[idim] == 0) { goto cleanup; } } } mystart = (hsize_t *)calloc(varp->ndims * 7, sizeof(hsize_t)); if (mystart == NULL) { goto cleanup; } myedges = (mystart + varp->ndims); iocount = myedges + varp->ndims; stop = iocount + varp->ndims; length = stop + varp->ndims; mystride = (ptrdiff_t *)(length + varp->ndims); mymap = mystride + varp->ndims; /* * Initialize I/O parameters. */ for (idim = maxidim; idim >= 0; --idim) { mystart[idim] = start != NULL ? start[idim] : 0; /* Hmm - should this even _BE_ here? */ if (edges[idim] == 0) { status = MI_NOERROR; /* nothing to do here */ goto cleanup; } if (edges != NULL) { myedges[idim] = edges[idim]; } else { myedges[idim] = varp->dims[idim] - mystart[idim]; } if (stride != NULL) { mystride[idim] = stride[idim]; } else { mystride[idim] = 1; } if (map != NULL) { mymap[idim] = map[idim]; } else if (idim == maxidim) { mymap[idim] = 1; } else { mymap[idim] = mymap[idim+1] * (ptrdiff_t) myedges[idim+1]; } iocount[idim] = 1; length[idim] = mymap[idim] * myedges[idim]; stop[idim] = mystart[idim] + myedges[idim] * mystride[idim]; } /* * Check start, edges */ for (idim = 0; idim < maxidim; idim++) { if (mystart[idim] >= varp->dims[idim]) { status = MI_ERROR; goto cleanup; } if (mystart[idim] + myedges[idim] > varp->dims[idim]) { status = MI_ERROR; goto cleanup; } } /* * As an optimization, adjust I/O parameters when the fastest * dimension has unity stride both externally and internally. * In this case, the user could have called a simpler routine * (i.e. ncvarnc_put_vara_uchar() */ if (mystride[maxidim] == 1 && mymap[maxidim] == 1) { iocount[maxidim] = myedges[maxidim]; mystride[maxidim] = (ptrdiff_t) myedges[maxidim]; mymap[maxidim] = (ptrdiff_t) length[maxidim]; } mspc_id = H5Screate_simple(varp->ndims, iocount, NULL); /* * Perform I/O. Exit when done. */ for (;;) { status = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, mystart, NULL, iocount, NULL); if (status < 0) { milog_message(MI_MSG_SNH); goto cleanup; } status = H5Dwrite(dst_id, typ_id, mspc_id, fspc_id, H5P_DEFAULT, value); if (status < 0) { milog_message(MI_MSG_WRITEDSET, varp->path); goto cleanup; } /* * The following code permutes through the variable's * external start-index space and it's internal address * space. At the UPC, this algorithm is commonly * called "odometer code". */ idim = maxidim; carry: value = ((const char *)value) + mymap[idim]; mystart[idim] += mystride[idim]; if (mystart[idim] == stop[idim]) { mystart[idim] = start[idim]; value = ((const char *)value) - length[idim]; if (--idim < 0) break; /* normal return */ goto carry; } } /* I/O loop */ cleanup: if (mystart != NULL) { free(mystart); } if (mspc_id >= 0) { H5Sclose(mspc_id); } return (status); } int hdf_vargetg(int fd, int varid, const long *start, const long *edges, const long *stride, const long *map, void *value) { int status = MI_NOERROR; struct m2_var *varp; int maxidim; /* maximum dimensional index */ struct m2_file *file; int idim; long *mystart = NULL; long *myedges; long *iocount; /* count vector */ long *stop; /* stop indexes */ long *length; /* edge lengths in bytes */ long *mystride; long *mymap; file = hdf_id_check(fd); if (file == NULL) { return (MI_ERROR); } varp = hdf_var_byid(file, varid); if (varp == NULL) { return (MI_ERROR); } maxidim = (int) varp->ndims - 1; if (maxidim < 0) { /* * The variable is a scalar! */ milog_message(MI_MSG_SNH); return (MI_ERROR); } /* * Verify stride argument. */ if (stride != NULL) { for (idim = 0; idim <= maxidim; idim++) { if (stride[idim] == 0) { return MI_ERROR; } } } mystart = (long *)calloc(varp->ndims * 7, sizeof(long)); if (mystart == NULL) { return (MI_ERROR); } myedges = mystart + varp->ndims; iocount = myedges + varp->ndims; stop = iocount + varp->ndims; length = stop + varp->ndims; mystride = length + varp->ndims; mymap = mystride + varp->ndims; /* * Initialize I/O parameters. */ for (idim = maxidim; idim >= 0; --idim) { mystart[idim] = start != NULL ? start[idim] : 0; /* Hmm - should this even _BE_ here? */ if (edges[idim] == 0) { status = MI_NOERROR; /* nothing to do here */ goto done; } if (edges != NULL) { myedges[idim] = edges[idim]; } else { myedges[idim] = varp->dims[idim] - mystart[idim]; } if (stride != NULL) { mystride[idim] = stride[idim]; } else { mystride[idim] = 1; } if (map != NULL) { mymap[idim] = map[idim]; } else if (idim == maxidim) { mymap[idim] = 1; } else { mymap[idim] = mymap[idim+1] * (ptrdiff_t) myedges[idim+1]; } iocount[idim] = 1; length[idim] = mymap[idim] * myedges[idim]; stop[idim] = mystart[idim] + myedges[idim] * mystride[idim]; } /* * Check start, edges */ for (idim = 0; idim < maxidim; idim++) { if ((hsize_t) mystart[idim] >= varp->dims[idim]) { status = MI_ERROR; goto done; } if ((hsize_t) (mystart[idim] + myedges[idim]) > varp->dims[idim]) { status = MI_ERROR; goto done; } } /* * As an optimization, adjust I/O parameters when the fastest * dimension has unity stride both externally and internally. * In this case, the user could have called a simpler routine * (i.e. ncvarnc_put_vara_uchar() */ if (mystride[maxidim] == 1 && mymap[maxidim] == 1) { iocount[maxidim] = myedges[maxidim]; mystride[maxidim] = (ptrdiff_t) myedges[maxidim]; mymap[maxidim] = (ptrdiff_t) length[maxidim]; } /* * Perform I/O. Exit when done. */ for (;;) { int lstatus = hdf_varget(fd, varid, mystart, iocount, value); if (lstatus != MI_NOERROR && status == MI_NOERROR) { status = lstatus; } /* * The following code permutes through the variable's * external start-index space and it's internal address * space. At the UPC, this algorithm is commonly * called "odometer code". */ idim = maxidim; carry: value = ((char *)value) + mymap[idim]; mystart[idim] += mystride[idim]; if (mystart[idim] == stop[idim]) { mystart[idim] = start[idim]; value = ((char *)value) - length[idim]; if (--idim < 0) break; /* normal return */ goto carry; } } /* I/O loop */ done: free(mystart); return (status); } int hdf_varput(int fd, int varid, const long *start_ptr, const long *length_ptr, const void *val_ptr) { int status = MI_ERROR; int dst_id; int typ_id; int fspc_id; int mspc_id = -1; int i; int ndims; hsize_t fstart[MAX_VAR_DIMS]; hsize_t count[MAX_VAR_DIMS]; struct m2_file *file; struct m2_var *var; if (varid == MI_ROOTVARIABLE_ID) { return (MI_NOERROR); } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } dst_id = var->dset_id; typ_id = var->mtyp_id; fspc_id = var->fspc_id; ndims = var->ndims; if (ndims == 0) { mspc_id = H5Screate(H5S_SCALAR); } else { for (i = 0; i < ndims; i++) { fstart[i] = start_ptr[i]; count[i] = length_ptr[i]; } status = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, fstart, NULL, count, NULL); if (status < 0) { milog_message(MI_MSG_SNH); goto cleanup; } mspc_id = H5Screate_simple(ndims, count, NULL); if (mspc_id < 0) { milog_message(MI_MSG_SNH); goto cleanup; } } status = H5Dwrite(dst_id, typ_id, mspc_id, fspc_id, H5P_DEFAULT, val_ptr); if (status < 0) { milog_message(MI_MSG_WRITEDSET, var->path); goto cleanup; } cleanup: if (mspc_id >= 0) H5Sclose(mspc_id); return (status); } int hdf_varput1(int fd, int varid, const long *mindex_ptr, const void *val_ptr) { long length[MAX_VAR_DIMS]; int i; /* Rather than querying the number of dimensions, just fill the whole * darn thing with 1's. */ for (i = 0; i < MAX_VAR_DIMS; i++) { length[i] = 1; } return hdf_varput(fd, varid, mindex_ptr, length, val_ptr); } /** Emulates ncattdel(). Like many of the netCDF attribute functions, * success here is indicated by a return value of one. */ int hdf_attdel(int fd, int varid, const char *attnm) { hid_t loc_id; struct m2_file *file; struct m2_var *var; if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if (varid == NC_GLOBAL) { var = NULL; loc_id = file->grp_id; } else { if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } loc_id = var->dset_id; } H5E_BEGIN_TRY { H5Adelete(loc_id, attnm); } H5E_END_TRY; return 1; /* success */ } /* Get the current size of a variable. This is needed in HDF5 since * dimensions are associated with individual variable's dataspaces, * and can be independent, rather than set by a file-wide "dimension" * object. */ int hdf_varsize(int fd, int varid, long *size_ptr) { int i; hsize_t dims[MAX_VAR_DIMS]; struct m2_file *file; struct m2_var *var; if (varid == MI_ROOTVARIABLE_ID) { *size_ptr = 1; return (MI_NOERROR); } if ((file = hdf_id_check(fd)) == NULL) { return (MI_ERROR); } if ((var = hdf_var_byid(file, varid)) == NULL) { return (MI_ERROR); } if (var->ndims > MAX_VAR_DIMS) { milog_message(MI_MSG_TOOMANYDIMS, MAX_VAR_DIMS); exit(-1); } H5Sget_simple_extent_dims(var->fspc_id, dims, NULL); for (i = 0; i < var->ndims; i++) { size_ptr[i] = dims[i]; } return (MI_NOERROR); } herr_t hdf_copy_attr(hid_t in_id, const char *attr_name, void *op_data) { hid_t out_id = *((hid_t*) op_data); hid_t inatt_id = -1; hid_t outatt_id = -1; hid_t spc_id = -1; hid_t typ_id = -1; void *val_ptr = NULL; int status = MI_ERROR; if ((inatt_id = H5Aopen_name(in_id, attr_name)) < 0) goto cleanup; if ((spc_id = H5Aget_space(inatt_id)) < 0) goto cleanup; if ((typ_id = H5Aget_type(inatt_id)) < 0) goto cleanup; outatt_id = H5Acreate2(out_id, attr_name, typ_id, spc_id, H5P_DEFAULT, H5P_DEFAULT); if (outatt_id < 0) { /* This can happen if the attribute already exists. If it does, we * don't overwrite the existing value. */ status = MI_NOERROR; goto cleanup; } if ((val_ptr = malloc(hdf_size(spc_id, typ_id))) == NULL) goto cleanup; if (H5Aread(inatt_id, typ_id, val_ptr) < 0) goto cleanup; if (H5Awrite(outatt_id, typ_id, val_ptr) < 0) goto cleanup; status = MI_NOERROR; cleanup: if (val_ptr != NULL) free(val_ptr); if (spc_id >= 0) H5Sclose(spc_id); if (typ_id >= 0) H5Tclose(typ_id); if (inatt_id >= 0) H5Aclose(inatt_id); if (outatt_id >= 0) H5Aclose(outatt_id); return (status); } static int hdf_open_dsets(struct m2_file *file, hid_t grp_id, char *cpath, int is_dim) { hsize_t nobjs; hsize_t idx; char temp[NC_MAX_NAME]; char tpath[NC_MAX_NAME]; hid_t new_id; herr_t result; result = H5Gget_num_objs(grp_id, &nobjs); if (result < 0) { return (MI_ERROR); } for (idx = 0; idx < nobjs; idx++) { switch (H5Gget_objtype_by_idx(grp_id, idx)) { case H5G_GROUP: H5Gget_objname_by_idx(grp_id, idx, temp, sizeof(temp)); strcpy(tpath, cpath); strcat(tpath, temp); strcat(tpath, "/"); new_id = H5Gopen1(grp_id, temp); if (new_id >= 0) { hdf_open_dsets(file, new_id, tpath, is_dim); H5Gclose(new_id); } break; case H5G_DATASET: H5Gget_objname_by_idx(grp_id, idx, temp, sizeof(temp)); strcpy(tpath, cpath); strcat(tpath, temp); new_id = H5Dopen1(grp_id, temp); if (new_id >= 0) { hid_t spc_id; spc_id = H5Dget_space(new_id); if (spc_id < 0) { milog_message(MI_MSG_SNH); } else { hsize_t dims[MAX_NC_DIMS]; int ndims; hdf_get_diminfo(new_id, &ndims, dims); hdf_var_add(file, temp, tpath, ndims, dims); } if (is_dim) { long length; hid_t att_id; att_id = H5Aopen_name(new_id, MI2_LENGTH); if (att_id > 0) { H5Aread(att_id, H5T_NATIVE_LONG, (void *) &length); H5Aclose(att_id); } else { milog_message(MI_MSG_SNH); length = 0; } hdf_dim_add(file, temp, length); } H5Dclose(new_id); } break; default: break; } } return (MI_NOERROR); } int hdf_open(const char *path, int mode) { hid_t fd; hid_t grp_id; hid_t dset_id; struct m2_file *file; hsize_t dims[MAX_NC_DIMS]; int ndims; struct m2_var *var; H5E_BEGIN_TRY { #if HDF5_MMAP_TEST if (mode & 0x8000) { hid_t prp_id; prp_id = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_mmap(prp_id, 8192, 1); fd = H5Fopen(path, mode & 0x7FFF, prp_id); H5Pclose(prp_id); } else { fd = H5Fopen(path, mode, H5P_DEFAULT); } #else fd = H5Fopen(path, mode, H5P_DEFAULT); #endif } H5E_END_TRY; if (fd < 0) { return (MI_ERROR); } file = hdf_id_add(fd); /* Add it to the list */ file->wr_ok = (mode & H5F_ACC_RDWR) != 0; /* Open the image variables. */ H5E_BEGIN_TRY { dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image"); if (dset_id >= 0) { hid_t type_id; int is_compound = 0; hdf_get_diminfo(dset_id, &ndims, dims); #ifndef NO_EMULATE_VECTOR_DIMENSION /* See if a vector_dimension needs to be emulated. */ type_id = H5Dget_type(dset_id); if (type_id >= 0) { if (H5Tget_class(type_id) == H5T_COMPOUND) { /* OK, it's compound type. */ struct m2_dim *dim = hdf_dim_add(file, MIvector_dimension, H5Tget_nmembers(type_id)); dim->is_fake = 1; dims[ndims++] = H5Tget_nmembers(type_id); is_compound = 1; } H5Tclose(type_id); } #endif /* NO_EMULATE_VECTOR_DIMENSION */ var = hdf_var_add(file, MIimage, "/minc-2.0/image/0/image", ndims, dims); var->is_cmpd = is_compound; H5Dclose(dset_id); } dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-min"); if (dset_id >= 0) { hdf_get_diminfo(dset_id, &ndims, dims); hdf_var_add(file, MIimagemin, "/minc-2.0/image/0/image-min", ndims, dims); H5Dclose(dset_id); } dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-max"); if (dset_id >= 0) { hdf_get_diminfo(dset_id, &ndims, dims); hdf_var_add(file, MIimagemax, "/minc-2.0/image/0/image-max", ndims, dims); H5Dclose(dset_id); } } H5E_END_TRY; /* Open all of the datasets in the "dimensions" category. */ grp_id = H5Gopen2(fd, "/minc-2.0/dimensions", H5P_DEFAULT); hdf_open_dsets(file, grp_id, "/minc-2.0/dimensions/", 1); H5Gclose(grp_id); /* Open all of the datasets in the "info" category. */ grp_id = H5Gopen2(fd, "/minc-2.0/info", H5P_DEFAULT); hdf_open_dsets(file, grp_id, "/minc-2.0/info/", 0); H5Gclose(grp_id); return (fd); } /** Create an HDF5 file. */ int hdf_create(const char *path, int cmode, struct mi2opts *opts_ptr) { hid_t grp_id; hid_t fd; hid_t tmp_id; struct m2_file *file; hid_t hdf_gpid; hid_t fpid; fpid = H5Pcreate (H5P_FILE_ACCESS); /* Convert the MINC (NetCDF) mode to a HDF5 mode. */ if (cmode & NC_NOCLOBBER) { cmode = H5F_ACC_EXCL; } else { cmode = H5F_ACC_TRUNC; } /*VF use all the features of new HDF5 1.8*/ H5Pset_libver_bounds (fpid, H5F_LIBVER_18, H5F_LIBVER_18); H5E_BEGIN_TRY { fd = H5Fcreate(path, cmode, H5P_DEFAULT, fpid); } H5E_END_TRY; if (fd < 0) { fprintf(stderr, "Error creating HDF file '%s' with mode '%x', result %d\n", path, cmode, fd); H5Eprint1(stderr); return (MI_ERROR); } hdf_gpid = H5Pcreate (H5P_GROUP_CREATE); H5Pset_attr_phase_change (hdf_gpid, 0, 0); /* Create the default groups. * Should we use a non-zero value for size_hint (parameter 3)??? */ if ((grp_id = H5Gcreate2(fd, MI2_GRPNAME, H5P_DEFAULT, hdf_gpid, H5P_DEFAULT)) < 0) { fprintf(stderr, "Error creating groups on line %d\n", __LINE__); H5Eprint1(stderr); return (MI_ERROR); } if ((tmp_id = H5Gcreate2(grp_id, "dimensions", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT)) < 0) { fprintf(stderr, "Error creating groups on line %d\n", __LINE__); H5Eprint1(stderr); return (MI_ERROR); } H5Gclose(tmp_id); if ((tmp_id = H5Gcreate2(grp_id, "info", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT)) < 0) { fprintf(stderr, "Error creating groups on line %d\n", __LINE__); H5Eprint1(stderr); return (MI_ERROR); } H5Gclose(tmp_id); if ((tmp_id = H5Gcreate2(grp_id, "image", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT)) < 0) { fprintf(stderr, "Error creating groups on line %d\n", __LINE__); H5Eprint1(stderr); return (MI_ERROR); } H5Gclose(tmp_id); if ((tmp_id = H5Gcreate2(grp_id, "image/0", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT)) < 0) { fprintf(stderr, "Error creating groups on line %d\n", __LINE__); H5Eprint1(stderr); return (MI_ERROR); } H5Pclose( hdf_gpid ); H5Gclose(tmp_id); H5Gclose(grp_id); file = hdf_id_add(fd); /* Add it to the list */ if (file == NULL) { fprintf(stderr, "Error adding ID to list.\n"); H5Eprint1(stderr); return (MI_ERROR); /* Should not happen?? */ } file->wr_ok = 1; if (opts_ptr != NULL && opts_ptr->struct_version == MI2_OPTS_V1) { file->comp_type = opts_ptr->comp_type; file->comp_param = opts_ptr->comp_param; file->chunk_type = opts_ptr->chunk_type; file->chunk_param = opts_ptr->chunk_param; } return ((int) fd); } int hdf_close(int fd) { hdf_dim_commit(fd); /* Make sure all dimensions were saved. */ hdf_id_del(fd); /* Delete it from the list. */ H5Fclose(fd); return (MI_NOERROR); } /* * Returns one (1) if the argument is the path name of an existing HDF5 * file, or zero if the file does not exist or is not in the right format. */ int hdf_access(const char *path) { htri_t status; H5E_BEGIN_TRY { status = H5Fis_hdf5(path); } H5E_END_TRY; return (status > 0); /* Return non-zero if success */ } #endif /* MINC2 defined */ libminc-libminc-2-3-00/libsrc/hdf_convenience.h000066400000000000000000000044501257462267400214100ustar00rootroot00000000000000/* hdf_convenience.h */ /* DUMMY rootvariable ID */ #define MI_ROOTVARIABLE_ID (NC_MAX_VARS + 1) /* Impossible value */ /* HDF functions for compatibility layer. */ extern int hdf_varname(int fd, int varid, char *varnm); extern int hdf_varid(int fd, const char *varnm); extern int hdf_attget(int fd, int varid, const char *attnm, void *value); extern int hdf_attput(int fd, int varid, const char *attnm, nc_type val_typ, int val_len, const void *val_ptr); extern int hdf_attdel(int fd, int varid, const char *attnm); extern int hdf_attinq(int fd, int varid, const char *attnm, nc_type *type_ptr, int *length_ptr); extern int hdf_attname(int fd, int varid, int attnum, char *name); extern int hdf_inquire(int fd, int *ndims_ptr, int *nvars_ptr, int *natts_ptr, int *unlimdim_ptr); extern int hdf_varinq(int fd, int varid, char *varnm_ptr, nc_type *type_ptr, int *ndims_ptr, int *dims_ptr, int *natts_ptr); extern int hdf_dimid(int fd, const char *dimnm); extern int hdf_diminq(int fd, int dimid, char *dimnm_ptr, long *len_ptr); extern int hdf_dimdef(int fd, const char *dimnm, long length); extern void hdf_enddef(int fd); extern int hdf_vardef(int fd, const char *varnm, nc_type vartype, int ndims, const int *dimids); extern int hdf_varget(int fd, int varid, const long *start_ptr, const long *count_ptr, void *val_ptr); extern int hdf_vargetg(int fd, int varid, const long *startp, const long *countp, const long *stridep, const long *imapp, void *valp); extern int hdf_varput(int fd, int varid, const long *start_ptr, const long *count_ptr, const void *val_ptr); extern int hdf_varput1(int fd, int varid, const long *mindex_ptr, const void *val_ptr); extern int hdf_varputg(int fd, int varid, const long *startp, const long *countp, const long *stridep, const long *imapp, const void *valp); extern int hdf_varsize(int fd, int varid, long *size_ptr); extern int hdf_dimrename(int fd, int dimid, const char *new_name); extern herr_t hdf_copy_attr(hid_t in_id, const char *attr_name, void *op_data); extern int hdf_open(const char *path, int mode); extern int hdf_create(const char *path, int mode, struct mi2opts *opts_ptr); extern int hdf_close(int fd); extern int hdf_access(const char *path); libminc-libminc-2-3-00/libsrc/image_conversion.c000066400000000000000000001714031257462267400216200ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : image_conversion.c @DESCRIPTION: File of functions to manipulate image conversion variables (icv). These variables allow conversion of netcdf variables (the MINC image variable, in particular) to a form more convenient for a program. @METHOD : Routines included in this file : public : miicv_create miicv_free miicv_setdbl miicv_setint miicv_setlong miicv_setstr miicv_inqdbl miicv_inqint miicv_inqlong miicv_inqstr miicv_ndattach miicv_detach miicv_get miicv_put semiprivate : MI_icv_chkid private : MI_icv_get_type MI_icv_get_vrange MI_get_default_range MI_icv_get_norm MI_icv_access MI_icv_zero_buffer MI_icv_coords_tovar MI_icv_calc_scale @CREATED : July 27, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: image_conversion.c,v $ * Revision 6.17 2010-03-02 12:23:14 rotor * * ported HDF calls to 1.8.x * * Makefile.am: updated for minccmp * * Revision 6.16 2008/01/17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.15 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.14 2007/12/12 20:55:26 rotor * * added a bunch of bug fixes from Claude. * * Revision 6.13 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.12 2004/10/15 13:45:28 bert * Minor changes for Windows compatibility * * Revision 6.11 2004/04/27 15:40:22 bert * Revised logging/error handling * * Revision 6.10 2003/09/18 16:17:00 bert * Correctly cast double to nc_type * * Revision 6.9 2001/11/28 15:38:07 neelin * Removed limit on number of icvs that can exist at one time. * * Revision 6.8 2001/11/13 21:00:24 neelin * Modified icv scaling calculations for no normalization. When the icv * type is double, normalization is always done, regardless of the * normalization setting. When the external type is floating point, * normalization to the slice real range is done (essentially a valid * range scaling, but where the valid range for a float is the slice real * range). * * Revision 6.7 2001/11/13 14:15:17 neelin * Added functions miget_image_range and mivar_exists * * Revision 6.6 2001/08/20 13:16:53 neelin * Removed extraneous variables from MI_icv_get_vrange. * * Revision 6.5 2001/08/16 19:24:11 neelin * Fixes to the code handling valid_range values. * * Revision 6.4 2001/08/16 16:41:31 neelin * Added library functions to handle reading of datatype, sign and valid range, * plus writing of valid range and setting of default ranges. These functions * properly handle differences between valid_range type and image type. Such * difference can cause valid data to appear as invalid when double to float * conversion causes rounding in the wrong direction (out of range). * Modified voxel_loop, volume_io and programs to use these functions. * * Revision 6.3 2001/08/16 13:32:18 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.2 2001/04/17 18:40:12 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:07 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.3 1997/04/21 17:32:04 neelin * Fixed calculation of scale for icv so that values are not re-scaled * from real values to file floating-point values. * * Revision 3.2 1997/04/10 19:22:18 neelin * Removed redefinition of NULL and added pointer casts in appropriate places. * * Revision 3.1 1997/04/10 18:14:50 neelin * Fixed handling of invalid data when icv scale is zero. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.3 1995/02/08 19:14:44 neelin * More changes for irix 5 lint. * * Revision 2.2 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.1 1994/12/09 09:12:30 neelin * Added test in miicv_detach to make sure that icv is attached before * detaching it. * * Revision 2.0 94/09/28 10:37:55 neelin * Release of minc version 0.2 * * Revision 1.18 94/09/28 10:37:06 neelin * Pre-release * * Revision 1.17 93/08/11 12:59:31 neelin * We need only increment the chunk pointer (see previous fix) if we are * not doing dimension conversion (dimension conversion handles the * offsets itself). * * Revision 1.16 93/08/11 11:49:36 neelin * Added RCS logging in source. * Fixed bug in MI_icv_access so that pointer to values buffer is incremented * as we loop through the chunks. This affected calls to miicv_get/put that * had MIimagemax/min varying over the values read in one call (ie. reading * or writing a volume with MIimagemax/min varying over slices will give * incorrect results if the volume is read with one call). * January 22, 1993 (P.N.) - Modified handling of icv properties with miicv_set. Removed routine miicv_set. Use routines miicv_setdbl, miicv_setint, miicv_setlong, miicv_setstr instead (this gives type checking at compile time). @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include "type_limits.h" /* Private functions */ PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid); PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid); PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign); PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid); PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[], long count[], void *values); PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values); PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp, long icv_start[], long icv_count[], long var_start[], long var_count[]); PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[]); /* Array of pointers to image conversion structures */ static int minc_icv_list_nalloc = 0; static mi_icv_type **minc_icv_list = NULL; /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_create @INPUT : (none) @OUTPUT : (none) @RETURNS : icv id or MI_ERROR when an error occurs @DESCRIPTION: Creates an image conversion variable (icv) and returns a handle to it. @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_create() { int new_icv; /* Id of newly created icv */ mi_icv_type *icvp; /* Pointer to new icv structure */ int idim; int new_nalloc; MI_SAVE_ROUTINE_NAME("miicv_create"); /* Look for free slot */ for (new_icv=0; new_icv=minc_icv_list_nalloc) { /* How much space will be needed? */ new_nalloc = minc_icv_list_nalloc + MI_MAX_NUM_ICV; /* Check for first allocation */ if (minc_icv_list_nalloc == 0) { minc_icv_list = MALLOC(new_nalloc, mi_icv_type *); } else { minc_icv_list = REALLOC(minc_icv_list, new_nalloc, mi_icv_type *); } /* Check that the allocation was successful */ if (minc_icv_list == NULL) { MI_LOG_SYS_ERROR1("miicv_create"); MI_RETURN(MI_ERROR); } /* Put in NULL pointers */ for (new_icv=minc_icv_list_nalloc; new_icvdo_scale = FALSE; icvp->do_dimconvert = FALSE; icvp->do_fillvalue = FALSE; icvp->fill_valid_min = -DBL_MAX; icvp->fill_valid_max = DBL_MAX; /* User defaults */ icvp->user_type = NC_SHORT; icvp->user_typelen = nctypelen(icvp->user_type); icvp->user_sign = MI_PRIV_SIGNED; icvp->user_do_range = TRUE; icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); icvp->user_do_norm = FALSE; icvp->user_user_norm = FALSE; icvp->user_maxvar = strdup(MIimagemax); icvp->user_minvar = strdup(MIimagemin); icvp->user_imgmax = MI_DEFAULT_MAX; icvp->user_imgmin = MI_DEFAULT_MIN; icvp->user_do_dimconv = FALSE; icvp->user_do_scalar = TRUE; icvp->user_xdim_dir = MI_ICV_POSITIVE; icvp->user_ydim_dir = MI_ICV_POSITIVE; icvp->user_zdim_dir = MI_ICV_POSITIVE; icvp->user_num_imgdims = 2; icvp->user_keep_aspect = TRUE; icvp->user_do_fillvalue = FALSE; icvp->user_fillvalue = -DBL_MAX; for (idim=0; idimuser_dim_size[idim]=MI_ICV_ANYSIZE; } /* Variable values */ icvp->cdfid = MI_ERROR; /* Set so that we can recognise an */ icvp->varid = MI_ERROR; /* unattached icv */ /* Values that can be read by user */ icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; for (idim=0; idimderv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } MI_RETURN(new_icv); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_free @INPUT : icvid @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Frees the image conversion variable (icv) @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_free(int icvid) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_free"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Detach the icv if it is attached */ if (icvp->cdfid != MI_ERROR) { if (miicv_detach(icvid) < 0) { MI_RETURN(MI_ERROR); } } /* Free anything allocated at creation time */ FREE(icvp->user_maxvar); FREE(icvp->user_minvar); /* Free the structure */ FREE(icvp); minc_icv_list[icvid]=NULL; /* Delete entire structure if no longer in use. */ int new_icv; for (new_icv=0; new_icv=minc_icv_list_nalloc) { FREE(minc_icv_list); minc_icv_list_nalloc=0; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setdbl @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given double value Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setdbl(int icvid, int icv_property, double value) { int ival, idim; mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_setdbl"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is not attached to a file */ if (icvp->cdfid != MI_ERROR) { milog_message(MI_MSG_ICVATTACHED); MI_RETURN(MI_ERROR); } /* Set the property */ switch (icv_property) { case MI_ICV_TYPE: icvp->user_type = (nc_type) (int) value; icvp->user_typelen= nctypelen(icvp->user_type); icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); break; case MI_ICV_DO_RANGE: icvp->user_do_range = value; break; case MI_ICV_VALID_MAX: icvp->user_vmax = value; break; case MI_ICV_VALID_MIN: icvp->user_vmin = value; break; case MI_ICV_DO_NORM: icvp->user_do_norm = value; break; case MI_ICV_USER_NORM: icvp->user_user_norm = value; break; case MI_ICV_IMAGE_MAX: icvp->user_imgmax = value; break; case MI_ICV_IMAGE_MIN: icvp->user_imgmin = value; break; case MI_ICV_DO_FILLVALUE: icvp->user_do_fillvalue = value; break; case MI_ICV_FILLVALUE: icvp->user_fillvalue = value; break; case MI_ICV_DO_DIM_CONV: icvp->user_do_dimconv = value; break; case MI_ICV_DO_SCALAR: icvp->user_do_scalar = value; break; case MI_ICV_XDIM_DIR: ival = value; icvp->user_xdim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_YDIM_DIR: ival = value; icvp->user_ydim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_ZDIM_DIR: ival = value; icvp->user_zdim_dir = ((ival==MI_ICV_POSITIVE) || (ival==MI_ICV_NEGATIVE)) ? ival : MI_ICV_ANYDIR; break; case MI_ICV_NUM_IMGDIMS: ival = value; if ((ival<0) || (ival>MI_MAX_IMGDIMS)) { milog_message(MI_MSG_BADPROP, _("MI_ICV_NUM_IMGDIMS out of range")); MI_RETURN(MI_ERROR); } icvp->user_num_imgdims = ival; break; case MI_ICV_ADIM_SIZE: icvp->user_dim_size[0] = value; break; case MI_ICV_BDIM_SIZE: icvp->user_dim_size[1] = value; break; case MI_ICV_KEEP_ASPECT: icvp->user_keep_aspect = value; break; case MI_ICV_SIGN: case MI_ICV_MAXVAR: case MI_ICV_MINVAR: milog_message(MI_MSG_BADPROP, _("Can't store a number in a string value")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_dim_size[idim] = value; } else { milog_message(MI_MSG_BADPROP, "Unknown code"); MI_RETURN(MI_ERROR); } break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setint @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given integer value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : January 22, 1993 (P.N.) - modified handling of icv properties ---------------------------------------------------------------------------- */ MNCAPI int miicv_setint(int icvid, int icv_property, int value) { MI_SAVE_ROUTINE_NAME("miicv_setint"); if (miicv_setdbl(icvid, icv_property, (double) value) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setlong @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given long integer value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setlong(int icvid, int icv_property, long value) { MI_SAVE_ROUTINE_NAME("miicv_setlong"); if (miicv_setdbl(icvid, icv_property, (double) value) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_setstr @INPUT : icvid - icv id icv_property - property of icv to set value - value to set it to @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Sets a property of an icv to a given string value. Properties cannot be modified while the icv is attached to a cdf file and variable (see miicv_attach and miicv_detach). @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_setstr(int icvid, int icv_property, const char *value) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_setstr"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is not attached to a file */ if (icvp->cdfid != MI_ERROR) { milog_message(MI_MSG_ICVATTACHED); MI_RETURN(MI_ERROR); } /* Set the property */ switch (icv_property) { case MI_ICV_SIGN: icvp->user_sign = MI_get_sign_from_string(icvp->user_type, value); icvp->user_vmax = MI_get_default_range(MIvalid_max, icvp->user_type, icvp->user_sign); icvp->user_vmin = MI_get_default_range(MIvalid_min, icvp->user_type, icvp->user_sign); break; case MI_ICV_MAXVAR: if (value!=NULL) { FREE(icvp->user_maxvar); icvp->user_maxvar = strdup(value); } break; case MI_ICV_MINVAR: if (value!=NULL) { FREE(icvp->user_minvar); icvp->user_minvar = strdup(value); } break; case MI_ICV_TYPE: case MI_ICV_DO_RANGE: case MI_ICV_VALID_MAX: case MI_ICV_VALID_MIN: case MI_ICV_DO_NORM: case MI_ICV_USER_NORM: case MI_ICV_IMAGE_MAX: case MI_ICV_IMAGE_MIN: case MI_ICV_DO_DIM_CONV: case MI_ICV_DO_SCALAR: case MI_ICV_XDIM_DIR: case MI_ICV_YDIM_DIR: case MI_ICV_ZDIM_DIR: case MI_ICV_NUM_IMGDIMS: case MI_ICV_ADIM_SIZE: case MI_ICV_BDIM_SIZE: case MI_ICV_KEEP_ASPECT: milog_message(MI_MSG_BADPROP, "Can't store a string in a numeric property"); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_type; break; case MI_ICV_DO_RANGE: *value = icvp->user_do_range; break; case MI_ICV_VALID_MAX: *value = icvp->user_vmax; break; case MI_ICV_VALID_MIN: *value = icvp->user_vmin; break; case MI_ICV_DO_NORM: *value = icvp->user_do_norm; break; case MI_ICV_USER_NORM: *value = icvp->user_user_norm; break; case MI_ICV_IMAGE_MAX: *value = icvp->user_imgmax; break; case MI_ICV_IMAGE_MIN: *value = icvp->user_imgmin; break; case MI_ICV_NORM_MAX: *value = icvp->derv_imgmax; break; case MI_ICV_NORM_MIN: *value = icvp->derv_imgmin; break; case MI_ICV_DO_FILLVALUE: *value = icvp->user_do_fillvalue; break; case MI_ICV_FILLVALUE: *value = icvp->user_fillvalue; break; case MI_ICV_DO_DIM_CONV: *value = icvp->user_do_dimconv; break; case MI_ICV_DO_SCALAR: *value = icvp->user_do_scalar; break; case MI_ICV_XDIM_DIR: *value = icvp->user_xdim_dir; break; case MI_ICV_YDIM_DIR: *value = icvp->user_ydim_dir; break; case MI_ICV_ZDIM_DIR: *value = icvp->user_zdim_dir; break; case MI_ICV_NUM_IMGDIMS: *value = icvp->user_num_imgdims; break; case MI_ICV_NUM_DIMS: *value = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) (*value)--; break; case MI_ICV_CDFID: *value = icvp->cdfid; break; case MI_ICV_VARID: *value = icvp->varid; break; case MI_ICV_ADIM_SIZE: *value = icvp->user_dim_size[0]; break; case MI_ICV_BDIM_SIZE: *value = icvp->user_dim_size[1]; break; case MI_ICV_ADIM_STEP: *value = icvp->derv_dim_step[0]; break; case MI_ICV_BDIM_STEP: *value = icvp->derv_dim_step[1]; break; case MI_ICV_ADIM_START: *value = icvp->derv_dim_start[0]; break; case MI_ICV_BDIM_START: *value = icvp->derv_dim_start[1]; break; case MI_ICV_KEEP_ASPECT: *value = icvp->user_keep_aspect; break; case MI_ICV_SIGN: case MI_ICV_MAXVAR: case MI_ICV_MINVAR: milog_message(MI_MSG_BADPROP, _("Tried to get icv string property as a number")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if ((icv_property>=MI_ICV_DIM_SIZE) && (icv_propertyuser_dim_size[idim]; } else if ((icv_property>=MI_ICV_DIM_STEP) && (icv_propertyderv_dim_step[idim]; } else if ((icv_property>=MI_ICV_DIM_START) && (icv_propertyderv_dim_start[idim]; } else { milog_message(MI_MSG_BADPROP, _("Tried to get unknown icv property")); MI_RETURN(MI_ERROR); } break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqint @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqint(int icvid, int icv_property, int *value) { double dvalue; MI_SAVE_ROUTINE_NAME("miicv_inqint"); if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) { MI_RETURN(MI_ERROR); } *value = dvalue; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqlong @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : January 22, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqlong(int icvid, int icv_property, long *value) { double dvalue; MI_SAVE_ROUTINE_NAME("miicv_inqlong"); if (miicv_inqdbl(icvid, icv_property, &dvalue) < 0) { MI_RETURN(MI_ERROR); } *value = dvalue; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_inqstr @INPUT : icvid - icv id icv_property - icv property to get @OUTPUT : value - value returned. Caller must allocate enough space for return string. @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the value of an icv property @METHOD : @GLOBALS : @CALLS : @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_inqstr(int icvid, int icv_property, char *value) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_inqstr"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Set the property */ switch (icv_property) { case MI_ICV_SIGN: if (icvp->user_sign==MI_PRIV_SIGNED) (void) strcpy(value, MI_SIGNED); else if (icvp->user_sign==MI_PRIV_UNSIGNED) (void) strcpy(value, MI_UNSIGNED); else (void) strcpy(value, MI_EMPTY_STRING); break; case MI_ICV_MAXVAR: (void) strcpy(value, icvp->user_maxvar); break; case MI_ICV_MINVAR: (void) strcpy(value, icvp->user_minvar); break; case MI_ICV_TYPE: case MI_ICV_DO_RANGE: case MI_ICV_VALID_MAX: case MI_ICV_VALID_MIN: case MI_ICV_DO_NORM: case MI_ICV_USER_NORM: case MI_ICV_IMAGE_MAX: case MI_ICV_IMAGE_MIN: case MI_ICV_NORM_MAX: case MI_ICV_NORM_MIN: case MI_ICV_DO_DIM_CONV: case MI_ICV_DO_SCALAR: case MI_ICV_XDIM_DIR: case MI_ICV_YDIM_DIR: case MI_ICV_ZDIM_DIR: case MI_ICV_NUM_IMGDIMS: case MI_ICV_ADIM_SIZE: case MI_ICV_BDIM_SIZE: case MI_ICV_ADIM_STEP: case MI_ICV_BDIM_STEP: case MI_ICV_ADIM_START: case MI_ICV_BDIM_START: case MI_ICV_KEEP_ASPECT: case MI_ICV_NUM_DIMS: case MI_ICV_CDFID: case MI_ICV_VARID: milog_message(MI_MSG_BADPROP, _("Tried to get icv numeric property as a string")); MI_RETURN(MI_ERROR); break; default: /* Check for image dimension properties */ if (((icv_property>=MI_ICV_DIM_SIZE) && (icv_property=MI_ICV_DIM_STEP) && (icv_property=MI_ICV_DIM_START) && (icv_propertycdfid != MI_ERROR) { if (miicv_detach(icvid) < 0) { MI_RETURN(MI_ERROR); } } /* Inquire about the variable's type, sign and number of dimensions */ if (MI_icv_get_type(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } /* If not doing range calculations, just set derv_firstdim for MI_icv_access, otherwise, call routines to calculate range and normalization */ if (!icvp->user_do_range) { icvp->derv_firstdim = -1; } else { /* Get valid range */ if (MI_icv_get_vrange(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } /* Get normalization info */ if (MI_icv_get_norm(icvp, cdfid, varid) < 0) { MI_RETURN(MI_ERROR); } } /* Set other fields to defaults */ icvp->var_is_vector = FALSE; icvp->var_vector_size = 1; icvp->derv_do_zero = FALSE; icvp->derv_do_bufsize_step = FALSE; icvp->derv_var_pix_off = NULL; icvp->derv_usr_pix_off = NULL; for (idim=0; idimuser_num_imgdims; idim++) { icvp->derv_dim_flip[idim] = FALSE; icvp->derv_dim_grow[idim] = TRUE; icvp->derv_dim_scale[idim] = 1; icvp->derv_dim_off[idim] = 0; icvp->derv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } /* Set the do_scale and do_dimconvert fields of icv structure We have to scale only if do_range is TRUE. If ranges don't match, or we have to do user normalization, or if we are normalizing and MIimagemax or MIimagemin vary over the variable. We don't have to scale if input and output are both floating point. */ icvp->do_scale = (icvp->user_do_range && ((icvp->user_vmax!=icvp->var_vmax) || (icvp->user_vmin!=icvp->var_vmin) || (icvp->user_do_norm && icvp->user_user_norm) || (icvp->user_do_norm && (icvp->derv_firstdim>=0))) ); if ((icvp->derv_usr_float && icvp->derv_var_float)) icvp->do_scale = FALSE; icvp->do_dimconvert = FALSE; /* Set the cdfid and varid fields */ icvp->cdfid = cdfid; icvp->varid = varid; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_type @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the type and sign of a variable for miicv_attach. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_type(mi_icv_type *icvp, int cdfid, int varid) { int oldncopts; /* For saving value of ncopts */ char stringa[MI_MAX_ATTSTR_LEN]; char *string=stringa; /* String for sign info */ MI_SAVE_ROUTINE_NAME("MI_icv_get_type"); /* Inquire about the variable */ if (ncvarinq(cdfid, varid, NULL, &(icvp->var_type), &(icvp->var_ndims), icvp->var_dim, NULL) < 0) { MI_RETURN(MI_ERROR); } /* Check that the variable type is numeric */ if (icvp->var_type==NC_CHAR) { milog_message(MI_MSG_VARNOTNUM); MI_RETURN(MI_ERROR); } /* Try to find out the sign of the variable using MIsigntype. */ oldncopts = ncopts; ncopts = 0; string=miattgetstr(cdfid, varid, MIsigntype, MI_MAX_ATTSTR_LEN, string); ncopts = oldncopts; icvp->var_sign = MI_get_sign_from_string(icvp->var_type, string); /* Get type lengths */ icvp->var_typelen = nctypelen(icvp->var_type); icvp->user_typelen = nctypelen(icvp->user_type); MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_vrange @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the valid range of a variable @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_vrange(mi_icv_type *icvp, int cdfid, int varid) { double vrange[2]; /* Valid range buffer */ MI_SAVE_ROUTINE_NAME("MI_icv_get_vrange"); if (miget_valid_range(cdfid, varid, vrange) == MI_ERROR) { MI_RETURN(MI_ERROR); } icvp->var_vmin = vrange[0]; icvp->var_vmax = vrange[1]; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_get_default_range @INPUT : what - MIvalid_min means get default min, MIvalid_min means get default min datatype - type of variable sign - sign of variable @OUTPUT : (none) @RETURNS : default maximum or minimum for the datatype @DESCRIPTION: Return the defaults maximum or minimum for a given datatype and sign. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE double MI_get_default_range(char *what, nc_type datatype, int sign) { double range[2]; MI_SAVE_ROUTINE_NAME("MI_get_default_range"); (void) miget_default_range(datatype, (sign == MI_PRIV_SIGNED), range); if (STRINGS_EQUAL(what, MIvalid_max)) { MI_RETURN(range[1]); } else if (STRINGS_EQUAL(what, MIvalid_min)) { MI_RETURN(range[0]); } else { ncopts = NC_VERBOSE | NC_FATAL; MI_LOG_PKG_ERROR2(-1,"MINC bug - this line should never be printed"); } MI_RETURN(MI_DEFAULT_MIN); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_get_norm @INPUT : icvp - pointer to icv structure cdfid - cdf file id varid - variable id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets the normalization info for a variable @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_get_norm(mi_icv_type *icvp, int cdfid, int varid) /* ARGSUSED */ { int oldncopts; /* For saving value of ncopts */ int vid[2]; /* Variable ids for max and min */ int ndims; /* Number of dimensions for image max and min */ int dim[MAX_VAR_DIMS]; /* Dimensions */ int imm; /* Counter for looping through max and min */ double image_range[2]; int idim, i; MI_SAVE_ROUTINE_NAME("MI_icv_get_norm"); /* Check for floating point or double precision values for user or in variable - set flag to not do normalization if needed */ icvp->derv_var_float = ((icvp->var_type == NC_DOUBLE) || (icvp->var_type == NC_FLOAT)); icvp->derv_usr_float = ((icvp->user_type == NC_DOUBLE) || (icvp->user_type == NC_FLOAT)); /* Initialize first dimension over which MIimagemax or MIimagemin vary - assume that they don't vary at all */ icvp->derv_firstdim=(-1); /* Look for image max, image min variables */ oldncopts=ncopts; ncopts=0; icvp->imgmaxid=ncvarid(cdfid, icvp->user_maxvar); icvp->imgminid=ncvarid(cdfid, icvp->user_minvar); ncopts = oldncopts; /* Check to see if normalization to variable max, min should be done */ if (!icvp->user_do_norm) { icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; } else { /* Get the image min and max, either from the user definition or from the file. */ if (icvp->user_user_norm) { icvp->derv_imgmax = icvp->user_imgmax; icvp->derv_imgmin = icvp->user_imgmin; } else { if (miget_image_range(cdfid, image_range) < 0) { MI_RETURN(MI_ERROR); } icvp->derv_imgmin = image_range[0]; icvp->derv_imgmax = image_range[1]; } /* Check each of the dimensions of image-min/max variables to see which is the fastest varying dimension of the image variable. */ vid[0]=icvp->imgminid; vid[1]=icvp->imgmaxid; if ((vid[0] != MI_ERROR) && (vid[1] != MI_ERROR)) { for (imm=0; imm < 2; imm++) { if (ncvarinq(cdfid, vid[imm], NULL, NULL, &ndims, dim, NULL) < 0) { MI_RETURN(MI_ERROR); } for (idim=0; idimvar_ndims; i++) { if (icvp->var_dim[i]==dim[idim]) icvp->derv_firstdim = MAX(icvp->derv_firstdim, i); } } } } } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_detach @INPUT : icvid - icv id @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Detaches the cdf file and variable from the image conversion variable, allowing modifications to the icv. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_detach(int icvid) { mi_icv_type *icvp; int idim; MI_SAVE_ROUTINE_NAME("miicv_detach"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Check that the icv is in fact attached */ if (icvp->cdfid == MI_ERROR) MI_RETURN(MI_NOERROR); /* Free the pixel offset arrays */ if (icvp->derv_var_pix_off != NULL) FREE(icvp->derv_var_pix_off); if (icvp->derv_usr_pix_off != NULL) FREE(icvp->derv_usr_pix_off); /* Reset values that are read-only (and set when attached) */ icvp->derv_imgmax = MI_DEFAULT_MAX; icvp->derv_imgmin = MI_DEFAULT_MIN; for (idim=0; idimderv_dim_step[idim] = 0.0; icvp->derv_dim_start[idim] = 0.0; } /* Set cdfid field to MI_ERROR to indicate that icv is detached */ icvp->cdfid = MI_ERROR; icvp->varid = MI_ERROR; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_get @INPUT : icvid - icv id start - coordinates of start of hyperslab (see ncvarget) count - size of hyperslab (see ncvarget) @OUTPUT : values - array of values returned @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Gets a hyperslab of values from a netcdf variable through the image conversion variable (icvid) @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_get(int icvid, long start[], long count[], void *values) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_get"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); /* Get the data */ if (MI_icv_access(MI_PRIV_GET, icvp, start, count, values) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miicv_put @INPUT : icvid - icv id start - coordinates of start of hyperslab (see ncvarput) count - size of hyperslab (see ncvarput) values - array of values to store @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Stores a hyperslab of values in a netcdf variable through the image conversion variable (icvid) @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miicv_put(int icvid, long start[], long count[], void *values) { mi_icv_type *icvp; MI_SAVE_ROUTINE_NAME("miicv_put"); /* Check icv id */ if ((icvp=MI_icv_chkid(icvid)) == NULL) MI_RETURN(MI_ERROR); if (MI_icv_access(MI_PRIV_PUT, icvp, start, count, values) < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_access @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvid - icv id start - coordinates of start of hyperslab (see ncvarput) count - size of hyperslab (see ncvarput) values - array of values to put @OUTPUT : values - array of values to get @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Does the work of getting or putting values from an icv. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 11, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_access(int operation, mi_icv_type *icvp, long start[], long count[], void *values) { int *bufsize_step; /* Pointer to array giving increments for allocating variable buffer (NULL if we don't care) */ long chunk_count[MAX_VAR_DIMS]; /* Number of elements to get for chunk */ long chunk_start[MAX_VAR_DIMS]; /* Starting index for getting a chunk */ long chunk_size; /* Size of chunk in bytes */ void *chunk_values; /* Pointer to next chunk to get */ long var_start[MAX_VAR_DIMS]; /* Coordinates of first var element */ long var_count[MAX_VAR_DIMS]; /* Edge lengths in variable */ long var_end[MAX_VAR_DIMS]; /* Coordinates of last var element */ int firstdim; int idim, ndims; MI_SAVE_ROUTINE_NAME("MI_icv_access"); /* Check that icv is attached to a variable */ if (icvp->cdfid == MI_ERROR) { milog_message(MI_MSG_ICVNOTATTACHED); MI_RETURN(MI_ERROR); } /* Zero the user's buffer if needed */ if ((operation == MI_PRIV_GET) && (icvp->derv_do_zero)) if (MI_icv_zero_buffer(icvp, count, values) < 0) { MI_RETURN(MI_ERROR); } /* Translate icv coordinates to variable coordinates */ if (MI_icv_coords_tovar(icvp, start, count, var_start, var_count) < 0) { MI_RETURN(MI_ERROR); } /* Save icv coordinates for future reference (for dimension conversion routines) */ ndims = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) ndims--; for (idim=0; idim < ndims; idim++) { icvp->derv_icv_start[idim] = start[idim]; icvp->derv_icv_count[idim] = count[idim]; } /* Do we care about getting variable in convenient increments ? Only if we are getting data and the icv structure wants it */ if ((operation==MI_PRIV_GET) && (icvp->derv_do_bufsize_step)) bufsize_step = icvp->derv_bufsize_step; else bufsize_step = NULL; /* Set up variables for looping through variable. The biggest chunk that we can get in one call is determined by the subscripts of MIimagemax and MIimagemin. These must be constant over the chunk that we get if we are doing normalization. */ for (idim=0; idimvar_ndims; idim++) { chunk_start[idim] = var_start[idim]; var_end[idim]=var_start[idim]+var_count[idim]; } (void) miset_coords(icvp->var_ndims, 1L, chunk_count); /* Get size of chunk in user's buffer. Dimension conversion routines don't need the buffer pointer incremented - they do it themselves */ if (!icvp->do_dimconvert) chunk_size = nctypelen(icvp->user_type); else chunk_size = 0; for (idim=MAX(icvp->derv_firstdim+1,0); idim < icvp->var_ndims; idim++) { chunk_count[idim]=var_count[idim]; chunk_size *= chunk_count[idim]; } firstdim = MAX(icvp->derv_firstdim, 0); /* Loop through variable */ chunk_values = values; while (chunk_start[0] < var_end[0]) { /* Set the do_fillvalue flag if the user wants it and we are doing a get. We must do it inside the loop since the scale factor calculation can change it if the scale is zero. (Fillvalue checking is always done if the the scale is zero.) */ icvp->do_fillvalue = icvp->user_do_fillvalue && (operation == MI_PRIV_GET); icvp->fill_valid_min = icvp->var_vmin; icvp->fill_valid_max = icvp->var_vmax; /* Calculate scale factor */ if (icvp->do_scale) { if (MI_icv_calc_scale(operation, icvp, chunk_start) < 0) { MI_RETURN(MI_ERROR); } } // fprintf(stderr, "Getting values at %p\n", chunk_start); /* Get the values */ if (MI_varaccess(operation, icvp->cdfid, icvp->varid, chunk_start, chunk_count, icvp->user_type, icvp->user_sign, chunk_values, bufsize_step, icvp) < 0) { MI_RETURN(MI_ERROR); } /* Increment the start counter */ chunk_start[firstdim] += chunk_count[firstdim]; for (idim=firstdim; (idim>0) && (chunk_start[idim]>=var_end[idim]); idim--) { chunk_start[idim]=var_start[idim]; chunk_start[idim-1]++; } /* Increment the pointer to values */ chunk_values = (void *) ((char *) chunk_values + (size_t) chunk_size); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_zero_buffer @INPUT : icvp - icv structure pointer count - count vector values - pointer to user's buffer @OUTPUT : @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Zeros the user's buffer, with a size given by the vector count. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 9, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_zero_buffer(mi_icv_type *icvp, long count[], void *values) { double zeroval, zerobuf; void *zerostart; int zerolen, idim, ndims; char *bufptr, *bufend, *zeroptr, *zeroend; long buflen; MI_SAVE_ROUTINE_NAME("MI_icv_zero_buffer"); /* Create a zero pixel and get its size */ zerostart = (void *) (&zerobuf); if (icvp->do_scale) zeroval = icvp->offset; else zeroval = 0.0; {MI_FROM_DOUBLE(zeroval, icvp->user_type, icvp->user_sign, zerostart)} zerolen = icvp->user_typelen; /* Get the buffer size */ ndims = icvp->var_ndims; if (icvp->var_is_vector && icvp->user_do_scalar) ndims--; buflen = zerolen; for (idim=0; idim= zeroend) zeroptr = (char *) zerostart; *bufptr = *zeroptr; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_coords_tovar @INPUT : icvp - icv structure pointer icv_start - start vector for icv icv_count - count vector for icv @OUTPUT : var_start - start vector for variable var_count - count vector for variable @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Converts a start and count vector for referencing an icv to the corresponding vectors for referencing a NetCDF variable. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : September 1, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_coords_tovar(mi_icv_type *icvp, long icv_start[], long icv_count[], long var_start[], long var_count[]) { int i, j; int num_non_img_dims; long coord, last_coord, icv_dim_size; MI_SAVE_ROUTINE_NAME("MI_icv_coords_tovar"); /* Do we have to worry about dimension conversions? If not, then just copy the vectors and return. */ if (!icvp->do_dimconvert) { for (i=0; i < icvp->var_ndims; i++) { var_count[i] = icv_count[i]; var_start[i] = icv_start[i]; } MI_RETURN(MI_NOERROR); } /* Get the number of non image dimensions */ num_non_img_dims=icvp->var_ndims-icvp->user_num_imgdims; if (icvp->var_is_vector) num_non_img_dims--; /* Go through first, non-image dimensions */ for (i=0; i < num_non_img_dims; i++) { var_count[i] = icv_count[i]; var_start[i] = icv_start[i]; } /* Go through image dimensions */ for (i=num_non_img_dims, j=icvp->user_num_imgdims-1; i < num_non_img_dims+icvp->user_num_imgdims; i++, j--) { /* Check coordinates. */ icv_dim_size = (icvp->user_dim_size[j] > 0) ? icvp->user_dim_size[j] : icvp->var_dim_size[j]; last_coord = icv_start[i] + icv_count[i] - 1; if ((icv_start[i]<0) || (icv_start[i]>=icv_dim_size) || (last_coord<0) || (last_coord>=icv_dim_size) || (icv_count[i]<0)) { milog_message(MI_MSG_ICVCOORDS); MI_RETURN(MI_ERROR); } /* Remove offset */ coord = icv_start[i]-icvp->derv_dim_off[j]; /* Check for growing or shrinking */ if (icvp->derv_dim_grow[j]) { var_count[i] = (icv_count[i]+icvp->derv_dim_scale[j]-1) /icvp->derv_dim_scale[j]; coord /= icvp->derv_dim_scale[j]; } else { var_count[i] = icv_count[i]*icvp->derv_dim_scale[j]; coord *= icvp->derv_dim_scale[j]; } /* Check for flipping */ if (icvp->derv_dim_flip[j]) coord = icvp->var_dim_size[j] - coord - ((icv_count!=NULL) ? var_count[i] : 0L); var_start[i] = coord; /* Check for indices out of variable bounds (but in icv bounds) */ last_coord = var_start[i] + var_count[i]; if ((var_start[i]<0) || (last_coord>=icvp->var_dim_size[j])) { if (var_start[i]<0) var_start[i] = 0; if (last_coord>=icvp->var_dim_size[j]) last_coord = icvp->var_dim_size[j] - 1; /* Enforce similar bounds on var_start (bert) */ if (var_start[i] >= icvp->var_dim_size[j]) var_start[i] = icvp->var_dim_size[j] - 1; var_count[i] = last_coord - var_start[i] + 1; } } /* Check for vector dimension */ if (icvp->var_is_vector) { if (icvp->user_do_scalar) { var_count[icvp->var_ndims-1] = icvp->var_vector_size; var_start[icvp->var_ndims-1] = 0; } else { var_count[icvp->var_ndims-1] = icv_count[icvp->var_ndims-1]; var_start[icvp->var_ndims-1] = icv_start[icvp->var_ndims-1]; } } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_calc_scale @INPUT : operation - MI_PRIV_GET or MI_PRIV_PUT icvp - icv structure pointer coords - coordinates of first value to get or put @OUTPUT : icvp - fields scale and offset set @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Calculates the scale and offset needed for getting or putting values, starting at index coords (assumes that scale is constant over that range). @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 10, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_icv_calc_scale(int operation, mi_icv_type *icvp, long coords[]) { long mmcoords[MAX_VAR_DIMS]; /* Coordinates for max/min variable */ double usr_imgmax, usr_imgmin; double var_imgmax, var_imgmin; double var_imgmax_true, var_imgmin_true; double usr_vmax, usr_vmin; double var_vmax, var_vmin; double slice_imgmax, slice_imgmin; double usr_scale; double denom; MI_SAVE_ROUTINE_NAME("MI_icv_calc_scale"); /* Set variable valid range */ var_vmax = icvp->var_vmax; var_vmin = icvp->var_vmin; /* Set image max/min for user and variable values depending on whether normalization should be done or not. Whenever floating-point values are involved, some type of normalization is done. When the icv type is floating point, normalization is always done. When the file type is floating point and the icv type is integer, slices are normalized to the real range of the slice (or chunk being read). */ if (!icvp->derv_var_float && !icvp->derv_usr_float && !icvp->user_do_norm) { usr_imgmax = var_imgmax = MI_DEFAULT_MAX; usr_imgmin = var_imgmin = MI_DEFAULT_MIN; } else { /* Get the real range for the slice or chunk that is being examined */ slice_imgmax = MI_DEFAULT_MAX; slice_imgmin = MI_DEFAULT_MIN; if ((!icvp->derv_var_float || !icvp->user_do_norm) && (icvp->imgmaxid!=MI_ERROR) && (icvp->imgminid!=MI_ERROR)) { if (mitranslate_coords(icvp->cdfid, icvp->varid, coords, icvp->imgmaxid, mmcoords) == NULL) MI_RETURN(MI_ERROR); if (mivarget1(icvp->cdfid, icvp->imgmaxid, mmcoords, NC_DOUBLE, NULL, &slice_imgmax) < 0) { MI_RETURN(MI_ERROR); } if (mitranslate_coords(icvp->cdfid, icvp->varid, coords, icvp->imgminid, mmcoords) == NULL) { MI_RETURN(MI_ERROR); } if (mivarget1(icvp->cdfid, icvp->imgminid, mmcoords, NC_DOUBLE, NULL, &slice_imgmin) < 0) { MI_RETURN(MI_ERROR); } } /* Get the user real range */ if (icvp->user_do_norm) { usr_imgmax = icvp->derv_imgmax; usr_imgmin = icvp->derv_imgmin; } else { usr_imgmax = slice_imgmax; usr_imgmin = slice_imgmin; } /* Get the file real range */ if (icvp->derv_var_float) { var_imgmax = var_vmax; var_imgmin = var_vmin; } else { var_imgmax = slice_imgmax; var_imgmin = slice_imgmin; } } /* Prevent scaling between file floats and real value */ if (icvp->derv_var_float) { var_imgmax = var_vmax; var_imgmin = var_vmin; } /* Get user valid range */ if (icvp->derv_usr_float) { usr_vmax = usr_imgmax; usr_vmin = usr_imgmin; } else { usr_vmax = icvp->user_vmax; usr_vmin = icvp->user_vmin; } /* Save real var_imgmin/max for fillvalue checking later */ var_imgmax_true = var_imgmax; var_imgmin_true = var_imgmin; /* Even though we have already carefully set the vmax/min and imgmax/min values to handle the floating point case, we can still have problems with the scale calculations (rounding errors) if full range max/min are used (-FLT_MAX to FLT_MAX). To avoid this, we just force the values to 0 and 1 which will give the correct scale. That is why we save the true values above. */ if (icvp->derv_usr_float) { usr_imgmax = usr_vmax = MI_DEFAULT_MAX; usr_imgmin = usr_vmin = MI_DEFAULT_MIN; } if (icvp->derv_var_float) { var_imgmax = var_vmax = MI_DEFAULT_MAX; var_imgmin = var_vmin = MI_DEFAULT_MIN; } /* Calculate scale and offset for MI_PRIV_GET */ /* Scale */ denom = usr_imgmax - usr_imgmin; if (denom!=0.0) usr_scale=(usr_vmax - usr_vmin) / denom; else usr_scale=0.0; denom = var_vmax - var_vmin; if (denom!=0.0) icvp->scale = usr_scale * (var_imgmax - var_imgmin) / denom; else icvp->scale = 0.0; /* Offset */ icvp->offset = usr_vmin - icvp->scale * var_vmin + usr_scale * (var_imgmin - usr_imgmin); /* If we want a MI_PRIV_PUT, invert */ if (operation==MI_PRIV_PUT) { if (icvp->scale!=0.0) { icvp->offset = (-icvp->offset) / icvp->scale; icvp->scale = 1.0/icvp->scale; } else { icvp->offset = var_vmin; icvp->scale = 0.0; } } /* Do fill value checking if scale is zero */ if (icvp->scale == 0.0) { /* Check for floating point on both sides of conversion. We should not be doing scaling in this case, but we will check to be safe. */ if (icvp->derv_var_float && icvp->derv_usr_float) { icvp->do_scale = FALSE; icvp->do_fillvalue = FALSE; } else { /* Not pure floating point */ icvp->do_fillvalue = TRUE; /* For output, set the range properly depending on whether the user type is floating point or not */ if (operation == MI_PRIV_PUT) { if (icvp->derv_usr_float) { icvp->fill_valid_min = var_imgmin_true; icvp->fill_valid_max = var_imgmax_true; } else if (usr_scale != 0.0) { icvp->fill_valid_min = usr_vmin + (var_imgmin_true - usr_imgmin) / usr_scale; icvp->fill_valid_max = usr_vmin + (var_imgmax_true - usr_imgmin) / usr_scale; } else { icvp->fill_valid_min = usr_vmin; icvp->fill_valid_max = usr_vmax; } } /* If output operation */ } /* If not pure floating point */ } /* If scale == 0.0 */ MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_icv_chkid @INPUT : icvid - icv id @OUTPUT : (none) @RETURNS : Pointer to icv structure if it exists, otherwise NULL. @DESCRIPTION: Checks that an icv id is valid and returns a pointer to the structure. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ SEMIPRIVATE mi_icv_type *MI_icv_chkid(int icvid) { MI_SAVE_ROUTINE_NAME("MI_icv_chkid"); /* Check icv id */ if ((icvid<0) || (icvid>=minc_icv_list_nalloc) || (minc_icv_list[icvid]==NULL)) { milog_message(MI_MSG_BADICV); MI_RETURN((void *) NULL); } MI_RETURN(minc_icv_list[icvid]); } libminc-libminc-2-3-00/libsrc/minc.h000066400000000000000000000604341257462267400172250ustar00rootroot00000000000000#ifndef MINC_H #define MINC_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc.h @DESCRIPTION: Header file for minc (Medical Image NetCDF) file format standard. @METHOD : Includes name definitions for NetCDF dimensions, attributes and variables for the following : NetCDF standard attributes MI general variable attributes MI dimensions and associated variables MI dimension variable attributes MI root variable MI image variable MI patient variable MI study variable MI acquisition variable @CREATED : July 24, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: minc.h,v $ * Revision 6.21 2010-05-19 03:44:25 stever * Move definition of MNCAPI ahead of #include "minc_compat.h"; fix-up for * previous check-in. * * Revision 6.20 2010-05-19 03:18:42 stever * Ensure hdf5.h included before netcdf.h. * * Revision 6.19 2010-03-02 23:24:40 rotor * * libsrc/hdf_convenience.c: removed spurious debug output * * libsrc/minc.h: replaced MAX_NC_OPEN with 32 * * libsrc/voxel_loop.c: replaced MAX_NC_OPEN with MI_MAX_NUM_ICV * * Revision 6.18 2007/08/09 17:05:25 rotor * * added some fixes of Claudes for chunking and internal compression * * Revision 6.17 2005/08/26 21:04:57 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.16 2004/12/03 21:52:35 bert * Minor changes for Windows build * * Revision 6.15 2004/10/15 13:48:13 bert * Minor changes for Windows compatibility * * Revision 6.14 2004/08/11 20:50:54 bert * Fix incompatibility with netCDF 3.5.1 by fixing MI_MAX_IMGDIMS at 100 * * Revision 6.13 2004/06/04 18:14:52 bert * Add micreate_ident() * * Revision 6.12 2004/04/27 15:44:04 bert * Add MINC 2.0 specific stuff * * Revision 6.11 2004/04/15 21:13:21 bert * Add C++ linkage specifier * * Revision 6.10 2004/02/02 18:22:34 bert * Added miget_version() and miappend_history() * * Revision 6.9 2003/03/17 16:03:28 bert * Added declaration of micreate_tempfile() * * Revision 6.8 2001/11/13 14:15:17 neelin * Added functions miget_image_range and mivar_exists * * Revision 6.7 2001/08/20 13:19:14 neelin * Added function miattget_with_sign to allow the caller to specify the sign * of the input attribute since this information is ambiguous. This is * necessary for the valid_range attribute which should have the same sign * as the image data. Modified miget_valid_range to make use of this function. * * Revision 6.6 2001/08/16 16:41:31 neelin * Added library functions to handle reading of datatype, sign and valid range, * plus writing of valid range and setting of default ranges. These functions * properly handle differences between valid_range type and image type. Such * difference can cause valid data to appear as invalid when double to float * conversion causes rounding in the wrong direction (out of range). * Modified voxel_loop, volume_io and programs to use these functions. * * Revision 6.5 2001/08/16 13:32:18 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.4 2001/04/24 13:38:40 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.3 2001/04/17 18:40:13 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.2 2001/04/10 22:05:30 neelin * Start of modifications to get minc working with netcdf 3.5. * * Revision 6.1 1999/10/19 14:45:08 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.1 1995/06/12 17:37:05 neelin * Added MI_LABEL modality. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.4 1995/01/24 08:34:30 neelin * Added optional tempfile argument to miexpand_file. * * Revision 2.3 95/01/23 08:28:31 neelin * Changed name of midecompress_file to miexpand_file. * * Revision 2.2 95/01/20 15:21:16 neelin * Added midecompress_file with ability to decompress only the header of a file. * * Revision 2.1 94/11/25 15:32:32 neelin * Added #undef for public if it wasn't previously defined so that C++ * code won't get upset. * * Revision 2.0 94/09/28 10:38:00 neelin * Release of minc version 0.2 * * Revision 1.29 94/09/28 10:37:24 neelin * Pre-release * * Revision 1.28 93/11/03 13:08:37 neelin * Added prototypes for miopen, miclose, micreate. * * Revision 1.27 93/11/03 12:29:11 neelin * Added error code for failure to uncompress a file. * * Revision 1.26 93/08/11 12:06:34 neelin * Added RCS logging in source. * July 15, 1993 (P.N.) - added MI_ICV_DO_FILLVALUE and MI_FILLVALUE @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc.h,v 6.21 2010-05-19 03:44:25 stever Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ #ifndef MNCAPI #if defined(_MSC_VER) /* If we are building on the Microsoft C compiler, we want to * explicitly import all public functions from the DLL */ #define MNCAPI __declspec(dllimport) #else #define MNCAPI #endif /* _MSC_VER not defined */ #endif /* MNCAPI not defined */ #if MINC2 #include #include #include "minc_compat.h" #else #include #endif #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* For compatibility with NetCDF 2.x which uses NC_LONG rather than NC_INT. Since NC_INT is defined in an enum, and since there are no version macros, we use NC_FILL_INT as an indicator of whether NC_INT is defined. */ #ifndef NC_FILL_INT # define NC_INT NC_LONG #endif /* Constant used with nc_type variables to indicate that the data type should be taken from the source object or file. This replaces earlier use of NC_UNSPECIFIED and is equivalent to NetCDF NC_NAT in version 3.5 and later. */ #define MI_ORIGINAL_TYPE ((nc_type) 0) /* NC_UNSPECIFIED is defined here for backwards compatibility. With NetCDF 2.x, NC_UNSPECIFIED may already be defined either through a macro or an enum. In the latter case, this macro will override the enum. */ #ifndef NC_UNSPECIFIED # define NC_UNSPECIFIED MI_ORIGINAL_TYPE #endif /* Some useful constants */ #define MI_EMPTY_STRING "" /* Error flags */ #define MI_ERROR (-1) #define MI_NOERROR 0 /* Maximum length of standard attributes */ #define MI_MAX_ATTSTR_LEN 64 /* Number of spatial dimensions */ #define MI_NUM_SPACE_DIMS 3 /* Maximum number of image dimensions for image conversion */ /* Bert 10-Aug-2004 - MI_MAX_IMGDIMS used to be defined to be MAX_VAR_DIMS, * a constant defined in netcdf.h. For many years MAX_VAR_DIMS was 100, * but in netCDF 3.5.1 the value was changed to 512. * Unfortunately, the definitions of MI_ICV_DIM_SIZE, MI_ICV_DIM_STEP, * and MI_ICV_DIM_START assume that MI_MAX_IMGDIMS is less than or * equal to 100. To avoid changing the MINC API, we have to define * MI_MAX_IMGDIMS to 100 here. Otherwise the miicv_inqdbl() function * will return bogus values for these ICV properties. */ #define MI_MAX_IMGDIMS 100 /* NetCDF standard attributes */ #define MIunits "units" #define MIlong_name "long_name" #define MIvalid_range "valid_range" #define MIvalid_max "valid_max" #define MIvalid_min "valid_min" #define MI_FillValue "_FillValue" #define MItitle "title" #define MIhistory "history" /* General variable attributes */ #define MIvartype "vartype" #define MIvarid "varid" #define MIsigntype "signtype" #define MIparent "parent" #define MIchildren "children" #define MIcomments "comments" #define MIversion "version" /* General attribute constants */ /* Prefix for identifying a variable attribute pointer */ #define MI_VARATT_POINTER_PREFIX "--->" /* Separator for elements of MIchildren */ #define MI_CHILD_SEPARATOR "\n" /* MIvartype values */ #define MI_GROUP "group________" #define MI_DIMENSION "dimension____" #define MI_DIM_WIDTH "dim-width____" #define MI_VARATT "var_attribute" /* MIvarid value */ #define MI_STDVAR "MINC standard variable" /* MIsigntype values */ #define MI_SIGNED "signed__" #define MI_UNSIGNED "unsigned" /* MIversion value */ #define MI_VERSION_1_0 "MINC Version 1.0" #define MI_CURRENT_VERSION MI_VERSION_1_0 /* Generally useful values for boolean attributes */ #define MI_TRUE "true_" #define MI_FALSE "false" /* Dimension names and names of associated variables */ #define MIxspace "xspace" #define MIyspace "yspace" #define MIzspace "zspace" #define MItime "time" #define MItfrequency "tfrequency" #define MIxfrequency "xfrequency" #define MIyfrequency "yfrequency" #define MIzfrequency "zfrequency" #define MIvector_dimension "vector_dimension" #define MIxspace_width "xspace-width" #define MIyspace_width "yspace-width" #define MIzspace_width "zspace-width" #define MItime_width "time-width" #define MItfrequency_width "tfrequency-width" #define MIxfrequency_width "xfrequency-width" #define MIyfrequency_width "yfrequency-width" #define MIzfrequency_width "zfrequency-width" /* Dimension variable attribute names */ /* For dimension variables (MIspacing is also for dimension width vars) */ #define MIspacing "spacing" #define MIstep "step" #define MIstart "start" #define MIspacetype "spacetype" #define MIalignment "alignment" #define MIdirection_cosines "direction_cosines" /* For dimension width variables */ #define MIwidth "width" #define MIfiltertype "filtertype" /* Dimension attribute constants */ /* MIgridtype values */ #define MI_REGULAR "regular__" #define MI_IRREGULAR "irregular" /* MIspacetype values */ #define MI_NATIVE "native____" #define MI_TALAIRACH "talairach_" #define MI_CALLOSAL "callosal__" /* MIalignment values */ #define MI_START "start_" #define MI_CENTRE "centre" #define MI_END "end___" #define MI_CENTER MI_CENTRE /* MIfiltertype values */ #define MI_SQUARE "square____" #define MI_GAUSSIAN "gaussian__" #define MI_TRIANGULAR "triangular" /* The root variable */ #define MIrootvariable "rootvariable" /* The image variable and its attributes */ #define MIimage "image" #define MIimagemax "image-max" #define MIimagemin "image-min" #define MIcomplete "complete" /* The patient variable and its attributes */ #define MIpatient "patient" #define MIfull_name "full_name" #define MIother_names "other_names" #define MIidentification "identification" #define MIother_ids "other_ids" #define MIbirthdate "birthdate" #define MIsex "sex" #define MIage "age" #define MIweight "weight" #define MIsize "size" #define MIaddress "address" #define MIinsurance_id "insurance_id" /* Patient attribute constants */ #define MI_MALE "male__" #define MI_FEMALE "female" #define MI_OTHER "other_" /* The study variable and its attributes */ #define MIstudy "study" #define MIstart_time "start_time" #define MIstart_year "start_year" #define MIstart_month "start_month" #define MIstart_day "start_day" #define MIstart_hour "start_hour" #define MIstart_minute "start_minute" #define MIstart_seconds "start_seconds" #define MImodality "modality" #define MImanufacturer "manufacturer" #define MIdevice_model "device_model" #define MIinstitution "institution" #define MIdepartment "department" #define MIstation_id "station_id" #define MIreferring_physician "referring_physician" #define MIattending_physician "attending_physician" #define MIradiologist "radiologist" #define MIoperator "operator" #define MIadmitting_diagnosis "admitting_diagnosis" #define MIprocedure "procedure" #define MIstudy_id "study_id" /* Study attribute constants */ #define MI_PET "PET__" #define MI_SPECT "SPECT" #define MI_GAMMA "GAMMA" #define MI_MRI "MRI__" #define MI_MRS "MRS__" #define MI_MRA "MRA__" #define MI_CT "CT___" #define MI_DSA "DSA__" #define MI_DR "DR___" #define MI_LABEL "label" /* The acquisition variable and its attributes */ #define MIacquisition "acquisition" #define MIprotocol "protocol" #define MIscanning_sequence "scanning_sequence" #define MIrepetition_time "repetition_time" #define MIecho_time "echo_time" #define MIinversion_time "inversion_time" #define MInum_averages "num_averages" #define MIimaging_frequency "imaging_frequency" #define MIimaged_nucleus "imaged_nucleus" #define MIradionuclide "radionuclide" #define MIcontrast_agent "contrast_agent" #define MIradionuclide_halflife "radionuclide_halflife" #define MItracer "tracer" #define MIinjection_time "injection_time" #define MIinjection_year "injection_year" #define MIinjection_month "injection_month" #define MIinjection_day "injection_day" #define MIinjection_hour "injection_hour" #define MIinjection_minute "injection_minute" #define MIinjection_seconds "injection_seconds" #define MIinjection_length "injection_length" #define MIinjection_dose "injection_dose" #define MIdose_units "dose_units" #define MIinjection_volume "injection_volume" #define MIinjection_route "injection_route" /* Constants for image conversion variable (icv) properties */ /* This value is not really enforced in the code (see miicv_create() in * image_conversion.c), but this value is used in voxel_loop.c to set the * maximum number of allowable open files. */ #define MI_MAX_NUM_ICV 1000 /**< Maximum number of icv's allowed */ /* Default max and min for normalization */ #define MI_DEFAULT_MAX 1.0 #define MI_DEFAULT_MIN 0.0 /* For converting data type */ #define MI_ICV_TYPE 1 #define MI_ICV_SIGN 2 #define MI_ICV_DO_RANGE 3 #define MI_ICV_VALID_MAX 4 #define MI_ICV_VALID_MIN 5 /* For doing normalization */ #define MI_ICV_DO_NORM 6 #define MI_ICV_USER_NORM 7 #define MI_ICV_IMAGE_MAX 8 #define MI_ICV_IMAGE_MIN 9 /* Values actually used in normalization - read-only */ #define MI_ICV_NORM_MAX 10 #define MI_ICV_NORM_MIN 11 /* For doing dimension conversions */ #define MI_ICV_DO_DIM_CONV 12 /* For converting vector fields to scalar */ #define MI_ICV_DO_SCALAR 13 /* For flipping axis direction */ #define MI_ICV_XDIM_DIR 14 #define MI_ICV_YDIM_DIR 15 #define MI_ICV_ZDIM_DIR 16 /* For changing size of first two dimensions (excluding MIvector_dimension) */ #define MI_ICV_ADIM_SIZE 17 #define MI_ICV_BDIM_SIZE 18 #define MI_ICV_KEEP_ASPECT 19 /* The pixel size and location of first two dimensions (these are readonly) */ #define MI_ICV_ADIM_STEP 20 #define MI_ICV_BDIM_STEP 21 #define MI_ICV_ADIM_START 22 #define MI_ICV_BDIM_START 23 /* Number of image dimensions for dimension conversion */ #define MI_ICV_NUM_IMGDIMS 24 /* Number of dimensions of image variable taking into account vector/scalar data (read-only property) */ #define MI_ICV_NUM_DIMS 25 /* Id of file and image variable (read-only properties) */ #define MI_ICV_CDFID 26 #define MI_ICV_VARID 27 /* Names of MIimagemax and MIimagemin variables */ #define MI_ICV_MAXVAR 28 #define MI_ICV_MINVAR 29 /* For setting input values to a specified fillvalue */ #define MI_ICV_DO_FILLVALUE 30 #define MI_ICV_FILLVALUE 31 /* Image dimension properties. For each dimension, add the dimension number (counting from fastest to slowest). */ #define MI_ICV_DIM_SIZE 1000 #define MI_ICV_DIM_STEP 1100 #define MI_ICV_DIM_START 1200 /* Constants that can be used as values for the above properties. */ /* Possible values for MI_ICV_?DIM_DIR */ #define MI_ICV_POSITIVE 1 #define MI_ICV_NEGATIVE (-1) #define MI_ICV_ANYDIR 0 /* Possible value for MI_ICV_?DIM_SIZE */ #define MI_ICV_ANYSIZE (-1) /* Error codes. Note that they must not conflict with NetCDF error codes since they are stored in the same global variable. */ #define MI_ERR_NONNUMERIC 1331 /* Non-numeric type */ #define MI_ERR_NONCHAR 1332 /* Non-character type */ #define MI_ERR_NONSCALAR 1333 /* Non-scalar attribute */ #define MI_ERR_BADOP 1334 /* Bad operation for MI_varaccess */ #define MI_ERR_NOTPOINTER 1335 /* Attribute is not a pointer */ #define MI_ERR_BAD_STDVAR 1336 /* Not a standard variable */ #define MI_ERR_BADSUFFIX 1337 /* Bad dimension width suffix */ #define MI_ERR_NOICV 1338 /* Out of icv slots */ #define MI_ERR_BADICV 1339 /* Illegal icv identifier */ #define MI_ERR_BADPROP 1340 /* Unknown icv property */ #define MI_ERR_ICVATTACHED 1341 /* Tried to modify attached icv */ #define MI_ERR_TOOFEWDIMS 1342 /* Too few dimensions to be an image */ #define MI_ERR_ICVNOTATTACHED 1343 /* Tried to access an unattached icv */ #define MI_ERR_DIMSIZE 1344 /* Dimensions differ in size */ #define MI_ERR_ICV_INVCOORDS 1345 /* Invalid icv coordinates */ #define MI_ERR_WRONGNDIMS 1346 /* Too many dimensions for a dim var */ #define MI_ERR_BADMATCH 1347 /* Variables do not match for copy */ #define MI_ERR_MAXMIN_DIMS 1348 /* Imagemax/min variables vary over image dimensions */ #define MI_ERR_UNCOMPRESS 1349 /* Not able to uncompress file */ /* MINC public functions */ /* From netcdf_convenience.c */ MNCAPI char *miexpand_file(const char *path, char *tempfile, int header_only, int *created_tempfile); MNCAPI int miopen(const char *path, int mode); MNCAPI int micreate(const char *path, int cmode); MNCAPI int miclose(int cdfid); MNCAPI int miattget_with_sign(int cdfid, int varid, const char *name, char *insign, nc_type datatype, char *outsign, int max_length, void *value, int *att_length); MNCAPI int miattget(int cdfid, int varid, const char *name, nc_type datatype, int max_length, void *value, int *att_length); MNCAPI int miattget1(int cdfid, int varid, const char *name, nc_type datatype, void *value); MNCAPI char *miattgetstr(int cdfid, int varid, const char *name, int maxlen, char *value); MNCAPI int miattputint(int cdfid, int varid, const char *name, int value); MNCAPI int miattputdbl(int cdfid, int varid, const char *name, double value); MNCAPI int miattputstr(int cdfid, int varid, const char *name, const char *value); MNCAPI int mivarget(int cdfid, int varid, long start[], long count[], nc_type datatype, const char *sign, void *values); MNCAPI int mivarget1(int cdfid, int varid, long mindex[], nc_type datatype, const char *sign, void *value); MNCAPI int mivarput(int cdfid, int varid, long start[], long count[], nc_type datatype, const char *sign, void *values); MNCAPI int mivarput1(int cdfid, int varid, long mindex[], nc_type datatype, const char *sign, void *value); MNCAPI long *miset_coords(int nvals, long value, long coords[]); MNCAPI long *mitranslate_coords(int cdfid, int invar, long incoords[], int outvar, long outcoords[]); MNCAPI int micopy_all_atts(int incdfid, int invarid, int outcdfid, int outvarid); MNCAPI int micopy_var_def(int incdfid, int invarid, int outcdfid); MNCAPI int micopy_var_values(int incdfid, int invarid, int outcdfid, int outvarid); MNCAPI int micopy_all_var_defs(int incdfid, int outcdfid, int nexclude, int excluded_vars[]); MNCAPI int micopy_all_var_values(int incdfid, int outcdfid, int nexclude, int excluded_vars[]); MNCAPI char *micreate_tempfile(void); /* From minc_convenience.c */ MNCAPI int miget_datatype(int cdfid, int imgid, nc_type *datatype, int *is_signed); MNCAPI int miget_default_range(nc_type datatype, int is_signed, double default_range[]); MNCAPI int miget_valid_range(int cdfid, int imgid, double valid_range[]); MNCAPI int miset_valid_range(int cdfid, int imgid, const double valid_range[]); MNCAPI int miget_image_range(int cdfid, double image_range[]); MNCAPI int mivar_exists(int cdfid, const char *varname); MNCAPI int miattput_pointer(int cdfid, int varid, const char *name, int ptrvarid); MNCAPI int miattget_pointer(int cdfid, int varid, const char *name); MNCAPI int miadd_child(int cdfid, int parent_varid, int child_varid); MNCAPI int micreate_std_variable(int cdfid, const char *name, nc_type datatype, int ndims, int dim[]); MNCAPI int micreate_group_variable(int cdfid, const char *name); MNCAPI const char *miget_version(void); MNCAPI int miappend_history(int fd, const char *tm_stamp); MNCAPI int micreate_ident(char * id_str, size_t length); /* From image_conversion.c */ MNCAPI int miicv_create(void); MNCAPI int miicv_free(int icvid); MNCAPI int miicv_setdbl(int icvid, int icv_property, double value); MNCAPI int miicv_setint(int icvid, int icv_property, int value); MNCAPI int miicv_setlong(int icvid, int icv_property, long value); MNCAPI int miicv_setstr(int icvid, int icv_property, const char *value); MNCAPI int miicv_inqdbl(int icvid, int icv_property, double *value); MNCAPI int miicv_inqint(int icvid, int icv_property, int *value); MNCAPI int miicv_inqlong(int icvid, int icv_property, long *value); MNCAPI int miicv_inqstr(int icvid, int icv_property, char *value); MNCAPI int miicv_ndattach(int icvid, int cdfid, int varid); MNCAPI int miicv_detach(int icvid); MNCAPI int miicv_get(int icvid, long start[], long count[], void *values); MNCAPI int miicv_put(int icvid, long start[], long count[], void *values); /* From dim_conversion.c */ MNCAPI int miicv_attach(int icvid, int cdfid, int varid); /* From minc_error.c */ MNCAPI void milog_init(const char *); MNCAPI int milog_set_verbosity(int); /* from minc_format_convert.h*/ MNCAPI int minc_format_convert(const char *input,const char *output); /* default voxel loop buffer size */ #define MI2_DEF_BUFF_SIZE 4096 #define MI2_DEF_MAX_MEM 104857 #if MINC2 /* New functions, not directly part of compatibility layer. */ extern int MI2varsize(int fd, int varid, long *size_ptr); extern int miget_file_type(const char *filename); #define MI2_GRPNAME "/minc-2.0" /* These must not interfere with any NC_ flags we might have to support. */ #define MI2_CREATE_V2 0x1000 /* Force V2 format */ #define MI2_CREATE_V1 0x2000 /* Force V1 format */ /* Possible compression type values. */ #define MI2_COMP_UNKNOWN (-1) #define MI2_COMP_NONE 0 #define MI2_COMP_ZLIB 1 #define MI2_CHUNK_UNKNOWN (-1) #define MI2_CHUNK_OFF 0 #define MI2_CHUNK_ON 1 #define MI2_CHUNK_MIN_SIZE 4 #define MI2_OPTS_V1 1 struct mi2opts { int struct_version; int comp_type; int comp_param; int chunk_type; int chunk_param; }; #define MI2_ISH5OBJ(x) (H5Iget_type(x) > 0) MNCAPI int micreatex(const char *path, int cmode, struct mi2opts *opts_ptr); #else #define MI2_ISH5OBJ(x) (0) #endif /* MINC2 */ #ifdef __cplusplus } #endif /* __cplusplus */ /* End ifndef MINC_H */ #endif libminc-libminc-2-3-00/libsrc/minc_basic.h000066400000000000000000000167541257462267400203740ustar00rootroot00000000000000#ifndef MINC_BASIC_H #define MINC_BASIC_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_basic.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Constants and macros for private use by MINC routines. @METHOD : @GLOBALS : @CALLS : @CREATED : August 28, 1992 (Peter Neelin) @MODIFIED : * $Log: minc_basic.h,v $ * Revision 6.8 2010-03-27 15:09:28 rotor * * back to 1000000 * * Revision 6.7 2010-03-02 12:23:14 rotor * * ported HDF calls to 1.8.x * * Makefile.am: updated for minccmp * * Revision 6.6 2008/04/11 05:15:00 rotor * * rewrote error code (Claude) to remove global defs that were * causing build problems with DYLIB on OSX * * Revision 6.5 2008/01/12 01:05:37 rotor * * initial commits from Steve to remove warnings * * Revision 6.4 2007/08/09 17:05:25 rotor * * added some fixes of Claudes for chunking and internal compression * * Revision 6.3 2004/04/27 15:48:15 bert * Minor changes * * Revision 6.2 2001/04/17 18:40:13 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:08 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:01 neelin * Release of minc version 0.2 * * Revision 1.8 94/09/28 10:37:26 neelin * Pre-release * * Revision 1.7 93/10/28 10:18:23 neelin * Added FILLVALUE_EPSILON for doing fillvalue checking in icv's. * * Revision 1.6 93/08/11 12:06:37 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_basic.h,v 6.8 2010-03-27 15:09:28 rotor Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ #include /* --------- MINC specific constants ------------- */ /* Maximum buffer size for conversions. Should not be a power of 2 - this can cause poor performance on some systems (e.g. SGI) due to caching- related inefficiencies */ #define MI_MAX_VAR_BUFFER_SIZE 1000000 /* Possible values for sign of a value */ #define MI_PRIV_DEFSIGN 0 #define MI_PRIV_SIGNED 1 #define MI_PRIV_UNSIGNED 2 /* Operations for MI_varaccess */ #define MI_PRIV_GET 10 #define MI_PRIV_PUT 11 /* Suffix for dimension width variable names */ #define MI_WIDTH_SUFFIX "-width" /* Epsilon for detecting fillvalues */ #define FILLVALUE_EPSILON (10.0 * FLT_EPSILON) /* NetCDF routine name variable (for error logging) */ extern char *cdf_routine_name ; /* defined in globdef.c */ #define MI_NC_ROUTINE_VAR cdf_routine_name /* Macros for logging errors. All routines should start with MI_SAVE_ROUTINE and exit with MI_RETURN (which includes MI_RETURN_ERROR and MI_CHK_ERROR). All the macros except MI_CHK_ERROR are single line commands. MI_CHK_ERROR is in a block and so should not be followed by a ';' */ #define MI_SAVE_ROUTINE_NAME(name) MI_save_routine_name(name) #define MI_RETURN(value) \ return( MI_return() ? (value) : (value) ) #define MI_RETURN_ERROR(error) \ return( MI_return_error() ? (error) : (error) ) #define MI_LOG_PKG_ERROR2(p1,p2) MI_log_pkg_error2(p1, p2) #define MI_LOG_PKG_ERROR3(p1,p2,p3) MI_log_pkg_error3(p1, p2, p3) #define MI_LOG_SYS_ERROR1(p1) MI_log_sys_error1(p1) #define MI_CHK_ERR(expr) {if ((expr)<0) MI_RETURN_ERROR(MI_ERROR);} /* Macros for converting data types. These macros are compound statements, so don't put a semi-colon after them. dvalue should be a double, type is an int NetCDF type, sign is one of MI_PRIV_UNSIGNED and MI_PRIV_SIGNED and ptr is a void pointer */ #define MI_TO_DOUBLE(dvalue, type, sign, ptr) \ switch (type) { \ case NC_BYTE : \ case NC_CHAR: \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = (double) *((unsigned char *) ptr); break; \ case MI_PRIV_SIGNED : \ dvalue = (double) *((signed char *) ptr); break; \ } \ break; \ case NC_SHORT : \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = (double) *((unsigned short *) ptr); break; \ case MI_PRIV_SIGNED : \ dvalue = (double) *((signed short *) ptr); break; \ } \ break; \ case NC_INT : \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = (double) *((unsigned int *) ptr); break; \ case MI_PRIV_SIGNED : \ dvalue = (double) *((signed int *) ptr); break; \ } \ break; \ case NC_FLOAT : \ dvalue = (double) *((float *) ptr); \ break; \ case NC_DOUBLE : \ dvalue = (double) *((double *) ptr); \ break; \ case NC_NAT : \ MI_LOG_PKG_ERROR2(MI_ERR_NONNUMERIC, \ "Attempt to convert NC_NAT value to double"); \ dvalue = 0; \ break; \ } #define MI_FROM_DOUBLE(dvalue, type, sign, ptr) \ switch (type) { \ case NC_BYTE : \ case NC_CHAR : \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = MAX(0, dvalue); \ dvalue = MIN(UCHAR_MAX, dvalue); \ *((unsigned char *) ptr) = ROUND(dvalue); \ break; \ case MI_PRIV_SIGNED : \ dvalue = MAX(SCHAR_MIN, dvalue); \ dvalue = MIN(SCHAR_MAX, dvalue); \ *((signed char *) ptr) = ROUND(dvalue); \ break; \ } \ break; \ case NC_SHORT : \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = MAX(0, dvalue); \ dvalue = MIN(USHRT_MAX, dvalue); \ *((unsigned short *) ptr) = ROUND(dvalue); \ break; \ case MI_PRIV_SIGNED : \ dvalue = MAX(SHRT_MIN, dvalue); \ dvalue = MIN(SHRT_MAX, dvalue); \ *((signed short *) ptr) = ROUND(dvalue); \ break; \ } \ break; \ case NC_INT : \ switch (sign) { \ case MI_PRIV_UNSIGNED : \ dvalue = MAX(0, dvalue); \ dvalue = MIN(UINT_MAX, dvalue); \ *((unsigned int *) ptr) = ROUND(dvalue); \ break; \ case MI_PRIV_SIGNED : \ dvalue = MAX(INT_MIN, dvalue); \ dvalue = MIN(INT_MAX, dvalue); \ *((signed int *) ptr) = ROUND(dvalue); \ break; \ } \ break; \ case NC_FLOAT : \ dvalue = MAX(-FLT_MAX,dvalue); \ *((float *) ptr) = MIN(FLT_MAX,dvalue); \ break; \ case NC_DOUBLE : \ *((double *) ptr) = dvalue; \ break; \ case NC_NAT : \ MI_LOG_PKG_ERROR2(MI_ERR_NONNUMERIC, \ "Attempt to convert to NC_NAT from double"); \ dvalue = 0; \ break; \ } /**/ #define _(x) x /* For future gettext */ #endif libminc-libminc-2-3-00/libsrc/minc_compat.c000066400000000000000000000210271257462267400205560ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #if MINC2 /* Ignore if not MINC2 */ /* minc_compat.c * * this code exists to provide a dispatch layer between the MI2* low-level * calls and their respective HDF5 and NetCDF implementations. * * Since each of these calls uses exactly one file descriptor, the logic is * simple: we just apply the correct operation based on a quick determination * of whether this is an HDF5 handle or a NetCDF file descriptor. */ #define _MI2_FORCE_NETCDF_ #include "minc_private.h" #include "hdf_convenience.h" /* */ MNCAPI int MI2varname(int fd, int varid, char *varnm) { if (MI2_ISH5OBJ(fd)) { return (hdf_varname(fd, varid, varnm)); } else { return (nc_inq_varname(fd, varid, varnm)); } } /* */ MNCAPI int MI2varid(int fd, const char *varnm) { if (MI2_ISH5OBJ(fd)) { return (hdf_varid(fd, varnm)); } else { return (ncvarid(fd, varnm)); } } /* */ MNCAPI int MI2attinq(int fd, int varid, const char *attnm, nc_type *type_ptr, int *length_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_attinq(fd, varid, attnm, type_ptr, length_ptr)); } else { int status; int oldncopts = ncopts; ncopts = 0; status = ncattinq(fd, varid, attnm, type_ptr, length_ptr); ncopts = oldncopts; if (status != 1 && oldncopts != 0) { fprintf(stderr, _("ncattinq: ncid %d: varid: %d: Attribute '%s' not found"), fd, varid, attnm); } return (status); } } MNCAPI int MI2attname(int fd, int varid, int attid, char *name) { if (MI2_ISH5OBJ(fd)) { return (hdf_attname(fd, varid, attid, name)); } else { return (ncattname(fd, varid, attid, name)); } } /* */ MNCAPI int MI2inquire(int fd, int *ndims_ptr, int *nvars_ptr, int *natts_ptr, int *unlimdim_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_inquire(fd, ndims_ptr, nvars_ptr, natts_ptr, unlimdim_ptr)); } else { return (ncinquire(fd, ndims_ptr, nvars_ptr, natts_ptr, unlimdim_ptr)); } } /* */ MNCAPI int MI2varinq(int fd, int varid, char *varnm_ptr, nc_type *type_ptr, int *ndims_ptr, int *dims_ptr, int *natts_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_varinq(fd, varid, varnm_ptr, type_ptr, ndims_ptr, dims_ptr, natts_ptr)); } else { return (ncvarinq(fd, varid, varnm_ptr, type_ptr, ndims_ptr, dims_ptr, natts_ptr)); } } /* */ MNCAPI int MI2dimid(int fd, const char *dimnm) { if (MI2_ISH5OBJ(fd)) { return (hdf_dimid(fd, dimnm)); } else { return (ncdimid(fd, dimnm)); } } /* */ MNCAPI int MI2diminq(int fd, int dimid, char *dimnm_ptr, long *len_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_diminq(fd, dimid, dimnm_ptr, len_ptr)); } else { return (ncdiminq(fd, dimid, dimnm_ptr, len_ptr)); } } /* */ MNCAPI int MI2dimdef(int fd, const char *dimnm, long length) { if (MI2_ISH5OBJ(fd)) { return (hdf_dimdef(fd, dimnm, length)); } else { return (ncdimdef(fd, dimnm, length)); } } /* */ MNCAPI int MI2attget(int fd, int varid, const char *attnm, void *value) { if (MI2_ISH5OBJ(fd)) { return (hdf_attget(fd, varid, attnm, value)); } else { return (ncattget(fd, varid, attnm, value)); } } /* */ MNCAPI int MI2attput(int fd, int varid, const char *attnm, nc_type val_typ, int val_len, const void *val_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_attput(fd, varid, attnm, val_typ, val_len, val_ptr)); } else { int old_ncopts = ncopts; int result; ncopts = 0; result = ncattput(fd, varid, attnm, val_typ, val_len, val_ptr); ncopts = old_ncopts; return (result); } } /* */ MNCAPI int MI2endef(int fd) { if (MI2_ISH5OBJ(fd)) { return (MI_NOERROR); /* Just a stub, HDF5 doesn't do this! */ } else { return (ncendef(fd)); } } /* */ MNCAPI int MI2vardef(int fd, const char *varnm, nc_type vartype, int ndims, const int *dimids) { if (MI2_ISH5OBJ(fd)) { return (hdf_vardef(fd, varnm, vartype, ndims, dimids)); } else { return (ncvardef(fd, varnm, vartype, ndims, dimids)); } } /* */ MNCAPI int MI2varget(int fd, int varid, const long *start_ptr, const long *count_ptr, void *val_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_varget(fd, varid, start_ptr, count_ptr, val_ptr)); } else { return (ncvarget(fd, varid, start_ptr, count_ptr, val_ptr)); } } /* */ MNCAPI int MI2varput(int fd, int varid, const long *start_ptr, const long *count_ptr, const void *val_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_varput(fd, varid, start_ptr, count_ptr, val_ptr)); } else { return (ncvarput(fd, varid, start_ptr, count_ptr, val_ptr)); } } /* */ MNCAPI int MI2varput1(int fd, int varid, const long *mindex_ptr, const void *val_ptr) { if (MI2_ISH5OBJ(fd)) { return (hdf_varput1(fd, varid, mindex_ptr, val_ptr)); } else { return (ncvarput1(fd, varid, mindex_ptr, val_ptr)); } } MNCAPI int MI2attdel(int fd, int varid, const char *attnm) { if (MI2_ISH5OBJ(fd)) { return (hdf_attdel(fd, varid, attnm)); } else { return (ncattdel(fd, varid, attnm)); } } /* */ MNCAPI int MI2dimrename(int fd, int dimid, const char *new_name) { if (MI2_ISH5OBJ(fd)) { return (hdf_dimrename(fd, dimid, new_name)); } else { return (ncdimrename(fd, dimid, new_name)); } } MNCAPI int MI2varputg(int fd, int varid, const long *startp, const long *countp, const long *stridep, const long *imapp, const void *valp) { if (MI2_ISH5OBJ(fd)) { return (hdf_varputg(fd, varid, startp, countp, stridep, imapp, valp)); } else { return (ncvarputg(fd, varid, startp, countp, stridep, imapp, valp)); } } MNCAPI int MI2attcopy(int infd, int invarid, const char *name, int outfd, int outvarid) { if (!MI2_ISH5OBJ(infd) && !MI2_ISH5OBJ(outfd)) { /* Trivial case. */ return (ncattcopy(infd, invarid, name, outfd, outvarid)); } else { /* Complex case. Using our own compatibility layer functions lets us * handle all three other possible combinations of infd & outfd types. */ nc_type att_type; int att_length; void *val_ptr; int status; status = MI2attinq(infd, invarid, name, &att_type, &att_length); if (status == MI_ERROR) { return (MI_ERROR); } /* Special case for att_type == NC_CHAR && att_length == 0 */ if (att_type == NC_CHAR && att_length == 0) { val_ptr = malloc(1); if (val_ptr == NULL) { return (MI_ERROR); } *(char *)val_ptr = '\0'; att_length = 1; status = MI_NOERROR; } else { val_ptr = malloc(MI2typelen(att_type) * att_length); if (val_ptr == NULL) { return (MI_ERROR); } status = MI2attget(infd, invarid, name, val_ptr); } if (status != MI_ERROR) { status = MI2attput(outfd, outvarid, name, att_type, att_length, val_ptr); } free(val_ptr); return (status); } } MNCAPI int MI2typelen(int type_id) { switch (type_id) { case NC_BYTE: case NC_CHAR: return (1); case NC_SHORT: return (2); case NC_INT: case NC_FLOAT: return (4); case NC_DOUBLE: return (8); default: break; } fprintf(stderr, _("Unknown type %d"), type_id); return (-1); } MNCAPI int MI2redef(int fd) { if (MI2_ISH5OBJ(fd)) { /* Do nothing, since there is no equivalent in HDF5. */ return (MI_NOERROR); } else { return (ncredef(fd)); } } MNCAPI int MI2sync(int fd) { if (MI2_ISH5OBJ(fd)) { /* Commit the (entire) file to disk. */ if (H5Fflush(fd, H5F_SCOPE_GLOBAL) < 0) { return (MI_ERROR); } else { return (MI_NOERROR); } } else { return (ncsync(fd)); } } MNCAPI int MI2setfill(int fd, int fillmode) { if (MI2_ISH5OBJ(fd)) { /* TODO: ??? */ return (MI_NOERROR); } else { return (ncsetfill(fd, fillmode)); } } #endif /* MINC2 defined */ libminc-libminc-2-3-00/libsrc/minc_compat.h000066400000000000000000000056421257462267400205700ustar00rootroot00000000000000#if MINC2 /* Functions for enabling/disabling error messages from the library. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ MNCAPI int MI2typelen(int); MNCAPI int MI2varname(int fd, int varid, char *varnm); MNCAPI int MI2varid(int fd, const char *varnm); MNCAPI int MI2attinq(int fd, int varid, const char *attnm, nc_type *type_ptr, int *length_ptr); MNCAPI int MI2attname(int fd, int varid, int attid, char *name); MNCAPI int MI2inquire(int fd, int *ndims_ptr, int *nvars_ptr, int *natts_ptr, int *unlimdim_ptr); MNCAPI int MI2varinq(int fd, int varid, char *varnm_ptr, nc_type *type_ptr, int *ndims_ptr, int *dims_ptr, int *natts_ptr); MNCAPI int MI2dimid(int fd, const char *dimnm); MNCAPI int MI2diminq(int fd, int dimid, char *dimnm_ptr, long *len_ptr); MNCAPI int MI2dimdef(int fd, const char *dimnm, long length); MNCAPI int MI2attget(int fd, int varid, const char *attnm, void *value); MNCAPI int MI2attput(int fd, int varid, const char *attnm, nc_type val_typ, int val_len, const void *val_ptr); MNCAPI int MI2endef(int fd); MNCAPI int MI2vardef(int fd, const char *varnm, nc_type vartype, int ndims, const int *dimids); MNCAPI int MI2varget(int fd, int varid, const long *start_ptr, const long *count_ptr, void *val_ptr); MNCAPI int MI2varput(int fd, int varid, const long *start_ptr, const long *count_ptr, const void *val_ptr); MNCAPI int MI2varput1(int fd, int varid, const long *mindex_ptr, const void *val_ptr); MNCAPI int MI2attdel(int fd, int varid, const char *attnm); MNCAPI int MI2dimrename(int fd, int dimid, const char *new_name); MNCAPI int MI2varputg(int fd, int varid, const long *startp, const long *countp, const long *stridep, const long *imapp, const void *valp); MNCAPI int MI2attcopy(int infd, int invarid, const char *name, int outfd, int outvarid); MNCAPI int MI2redef(int fd); MNCAPI int MI2sync(int fd); MNCAPI int MI2setfill(int fd, int fillmode); #ifndef _MI2_FORCE_NETCDF_ #define nctypelen MI2typelen #define ncvarname MI2varname #define ncvarid MI2varid #define ncdimid MI2dimid #define ncvarinq MI2varinq #define ncdiminq MI2diminq #define ncdimdef MI2dimdef #define ncattdel MI2attdel #define ncvardef MI2vardef #define ncvarput1 MI2varput1 #define ncvarput MI2varput #define ncvarget MI2varget #define ncattinq MI2attinq #define ncvarputg MI2varputg #define nccreate micreate #define ncopen miopen #define ncclose miclose #define ncattput MI2attput #define ncinquire MI2inquire #define ncattname MI2attname #define ncdimrename MI2dimrename #define ncattcopy MI2attcopy #define ncendef MI2endef #define ncattget MI2attget #define ncredef MI2redef #define ncsync MI2sync #define ncsetfill MI2setfill #ifndef NC_NOFILL #define NC_NOFILL 0x100 #endif #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _MI2_FORCE_NETCDF_ not defined */ #endif /* MINC2 */ libminc-libminc-2-3-00/libsrc/minc_config.h000066400000000000000000000007451257462267400205510ustar00rootroot00000000000000#ifndef MINC_CONFIG_H #define MINC_CONFIG_H #define MICFG_FORCE_V2 "MINC_FORCE_V2" #define MICFG_COMPRESS "MINC_COMPRESS" #define MICFG_CHUNKING "MINC_CHUNKING" #define MICFG_LOGFILE "MINC_LOGFILE" #define MICFG_LOGLEVEL "MINC_LOGLEVEL" #define MICFG_MAXBUF "MINC_MAX_FILE_BUFFER_KB" #define MICFG_MAXMEM "MINC_MAX_MEMORY_KB" extern int miget_cfg_bool(const char *); extern int miget_cfg_int(const char *); extern char * miget_cfg_str(const char *); #endif /* MINC_CONFIG_H */ libminc-libminc-2-3-00/libsrc/minc_convenience.c000066400000000000000000001537461257462267400216050ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_convenience.c @DESCRIPTION: File of convenience functions following the minc standard. @METHOD : Routines included in this file : public : miget_datatype miget_default_range miget_valid_range miset_valid_range miget_image_range mivar_exists miattput_pointer miattget_pointer miadd_child micreate_std_variable micreate_group_variable miget_version miappend_history micreate_ident private : MI_create_dim_variable MI_create_dimwidth_variable MI_create_image_variable MI_create_imaxmin_variable MI_verify_maxmin_dims MI_create_root_variable MI_create_simple_variable MI_add_stdgroup MI_is_in_list @CREATED : July 27, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: minc_convenience.c,v $ * Revision 6.21 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.20 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.19 2007/12/03 14:19:35 rotor * * fixed history appending bug (Thanks Claude) * * updated version for release * * few more fixes for CMake build * * Revision 6.18 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.17 2004/12/03 21:52:35 bert * Minor changes for Windows build * * Revision 6.16 2004/10/15 13:46:15 bert * Minor changes for Windows compatibility * * Revision 6.15 2004/08/26 16:14:21 bert * Fix up miappend_history() again * * Revision 6.14 2004/06/04 18:15:46 bert * Added micreate_ident() * * Revision 6.13 2004/04/27 15:49:17 bert * Use new logging, gettext preparation * * Revision 6.12 2004/03/24 20:53:48 bert * Increase att_length by one in miappend_history() in order to read the entire attribute * * Revision 6.11 2004/02/02 18:22:46 bert * Added miget_version() and miappend_history() * * Revision 6.10 2001/12/06 14:09:07 neelin * Corrected return from mivar_exists to use minc macro MI_RETURN so that * ncopts is properly restored. * * Revision 6.9 2001/11/13 14:15:18 neelin * Added functions miget_image_range and mivar_exists * * Revision 6.8 2001/10/17 14:32:20 neelin * Modified miset_valid_range to write out valid_range as double in all * cases except float. Unfortunately, writing out values in a type that * matched the type of the image variable caused problems with programs * linked against old minc libraries. * * Revision 6.7 2001/09/18 15:44:27 neelin * When output type is NC_BYTE, valid_range attribute should have type NC_SHORT. * * Revision 6.6 2001/08/20 13:19:14 neelin * Added function miattget_with_sign to allow the caller to specify the sign * of the input attribute since this information is ambiguous. This is * necessary for the valid_range attribute which should have the same sign * as the image data. Modified miget_valid_range to make use of this function. * * Revision 6.5 2001/08/16 19:24:11 neelin * Fixes to the code handling valid_range values. * * Revision 6.4 2001/08/16 16:41:32 neelin * Added library functions to handle reading of datatype, sign and valid range, * plus writing of valid range and setting of default ranges. These functions * properly handle differences between valid_range type and image type. Such * difference can cause valid data to appear as invalid when double to float * conversion causes rounding in the wrong direction (out of range). * Modified voxel_loop, volume_io and programs to use these functions. * * Revision 6.3 2001/08/16 13:32:18 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.2 2001/04/17 18:40:13 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:09 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.1 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.0 1994/09/28 10:38:02 neelin * Release of minc version 0.2 * * Revision 1.18 94/09/28 10:37:12 neelin * Pre-release * * Revision 1.17 93/08/11 12:06:19 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include "type_limits.h" #include "minc_varlists.h" #include #if HAVE_UNISTD_H #include /* for getpid() */ #endif /* HAVE_UNISTD_H */ /* Private functions */ PRIVATE int MI_create_dim_variable(int cdfid, const char *name, nc_type datatype, int ndims); PRIVATE int MI_create_dimwidth_variable(int cdfid, const char *name, nc_type datatype, int ndims); PRIVATE int MI_create_image_variable(int cdfid, const char *name, nc_type datatype, int ndims, const int dim[]); PRIVATE int MI_create_imaxmin_variable(int cdfid, const char *name, nc_type datatype, int ndims, const int dim[]); PRIVATE int MI_verify_maxmin_dims(int cdfid, int image_ndims, const int image_dim[], int maxmin_ndims, const int maxmin_dim[]); PRIVATE int MI_create_root_variable(int cdfid, const char *name); PRIVATE int MI_create_simple_variable(int cdfid, const char *name); PRIVATE int MI_add_stdgroup(int cdfid, int varid); PRIVATE int MI_is_in_list(const char *string, const char *list[]); /* ----------------------------- MNI Header ----------------------------------- @NAME : miget_datatype @INPUT : cdfid - cdf file id imgid - image variable id @OUTPUT : datatype is_signed - TRUE if type is signed @RETURNS : MI_ERROR when an error occurs. @DESCRIPTION: Gets the datatype and sign of the image variable. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines. @CREATED : August 15, 2001 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miget_datatype(int cdfid, int imgid, nc_type *datatype, int *is_signed) { int old_ncopts; int use_default_sign; char attstr[MI_MAX_ATTSTR_LEN]; MI_SAVE_ROUTINE_NAME("miget_datatype"); /* Get the type information for the variable */ if (ncvarinq(cdfid, imgid, NULL, datatype, NULL, NULL, NULL) == MI_ERROR) MI_RETURN(MI_ERROR); /* Save the ncopts value */ old_ncopts = ncopts; ncopts = 0; /* Get the sign information */ if ((miattgetstr(cdfid, imgid, MIsigntype, MI_MAX_ATTSTR_LEN, attstr) != NULL)) { use_default_sign = FALSE; if (strcmp(attstr, MI_SIGNED) == 0) *is_signed = TRUE; else if (strcmp(attstr, MI_UNSIGNED) == 0) *is_signed = FALSE; else use_default_sign = TRUE; } else { use_default_sign = TRUE; } /* Set a default sign if needed */ if (use_default_sign) { if (*datatype == NC_BYTE) *is_signed = FALSE; else *is_signed = TRUE; } /* Restore ncopts */ ncopts = old_ncopts; MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miget_default_range @INPUT : datatype is_signed - TRUE if type is signed @OUTPUT : default_range - array containing default range for variable @RETURNS : MI_NOERROR @DESCRIPTION: Gets the default range for a data type. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines. @CREATED : August 15, 2001 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miget_default_range(nc_type datatype, int is_signed, double default_range[]) { MI_SAVE_ROUTINE_NAME("miget_default_range"); switch (datatype) { case NC_INT: default_range[0] = (is_signed) ? INT_MIN : 0; default_range[1] = (is_signed) ? INT_MAX : UINT_MAX; break; case NC_SHORT: default_range[0] = (is_signed) ? SHRT_MIN : 0; default_range[1] = (is_signed) ? SHRT_MAX : USHRT_MAX; break; case NC_BYTE: default_range[0] = (is_signed) ? SCHAR_MIN : 0; default_range[1] = (is_signed) ? SCHAR_MAX : UCHAR_MAX; break; case NC_FLOAT: default_range[0] = -FLT_MAX; default_range[1] = FLT_MAX; break; case NC_DOUBLE: default_range[0] = -DBL_MAX; default_range[1] = DBL_MAX; break; default: default_range[0]= MI_DEFAULT_MIN; default_range[1]= MI_DEFAULT_MAX; break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miget_valid_range @INPUT : cdfid - cdf file id imgid - image variable id @OUTPUT : valid_range - array containing valid min and max of image @RETURNS : MI_ERROR when an error occurs. @DESCRIPTION: Gets the valid range for an image variable. Ensures that the values are cast to the appropriate type to ensure that they are correctly truncated. This is particularly important for float images. If the range cannot be found, then use the default values. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines. @CREATED : August 15, 2001 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miget_valid_range(int cdfid, int imgid, double valid_range[]) { int old_ncopts; int status; int length; nc_type datatype; int is_signed; char *att_sign; double temp; MI_SAVE_ROUTINE_NAME("miget_valid_range"); /* Get the type information for the variable */ if (miget_datatype(cdfid, imgid, &datatype, &is_signed) == MI_ERROR) MI_RETURN(MI_ERROR); /* Save the ncopts value */ old_ncopts = ncopts; ncopts = 0; /* Get the sign string for the attribute */ if (is_signed) att_sign = MI_SIGNED; else att_sign = MI_UNSIGNED; /* Get valid range */ status=miattget_with_sign(cdfid, imgid, MIvalid_range, att_sign, NC_DOUBLE, NULL, 2, valid_range, &length); /* If not there, look for the max and min */ if ((status==MI_ERROR) || (length!=2)) { /* Get the default range for the type */ (void) miget_default_range(datatype, is_signed, valid_range); /* Try to read the valid max */ (void) miattget_with_sign(cdfid, imgid, MIvalid_max, att_sign, NC_DOUBLE, NULL, 1, &valid_range[1], NULL); /* Try to read the valid min */ (void) miattget_with_sign(cdfid, imgid, MIvalid_min, att_sign, NC_DOUBLE, NULL, 1, &valid_range[0], NULL); } /* Restore the ncopts value */ ncopts = old_ncopts; /* Make sure that the first element is the minimum */ if (valid_range[1] < valid_range[0]) { temp = valid_range[0]; valid_range[0] = valid_range[1]; valid_range[1] = temp; } /* Cast to the appropriate type and back to make sure that things are rounded/truncated properly. This is only really needed for floats */ switch (datatype) { case NC_INT: case NC_SHORT: case NC_BYTE: if (is_signed) { valid_range[0] = (int) valid_range[0]; valid_range[1] = (int) valid_range[1]; } else { valid_range[0] = (unsigned int) valid_range[0]; valid_range[1] = (unsigned int) valid_range[1]; } break; case NC_FLOAT: valid_range[0] = (float) valid_range[0]; valid_range[1] = (float) valid_range[1]; break; } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miset_valid_range @INPUT : cdfid - cdf file id imgid - image variable id valid_range - array containing valid min and max of image @OUTPUT : (none) @RETURNS : MI_ERROR when an error occurs. @DESCRIPTION: Sets the valid range for an image variable. Ensures that the attribute types match the image variable type. This is particularly important for float images because of potential rounding when going from double to float. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines. @CREATED : August 15, 2001 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miset_valid_range(int cdfid, int imgid, const double valid_range[]) { nc_type datatype; int is_signed; int status; char *attname; float fval[2]; MI_SAVE_ROUTINE_NAME("miset_valid_range"); /* Get the type information for the variable */ if (miget_datatype(cdfid, imgid, &datatype, &is_signed) == MI_ERROR) MI_RETURN(MI_ERROR); /* Cast to the appropriate type and save. Originally, it was thought that casting to the type of the image variable would be a good idea because NetCDF says that it should. Unfortunately, this breaks compatibility in some cases with programs linked with old minc libraries, so we only cast for floats to avoid rounding problems (cast from double to float can put values out of range) - for everything else double is used. */ attname = MIvalid_range; switch (datatype) { case NC_FLOAT: fval[0] = valid_range[0]; fval[1] = valid_range[1]; status = ncattput(cdfid, imgid, attname, datatype, 2, fval); break; default: status = ncattput(cdfid, imgid, attname, NC_DOUBLE, 2, valid_range); break; } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miget_image_range @INPUT : cdfid - cdf file id @OUTPUT : image_range - array containing min and max of image-min/max for the entire file @RETURNS : MI_ERROR when an error occurs. @DESCRIPTION: Gets the image range for a file - that is, the maximum image-max value and the minimum image-min value. For float images, ensures that values are cast to float to ensure that they are correctly truncated. If the range cannot be found, then use the default values for int images and valid_range for floating-point images. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines. @CREATED : October 19, 2001 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miget_image_range(int cdfid, double image_range[]) { int oldncopts; /* For saving value of ncopt */ int vid[2]; /* Variable ids for min and max */ int imgid; /* Image variable id */ nc_type datatype; /* Type of image variable */ int is_signed; /* Indicates if image variable is signed */ int is_float, no_range_found; /* Flags */ int imm; /* For looping over min and max */ int ndims; /* Number of dimensions of variable */ int idim; /* For looping over dimensions */ int dim[MAX_VAR_DIMS]; /* Dimension ids of variable */ long ientry; /* For stepping through values */ long size; /* Size of min and max variables */ long start[MAX_VAR_DIMS]; /* Start of variable */ long count[MAX_VAR_DIMS]; /* Dimension sizes */ double *buffer; /* Pointer to buffer for min/max values */ MI_SAVE_ROUTINE_NAME("miget_image_range"); /* Set default values for image_range */ image_range[0] = MI_DEFAULT_MIN; image_range[1] = MI_DEFAULT_MAX; /* Get the image-min/max variable ids */ oldncopts=ncopts; ncopts=0; vid[0] = ncvarid(cdfid, MIimagemin); vid[1] = ncvarid(cdfid, MIimagemax); ncopts = oldncopts; /* Get the type information for the image variable */ if ( ((imgid = ncvarid(cdfid, MIimage)) == MI_ERROR) || (miget_datatype(cdfid, imgid, &datatype, &is_signed) == MI_ERROR) ) MI_RETURN(MI_ERROR); /* No max/min variables, so use valid_range values for floats if it is set and defaults otherwise */ if ((vid[0] == MI_ERROR) || (vid[1] == MI_ERROR)) { /* Check for a floating-point type - if it is, try to get the valid_range. If the valid_range was set to full range for the type, then that means that the valid range was probably not set (and if it was, it was not particularly reasonable). */ is_float = (datatype == NC_FLOAT || datatype == NC_DOUBLE); no_range_found = FALSE; if (is_float) { if (miget_valid_range(cdfid, imgid, image_range) == MI_ERROR) MI_RETURN(MI_ERROR); no_range_found = (datatype == NC_FLOAT && image_range[1] == FLT_MAX) || (datatype == NC_DOUBLE && image_range[1] == DBL_MAX); } /* If it is not a float, or if the valid range was not set, then use the default. */ if (!is_float || no_range_found) { image_range[0] = MI_DEFAULT_MIN; image_range[1] = MI_DEFAULT_MAX; } } /* If the variables are there then get the max and min and fastest varying dimension */ else { /* Set initial values */ image_range[0] = DBL_MAX; image_range[1] = -DBL_MAX; /* Loop over min and max */ for (imm=0; imm<2; imm++) { /* Get dimension list */ MI_CHK_ERR(ncvarinq(cdfid, vid[imm], NULL, NULL, &ndims, dim, NULL)) /* Loop through dimensions, getting dimension sizes and total min/max variable size */ size=1; /* Size of MIimagemin/max variable */ for (idim=0; idim0) { if (ncattget(cdfid, parent_varid, MIchildren, child_list) == MI_ERROR) { FREE(child_list); milog_message(MI_MSG_READATTR, MIchildren); MI_RETURN(MI_ERROR); } if (child_list[child_list_size-1] == '\0') child_list_size--; /* Copy the child list element separator (only if there are other elements in the list */ (void) strcpy(&child_list[child_list_size], MI_CHILD_SEPARATOR); child_list_size += strlen(MI_CHILD_SEPARATOR); } /* Get pointer to name of new child */ new_child = &child_list[child_list_size]; /* Add the new child name to the list */ if (ncvarinq(cdfid, child_varid, new_child, NULL, NULL, NULL, NULL) == MI_ERROR) { FREE(child_list); MI_RETURN_ERROR(MI_ERROR); } /* Check for multiple copies of child */ if (strstr(child_list, new_child) != new_child) { child_list_size -= strlen(MI_CHILD_SEPARATOR); child_list[child_list_size] = '\0'; } /* Put the attribute MIchildren */ if (miattputstr(cdfid, parent_varid, MIchildren, child_list) == MI_ERROR) { FREE(child_list); MI_RETURN_ERROR(MI_ERROR); } /* Get the parent variable name */ if (ncvarinq(cdfid, parent_varid, child_list, NULL, NULL, NULL, NULL) == MI_ERROR) { FREE(child_list); MI_RETURN_ERROR(MI_ERROR); } /* Put the attribute MIparent */ if (miattputstr(cdfid, child_varid, MIparent, child_list) == MI_ERROR) { FREE(child_list); MI_RETURN_ERROR(MI_ERROR); } FREE(child_list); MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micreate_std_variable @INPUT : cdfid - cdf file id name - name of standard variable to create datatype - type of data to store (see ncvardef) ndims - number of dimensions of variable (see ncvardef) dim - vector of variable dimensions (see ncvardef) @OUTPUT : (none) @RETURNS : id of created variable, or MI_ERROR if an error occurs @DESCRIPTION: Creates a standard MINC variable by calling ncvardef and then sets default attributes. The standard variables are identified by name, so an unrecognised name produces an error. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 5, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int micreate_std_variable(int cdfid, const char *name, nc_type datatype, int ndims, int dim[]) { int varid; /* Created variable id */ MI_SAVE_ROUTINE_NAME("micreate_std_variable"); /* Check to see if it is a standard dimension */ if (MI_is_in_list(name, dimvarlist)) { MI_CHK_ERR(varid=MI_create_dim_variable(cdfid, name, datatype, ndims)) } /* Check for a dimension width */ else if (MI_is_in_list(name, dimwidthlist)) { MI_CHK_ERR(varid=MI_create_dimwidth_variable(cdfid, name, datatype, ndims)) } /* Check for a standard variable or group */ else if (MI_is_in_list(name, varlist)) { if (STRINGS_EQUAL(name, MIimage)) MI_CHK_ERR(varid=MI_create_image_variable(cdfid, name, datatype, ndims, dim)) else if ((STRINGS_EQUAL(name, MIimagemax)) || (STRINGS_EQUAL(name, MIimagemin))) MI_CHK_ERR(varid=MI_create_imaxmin_variable(cdfid, name, datatype, ndims, dim)) else if (STRINGS_EQUAL(name, MIrootvariable)) MI_CHK_ERR(varid=MI_create_root_variable(cdfid, name)) else if (STRINGS_EQUAL(name, MIpatient)) MI_CHK_ERR(varid=MI_create_simple_variable(cdfid, name)) else if (STRINGS_EQUAL(name, MIstudy)) MI_CHK_ERR(varid=MI_create_simple_variable(cdfid, name)) else if (STRINGS_EQUAL(name, MIacquisition)) MI_CHK_ERR(varid=MI_create_simple_variable(cdfid, name)) else { /*milog_message(MI_MSG_VARNOTSTD, name);*/ MI_RETURN(MI_ERROR); } } /* If not in any list, then return an error */ else { /*milog_message(MI_MSG_VARNOTSTD, name);*/ MI_RETURN(MI_ERROR); } MI_RETURN(varid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_create_dim_variable @INPUT : cdfid - cdf file id name - name of standard variable to create datatype - type of data to store ndims - number of dimensions - must be 0 or 1 @OUTPUT : (none) @RETURNS : id of created variable, or MI_ERROR if an error occurs @DESCRIPTION: Creates a standard MINC dimension variable by calling ncvardef and then sets default attributes. The standard variables are identified by name, so an unrecognised name produces an error. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 5, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_create_dim_variable(int cdfid, const char *name, nc_type datatype, int ndims) { int dimid; /* Dimension id (for dimensions variables) */ int varid; /* Created variable id */ MI_SAVE_ROUTINE_NAME("MI_create_dim_variable"); /* Check for MIvector_dimension - no associated variable */ if (STRINGS_EQUAL(name, MIvector_dimension)) { /*milog_message(MI_MSG_VARNOTSTD, name);*/ MI_RETURN(MI_ERROR); } /* Check for ndims being 0 or 1 */ if (ndims>1) { milog_message(MI_MSG_TOOMANYDIMS, 1); MI_RETURN(MI_ERROR); } /* Look for dimension and create the variable */ MI_CHK_ERR(dimid=ncdimid(cdfid, name)) MI_CHK_ERR(varid=ncvardef(cdfid, name, datatype, ndims, &dimid)) /* Standard attributes */ MI_CHK_ERR(miattputstr(cdfid, varid, MIvarid, MI_STDVAR)) MI_CHK_ERR(miattputstr(cdfid, varid, MIvartype, MI_DIMENSION)) MI_CHK_ERR(miattputstr(cdfid, varid, MIversion, MI_CURRENT_VERSION)) /* Add comments for spatial dimensions */ if (STRINGS_EQUAL(name, MIxspace)) {MI_CHK_ERR(miattputstr(cdfid, varid, MIcomments, _("X increases from patient left to right")))} else if (STRINGS_EQUAL(name, MIyspace)) {MI_CHK_ERR(miattputstr(cdfid, varid, MIcomments, _("Y increases from patient posterior to anterior")))} else if (STRINGS_EQUAL(name, MIzspace)) {MI_CHK_ERR(miattputstr(cdfid, varid, MIcomments, _("Z increases from patient inferior to superior")))} /* Dimension attributes */ if (ndims==0) { MI_CHK_ERR(miattputstr(cdfid, varid, MIspacing, MI_REGULAR)) } else { MI_CHK_ERR(miattputstr(cdfid, varid, MIspacing, MI_IRREGULAR)) } if (STRINGS_EQUAL(name, MItime)) MI_CHK_ERR(miattputstr(cdfid, varid, MIalignment, MI_START)) else MI_CHK_ERR(miattputstr(cdfid, varid, MIalignment, MI_CENTRE)) MI_RETURN(varid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_create_dimwidth_variable @INPUT : cdfid - cdf file id name - name of standard variable to create datatype - type of data to store ndims - number of dimensions - must be 0 or 1 @OUTPUT : (none) @RETURNS : id of created variable, or MI_ERROR if an error occurs @DESCRIPTION: Creates a standard MINC dimension width variable by calling ncvardef and then sets default attributes. The standard variables are identified by name, so an unrecognised name produces an error. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 5, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_create_dimwidth_variable(int cdfid, const char *name, nc_type datatype, int ndims) { int dimid; /* Dimension id (for dimensions variables) */ int varid; /* Created variable id */ char string[MAX_NC_NAME]; /* String for dimension name */ char *str; MI_SAVE_ROUTINE_NAME("MI_create_dimwidth_variable"); /* Look for dimension name in name (remove width suffix) */ if ((str=strstr(strcpy(string, name),MI_WIDTH_SUFFIX)) == NULL) { milog_message(MI_MSG_DIMWIDTH); MI_RETURN(MI_ERROR); } *str='\0'; /* Check for ndims being 0 or 1 */ if (ndims>1) { milog_message(MI_MSG_TOOMANYDIMS, 1); MI_RETURN(MI_ERROR); } /* Look for the dimension */ MI_CHK_ERR(dimid=ncdimid(cdfid, string)) /* Create the variable and set defaults */ MI_CHK_ERR(varid=ncvardef(cdfid, name, datatype, ndims, &dimid)) MI_CHK_ERR(miattputstr(cdfid, varid, MIvarid, MI_STDVAR)) MI_CHK_ERR(miattputstr(cdfid, varid, MIvartype, MI_DIM_WIDTH)) MI_CHK_ERR(miattputstr(cdfid, varid, MIversion, MI_CURRENT_VERSION)) if (ndims==0) { MI_CHK_ERR(miattputstr(cdfid, varid, MIspacing, MI_REGULAR)) } else { MI_CHK_ERR(miattputstr(cdfid, varid, MIspacing, MI_IRREGULAR)) } MI_CHK_ERR(miattputstr(cdfid, varid, MIfiltertype, MI_SQUARE)) MI_RETURN(varid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_create_image_variable @INPUT : cdfid - cdf file id name - name of standard variable to create datatype - type of data to store (see ncvardef) ndims - number of dimensions of variable (see ncvardef) dim - vector of variable dimensions (see ncvardef) @OUTPUT : (none) @RETURNS : id of created variable, or MI_ERROR if an error occurs @DESCRIPTION: Creates a standard MINC image variable by calling ncvardef and then sets default attributes. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 6, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_create_image_variable(int cdfid, const char *name, nc_type datatype, int ndims, const int dim[]) { int varid; /* Created variable id */ int max_varid; /* Variable id for dimensional attribute */ int min_varid; /* Variable id for dimensional attribute */ int maxmin_ndims; /* Number of dimensions in max/min variable */ int maxmin_dim[MAX_VAR_DIMS]; /* Dimensions of max/min variable */ int oldncopts; /* For saving and restoring ncopts */ MI_SAVE_ROUTINE_NAME("MI_create_image_variable"); /* Look to see if MIimagemax or MIimagemin exist for dimension checking and pointers */ oldncopts=ncopts; ncopts=0; max_varid=ncvarid(cdfid, MIimagemax); min_varid=ncvarid(cdfid, MIimagemin); ncopts=oldncopts; if (max_varid != MI_ERROR) { /* Get MIimagemax dimensions */ MI_CHK_ERR(ncvarinq(cdfid, max_varid, NULL, NULL, &maxmin_ndims, maxmin_dim, NULL)) MI_CHK_ERR(MI_verify_maxmin_dims(cdfid, ndims, dim, maxmin_ndims, maxmin_dim)) } if (min_varid != MI_ERROR) { /* Get MIimagemin dimensions */ MI_CHK_ERR(ncvarinq(cdfid, min_varid, NULL, NULL, &maxmin_ndims, maxmin_dim, NULL)) MI_CHK_ERR(MI_verify_maxmin_dims(cdfid, ndims, dim, maxmin_ndims, maxmin_dim)) } /* Create the variable */ MI_CHK_ERR(varid=ncvardef(cdfid, name, datatype, ndims, dim)) /* Standard attributes */ MI_CHK_ERR(MI_add_stdgroup(cdfid, varid)) /* Create pointers to MIimagemax and MIimagemin if they exist */ if (max_varid!=MI_ERROR) MI_CHK_ERR(miattput_pointer(cdfid, varid, MIimagemax, max_varid)) if (min_varid!=MI_ERROR) MI_CHK_ERR(miattput_pointer(cdfid, varid, MIimagemin, min_varid)) MI_RETURN(varid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_create_imaxmin_variable @INPUT : cdfid - cdf file id name - name of standard variable to create datatype - type of data to store (see ncvardef) ndims - number of dimensions of variable (see ncvardef) dim - vector of variable dimensions (see ncvardef) @OUTPUT : (none) @RETURNS : id of created variable, or MI_ERROR if an error occurs @DESCRIPTION: Creates a standard MINC image maximum or minimum dimensional attribute variable by calling ncvardef and then sets default attributes. If MIimage exists, then dimensions are checked (MIimagemax and MIimagemin cannot vary over the first two dimensions of MIimage (or first three if the first is MIvector_dimension)), and a pointer attribute is added to MIimage. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 6, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_create_imaxmin_variable(int cdfid, const char *name, nc_type datatype, int ndims, const int dim[]) { int varid; /* Created variable id */ int image_varid; /* Variable id for image */ int image_ndims; /* Number of image dimensions */ int image_dim[MAX_VAR_DIMS]; /* Image dimensions */ void *fillp; /* Pointer to fill value */ int oldncopts; /* For saving and restoring ncopts */ int index; static char fill_b[]={0,1}; static short fill_s[]={0,1}; static int fill_i[]={0,1}; static float fill_f[]={0.0,1.0}; static double fill_d[]={0.0,1.0}; MI_SAVE_ROUTINE_NAME("MI_create_imaxmin_variable"); /* Look to see if MIimage exists for dimension checking and pointers */ oldncopts=ncopts; ncopts=0; image_varid=ncvarid(cdfid, MIimage); ncopts=oldncopts; if (image_varid != MI_ERROR) { /* Get image dimensions */ MI_CHK_ERR(ncvarinq(cdfid, image_varid, NULL, NULL, &image_ndims, image_dim, NULL)) MI_CHK_ERR(MI_verify_maxmin_dims(cdfid, image_ndims, image_dim, ndims, dim)) } /* Create the variable */ MI_CHK_ERR(varid=ncvardef(cdfid, name, datatype, ndims, dim)) /* Standard attributes */ MI_CHK_ERR(miattputstr(cdfid, varid, MIvarid, MI_STDVAR)) MI_CHK_ERR(miattputstr(cdfid, varid, MIvartype, MI_VARATT)) MI_CHK_ERR(miattputstr(cdfid, varid, MIversion, MI_CURRENT_VERSION)) /* Attribute for setting default values to something reasonable */ index = STRINGS_EQUAL(name, MIimagemax) ? 1 : 0; fillp = ((datatype==NC_BYTE) ? (void *) &fill_b[index] : (datatype==NC_SHORT) ? (void *) &fill_s[index] : (datatype==NC_INT) ? (void *) &fill_i[index] : (datatype==NC_FLOAT) ? (void *) &fill_f[index] : (datatype==NC_DOUBLE) ? (void *) &fill_d[index] : (void *) NULL); if (fillp != NULL) { MI_CHK_ERR(ncattput(cdfid, varid, MI_FillValue, datatype, 1, fillp)) } /* Create pointer from MIimage to max or min if MIimage exists */ if (image_varid != MI_ERROR) MI_CHK_ERR(miattput_pointer(cdfid, image_varid, name, varid)) MI_RETURN(varid); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_verify_maxmin_dims @INPUT : cdfid - cdf file id image_ndims - number of MIimage dimensions image_dim - image dimensions maxmin_ndims - number of MIimagemax or MIimagemin dimensions maxmin_dim - max/min dimensions @OUTPUT : (none) @RETURNS : MI_ERROR if dimensions don't agree @DESCRIPTION: Verifies that MIimage dimensions and MIimagemax/MIimagemin dimensions agree. MIimagemax/MIimagemin cannot vary over the two fastest varying (last) dimensions of MIimage - three fastest dimensions if MIvector_dimension is the fastest varying dimension of MIimage (this maintains the image nature of MIimage and its dimensional attributes MIimagemax and MIimagemin). @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : August 7, 1992 @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_verify_maxmin_dims(int cdfid, int image_ndims, const int image_dim[], int maxmin_ndims, const int maxmin_dim[]) { char dimname[MAX_NC_NAME]; int i,j; int nbaddims = 2; /* Number of dimension over which max/min should not vary */ MI_SAVE_ROUTINE_NAME("MI_verify_maxmin_dims"); /* Check to see if last dimension is MIvectordimension */ MI_CHK_ERR(ncdiminq(cdfid, image_dim[image_ndims-1], dimname, NULL)) if (STRINGS_EQUAL(dimname, MIvector_dimension)) nbaddims++; /* Loop through illegal image dimensions (last nbaddims) checking dimensions against maxmin_dim */ for (i=MAX(0,image_ndims-nbaddims); i 0 ) { if (att_val[att_len-1] != '\n') { att_val[att_len] = '\n'; att_len++; } } } /* Append the new history. */ strcpy(att_val + att_len, tm_stamp); r = miattputstr(fd, NC_GLOBAL, MIhistory, att_val); free(att_val); return (r); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_is_in_list @INPUT : string - string for which to look list - list in which to look (must be NULL terminated) @OUTPUT : (none) @RETURNS : TRUE if found, FALSE if not @DESCRIPTION: Searches a list of character strings for string and returns TRUE if the string is in the list. @METHOD : @GLOBALS : @CALLS : @CREATED : August 5, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_is_in_list(const char *string, const char *list[]) { int i; MI_SAVE_ROUTINE_NAME("MI_is_in_list"); for (i=0; list[i] != NULL; i++) { if (STRINGS_EQUAL(string, list[i])) MI_RETURN(TRUE); } MI_RETURN(FALSE); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miget_version @INPUT : (none) @OUTPUT : const char * @RETURNS : A string describing the MINC library version. @DESCRIPTION: Just returns a fixed string. @METHOD : @GLOBALS : @CALLS : @CREATED : December 8 2003 @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI const char * miget_version(void) { return (MINC_VERSION); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micreate_ident @INPUT : (none) @OUTPUT : int @RETURNS : The length of the ID string @DESCRIPTION: Creates a (hopefully) unique identifier to associate with a MINC file, by concatenating various information about the system, process, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : 2004-May-11 @MODIFIED : ---------------------------------------------------------------------------- */ #define MI_IDENT_SEP ':' MNCAPI int micreate_ident( char * id_str, size_t length ) { static int identx = 1; /* Static ID counter */ time_t now; struct tm tm_buf; char host_str[128]; char user_str[128]; char *temp_ptr; char time_str[26]; int result; if (gethostname(host_str, sizeof(host_str)) != 0) { strcpy(host_str, "unknown"); } temp_ptr = getenv("LOGNAME"); if (temp_ptr != NULL) { strcpy(user_str, temp_ptr); } else { strcpy(user_str, "nobody"); } time(&now); #ifdef _MSC_VER memcpy(&tm_buf, localtime(&now), sizeof(tm_buf)); #else localtime_r(&now, &tm_buf); #endif strftime(time_str, sizeof(time_str), "%Y.%m.%d.%H.%M.%S", &tm_buf); result = snprintf(id_str, length, "%s%c%s%c%s%c%u%c%u", user_str, MI_IDENT_SEP, host_str, MI_IDENT_SEP, time_str, MI_IDENT_SEP, getpid(), MI_IDENT_SEP, identx++); return (result); } libminc-libminc-2-3-00/libsrc/minc_error.c000066400000000000000000000256371257462267400204370ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_error.c @DESCRIPTION: File containing routines to do error handling for MINC package. Should be called through macros in minc_private.h @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : * $Log: minc_error.c,v $ * Revision 6.8 2009-01-20 11:58:13 rotor * * CMakeLists.txt: updated version * * Updated Changelog to include releases * * Warning cleanups below * * conversion/dcm2mnc/minc_file.c: fixed printf type * * conversion/dcm2mnc/siemens_to_dicom.c: fixed printf type * * conversion/ecattominc/machine_indep.c: added string.h and fixed * 2 fprintf missing format args * * conversion/micropet/upet2mnc.c: fixed two fprintf format args * * conversion/minctoecat/ecat_write.c: added string.h * * conversion/minctoecat/minctoecat.c: added missing argument to fprintf * * conversion/nifti1/mnc2nii.c: fixed incorrect printf type * * progs/mincview/invert_raw_image.c: added fwrite checking * * Revision 6.7 2008/04/11 05:15:00 rotor * * rewrote error code (Claude) to remove global defs that were * causing build problems with DYLIB on OSX * * Revision 6.6 2008/01/17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.5 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.4 2004/10/15 13:46:15 bert * Minor changes for Windows compatibility * * Revision 6.3 2004/04/27 15:47:25 bert * Move most message text into this file * * Revision 6.2 2001/04/17 18:40:13 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:09 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:04 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:37:16 neelin * Pre-release * * Revision 1.6 93/08/11 12:06:24 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include #include #include #include struct mierror_entry { int level; char *msgfmt; }; /* MINC routine name variable, call depth counter (for keeping track of minc routines calling minc routines) and variable for keeping track of callers ncopts. All of these are for error logging. */ static char *minc_routine_name = "MINC"; static int minc_call_depth = 0; static int minc_trash_var = 0; static struct mierror_entry mierror_table[] = { { MI_MSG_ERROR, "Cannot uncompress the file" }, /* MI_MSG_UNCMPFAIL */ { MI_MSG_ERROR, "Can't write compressed file" }, /* MI_MSG_NOWRITECMP */ { MI_MSG_ERROR, "Unable to open file '%s'" }, /* MI_MSG_OPENFILE */ { MI_MSG_ERROR, "Unable to create file '%s'"}, /* MI_MSG_CREATEFILE */ { MI_MSG_ERROR, "Error closing file"}, /* MI_MSG_CLOSEFILE */ { MI_MSG_WARNING, "Attribute '%s' not found"}, /* MI_MSG_FINDATTR */ { MI_MSG_ERROR, "Attribute '%s' is non-numeric"}, /* MI_MSG_ATTRNOTNUM */ { MI_MSG_ERROR, "Can't read attribute '%s'"}, /* MI_MSG_READATTR */ { MI_MSG_FATAL, "No memory for attribute '%s'"}, /* MI_MSG_NOMEMATTR */ { MI_MSG_ERROR, "Conversion error for attribute '%s'"}, /* MI_MSG_CONVATTR */ { MI_MSG_ERROR, "Attribute '%s' is not a scalar"}, /* MI_MSG_ATTRNOTSCALAR */ { MI_MSG_ERROR, "Attribute '%s' is not a string"}, /* MI_MSG_ATTRNOTSTR */ { MI_MSG_ERROR, "Can't write attribute '%s'"}, /* MI_MSG_WRITEATTR */ { MI_MSG_ERROR, "Can't read variable ID# %d"}, /* MI_MSG_READVAR */ { MI_MSG_ERROR, "Can't write variable ID# %d"}, /* MI_MSG_WRITEVAR */ { MI_MSG_ERROR, "Can't find variable ID# %d"}, /* MI_MSG_FINDVAR */ { MI_MSG_ERROR, "Can't read attribute count"}, /* MI_MSG_ATTRCOUNT */ { MI_MSG_ERROR, "Can't read attribute name"}, /* MI_MSG_ATTRNAME */ { MI_MSG_ERROR, "Can't copy attribute '%s'"}, /* MI_MSG_COPYATTR */ { MI_MSG_ERROR, "Can't read variable information"}, /* MI_MSG_VARINQ */ { MI_MSG_ERROR, "Can't get unlimited dimension"}, /* MI_MSG_UNLIMDIM */ { MI_MSG_ERROR, "Can't get dimension information"}, /* MI_MSG_DIMINQ */ { MI_MSG_ERROR, "Variable already defined with different size"}, /* MI_MSG_VARCONFLICT */ { MI_MSG_ERROR, "Can't define dimension '%s'"}, /* MI_MSG_DIMDEF */ { MI_MSG_ERROR, "Can't define variable '%s'"}, /* MI_MSG_VARDEF */ { MI_MSG_ERROR, "Variables do not match for value copy"}, /* MI_MSG_VARMISMATCH */ { MI_MSG_ERROR, "Variables have dimensions of different size"}, /* MI_MSG_VARDIFFSIZE */ { MI_MSG_ERROR, "Can't read variable count"}, /* MI_MSG_VARCOUNT */ { MI_MSG_ERROR, "Variable '%s' not copied"}, /* MI_MSG_OUTPUTVAR */ { MI_MSG_ERROR, "Error copying variable"}, /* MI_MSG_COPYVAR */ { MI_MSG_ERROR, "Non-numeric datatype"}, /* MI_MSG_VARNOTNUM */ { MI_MSG_FATAL, "Can't allocate %d bytes"}, /* MI_MSG_OUTOFMEM */ { MI_MSG_ERROR, "Attribute '%s' is not a pointer"}, /* MI_MSG_ATTRNOTPTR */ { MI_MSG_ERROR, "Variable '%s' is not a standard MINC variable"}, /* MI_MSG_VARNOTSTD */ { MI_MSG_ERROR, "Bad dimension width suffix"}, /* MI_MSG_DIMWIDTH */ { MI_MSG_ERROR, "Imagemax/min dimensions vary over image dimensions"}, /* MI_MSG_MAXMINVARY */ { MI_MSG_FATAL, "Should not happen!"}, /* MI_MSG_SNH */ { MI_MSG_FATAL, "Unknown integer size %d"}, /* MI_MSG_INTSIZE */ { MI_MSG_FATAL, "Unknown float size %d"}, /* MI_MSG_FLTSIZE */ { MI_MSG_FATAL, "Unknown type class %d"}, /* MI_MSG_TYPECLASS */ { MI_MSG_ERROR, "Function '%s' not implemented"}, /* MI_MSG_NOTIMPL */ { MI_MSG_FATAL, "Unknown type %d"}, /* MI_MSG_BADTYPE */ { MI_MSG_ERROR, "Can't open dataset %s"}, /* MI_MSG_OPENDSET */ { MI_MSG_ERROR, "Can't read dataset %s"}, /* MI_MSG_READDSET */ { MI_MSG_ERROR, "Can't write dataset %s"}, /* MI_MSG_WRITEDSET */ { MI_MSG_ERROR, "Can't use more than %d dimensions"}, /* MI_MSG_TOOMANYDIMS */ { MI_MSG_ERROR, "Attempt to modify an attached image conversion variable"}, /* MI_MSG_ICVATTACHED */ { MI_MSG_ERROR, "Illegal ICV identifier"}, /* MI_MSG_BADICV */ { MI_MSG_ERROR, "Error setting ICV property: %s"}, /* MI_MSG_BADPROP */ { MI_MSG_ERROR, "ICV is not attached"}, /* MI_MSG_ICVNOTATTACHED */ { MI_MSG_ERROR, "Invalid ICV coordinates"}, /* MI_MSG_ICVCOORDS */ { MI_MSG_ERROR, "Illegal variable access operation" } /* MI_MSG_BADOP */ }; SEMIPRIVATE int MI_save_routine_name(char *name) { /* no idea what peter was up to here */ /* minc_trash_var = (((minc_call_depth++)==0) ? MI_save_routine_name(name) : * MI_NOERROR)) */ if( (minc_call_depth++)==0 ) { minc_routine_name = name; minc_trash_var = TRUE; } else { minc_trash_var = MI_NOERROR; } return(TRUE); } SEMIPRIVATE int MI_return(void) { /* no idea what peter was up to here */ /* return( (((--minc_call_depth)!=0) || MI_return()) ? (value) : (value)) */ return( ((--minc_call_depth)!=0) || TRUE ); } SEMIPRIVATE int MI_return_error(void) { /* no idea what peter was up to here */ /* return( (((--minc_call_depth)!=0) || MI_return_error()) ? (error) : (error)) */ if( (--minc_call_depth)==0 ) { MI_LOG_PKG_ERROR2(0, "MINC package entry point"); } return( TRUE ); } SEMIPRIVATE void MI_log_pkg_error2(int p1, char *p2) { (void) fprintf(stderr, "%s: ", minc_routine_name); (void) fprintf(stderr, "%s", p2); (void) fputc('\n', stderr); (void) fflush(stderr); } SEMIPRIVATE void MI_log_pkg_error3(int p1, char *p2, char *p3) { (void) fprintf(stderr, "%s: ", minc_routine_name); (void) fprintf(stderr, p2, p3); (void) fputc('\n', stderr); (void) fflush(stderr); } SEMIPRIVATE void MI_log_sys_error1(char *p1) { char *message; int errnum = errno; (void) fprintf(stderr, "%s", minc_routine_name); (void) fprintf(stderr, "%s", p1); if (errnum == 0) { (void) fputc('\n', stderr); } else { message = strerror(errnum); if (message == NULL) message = "Unknown error"; (void) fprintf(stderr, ": %s\n", message); } (void) fflush(stderr); } /* By default, print all messages of severity error, or worse. */ static struct { int level; char prog[128]; FILE *fp; } _MI_log = { MI_MSG_ERROR, {""}, NULL }; MNCAPI void milog_init(const char *name) { char *fname_str = miget_cfg_str(MICFG_LOGFILE); int level = miget_cfg_int(MICFG_LOGLEVEL); if (fname_str == NULL) { _MI_log.fp = stderr; } else if (!strcmp(fname_str, "stdout") || !strcmp(fname_str, "-")) { _MI_log.fp = stdout; } else { if (*fname_str == '+') { _MI_log.fp = fopen(fname_str + 1, "w+"); } else { _MI_log.fp = fopen(fname_str, "w"); } } if (level != 0) { _MI_log.level = level; } strncpy(_MI_log.prog, name, sizeof(_MI_log.prog) - 1 ); if (fname_str != NULL) { free(fname_str); } } MNCAPI int milog_set_verbosity(int lvl) { int lvl_prev = _MI_log.level; _MI_log.level = lvl; return (lvl_prev); } MNCAPI int milog_message(mimsgcode_t code, ...) { va_list ap; int lvl; const char *fmt; if (_MI_log.fp == NULL) { _MI_log.fp = stderr; } lvl = mierror_table[code-MI_MSG_BASE].level; fmt = mierror_table[code-MI_MSG_BASE].msgfmt; /* Log the message if the the message priority * is less than the configured priority. Always log fatal errors. */ if ((lvl <= _MI_log.level) || lvl == MI_MSG_FATAL) { if (_MI_log.prog[0] != '\0') { fprintf(_MI_log.fp, "%s ", _MI_log.prog); } fprintf(_MI_log.fp, "(from %s): ", minc_routine_name); va_start(ap, code); vfprintf(_MI_log.fp, fmt, ap); va_end(ap); fprintf(_MI_log.fp, "\n"); fflush(_MI_log.fp); } /* For fatal messages, give up and exit. */ if (lvl == MI_MSG_FATAL) { exit(-1); } return (MI_ERROR); /* Just for convenience */ } libminc-libminc-2-3-00/libsrc/minc_error.h000066400000000000000000000054751257462267400204420ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_error.h @DESCRIPTION: File containing error codes for minc package. @GLOBALS : @CALLS : @CREATED : 17 Feburary, 2004 (Robert Vincent) @MODIFIED : * * $Log: minc_error.h,v $ * Revision 6.4 2008-04-11 05:15:00 rotor * * rewrote error code (Claude) to remove global defs that were * causing build problems with DYLIB on OSX * * Revision 6.3 2004/12/03 21:52:35 bert * Minor changes for Windows build * * Revision 6.2 2004/10/15 13:46:15 bert * Minor changes for Windows compatibility * * Revision 6.1 2004/04/27 15:42:47 bert * Define MINC logging codes * * @COPYRIGHT : Copyright 2004 Robert Vincent, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC_ERROR_H #define MINC_ERROR_H /* message levels */ #define MI_MSG_FATAL 0 #define MI_MSG_ERROR 1 #define MI_MSG_WARNING 2 #define MI_MSG_INFO 3 #define MI_MSG_DEBUG 4 #define MI_MSG_BASE (10000) typedef enum mimsgcode { MI_MSG_UNCMPFAIL = MI_MSG_BASE, MI_MSG_NOWRITECMP, MI_MSG_OPENFILE, MI_MSG_CREATEFILE, MI_MSG_CLOSEFILE, MI_MSG_FINDATTR, MI_MSG_ATTRNOTNUM, MI_MSG_READATTR, MI_MSG_NOMEMATTR, MI_MSG_CONVATTR, MI_MSG_ATTRNOTSCALAR, MI_MSG_ATTRNOTSTR, MI_MSG_WRITEATTR, MI_MSG_READVAR, MI_MSG_WRITEVAR, MI_MSG_FINDVAR, MI_MSG_ATTRCOUNT, MI_MSG_ATTRNAME, MI_MSG_COPYATTR, MI_MSG_VARINQ, MI_MSG_UNLIMDIM, MI_MSG_DIMINQ, MI_MSG_VARCONFLICT, MI_MSG_DIMDEF, MI_MSG_VARDEF, MI_MSG_VARMISMATCH, MI_MSG_VARDIFFSIZE, MI_MSG_VARCOUNT, MI_MSG_OUTPUTVAR, MI_MSG_COPYVAR, MI_MSG_VARNOTNUM, MI_MSG_OUTOFMEM, MI_MSG_ATTRNOTPTR, MI_MSG_VARNOTSTD, MI_MSG_DIMWIDTH, MI_MSG_MAXMINVARY, MI_MSG_SNH, MI_MSG_INTSIZE, MI_MSG_FLTSIZE, MI_MSG_TYPECLASS, MI_MSG_NOTIMPL, MI_MSG_BADTYPE, MI_MSG_OPENDSET, MI_MSG_READDSET, MI_MSG_WRITEDSET, MI_MSG_TOOMANYDIMS, MI_MSG_ICVATTACHED, MI_MSG_BADICV, MI_MSG_BADPROP, MI_MSG_ICVNOTATTACHED, MI_MSG_ICVCOORDS, MI_MSG_BADOP } mimsgcode_t; MNCAPI int milog_message(mimsgcode_t code, ...); #endif /* MINC_ERROR_H not defined */ libminc-libminc-2-3-00/libsrc/minc_format_convert.c000066400000000000000000000035451257462267400223300ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_format_convert.c @COPYRIGHT : Copyright 2013 Vladimir S. FONOV , McConnell Brain Imaging Centre, Copyright 2003 Robert Vincent, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include static int micopy(int old_fd, int new_fd) { /* Copy all variable definitions (and global attributes). */ micopy_all_var_defs(old_fd, new_fd, 0, NULL); ncendef(new_fd); micopy_all_var_values(old_fd, new_fd, 0, NULL); return MI_NOERROR; } MNCAPI int minc_format_convert(const char *input,const char *output) { int old_fd; int new_fd; int flags; struct mi2opts opts; old_fd = miopen(input, NC_NOWRITE); if (old_fd < 0) { perror(input); return MI_ERROR; } flags = NC_CLOBBER|MI2_CREATE_V2; memset(&opts,0,sizeof(struct mi2opts)); opts.struct_version = MI2_OPTS_V1; new_fd = micreatex(output, flags, &opts); if (new_fd < 0) { perror(output); exit MI_ERROR; } micopy(old_fd, new_fd); miclose(old_fd); miclose(new_fd); return MI_NOERROR; } libminc-libminc-2-3-00/libsrc/minc_format_convert.h000066400000000000000000000017341257462267400223330ustar00rootroot00000000000000#ifndef MINC_FORMAT_CONVERT_H #define MINC_FORMAT_CONVERT_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_format_convert.h @COPYRIGHT : Copyright 2013 Vladimir S. FONOV , McConnell Brain Imaging Centre, Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #endif /*MINC_FORMAT_CONVERT_H*/libminc-libminc-2-3-00/libsrc/minc_private.h000066400000000000000000000054261257462267400207570ustar00rootroot00000000000000#ifndef MINC_PRIVATE_H #define MINC_PRIVATE_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_private.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: The general include file for MINC routines. @METHOD : @GLOBALS : @CALLS : @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : * $Log: minc_private.h,v $ * Revision 6.4 2004-12-14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.3 2004/10/15 13:47:31 bert * Minor changes for Windows compatibility * * Revision 6.2 2004/04/27 15:47:47 bert * #include minc_config.h and minc_error.h * * Revision 6.1 1999/10/19 14:45:09 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.1 1995/02/08 19:00:55 neelin * Removed include of math.h * * Revision 2.0 1994/09/28 10:38:07 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:37:27 neelin * Pre-release * * Revision 1.6 93/10/06 10:00:30 neelin * Added include of memory.h for memcpy on SUNs. * * Revision 1.5 93/08/11 12:06:39 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_private.h,v 6.4 2004-12-14 23:53:46 bert Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ #if defined(_MSC_VER) /* If we are building on the Microsoft C compiler, we want to * explicitly export all public functions from the DLL */ #define MNCAPI __declspec(dllexport) #else #define MNCAPI #endif #include "config.h" #define _GNU_SOURCE 1 /* Include all BSD & GNU interfaces */ #include #include #include #include #include "minc.h" #include "minc_useful.h" #include "minc_basic.h" #include "minc_structures.h" #include "minc_routines.h" #include "minc_config.h" #include "minc_error.h" #endif libminc-libminc-2-3-00/libsrc/minc_routines.h000066400000000000000000000101161257462267400211450ustar00rootroot00000000000000#ifndef MINC_ROUTINES_H #define MINC_ROUTINES_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_routines.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Provides prototypes for private and semiprivate MINC routines. @METHOD : @GLOBALS : @CALLS : @CREATED : August 28, 1992 (Peter Neelin) @MODIFIED : * $Log: minc_routines.h,v $ * Revision 6.4 2005-08-26 21:04:58 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.3 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.2 2004/10/15 13:47:39 bert * Minor changes for Windows compatibility * * Revision 6.1 1999/10/19 14:45:10 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.2 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.1 1995/01/20 15:21:20 neelin * Added midecompress_file with ability to decompress only the header of a file. * * Revision 2.0 94/09/28 10:38:08 neelin * Release of minc version 0.2 * * Revision 1.9 94/09/28 10:37:29 neelin * Pre-release * * Revision 1.8 93/08/11 12:06:41 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_routines.h,v 6.4 2005-08-26 21:04:58 bert Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ /* MINC routines that should only be visible to the package (semiprivate) */ /* From minc_error.c */ SEMIPRIVATE int MI_save_routine_name(char *name); SEMIPRIVATE int MI_return(void); SEMIPRIVATE int MI_return_error(void); SEMIPRIVATE void MI_log_pkg_error2(int p1, char *p2); SEMIPRIVATE void MI_log_pkg_error3(int p1, char *p2, char *p3); SEMIPRIVATE void MI_log_sys_error1(char *p1); /* From value_conversion.c */ SEMIPRIVATE int MI_varaccess(int operation, int cdfid, int varid, long start[], long count[], nc_type datatype, int sign, void *values, int *bufsize_step, mi_icv_type *icvp); SEMIPRIVATE int MI_var_loop(int ndims, long start[], long count[], int value_size, int *bufsize_step, long max_buffer_size, void *caller_data, int (*action_func) (int, long [], long [], long, void *, void *)); SEMIPRIVATE int MI_get_sign_from_string(nc_type datatype, const char *sign); SEMIPRIVATE int MI_convert_type(long number_of_values, nc_type intype, int insign, void *invalues, nc_type outtype, int outsign, void *outvalues, mi_icv_type *icvp); /* From image_conversion.c */ SEMIPRIVATE mi_icv_type *MI_icv_chkid(int icvid); #if MINC2 extern int hdf_var_declare(int fd, char *varnm, char *varpath, int ndims, hsize_t *sizes); extern int hdf_create(const char *path, int cmode, struct mi2opts *opts_ptr); extern int hdf_open(const char *path, int mode); extern int hdf_close(int fd); #endif /* MINC2 */ #endif libminc-libminc-2-3-00/libsrc/minc_simple.c000066400000000000000000000756211257462267400205750ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_simple.c @DESCRIPTION: Simplified interface to 3D and 4D minc files @METHOD : Routines included in this file : @CREATED : August 20, 2004. (Bert Vincent, Montreal Neurological Institute) @MODIFIED : * $Log: minc_simple.c,v $ * Revision 6.6 2008-01-11 07:17:07 stever * Remove unused variables. * * Revision 6.5 2005/08/26 21:04:58 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.4 2005/05/20 21:01:52 bert * Declare all public functions MNCAPI * * Revision 6.3 2005/01/04 22:45:57 bert * Adopt Leila's changes to restructure_array() and make appropriate corrections to the rest of the code * * Revision 6.2 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.1 2004/11/01 22:06:48 bert * Initial checkin, simplified minc interface * @COPYRIGHT : Copyright 2004 Robert Vincent, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include /* for sqrt */ #include /* for DBL_MAX */ #include "minc_simple.h" #include "restructure.h" /* Trivial MINC interface */ #define MI_S_T 0 #define MI_S_Z 1 #define MI_S_Y 2 #define MI_S_X 3 #define MI_S_NDIMS 4 static char *minc_dimnames[] = { MItime, MIzspace, MIyspace, MIxspace }; /* Structures used to represent a file in memory. */ struct att_info { char att_name[128]; nc_type att_type; int att_len; void *att_val; }; struct var_info { char var_name[128]; nc_type var_type; int var_natts; int var_ndims; int var_dims[MAX_NC_DIMS]; struct att_info *var_atts; }; struct file_info { int file_ndims; int file_nvars; int file_natts; struct att_info *file_atts; struct var_info *file_vars; }; static int minc_simple_to_nc_type(int minctype, nc_type *nctype, char **signstr) { switch (minctype) { case MINC_TYPE_CHAR: *nctype = NC_BYTE; *signstr = MI_SIGNED; break; case MINC_TYPE_UCHAR: *nctype = NC_BYTE; *signstr = MI_UNSIGNED; break; case MINC_TYPE_SHORT: *nctype = NC_SHORT; *signstr = MI_SIGNED; break; case MINC_TYPE_USHORT: *nctype = NC_SHORT; *signstr = MI_UNSIGNED; break; case MINC_TYPE_INT: *nctype = NC_INT; *signstr = MI_SIGNED; break; case MINC_TYPE_UINT: *nctype = NC_INT; *signstr = MI_UNSIGNED; break; case MINC_TYPE_FLOAT: *nctype = NC_FLOAT; *signstr = MI_SIGNED; break; case MINC_TYPE_DOUBLE: *nctype = NC_DOUBLE; *signstr = MI_SIGNED; break; default: return (MINC_STATUS_ERROR); } return (MINC_STATUS_OK); } MNCAPI int minc_file_size(char *path, long *ct, long *cz, long *cy, long *cx, long *cvoxels, long *cbytes) { int fd; nc_type nctype; int dim_id[MI_S_NDIMS]; long dim_len[MI_S_NDIMS]; int i; int var_id; int var_ndims; int var_dims[MAX_NC_DIMS]; long voxel_count; long byte_count; int old_ncopts; fd = miopen(path, NC_NOWRITE); if (fd < 0) { return (MINC_STATUS_ERROR); } old_ncopts = ncopts; ncopts = 0; for (i = 0; i < MI_S_NDIMS; i++) { dim_id[i] = ncdimid(fd, minc_dimnames[i]); if (dim_id[i] >= 0) { ncdiminq(fd, dim_id[i], NULL, &dim_len[i]); } else { dim_len[i] = 0; } } ncopts = old_ncopts; if (ct != NULL) { *ct = dim_len[MI_S_T]; } if (cz != NULL) { *cz = dim_len[MI_S_Z]; } if (cy != NULL) { *cy = dim_len[MI_S_Y]; } if (cx != NULL) { *cx = dim_len[MI_S_X]; } var_id = ncvarid(fd, MIimage); if (cvoxels != NULL || cbytes != NULL) { ncvarinq(fd, var_id, NULL, &nctype, &var_ndims, var_dims, NULL); voxel_count = 1; for (i = 0; i < var_ndims; i++) { long length; ncdiminq(fd, var_dims[i], NULL, &length); voxel_count *= length; } byte_count = voxel_count * nctypelen(nctype); if (cvoxels != NULL) { *cvoxels = voxel_count; } if (cbytes != NULL) { *cbytes = byte_count; } } return (MINC_STATUS_OK); } MNCAPI int minc_load_data(char *path, void *dataptr, int datatype, long *ct, long *cz, long *cy, long *cx, double *dt, double *dz, double *dy, double *dx, void **infoptr) { int fd; /* MINC file descriptor */ nc_type nctype; /* netCDF type */ char *signstr; /* MI_SIGNED or MI_UNSIGNED */ int length; int dim_id[MI_S_NDIMS]; long dim_len[MI_S_NDIMS]; int i, j; /* Generic loop counters */ int var_id; int var_ndims; int var_dims[MAX_NC_DIMS]; int icv; /* MINC image conversion variable */ long start[MI_S_NDIMS]; long count[MI_S_NDIMS]; size_t ucount[MI_S_NDIMS]; int dir[MI_S_NDIMS]; /* Dimension "directions" */ int map[MI_S_NDIMS]; /* Dimension mapping */ int old_ncopts; /* For storing the old state of ncopts */ double *p_dtmp; long *p_ltmp; struct file_info *p_file; struct att_info *p_att; int r; /* Generic return code */ *infoptr = NULL; fd = miopen(path, NC_NOWRITE); if (fd < 0) { return (MINC_STATUS_ERROR); } old_ncopts = ncopts; ncopts = 0; for (i = 0; i < MI_S_NDIMS; i++) { dim_id[i] = ncdimid(fd, minc_dimnames[i]); if (dim_id[i] >= 0) { ncdiminq(fd, dim_id[i], NULL, &dim_len[i]); var_id = ncvarid(fd, minc_dimnames[i]); ncattinq(fd, var_id, MIstep, &nctype, &length); switch (i) { case MI_S_T: p_ltmp = ct; p_dtmp = dt; break; case MI_S_X: p_ltmp = cx; p_dtmp = dx; break; case MI_S_Y: p_ltmp = cy; p_dtmp = dy; break; case MI_S_Z: p_ltmp = cz; p_dtmp = dz; break; default: return (MINC_STATUS_ERROR); } if (nctype == NC_DOUBLE && length == 1) { ncattget(fd, var_id, MIstep, p_dtmp); } else { *p_dtmp = 0; /* Unknown/not set */ } *p_ltmp = dim_len[i]; } else { dim_len[i] = 0; } } ncopts = old_ncopts; var_id = ncvarid(fd, MIimage); ncvarinq(fd, var_id, NULL, &nctype, &var_ndims, var_dims, NULL); if (var_ndims != 3 && var_ndims != 4) { return (MINC_STATUS_ERROR); } /* We want the data to wind up in t, x, y, z order. */ for (i = 0; i < MI_S_NDIMS; i++) { map[i] = -1; } for (i = 0; i < var_ndims; i++) { if (var_dims[i] == dim_id[MI_S_T]) { map[MI_S_T] = i; } else if (var_dims[i] == dim_id[MI_S_X]) { map[MI_S_X] = i; } else if (var_dims[i] == dim_id[MI_S_Y]) { map[MI_S_Y] = i; } else if (var_dims[i] == dim_id[MI_S_Z]) { map[MI_S_Z] = i; } } icv = miicv_create(); minc_simple_to_nc_type(datatype, &nctype, &signstr); miicv_setint(icv, MI_ICV_TYPE, nctype); miicv_setstr(icv, MI_ICV_SIGN, signstr); miicv_attach(icv, fd, var_id); for (i = 0; i < var_ndims; i++) { start[i] = 0; } for (i = 0; i < MI_S_NDIMS; i++) { if (map[i] >= 0) { count[map[i]] = dim_len[i]; } } r = miicv_get(icv, start, count, dataptr); if (r < 0) { return (MINC_STATUS_ERROR); } if (map[MI_S_T] >= 0) { if (*dt < 0) { dir[MI_S_T] = -1; *dt = -*dt; } else { dir[MI_S_T] = 1; } } if (map[MI_S_X] >= 0) { if (*dx < 0) { dir[MI_S_X] = -1; *dx = -*dx; } else { dir[MI_S_X] = 1; } } if (map[MI_S_Y] >= 0) { if (*dy < 0) { dir[MI_S_Y] = -1; *dy = -*dy; } else { dir[MI_S_Y] = 1; } } if (map[MI_S_Z] >= 0) { if (*dz < 0) { dir[MI_S_Z] = -1; *dz = -*dz; } else { dir[MI_S_Z] = 1; } } if (var_ndims == 3) { for (i = 1; i < MI_S_NDIMS; i++) { map[i-1] = map[i]; dir[i-1] = dir[i]; } } j = 0; for (i = 0; i < MI_S_NDIMS; i++) { if (dim_len[i] > 0) { ucount[j++] = dim_len[i]; } } restructure_array(var_ndims, dataptr, ucount, nctypelen(nctype), map, dir); miicv_detach(icv); miicv_free(icv); old_ncopts = ncopts; ncopts = 0; /* Generate the complete infoptr array. * This is essentially an in-memory copy of the variables and attributes * in the file. */ p_file = (struct file_info *) malloc(sizeof (struct file_info)); ncinquire(fd, &p_file->file_ndims, &p_file->file_nvars, &p_file->file_natts, NULL); p_file->file_atts = (struct att_info *) malloc(sizeof (struct att_info) * p_file->file_natts); p_file->file_vars = (struct var_info *) malloc(sizeof (struct var_info) * p_file->file_nvars); for (i = 0; i < p_file->file_natts; i++) { p_att = &p_file->file_atts[i]; ncattname(fd, NC_GLOBAL, i, p_att->att_name); ncattinq(fd, NC_GLOBAL, p_att->att_name, &p_att->att_type, &p_att->att_len); p_att->att_val = malloc(p_att->att_len * nctypelen(p_att->att_type)); ncattget(fd, NC_GLOBAL, p_att->att_name, p_att->att_val); } for (i = 0; i < p_file->file_nvars; i++) { struct var_info *p_var = &p_file->file_vars[i]; ncvarinq(fd, i, p_var->var_name, &p_var->var_type, &p_var->var_ndims, p_var->var_dims, &p_var->var_natts); p_var->var_atts = malloc(p_var->var_natts * sizeof (struct att_info)); if (ncdimid(fd, p_var->var_name) >= 0) { /* It's a dimension variable, have to treat it specially... */ } for (j = 0; j < p_var->var_natts; j++) { p_att = &p_var->var_atts[j]; ncattname(fd, i, j, p_att->att_name); ncattinq(fd, i, p_att->att_name, &p_att->att_type, &p_att->att_len); p_att->att_val = malloc(p_att->att_len * nctypelen(p_att->att_type)); ncattget(fd, i, p_att->att_name, p_att->att_val); } } *infoptr = p_file; ncopts = old_ncopts; miclose(fd); return (MINC_STATUS_OK); } MNCAPI void minc_free_info(void *infoptr) { struct file_info *p_file; int i, j; if ((p_file = infoptr) != NULL) { if (p_file->file_natts != 0 && p_file->file_atts != NULL) { for (i = 0; i < p_file->file_natts; i++) { free(p_file->file_atts[i].att_val); } free(p_file->file_atts); } if (p_file->file_nvars != 0 && p_file->file_vars != NULL) { for (i = 0; i < p_file->file_nvars; i++) { if (p_file->file_vars[i].var_natts != 0 && p_file->file_vars[i].var_atts != NULL) { for (j = 0; j < p_file->file_vars[i].var_natts; j++) { if (p_file->file_vars[i].var_atts[j].att_val != NULL) { free(p_file->file_vars[i].var_atts[j].att_val); } } free(p_file->file_vars[i].var_atts); } } free(p_file->file_vars); } } } /* int minc_save_start() * * Returns an opaque handle which must be passed to successive calls to * minc_save_data() and ultimately to minc_save_done(). */ MNCAPI int minc_save_start(char *path, /* Path to the file */ int filetype, /* Date type as stored in the file */ long ct, /* Total length of time axis, in voxels */ long cz, /* Total length of Z axis, in voxels */ long cy, /* Total length of Y axis, in voxels */ long cx, /* Total length of X axis, in voxels */ double dt, /* Sample width along time axis, in seconds */ double dz, /* Sample width along Z axis, in mm */ double dy, /* Sample width along Y axis, in mm */ double dx, /* Sample width along X axis, in mm */ void *infoptr, /* Opaque file structure information */ const char *history) /* New history information */ { int fd; /* MINC file descriptor */ int dim_id[MI_S_NDIMS]; /* netCDF dimension ID array */ int var_ndims; /* Number of dimensions per variable */ int var_dims[MI_S_NDIMS]; /* Dimension ID's per variable */ int i, j; /* Generic loop counters */ int old_ncopts; /* For supressing fatal error messages */ struct file_info *p_file; /* For accessing the file structure */ struct var_info *p_var; struct att_info *p_att; int var_id; /* netCDF ID for variable */ char *signstr; nc_type nctype; old_ncopts = ncopts; ncopts = 0; fd = micreate(path, NC_CLOBBER); ncopts = old_ncopts; if (fd < 0) { return (MINC_STATUS_ERROR); } if (ct > 0) { dim_id[MI_S_T] = ncdimdef(fd, MItime, ct); micreate_std_variable(fd, MItime, NC_INT, 0, NULL); if (dt > 0.0) { miattputdbl(fd, ncvarid(fd, MItime), MIstep, dt); } } else { dim_id[MI_S_T] = -1; } if (cz > 0) { dim_id[MI_S_Z] = ncdimdef(fd, MIzspace, cz); micreate_std_variable(fd, MIzspace, NC_INT, 0, NULL); if (dz > 0.0) { miattputdbl(fd, ncvarid(fd, MIzspace), MIstep, dz); } } else { dim_id[MI_S_Z] = -1; } if (cy > 0) { dim_id[MI_S_Y] = ncdimdef(fd, MIyspace, cy); micreate_std_variable(fd, MIyspace, NC_INT, 0, NULL); if (dy > 0.0) { miattputdbl(fd, ncvarid(fd, MIyspace), MIstep, dy); } } else { return (MINC_STATUS_ERROR); /* Must define Y */ } if (cx > 0) { dim_id[MI_S_X] = ncdimdef(fd, MIxspace, cx); micreate_std_variable(fd, MIxspace, NC_INT, 0, NULL); if (dx > 0.0) { miattputdbl(fd, ncvarid(fd, MIxspace), MIstep, dx); } } else { return (MINC_STATUS_ERROR); /* Must define X */ } /* The var_dims[] array is the array of actual dimension ID's to * be used in defining the image variables. Here I set it up by * copying all valid dimension ID's from the dim_id[] array. */ var_ndims = 0; for (i = 0; i < MI_S_NDIMS; i++) { if (dim_id[i] >= 0) { var_dims[var_ndims] = dim_id[i]; var_ndims++; } } minc_simple_to_nc_type(filetype, &nctype, &signstr); /* Create the image variable with the standard * dimension order, and the same type as the template * file. */ micreate_std_variable(fd, MIimage, nctype, var_ndims, var_dims); micreate_std_variable(fd, MIimagemin, NC_DOUBLE, 1, var_dims); micreate_std_variable(fd, MIimagemax, NC_DOUBLE, 1, var_dims); /* Copy information from the infoptr to the output. */ if ((p_file = infoptr) != NULL) { old_ncopts = ncopts; ncopts = 0; for (i = 0; i < p_file->file_natts; i++) { p_att = &p_file->file_atts[i]; if (strcmp(p_att->att_name, "ident") != 0) { ncattput(fd, NC_GLOBAL, p_att->att_name, p_att->att_type, p_att->att_len, p_att->att_val); } } for (i = 0; i < p_file->file_nvars; i++) { p_var = &p_file->file_vars[i]; if ((var_id = ncvarid(fd, p_var->var_name)) < 0) { var_id = ncvardef(fd, p_var->var_name, p_var->var_type, p_var->var_ndims, p_var->var_dims); } for (j = 0; j < p_var->var_natts; j++) { p_att = &p_var->var_atts[j]; ncattput(fd, var_id, p_att->att_name, p_att->att_type, p_att->att_len, p_att->att_val); } } ncopts = old_ncopts; } miattputstr(fd, ncvarid(fd, MIimage), MIcomplete, MI_FALSE); miattputstr(fd, ncvarid(fd, MIimage), MIsigntype, signstr); miappend_history(fd, history); ncendef(fd); return fd; } /* Internal function */ static void find_minmax(void *dataptr, long datacount, int datatype, double *min, double *max) { *min = DBL_MAX; *max = -DBL_MAX; switch (datatype) { case MINC_TYPE_CHAR: { char *c_ptr = dataptr; while (datacount--) { if (*c_ptr > *max) { *max = *c_ptr; } if (*c_ptr < *min) { *min = *c_ptr; } c_ptr++; } } break; case MINC_TYPE_UCHAR: { unsigned char *c_ptr = dataptr; while (datacount--) { if (*c_ptr > *max) { *max = *c_ptr; } if (*c_ptr < *min) { *min = *c_ptr; } c_ptr++; } } break; case MINC_TYPE_SHORT: { short *s_ptr = dataptr; while (datacount--) { if (*s_ptr > *max) { *max = *s_ptr; } if (*s_ptr < *min) { *min = *s_ptr; } s_ptr++; } } break; case MINC_TYPE_USHORT: { unsigned short *s_ptr = dataptr; while (datacount--) { if (*s_ptr > *max) { *max = *s_ptr; } if (*s_ptr < *min) { *min = *s_ptr; } s_ptr++; } } break; case MINC_TYPE_INT: { int *i_ptr = dataptr; while (datacount--) { if (*i_ptr > *max) { *max = *i_ptr; } if (*i_ptr < *min) { *min = *i_ptr; } i_ptr++; } } break; case MINC_TYPE_UINT: { unsigned int *i_ptr = dataptr; while (datacount--) { if (*i_ptr > *max) { *max = *i_ptr; } if (*i_ptr < *min) { *min = *i_ptr; } i_ptr++; } } break; case MINC_TYPE_FLOAT: { float *f_ptr = dataptr; while (datacount--) { if (*f_ptr > *max) { *max = *f_ptr; } if (*f_ptr < *min) { *min = *f_ptr; } f_ptr++; } } break; case MINC_TYPE_DOUBLE: { double *d_ptr = dataptr; while (datacount--) { if (*d_ptr > *max) { *max = *d_ptr; } if (*d_ptr < *min) { *min = *d_ptr; } d_ptr++; } } break; default: return; } } MNCAPI int minc_save_data(int fd, void *dataptr, int datatype, long st, long sz, long sy, long sx, long ct, long cz, long cy, long cx) { nc_type nctype; char *signstr; int i; int var_id; int var_ndims; int var_dims[MAX_NC_DIMS]; int icv; long start[MI_S_NDIMS]; long count[MI_S_NDIMS]; int old_ncopts; int r; double min, max; long slice_size; long index; int dtbytes; /* Length of datatype in bytes */ old_ncopts = ncopts; ncopts = 0; var_id = ncvarid(fd, MIimage); ncvarinq(fd, var_id, NULL, NULL, &var_ndims, var_dims, NULL); ncopts = old_ncopts; if (var_ndims < 2 || var_ndims > 4) { return (MINC_STATUS_ERROR); } r = minc_simple_to_nc_type(datatype, &nctype, &signstr); if (r == MINC_STATUS_ERROR) { return (MINC_STATUS_ERROR); } dtbytes = nctypelen(nctype); /* Update the image-min and image-max values */ if (ct > 0) { slice_size = cz * cy * cx; index = st; for (i = 0; i < ct; i++) { find_minmax((char *) dataptr + (dtbytes * slice_size * i), slice_size, datatype, &min, &max); mivarput1(fd, ncvarid(fd, MIimagemin), &index, NC_DOUBLE, MI_SIGNED, &min); mivarput1(fd, ncvarid(fd, MIimagemax), &index, NC_DOUBLE, MI_SIGNED, &max); index++; } } else { slice_size = cy * cx; index = sz; for (i = 0; i < cz; i++) { find_minmax((char *) dataptr + (dtbytes * slice_size * i), slice_size, datatype, &min, &max); mivarput1(fd, ncvarid(fd, MIimagemin), &index, NC_DOUBLE, MI_SIGNED, &min); mivarput1(fd, ncvarid(fd, MIimagemax), &index, NC_DOUBLE, MI_SIGNED, &max); index++; } } /* We want the data to wind up in t, x, y, z order. */ icv = miicv_create(); if (icv < 0) { return (MINC_STATUS_ERROR); } r = miicv_setint(icv, MI_ICV_TYPE, nctype); if (r < 0) { return (MINC_STATUS_ERROR); } r = miicv_setstr(icv, MI_ICV_SIGN, signstr); if (r < 0) { return (MINC_STATUS_ERROR); } r = miicv_setint(icv, MI_ICV_DO_NORM, 1); if (r < 0) { return (MINC_STATUS_ERROR); } r = miicv_setint(icv, MI_ICV_DO_FILLVALUE, 1); if (r < 0) { return (MINC_STATUS_ERROR); } r = miicv_attach(icv, fd, var_id); if (r < 0) { return (MINC_STATUS_ERROR); } i = 0; switch (var_ndims) { case 4: count[i] = ct; start[i] = st; i++; /* fall through */ case 3: count[i] = cz; start[i] = sz; i++; /* fall through */ case 2: count[i] = cy; start[i] = sy; i++; count[i] = cx; start[i] = sx; i++; break; } r = miicv_put(icv, start, count, dataptr); if (r < 0) { return (MINC_STATUS_ERROR); } miicv_detach(icv); miicv_free(icv); return (MINC_STATUS_OK); } MNCAPI int minc_save_done(int fd) { miattputstr(fd, ncvarid(fd, MIimage), MIcomplete, MI_TRUE); miclose(fd); return (MINC_STATUS_OK); } static void normalize_vector(double vector[MINC_3D]) { int i; double magnitude; magnitude = 0.0; for (i = 0; i < MINC_3D; i++) { magnitude += (vector[i] * vector[i]); } magnitude = sqrt(magnitude); if (magnitude > 0.0) { for (i = 0; i < MINC_3D; i++) { vector[i] /= magnitude; } } } MNCAPI int minc_get_world_transform(char *path, double transform[MINC_3D][MINC_3D+1]) { int i, j; double dircos[MINC_3D]; double step, start; char *dimensions[] = { MIxspace, MIyspace, MIzspace }; int length; int fd; int varid; int old_ncopts; old_ncopts = ncopts; ncopts = 0; fd = miopen(path, NC_NOWRITE); if (fd < 0) { ncopts = old_ncopts; return (MINC_STATUS_ERROR); } /* Zero the matrix */ for (i = 0; i < MINC_3D; i++) { for (j = 0; j < MINC_3D + 1; j++) { transform[i][j] = 0.0; } transform[i][i] = 1.0; } for (j = 0; j < MINC_3D; j++) { /* Set default values */ step = 1.0; start = 0.0; for (i = 0; i < MINC_3D; i++) { dircos[i] = 0.0; } dircos[j] = 1.0; varid = ncvarid(fd, dimensions[j]); miattget(fd, varid, MIstart, NC_DOUBLE, 1, &start, &length); miattget(fd, varid, MIstep, NC_DOUBLE, 1, &step, &length); miattget(fd, varid, MIdirection_cosines, NC_DOUBLE, 3, dircos, &length); normalize_vector(dircos); /* Put them in the matrix. */ for (i = 0; i < MINC_3D; i++) { transform[i][j] = step * dircos[i]; transform[i][MINC_3D] += start * dircos[i]; } } ncopts = old_ncopts; return (MINC_STATUS_OK); } #ifdef MINC_SIMPLE_TEST /* #define NC_TYPE MINC_TYPE_FLOAT #define MM_TYPE float #define MM_FMT "%20.16g" #define EPSILON 1.0e-6 */ #define NC_TYPE MINC_TYPE_DOUBLE #define MM_TYPE double #define MM_FMT "%20.16g" #define EPSILON 1.0e-9 /* #define NC_TYPE MINC_TYPE_CHAR #define MM_TYPE unsigned char #define MM_FMT "%d" */ /* #define NC_TYPE MINC_TYPE_SHORT #define MM_TYPE short #define MM_FMT "%d" */ /* #define NC_TYPE MINC_TYPE_INT #define MM_TYPE int #define MM_FMT "%d" */ #define CX 11 #define CY 12 #define CZ 9 main(int argc, char **argv) { short buffer[1][256][256][256]; long ct, cx, cy, cz; double dt, dx, dy, dz; long cvoxels, cbytes; int i, j, k; int h; MM_TYPE buf_xyz[CX][CY][CZ]; MM_TYPE buf_xYz[CX][CY][CZ]; /* Negative Y */ MM_TYPE buf_xyZ[CX][CY][CZ]; /* Negative Z */ MM_TYPE buf_xzy[CX][CY][CZ]; MM_TYPE buf_yxz[CX][CY][CZ]; MM_TYPE buf_yzx[CX][CY][CZ]; MM_TYPE buf_zyx[CX][CY][CZ]; MM_TYPE buf_zxy[CX][CY][CZ]; void *inf_xyz; void *inf_xYz; void *inf_xyZ; void *inf_xzy; void *inf_yxz; void *inf_yzx; void *inf_zxy; void *inf_zyx; int errors[6]; printf("junk-xyz.mnc\n"); minc_load_data("junk-xyz.mnc", buf_xyz, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_xyz); printf("junk-xYz.mnc\n"); minc_load_data("junk-xYz.mnc", buf_xYz, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_xYz); printf("junk-xyZ.mnc\n"); minc_load_data("junk-xyZ.mnc", buf_xyZ, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_xyZ); printf("junk-xzy.mnc\n"); minc_load_data("junk-xzy.mnc", buf_xzy, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_xzy); printf("junk-yxz.mnc\n"); minc_load_data("junk-yxz.mnc", buf_yxz, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_yxz); printf("junk-yzx.mnc\n"); minc_load_data("junk-yzx.mnc", buf_yzx, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_yzx); printf("junk-zxy.mnc\n"); minc_load_data("junk-zxy.mnc", buf_zxy, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_zxy); printf("junk-zyx.mnc\n"); minc_load_data("junk-zyx.mnc", buf_zyx, NC_TYPE, &ct, &cx, &cy, &cz, &dt, &dx, &dy, &dz, &inf_zyx); for (i = 0; i < 6; i++) { errors[i] = 0; } #ifdef EPSILON #define CLOSE(a, b) (fabs(a - b) < EPSILON) #else #define CLOSE(a, b) (a == b) #endif for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { double e; if (!CLOSE(buf_xyz[i][j][k], buf_xzy[i][j][k])) { printf(MM_FMT, buf_xyz[i][j][k]); printf(" "); printf(MM_FMT, buf_xzy[i][j][k]); printf("\n"); errors[1]++; } if (!CLOSE(buf_xyz[i][j][k], buf_xYz[i][j][k])) { errors[0]++; } if (!CLOSE(buf_xyz[i][j][k], buf_xyZ[i][j][k])) { errors[0]++; } if (!CLOSE(buf_xyz[i][j][k], buf_yxz[i][j][k])) { errors[2]++; } if (!CLOSE(buf_xyz[i][j][k], buf_yzx[i][j][k])) { errors[3]++; } if (!CLOSE(buf_xyz[i][j][k], buf_zxy[i][j][k])) { errors[4]++; } if (!CLOSE(buf_xyz[i][j][k], buf_zyx[i][j][k])) { errors[5]++; } } } } for (i = 0; i < 6; i++) { printf("%d - ", errors[i]); printf("\n"); } h = minc_save_start("temp-xyz.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_xyz, "testing"); minc_save_data(h, buf_xyz, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); h = minc_save_start("temp-xzy.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_xzy, "testing"); minc_save_data(h, buf_xzy, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); h = minc_save_start("temp-yxz.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_yxz, "testing"); minc_save_data(h, buf_yxz, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); h = minc_save_start("temp-yzx.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_yzx, "testing"); minc_save_data(h, buf_yzx, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); h = minc_save_start("temp-zxy.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_zxy, "testing"); minc_save_data(h, buf_zxy, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); h = minc_save_start("temp-zyx.mnc", MINC_TYPE_SHORT, ct, cx, cy, cz, dt, dx, dy, dz, inf_zyx, "testing"); minc_save_data(h, buf_zyx, NC_TYPE, 0, 0, 0, 0, ct, cx, cy, cz); minc_save_done(h); } #endif /* MINC_SIMPLE_TEST */ libminc-libminc-2-3-00/libsrc/minc_simple.h000066400000000000000000000061721257462267400205750ustar00rootroot00000000000000/* minc_simple.h * * Simplified interface for MINC files. */ #define MINC_STATUS_OK 0 #define MINC_STATUS_ERROR (-1) #define MINC_TYPE_CHAR 10 #define MINC_TYPE_UCHAR 20 #define MINC_TYPE_SHORT 30 #define MINC_TYPE_USHORT 40 #define MINC_TYPE_INT 50 #define MINC_TYPE_UINT 60 #define MINC_TYPE_FLOAT 70 #define MINC_TYPE_DOUBLE 80 #define MINC_3D 3 /* Number of spatial dimensions */ /* Get information about a MINC file. */ MNCAPI int minc_file_size(char *path, /* Path to the file */ long *ct, /* Total length of time axis, in voxels */ long *cz, /* Total length of Z axis, in voxels */ long *cy, /* Total length of Y axis, in voxels */ long *cx, /* Total length of X axis, in voxels */ long *cvoxels, /* Total number of voxels */ long *cbytes); /* Total number of bytes as native datatype */ /* Load data from a MINC file. */ MNCAPI int minc_load_data(char *path, /* Path to the file */ void *dataptr, /* Buffer to store data */ int datatype, /* Type of data as read into memory */ long *ct, long *cz, long *cy, long *cx, double *dt, double *dz, double *dy, double *dx, void **infoptr); /* Define an output file. Return value is a file handle, or * MINC_STATUS_ERROR if a problem is detected. */ MNCAPI int minc_save_start(char *path, /* Path to the file */ int filetype, /* Date type as stored in the file */ long ct, /* Total length of time axis, in voxels */ long cz, /* Total length of Z axis, in voxels */ long cy, /* Total length of Y axis, in voxels */ long cx, /* Total length of X axis, in voxels */ double dt, /* Sample width along time axis, in seconds */ double dz, /* Sample width along Z axis, in mm */ double dy, /* Sample width along Y axis, in mm */ double dx, /* Sample width along X axis, in mm */ void *infoptr, /* Opaque file structure information */ const char *history); /* New history information */ /* Write data to file. Return value is MINC_STATUS_OK or MINC_STATUS_ERROR. */ MNCAPI int minc_save_data(int handle, /* Handle returned by minc_save_start */ void *dataptr, /* Data to write */ int datatype, /* Type of data in memory */ long st, /* Start position of 4D hyperslab */ long sz, long sy, long sx, long ct, /* Size of 4D hyperslab */ long cz, long cy, long cx); /* Called when a particular file is complete. */ MNCAPI int minc_save_done(int handle); /* Called to free memory associated with the infoptr. */ MNCAPI void minc_free_info(void *infoptr); MNCAPI int minc_get_world_transform(char *path, double transform[MINC_3D][MINC_3D + 1]); libminc-libminc-2-3-00/libsrc/minc_structures.h000066400000000000000000000217071257462267400215300ustar00rootroot00000000000000#ifndef MINC_STRUCTURES_H #define MINC_STRUCTURES_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_structures.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Defines structures for use by MINC routines @METHOD : @GLOBALS : @CALLS : @CREATED : August 28, 1992 (Peter Neelin) @MODIFIED : * $Log: minc_structures.h,v $ * Revision 6.1 1999-10-19 14:45:10 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/10 18:14:50 neelin * Fixed handling of invalid data when icv scale is zero. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:09 neelin * Release of minc version 0.2 * * Revision 1.10 94/09/28 10:37:30 neelin * Pre-release * * Revision 1.9 93/08/11 12:06:42 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_structures.h,v 6.1 1999-10-19 14:45:10 neelin Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ /* Image conversion variable structure type */ typedef struct mi_icv_struct mi_icv_type; struct mi_icv_struct { /* semiprivate : fields available to the package */ int do_scale; /* Indicates to MI_convert_type that scaling should be done */ double scale; /* For scaling in MI_convert_type */ double offset; int do_dimconvert; /* Indicates that dimensional conversion function should be given */ int (*dimconvert_func) (int operation, mi_icv_type *icvp, long start[], long count[], void *values, long bufstart[], long bufcount[], void *buffer); int do_fillvalue; /* Indicates to MI_convert_type that fillvalue checking should be done */ double fill_valid_min; /* Range limits for fillvalue checking */ double fill_valid_max; /* private : fields available only to icv routines */ /* Fields that hold values passed by user */ nc_type user_type; /* Type to that user wants */ int user_typelen; /* Length of type in bytes */ int user_sign; /* Sign that user wants */ int user_do_range; /* Does the user want range scaling? */ double user_vmax; /* Range of values that user wants */ double user_vmin; int user_do_norm; /* Indicates that user wants value normalization */ int user_user_norm; /* If TRUE, user specifies range for norm, otherwise norm is taken from variable range */ char *user_maxvar; /* Name of MIimagemax variable */ char *user_minvar; /* Name of MIimagemin variable */ double user_imgmax; /* Range for normalization */ double user_imgmin; int user_do_dimconv; /* Indicates that user wants to do dimension conversion stuff */ int user_do_scalar; /* Indicates that user wants scalar fields */ int user_xdim_dir; /* Direction for x, y and z dimensions */ int user_ydim_dir; int user_zdim_dir; int user_num_imgdims; /* Number of image (fastest varying) dimensions */ long user_dim_size[MI_MAX_IMGDIMS]; /* Size of fastest varying dimensions for user */ int user_keep_aspect; /* Indicates that user wants to preserve the aspect ratio when resizing images */ int user_do_fillvalue; /* Indicates that user wants fillvalue checking to be done */ double user_fillvalue; /* Fillvalue that user wants */ /* Fields that hold values from real variable */ int cdfid; /* Id of cdf */ int varid; /* Id of variable */ int imgmaxid; /* Id of MIimagemax */ int imgminid; /* Id of Miimagemin */ int var_ndims; /* Number of dimensions of variable */ int var_dim[MAX_VAR_DIMS]; /* Dimensions of variable */ nc_type var_type; /* Variable type */ int var_typelen; /* Length of type in bytes */ int var_sign; /* Variable sign */ double var_vmax; /* Range of values in variable */ double var_vmin; int var_is_vector; /* Is this variable a vector field */ long var_vector_size; /* Size of vector dimension */ long var_dim_size[MI_MAX_IMGDIMS]; /* Size of image dimensions in variable */ /* Fields derived from user values and variable values */ int derv_usr_float; /* Are user or variable values floating point? */ int derv_var_float; double derv_imgmax; /* Range for normalization */ double derv_imgmin; int derv_firstdim; /* First dimension (counting from fastest, ie. backwards) over which MIimagemax or MIimagemin vary */ int derv_do_zero; /* Indicates if we should zero user's buffer on GETs */ int derv_do_bufsize_step; /* Indicates if we need to worry about bufsize_step */ int derv_bufsize_step[MAX_VAR_DIMS]; /* Array of convenient multiples for buffer allocation */ int derv_var_compress; /* Indicate need for compressing variable or */ int derv_usr_compress; /* user buffer */ int derv_dimconv_fastdim; /* Fastest varying dimensions for dimension conversion */ long derv_var_pix_num; /* Number of pixels to compress/expand for */ long *derv_var_pix_off; /* variable and user buffers, as well as */ long derv_usr_pix_num; /* pointers to arrays of offsets */ long *derv_usr_pix_off; long derv_icv_start[MAX_VAR_DIMS]; /* Space for storing parameters to */ long derv_icv_count[MAX_VAR_DIMS]; /* MI_icv_access */ /* Stuff that affects first user_num_imgdims (excluding any vector dimension) as image dimensions */ int derv_dim_flip[MI_MAX_IMGDIMS]; /* Flip dimension? */ int derv_dim_grow[MI_MAX_IMGDIMS]; /* Expand variable to fit user's array? */ int derv_dim_scale[MI_MAX_IMGDIMS]; /* Grow/shrink scale factor */ int derv_dim_off[MI_MAX_IMGDIMS]; /* Pixels to skip in user's image */ double derv_dim_step[MI_MAX_IMGDIMS]; /* Step, start for user's image (analogous to MIstep, MIstart) for first user_num_imgdims dims */ double derv_dim_start[MI_MAX_IMGDIMS]; }; /* Structure for passing values for MI_varaccess */ typedef struct { int operation; int cdfid; int varid; nc_type var_type, call_type; int var_sign, call_sign; int var_value_size, call_value_size; mi_icv_type *icvp; int do_scale; int do_dimconvert; int do_fillvalue; long *start, *count; void *values; } mi_varaccess_type; /* Structure for passing values for micopy_var_values */ typedef struct { int value_size; /* Size of each value */ int incdfid, outcdfid; /* Input and output cdf files */ int invarid, outvarid; /* Input and output variables */ } mi_vcopy_type; /* Structure for passing values for MI_icv_dimconvert */ typedef struct { int do_compress, do_expand; long end[MAX_VAR_DIMS]; long in_pix_num, out_pix_num; /* Variables for compressing/expanding */ long *in_pix_off, *out_pix_off; void *in_pix_first, *out_pix_first; void *in_pix_last, *out_pix_last; nc_type intype, outtype; /* Variable types and signs */ int insign, outsign; long buf_step[MAX_VAR_DIMS]; /* Step sizes for pointers */ long usr_step[MAX_VAR_DIMS]; long *istep, *ostep; void *istart, *ostart; /* Beginning of buffers */ } mi_icv_dimconv_type; #endif libminc-libminc-2-3-00/libsrc/minc_useful.h000066400000000000000000000121331257462267400206010ustar00rootroot00000000000000#ifndef MINC_USEFUL_H #define MINC_USEFUL_H /* ----------------------------- MNI Header ----------------------------------- @NAME : minc_useful.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A set of macros and definitions useful for MINC routines. (derived from mni_def.h) @METHOD : @GLOBALS : @CALLS : @CREATED : July 15, 1991 David MacDonald @MODIFIED : * $Log: minc_useful.h,v $ * Revision 6.2 2004-10-15 13:47:55 bert * Minor changes for Windows compatibility * * Revision 6.1 1999/10/19 14:45:10 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/10 19:22:18 neelin * Removed redefinition of NULL and added pointer casts in appropriate places. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:10 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:37:31 neelin * Pre-release * * Revision 1.6 93/08/11 12:06:44 neelin * Added RCS logging in source. * July 29, 1992 Peter Neelin - changed name for MINC routines and added some macros @COPYRIGHT : Copyright 1993 David MacDonald and Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The authors and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_useful.h,v 6.2 2004-10-15 13:47:55 bert Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ /* ------------ define signed for vaxes ----------------- */ #if (defined(vax) && !defined(__STDC__)) #define signed #endif /* --------- define the prefixes to all functions ---------- */ #ifndef MNCAPI # define MNCAPI #endif #define PRIVATE static #define SEMIPRIVATE /* --------- define TRUE and FALSE ------------------------ */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* --------- macro to determine the size of a static array, e.g., int array[] = { 1, 3, 9, 5 }; ------------ */ #define SIZEOF_STATIC_ARRAY( array ) \ ( sizeof(array) / sizeof((array)[0])) /* --------- interpolate between a and b ------------------- */ #define INTERPOLATE( alpha, a, b ) ((a) + (alpha) * ((b) - (a))) /* --------- PI, and angles -------------------------------- */ #define PI M_PI /* from math.h */ #define DEG_TO_RAD (PI / 180.0) #define RAD_TO_DEG (180.0 / PI) /* --------- Absolute value, min, and max. Bear in mind that these may evaluate an expression multiple times, i.e., ABS( x - y ), and therefore may be inefficient, or incorrect, i.e, ABS( ++x ); ------------------ */ #define ABS( x ) ( ((x) > (0)) ? (x) : (-(x)) ) /* ---------- Round to nearest integer - must be cast to integer in order for rounding to take effect, and the cast must truncate towards zero - eg. i = (int) ROUND(x); Same caveats as ABS ------------------ */ #define ROUND( x ) ((x) + ( ((x) >= 0) ? 0.5 : (-0.5) ) ) #undef MAX #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MAX3( x, y, z ) MAX( x, MAX(y,z) ) #undef MIN #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define MIN3( x, y, z ) MIN( x, MIN(y,z) ) /* --------- gets the address of a 2-d array element in a 1-d array ----- */ #define IJ( i, j, nj ) ( (i) * (nj) + (j) ) /* --------- gets the address of a 3-d array element in a 1-d array ----- */ #define IJK( i, j, k, nj, nk ) ( (k) + (nk) * ((j) + (nj) * (i)) ) /* --------- memory allocation macros -------------------------- */ #define MALLOC( n_items, type ) \ ( (type *) malloc( (size_t) (n_items) * sizeof(type) ) ) #define CALLOC( n_items, type ) \ ( (type *) calloc( (size_t) (n_items), sizeof(type) ) ) #define REALLOC( ptr, n_items, type ) \ ( (type *) realloc( (void *) ptr, (size_t) (n_items) * sizeof(type) ) ) #define FREE( ptr ) \ free( (void *) ptr ) /* --------- environment variables -------------------------- */ #ifdef sun char *getenv(); /* on suns, this declaration is not in stdlib.h */ #endif #define ENV_EXISTS( env ) ( getenv(env) != (char *) 0 ) /* --------- string macros -------------------------- */ #define STRINGS_EQUAL(str1,str2) (strcmp(str1,str2)==0) #endif libminc-libminc-2-3-00/libsrc/minc_varlists.h000066400000000000000000000051601257462267400211470ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc_varlists.h @DESCRIPTION: Contains lists of minc variables for use by routines in file minc_convenience.c. @METHOD : Note that lists should be NULL terminated. @CREATED : Peter Neelin (August 7, 1992) @MODIFIED : * $Log: minc_varlists.h,v $ * Revision 6.1 1999-10-19 14:45:11 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:12 neelin * Release of minc version 0.2 * * Revision 1.5 94/09/28 10:37:32 neelin * Pre-release * * Revision 1.4 93/08/11 12:06:45 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @RCSID : $Header: /private-cvsroot/minc/libsrc/minc_varlists.h,v 6.1 1999-10-19 14:45:11 neelin Exp $ MINC (MNI) ---------------------------------------------------------------------------- */ /* Variables containing list of standard dimension names and variable names */ /* List of dimension variables. Note that MIvector_dimension is not included since it should not have an associated variable. */ static const char *dimvarlist[]={MIxspace, MIyspace, MIzspace, MItime, MItfrequency, MIxfrequency, MIyfrequency, MIzfrequency, NULL}; /* List of dimension width variables */ static const char *dimwidthlist[]={MIxspace_width, MIyspace_width, MIzspace_width, MItime_width, MItfrequency_width, MIxfrequency_width, MIyfrequency_width, MIzfrequency_width, NULL}; /* List of variables */ static const char *varlist[]={MIrootvariable, MIimage, MIimagemax, MIimagemin, MIpatient, MIstudy, MIacquisition, NULL}; libminc-libminc-2-3-00/libsrc/nd_loop.c000066400000000000000000000140651257462267400177230ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : nd_loop.c @DESCRIPTION: File containing routines for doing n-dimensional looping @METHOD : @GLOBALS : @CREATED : March 10, 1994 (Peter Neelin) @MODIFIED : * $Log: nd_loop.c,v $ * Revision 6.4 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.3 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.2 2004/10/15 13:47:13 bert * Minor changes for Windows compatibility * * Revision 6.1 2002/01/14 21:28:26 neelin * Moved nd_loop, voxel_loop, ParseArgv and time_stamp from ../progs/Proglib * in order to include them in the main minc library. * * Revision 6.1 1999/10/19 14:45:13 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:41 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:41 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:50 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:35 neelin * Release of minc version 0.3 * * Revision 1.3 1995/02/08 19:31:47 neelin * Moved ARGSUSED statements for irix 5 lint. * * Revision 1.2 1994/12/02 09:19:59 neelin * Added comments to clarify use of routines. * * Revision 1.1 94/12/02 08:40:12 neelin * Initial revision * @COPYRIGHT : Copyright 1994 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include "nd_loop.h" /* ----------------------------- MNI Header ----------------------------------- @NAME : nd_begin_looping @INPUT : start - vector of indices specifying starting subscript for each dimension ndims - number of dimensions in vector @OUTPUT : current - vector of indices giving current subscript @RETURNS : (none) @DESCRIPTION: Sets up current variable for looping @METHOD : @GLOBALS : @CALLS : @CREATED : October 28, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void nd_begin_looping(long start[], long current[], int ndims) { int idim; for (idim=0; idim < ndims; idim++) { current[idim] = start[idim]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : nd_end_of_loop @INPUT : current - vector of indices giving current subscript end - vector of indices specifying last subscripts plus one ndims - number of dimensions in vector @OUTPUT : (none) @RETURNS : TRUE if end of loop. @DESCRIPTION: Tests for end of a multi-dimensional loop. @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int nd_end_of_loop(long current[], long end[], int ndims) /* ARGSUSED */ { return (current[0] >= end[0]); } /* ----------------------------- MNI Header ----------------------------------- @NAME : nd_update_current_count @INPUT : current - vector of indices giving current subscript increment - vector of indices specifying increment for each dimension end - vector of indices specifying last subscripts plus one ndims - number of dimensions in vector @OUTPUT : current_count - vector of indices giving count for current position @RETURNS : (none) @DESCRIPTION: Sets the count so that we don't go past end @METHOD : @GLOBALS : @CALLS : @CREATED : October 28, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void nd_update_current_count(long current[], long increment[], long end[], long current_count[], int ndims) { int idim; for (idim=0; idim < ndims; idim++) { current_count[idim] = increment[idim]; if ((current[idim] + current_count[idim]) > end[idim]) { current_count[idim] = end[idim] - current[idim]; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : nd_increment_loop @INPUT : current - vector of indices giving current subscript start - vector of indices specifying starting subscript for each dimension increment - vector of indices specifying increment for each dimension end - vector of indices specifying last subscripts plus one ndims - number of dimensions in vector @OUTPUT : current - vector of indices giving new subscript @RETURNS : (none) @DESCRIPTION: Does incrementing for multi-dimensional loop @METHOD : @GLOBALS : @CALLS : @CREATED : March 10, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void nd_increment_loop(long current[], long start[], long increment[], long end[], int ndims) { int idim; idim = ndims-1; current[idim] += increment[idim]; while ( (idim>0) && (current[idim] >= end[idim])) { current[idim] = start[idim]; idim--; current[idim] += increment[idim]; } } libminc-libminc-2-3-00/libsrc/nd_loop.h000066400000000000000000000055761257462267400177370ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : nd_loop.h @DESCRIPTION: Header file for nd_loop.c @METHOD : Use these routines in the following way: Set start, end and increment vectors to define looping; nd_begin_looping(start, current, ndims); while (!nd_end_of_loop(current, end, ndims)) { nd_update_current_count(current, increment, end, current_count, ndims); Use current and current_count to work on hyperslab; nd_increment_loop(current, start, increment, end, ndims); } @GLOBALS : @CREATED : December 2, 1994 (Peter Neelin) @MODIFIED : * $Log: nd_loop.h,v $ * Revision 6.2 2004-10-15 13:47:13 bert * Minor changes for Windows compatibility * * Revision 6.1 2002/01/14 21:28:26 neelin * Moved nd_loop, voxel_loop, ParseArgv and time_stamp from ../progs/Proglib * in order to include them in the main minc library. * * Revision 6.1 1999/10/19 14:45:14 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:41 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:41 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:50 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:35 neelin * Release of minc version 0.3 * * Revision 1.3 1994/12/02 09:40:37 neelin * Fixed arguments to nd_end_of_loop. * * Revision 1.2 94/12/02 09:20:17 neelin * Added comments to clarify use of routines. * * Revision 1.1 94/12/02 08:40:31 neelin * Initial revision * @COPYRIGHT : Copyright 1994 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc.h" #if defined(__cplusplus) extern "C" { #endif MNCAPI void nd_begin_looping(long start[], long current[], int ndims); MNCAPI int nd_end_of_loop(long current[], long end[], int ndims); MNCAPI void nd_update_current_count(long current[], long increment[], long end[], long current_count[], int ndims); MNCAPI void nd_increment_loop(long current[], long start[], long increment[], long end[], int ndims); #if defined(__cplusplus) } #endif libminc-libminc-2-3-00/libsrc/netcdf_convenience.c000066400000000000000000002021351257462267400221050ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : netcdf_convenience.c @DESCRIPTION: File of convenience functions for netcdf routines. There is nothing MINC specific about these routines, they just provide convenient ways of getting netcdf data. The routines MI_convert_type, mivarget and mivarget1 break this rule by making use of the MINC variable attribute MIsigntype to determine the sign of an integer variable. @METHOD : Routines included in this file : public : miexpand_file miopen micreate miclose miattget miattget1 miattgetstr miattputint miattputdbl miattputstr mivarget mivarget1 mivarput mivarput1 miset_coords mitranslate_coords micopy_all_atts micopy_var_def micopy_var_values micopy_all_var_defs micopy_all_var_values micreate_tempfile miget_cfg_bool miget_cfg_int miget_cfg_str private : execute_decompress_command MI_vcopy_action @CREATED : July 27, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: netcdf_convenience.c,v $ * Revision 6.21 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.20 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.19 2005/08/26 21:04:58 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.18 2005/05/20 16:49:51 bert * Avoid direct usage of H5Fis_hdf5(), replace with hdf_access() function * * Revision 6.17 2005/05/20 15:39:45 bert * Remove and/or conditionalize test code for memory-mapped files (see HDF5_MMAP_TEST) * * Revision 6.16 2004/12/14 23:53:46 bert * Get rid of compilation warnings * * Revision 6.15 2004/12/03 21:52:35 bert * Minor changes for Windows build * * Revision 6.14 2004/10/15 13:48:33 bert * Minor changes for Windows compatibility * * Revision 6.13 2004/06/04 18:16:25 bert * Create and add an 'ident' attribute when a file is created * * Revision 6.12 2004/04/30 18:57:39 bert * Explicitly cast return values of NULL to char * where appropriate to make IRIX MIPSpro compiler happy * * Revision 6.11 2004/04/27 15:50:41 bert * The full 2.0 treatment * * Revision 6.10 2004/03/23 21:16:05 bert * Conditionally include fcntl.h * * Revision 6.9 2003/12/01 22:45:58 stever * Check for fork(); use for file decompression if available * * Revision 6.8 2003/03/17 16:15:33 bert * Added micreate_tempfile() to resolve issues with tempfile naming and creation, especially to suppress those annoying GNU linker messages about tempnam() and tmpnam(). * * Revision 6.7 2001/08/20 13:19:15 neelin * Added function miattget_with_sign to allow the caller to specify the sign * of the input attribute since this information is ambiguous. This is * necessary for the valid_range attribute which should have the same sign * as the image data. Modified miget_valid_range to make use of this function. * * Revision 6.6 2001/04/24 14:49:39 neelin * In execute_decompress_command, close all file handles in child after * fork to avoid problems with buffer flushing. * * Revision 6.5 2001/04/17 18:40:14 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.4 2000/09/13 14:02:00 neelin * Added support for bzip files. (Modified patch from Steve Robbins) * * Revision 6.3 2000/02/02 18:43:29 neelin * Fixed bug in miexpand_file that would call fclose with a NULL file handle. * For newer versions of glibc, this would cause a seg fault. * * Revision 6.2 1999/10/19 14:45:11 neelin * Fixed Log subsitutions for CVS * * Revision 6.1 1997/10/06 12:54:08 neelin * Changed call to tmpnam to tempnam so that TMPDIR variable is checked when * creating temporary files. * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.3 1997/04/10 19:22:18 neelin * Removed redefinition of NULL and added pointer casts in appropriate places. * * Revision 3.2 1995/09/29 14:34:09 neelin * Modified micopy_all_atts to handle MI_ERROR being passed in as a varid. * * Revision 3.1 1995/06/12 20:43:52 neelin * Modified miexpand_file and miopen to try adding compression exetensions * to filenames if the first open fails. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.6 1995/03/14 14:36:35 neelin * Got rid of broken pipe messages from miexpand_file by using exec * in system call. * * Revision 2.5 1995/02/08 19:14:44 neelin * More changes for irix 5 lint. * * Revision 2.4 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.3 1995/01/24 08:34:11 neelin * Added optional tempfile argument to miexpand_file. * * Revision 2.2 95/01/23 08:28:19 neelin * Changed name of midecompress_file to miexpand_file. * * Revision 2.1 95/01/20 15:20:33 neelin * Added midecompress_file with ability to decompress only the header of a file. * * Revision 2.0 94/09/28 10:38:13 neelin * Release of minc version 0.2 * * Revision 1.11 94/09/28 10:37:19 neelin * Pre-release * * Revision 1.10 93/11/03 12:28:04 neelin * Added miopen, micreate, miclose routines. * miopen will uncompress files before opening them, if needed. * * Revision 1.9 93/08/11 12:06:28 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include "ParseArgv.h" #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_WAIT_H #include #endif #if HAVE_SYS_STAT_H #include /* For S_IREAD, S_IWRITE */ #endif #include #if MINC2 #undef ncopen #undef ncclose #undef nccreate #include "hdf_convenience.h" #endif /* MINC2 defined */ #if HAVE_FCNTL_H #include #endif /* Private functions */ PRIVATE int MI_vcopy_action(int ndims, long start[], long count[], long nvalues, void *var_buffer, void *caller_data); #if MINC2 /* These flags are used to count miopen() calls which open either an HDF5 * file or a NetCDF file. The exact count is not important; the library * will automatically choose to create a HDF5 output file if only HDF5 * files have been opened. */ static int mi_nc_files = 0; static int mi_h5_files = 0; #endif /* MINC2 defined */ /* ----------------------------- MNI Header ----------------------------------- @NAME : execute_decompress_command @INPUT : command - command to execute infile - input file outfile - output file header_only - ignored @OUTPUT : (none) @RETURNS : status of decompress command (zero = success) @DESCRIPTION: Routine to execute a decompression command on a minc file. The command must take a file name argument and must send to standard output. @METHOD : @GLOBALS : @CALLS : NetCDF routines, external decompression programs @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int execute_decompress_command(char *command, const char *infile, char *outfile, int header_only) { char whole_command[1024]; int status; #if !(HAVE_WORKING_FORK && HAVE_SYSTEM && HAVE_POPEN) fprintf(stderr,"Can't decompress %s because system is not available!\n",infile); return 1; #else /* Unix */ /* we now ignore header_only and always uncompress the whole * file as the previous "header only" hack that used to work * on MINC1 files doesn't work reliably with MINC2 */ (void) sprintf(whole_command, "exec %s %s > %s 2> /dev/null", command, infile, outfile); status = system(whole_command); /* Return the status */ return status; #endif /* ifndef unix else */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : miexpand_file @INPUT : path - name of file to open. tempfile - user supplied name for temporary file. If NULL, then the routine generates its own name. header_only - TRUE if only the header needs to be expanded. @OUTPUT : created_tempfile - TRUE if a temporary file was created, FALSE if no file was created (either because the original file was not compressed or because of an error). @RETURNS : name of uncompressed file (either original or a temporary file) or NULL if an error occurred during decompression. The caller must free the string. If a system error occurs on file open or the decompression type is unknown, then the original file name is returned. @DESCRIPTION: Routine to expand a compressed minc file. If the original file is not compressed then its name is returned. If the name of a temporary file is returned, then *created_tempfile is set to TRUE. If header_only is TRUE, then only the header part of the file will be expanded - the data part may or may not be present. @METHOD : @GLOBALS : @CALLS : NetCDF routines, external decompression programs @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI char *miexpand_file(const char *path, char *tempfile, int header_only, int *created_tempfile) { typedef enum {BZIPPED, GZIPPED, COMPRESSED, PACKED, ZIPPED, UNKNOWN} Compress_type; int status, oldncopts, first_ncerr, iext; char *newfile, *compfile; const char *extension; FILE *fp; Compress_type compress_type; static struct { char *extension; Compress_type type; } compression_code_list[] = { {".bz", BZIPPED}, {".bz2", BZIPPED}, {".gz", GZIPPED}, {".Z", COMPRESSED}, {".z", PACKED}, {".zip", ZIPPED} }; static int complist_length = sizeof(compression_code_list) / sizeof(compression_code_list[0]); static int max_compression_code_length = 5; MI_SAVE_ROUTINE_NAME("miexpand_file"); /* We have not created a temporary file yet */ *created_tempfile = FALSE; #if MINC2 if (hdf_access(path)) { newfile = strdup(path); MI_RETURN(newfile); } #endif /* MINC2 defined */ /* Try to open the file (close it again immediately) */ oldncopts = ncopts; ncopts = 0; status = ncopen(path, NC_NOWRITE); if (status != MI_ERROR) { (void) ncclose(status); } ncopts = oldncopts; /* If there is no error then return the original file name */ if (status != MI_ERROR) { newfile = strdup(path); MI_RETURN(newfile); } /* Save the error code */ first_ncerr = ncerr; /* Check for the system error that doesn't show */ if (first_ncerr == NC_NOERR) { fp = fopen(path, "r"); if (fp == NULL) { first_ncerr = NC_SYSERR; } else { (void) fclose(fp); } } /* Get the file extension */ extension = strrchr(path, '.'); if (extension == NULL) { extension = &path[strlen(path)]; } /* Determine the type */ compress_type = UNKNOWN; for (iext = 0; iext < complist_length; iext++) { if (STRINGS_EQUAL(extension, compression_code_list[iext].extension)) { compress_type = compression_code_list[iext].type; break; } } /* If there was a system error and it's not already a compressed file, then maybe there exists a compressed version (with appropriate extension). Loop through the list of extensions. */ compfile = NULL; if ((first_ncerr == NC_SYSERR) && (compress_type == UNKNOWN)) { compfile = MALLOC(strlen(path) + max_compression_code_length + 2, char); for (iext=0; iext < complist_length; iext++) { (void) strcat(strcpy(compfile, path), compression_code_list[iext].extension); fp = fopen(compfile, "r"); if (fp != NULL) { (void) fclose(fp); break; } } if (iext >= complist_length) { FREE(compfile); newfile = strdup(path); MI_RETURN(newfile); } compress_type = compression_code_list[iext].type; path = compfile; } /* If there was a system error or we don't know what to do with the file, then return the original file name */ else if ((first_ncerr == NC_SYSERR) || (compress_type == UNKNOWN)) { newfile = strdup(path); MI_RETURN(newfile); } /* Create a temporary file name */ if (tempfile == NULL) { newfile = micreate_tempfile(); } else { newfile = strdup(tempfile); } *created_tempfile = TRUE; /* Try to use gunzip */ if ((compress_type == GZIPPED) || (compress_type == COMPRESSED) || (compress_type == PACKED) || (compress_type == ZIPPED)) { status = execute_decompress_command("gunzip -c", path, newfile, header_only); } else if (compress_type == BZIPPED) { status = execute_decompress_command("bunzip2 -c", path, newfile, header_only); } /* If that doesn't work, try something else */ if (status != 0) { if (compress_type == COMPRESSED) { status = execute_decompress_command("zcat", path, newfile, header_only); } else if (compress_type == PACKED) { status = execute_decompress_command("pcat", path, newfile, header_only); } } /* Free the compressed file name, if necessary */ if (compfile != NULL) { FREE(compfile); path = NULL; } /* Check for failure to uncompress the file */ if (status != 0) { (void) remove(newfile); *created_tempfile = FALSE; FREE(newfile); milog_message(MI_MSG_UNCMPFAIL); MI_RETURN((char *)NULL); /* Explicit cast needed for MIPSpro cc */ } /* Return the new file name */ MI_RETURN(newfile); } static int is_netcdf_file(const char *filename) { unsigned char magic[4]; size_t nread; FILE *fp = fopen(filename, "rb"); if (fp == NULL) { return 0; } nread = fread(magic, 1, 4, fp); fclose(fp); if (nread != 4) { return 0; } return (magic[0] == 'C' && magic[1] == 'D' && magic[2] == 'F' && (magic[3] == 1 || magic[3] == 2)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miopen @INPUT : path - name of file to open mode - NC_WRITE or NC_NOWRITE to indicate whether file should be opened for write or read-only. @OUTPUT : (nothing) @RETURNS : file id or MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncopen, but will de-compress (temporarily) read-only files as needed. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : November 2, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miopen(const char *path, int mode) { int status, oldncopts, created_tempfile; char *tempfile; #if MINC2 int hmode; #endif /* MINC2 defined */ MI_SAVE_ROUTINE_NAME("miopen"); if (is_netcdf_file(path)) { /* Try to open the file */ oldncopts = ncopts; ncopts = 0; status = ncopen(path, mode); ncopts = oldncopts; if (status != MI_ERROR) { mi_nc_files++; /* Count open netcdf files */ } MI_RETURN(status); } #if MINC2 if (mode & NC_WRITE) { hmode = H5F_ACC_RDWR; } else { hmode = H5F_ACC_RDONLY; } status = hdf_open(path, hmode); /* If there is no error then return */ if (status >= 0) { mi_h5_files++; /* Count open HDF5 files */ MI_RETURN(status); } #endif /* MINC2 defined */ /* If the user wants to modify the file then return an error, since * we don't allow write access to compressed files. */ if (mode & NC_WRITE) { milog_message(MI_MSG_NOWRITECMP); MI_RETURN(MI_ERROR); } /* Try to expand the file */ tempfile = miexpand_file(path, NULL, FALSE, &created_tempfile); /* Check for error */ if (tempfile == NULL) { MI_RETURN(MI_ERROR); } /* Open the temporary file and unlink it so that it will disappear when the file is closed */ if (is_netcdf_file(tempfile)) { oldncopts = ncopts; ncopts = 0; status = ncopen(tempfile, mode); ncopts = oldncopts; if (status != MI_ERROR) { mi_nc_files++; } } #if MINC2 else { status = hdf_open(tempfile, hmode); if (status >= 0) { mi_h5_files++; } } #endif /* MINC2 defined */ if (created_tempfile) { remove(tempfile); } if (status < 0) { milog_message(MI_MSG_OPENFILE, tempfile); } free(tempfile);/*free memory allocated in miexpand_file*/ MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micreate @INPUT : path - name of file to create cmode - NC_CLOBBER or NC_NOCLOBBER @OUTPUT : (nothing) @RETURNS : file id or MI_ERROR (=-1) when an error occurs @DESCRIPTION: A wrapper for routine nccreate, allowing future enhancements. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : November 2, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #if MINC2 MNCAPI int micreatex(const char *path, int cmode, struct mi2opts *opts_ptr) { int fd; MI_SAVE_ROUTINE_NAME("micreate"); if ((cmode & MI2_CREATE_V1) != 0) { fd = nccreate(path, cmode); } else if (miget_cfg_bool(MICFG_FORCE_V2) || (cmode & MI2_CREATE_V2) != 0) { fd = hdf_create(path, cmode, opts_ptr); } else { if (mi_nc_files == 0 && mi_h5_files != 0) { /* Create an HDF5 file. */ fd = hdf_create(path, cmode, opts_ptr); } else { /* Create a NetCDF file. */ fd = nccreate(path, cmode); } } if (fd < 0) { milog_message(MI_MSG_CREATEFILE, path); } else { char ident[128]; micreate_ident(ident, sizeof(ident)); miattputstr(fd, NC_GLOBAL, "ident", ident); miattputstr(fd, NC_GLOBAL, "minc_version", MINC_VERSION); } MI_RETURN(fd); } MNCAPI int micreate(const char *path, int cmode) { MI_SAVE_ROUTINE_NAME("micreate"); MI_RETURN(micreatex(path, cmode, NULL)); } #else MNCAPI int micreate(char *path, int cmode) { int fd; MI_SAVE_ROUTINE_NAME("micreate"); /* Create a NetCDF file. */ fd = nccreate(path, cmode); if (fd < 0) { milog_message(MI_MSG_CREATEFILE, path); } MI_RETURN(fd); } #endif /* MINC2 not defined */ /* ----------------------------- MNI Header ----------------------------------- @NAME : miclose @INPUT : cdfid - id of file to close @OUTPUT : (nothing) @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: A wrapper for routine ncclose, allowing future enhancements. read-only files as needed. @METHOD : @GLOBALS : @CALLS : NetCDF routines @CREATED : November 2, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miclose(int cdfid) { int status; MI_SAVE_ROUTINE_NAME("miclose"); #if MINC2 if (MI2_ISH5OBJ(cdfid)) { status = hdf_close(cdfid); } else { status = ncclose(cdfid); } #else status = ncclose(cdfid); #endif /* MINC2 not defined */ if (status < 0) { milog_message(MI_MSG_CLOSEFILE); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattget @INPUT : cdfid - cdf file id varid - variable id name - name of attribute datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) max_length - maximum length to return (number of elements) @OUTPUT : value - value of attribute att_length - actual length of attribute (number of elements) If NULL, then no value is returned. @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncattget, but the calling routine specifies the form in which data should be returned (datatype), as well as the maximum number of elements to get. The datatype can only be a numeric type. If the attribute in the file is of type NC_CHAR, then an error is returned. The actual length of the vector is returned in att_length. @METHOD : @GLOBALS : @CALLS : miattget_with_sign @CREATED : July 27, 1992 (Peter Neelin) @MODIFIED : August 20, 2001 (P.N.) - changed to call miattget_with_sign ---------------------------------------------------------------------------- */ MNCAPI int miattget(int cdfid, int varid, const char *name, nc_type datatype, int max_length, void *value, int *att_length) { int status; MI_SAVE_ROUTINE_NAME("miattget"); status = miattget_with_sign(cdfid, varid, name, NULL, datatype, NULL, max_length, value, att_length); MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattget_with_sign @INPUT : cdfid - cdf file id varid - variable id name - name of attribute insign - sign of input attribute. If NULL, then use default. datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) outsign - sign of type for calling routine. If NULL, then use default max_length - maximum length to return (number of elements) @OUTPUT : value - value of attribute att_length - actual length of attribute (number of elements) If NULL, then no value is returned. @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine miattget, but the calling routine specifies the sign of the attribute in the file (which may be ambiguous) as well as the sign of the return type. Sign strings can be MI_SIGNED, MI_UNSIGNED, an empty string or NULL - the latter two mean use the default for the type. @METHOD : @GLOBALS : @CALLS : NetCDF routines and MI_convert_type @CREATED : August 20, 2001 (Peter Neelin) - slightly modified version of old miattget @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miattget_with_sign(int cdfid, int varid, const char *name, char *insign, nc_type datatype, char *outsign, int max_length, void *value, int *att_length) { nc_type att_type; /* Type of attribute */ int actual_length; /* Actual length of attribute */ void *att_value; /* Pointer to attribute value */ int status; /* Status of nc routine */ int att_sign, data_sign; /* Integer sign values */ MI_SAVE_ROUTINE_NAME("miattget_with_sign"); /* Inquire about the attribute */ status = ncattinq(cdfid, varid, name, &att_type, &actual_length); if (status < 0) { milog_message(MI_MSG_FINDATTR, name); MI_RETURN(MI_ERROR); } /* Save the actual length of the attribute */ if (att_length != NULL) *att_length = actual_length; /* Check that the attribute type is numeric */ if ((datatype==NC_CHAR) || (att_type==NC_CHAR)) { milog_message(MI_MSG_ATTRNOTNUM, name); MI_RETURN(MI_ERROR); } /* Check to see if the type requested is the same as the attribute type and that the length is less than or equal to max_length. If it is, just get the value. */ if ((datatype == att_type) && (actual_length <= max_length)) { status = ncattget(cdfid, varid, name, value); if (status < 0) { milog_message(MI_MSG_READATTR, name); } MI_RETURN(status); } /* Otherwise, get space for the attribute */ if ((att_value = MALLOC(actual_length * nctypelen(att_type), char)) == NULL) { milog_message(MI_MSG_NOMEMATTR, name); MI_RETURN(MI_ERROR); } /* Get the attribute */ if (ncattget(cdfid, varid, name, att_value)==MI_ERROR) { FREE(att_value); milog_message(MI_MSG_READATTR, name); MI_RETURN(MI_ERROR); } /* Get the signs */ att_sign = MI_get_sign_from_string(att_type, insign); data_sign = MI_get_sign_from_string(datatype, outsign); /* Get the values. Call MI_convert_type with : MI_convert_type(number_of_values, intype, insign, invalues, outtype, outsign, outvalues, icvp) */ status=MI_convert_type(MIN(max_length, actual_length), att_type, att_sign, att_value, datatype, data_sign, value, NULL); FREE(att_value); if (status < 0) { milog_message(MI_MSG_CONVATTR, name); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattget1 @INPUT : cdfid - cdf file id varid - variable id name - name of attribute datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) @OUTPUT : value - value of attribute @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine miattget, but the its gets only one value of an attribute. If the attribute is longer, then an error occurs. @METHOD : @GLOBALS : @CALLS : NetCDF routines and miattget @CREATED : July 27, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miattget1(int cdfid, int varid, const char *name, nc_type datatype, void *value) { int att_length; /* Actual length of the attribute */ int status; MI_SAVE_ROUTINE_NAME("miattget1"); /* Get the attribute value and its actual length */ status = miattget(cdfid, varid, name, datatype, 1, value, &att_length); if (status < 0) { milog_message(MI_MSG_FINDATTR, name); MI_RETURN(MI_ERROR); } /* Check that the length is 1 */ if (att_length != 1) { milog_message(MI_MSG_ATTRNOTSCALAR, name); MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattgetstr @INPUT : cdfid - cdf file id varid - variable id name - name of attribute maxlen - maximum length of string to be copied @OUTPUT : value - string returned @RETURNS : pointer to string value or NULL if an error occurred. @DESCRIPTION: Gets a character attribute, copying up to maxlen characters into value and adding a terminating '\0' if necessary. A pointer to the string is returned to facilitate use. If the attribute is non-character, a NULL pointer is returned. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : July 28, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI char *miattgetstr(int cdfid, int varid, const char *name, int maxlen, char *value) { nc_type att_type; /* Type of attribute */ int att_length; /* Length of attribute */ char *att_value; /* Pointer to attribute value */ MI_SAVE_ROUTINE_NAME("miattgetstr"); /* Inquire about the attribute */ if (ncattinq(cdfid, varid, name, &att_type, &att_length)==MI_ERROR) { milog_message(MI_MSG_FINDATTR, name); MI_RETURN((char *)NULL); /* Explicit cast for MIPSpro cc */ } /* Check that the attribute type is character */ if (att_type!=NC_CHAR) { milog_message(MI_MSG_ATTRNOTSTR, name); MI_RETURN((char *)NULL); /* Explicit cast for MIPSpro cc */ } /* Check to see if the attribute length is less than maxlen. If it is, just get the value. */ if (att_length <= maxlen) { if (ncattget(cdfid, varid, name, value) == MI_ERROR) { milog_message(MI_MSG_READATTR, name); MI_RETURN((char *)NULL); /* Explicit cast for MIPSpro cc */ } /* Check the last character for a '\0' */ if (value[att_length-1] != '\0') { if (att_length==maxlen) value[att_length-1] = '\0'; else value[att_length] = '\0'; } MI_RETURN(value); } /* Otherwise, get space for the attribute */ if ((att_value = MALLOC(att_length * nctypelen(att_type), char)) ==NULL) { milog_message(MI_MSG_NOMEMATTR, name); MI_RETURN((char *)NULL); /* Explicit cast for MIPSpro cc */ } /* Get the attribute */ if (ncattget(cdfid, varid, name, att_value)==MI_ERROR) { FREE(att_value); milog_message(MI_MSG_READATTR, name); MI_RETURN((char *)NULL); } /* Copy the attribute */ (void) strncpy(value, att_value, (size_t) maxlen-1); value[maxlen-1] = '\0'; /* Free the string */ FREE(att_value); /* Return a pointer to the string */ MI_RETURN(value); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattputint @INPUT : cdfid - cdf file id varid - variable id name - name of attribute value - integer value for attribute @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) if an error occurs @DESCRIPTION: Convenience routine for calling ncattput with integers. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : November 25, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miattputint(int cdfid, int varid, const char *name, int value) { int lvalue; int status; MI_SAVE_ROUTINE_NAME("miattputint"); lvalue = value; status = ncattput(cdfid, varid, name, NC_INT, 1, &lvalue); if (status < 0) { milog_message(MI_MSG_WRITEATTR, name); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattputdbl @INPUT : cdfid - cdf file id varid - variable id name - name of attribute value - double value for attribute @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) if an error occurs @DESCRIPTION: Convenience routine for calling ncattput with doubles. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : August 5, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miattputdbl(int cdfid, int varid, const char *name, double value) { int status; MI_SAVE_ROUTINE_NAME("miattputdbl"); status = ncattput(cdfid, varid, name, NC_DOUBLE, 1, &value); if (status < 0) { milog_message(MI_MSG_WRITEATTR, name); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miattputstr @INPUT : cdfid - cdf file id varid - variable id name - name of attribute value - string value for attribute @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) if an error occurs @DESCRIPTION: Convenience routine for calling ncattput with character strings. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : July 28, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int miattputstr(int cdfid, int varid, const char *name, const char *value) { int status; MI_SAVE_ROUTINE_NAME("miattputstr"); status = ncattput(cdfid, varid, name, NC_CHAR, strlen(value) + 1, value); if (status < 0) { milog_message(MI_MSG_WRITEATTR, name); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mivarget @INPUT : cdfid - cdf file id varid - variable id start - vector of coordinates of corner of hyperslab count - vector of edge lengths of hyperslab datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) sign - sign that calling routine wants (one of EMPTY_STRING (or NULL) = use default sign MI_SIGNED = signed values MI_UNSIGNED = unsigned values @OUTPUT : values - value of variable @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncvarget, but the calling routine specifies the form in which data should be returned (datatype), as well as the sign. The datatype can only be a numeric type. If the variable in the file is of type NC_CHAR, then an error is returned. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int mivarget(int cdfid, int varid, long start[], long count[], nc_type datatype, const char *sign, void *values) { int status; MI_SAVE_ROUTINE_NAME("mivarget"); status = MI_varaccess(MI_PRIV_GET, cdfid, varid, start, count, datatype, MI_get_sign_from_string(datatype, sign), values, NULL, NULL); if (status < 0) { milog_message(MI_MSG_READVAR, varid); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mivarget1 @INPUT : cdfid - cdf file id varid - variable id mindex - vector of coordinates of value to get datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) sign - sign that calling routine wants (one of EMPTY_STRING (or NULL) = use default sign MI_SIGNED = signed values MI_UNSIGNED = unsigned values @OUTPUT : value - value of variable @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncvarget1, but the calling routine specifies the form in which data should be returned (datatype), as well as the sign. The datatype can only be a numeric type. If the variable in the file is of type NC_CHAR, then an error is returned. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int mivarget1(int cdfid, int varid, long mindex[], nc_type datatype, const char *sign, void *value) { int status; long count[MAX_VAR_DIMS]; MI_SAVE_ROUTINE_NAME("mivarget1"); status = MI_varaccess(MI_PRIV_GET, cdfid, varid, mindex, miset_coords(MAX_VAR_DIMS, 1L, count), datatype, MI_get_sign_from_string(datatype, sign), value, NULL, NULL); if (status < 0) { milog_message(MI_MSG_READVAR, varid); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mivarput @INPUT : cdfid - cdf file id varid - variable id start - vector of coordinates of corner of hyperslab count - vector of edge lengths of hyperslab datatype - type that calling routine is passing (one of the valid netcdf data types, excluding NC_CHAR) sign - sign that calling routine is passing (one of EMPTY_STRING (or NULL) = use default sign MI_SIGNED = signed values MI_UNSIGNED = unsigned values values - value of variable @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncvarput, but the calling routine specifies the form in which data is passes (datatype), as well as the sign. The datatype can only be a numeric type. If the variable in the file is of type NC_CHAR, then an error is returned. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int mivarput(int cdfid, int varid, long start[], long count[], nc_type datatype, const char *sign, void *values) { int status; MI_SAVE_ROUTINE_NAME("mivarput"); status = MI_varaccess(MI_PRIV_PUT, cdfid, varid, start, count, datatype, MI_get_sign_from_string(datatype, sign), values, NULL, NULL); if (status < 0) { milog_message(MI_MSG_WRITEVAR, varid); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mivarput1 @INPUT : cdfid - cdf file id varid - variable id mindex - vector of coordinates of value to put datatype - type that calling routine is passing (one of the valid netcdf data types, excluding NC_CHAR) sign - sign that calling routine is passing (one of EMPTY_STRING (or NULL) = use default sign MI_SIGNED = signed values MI_UNSIGNED = unsigned values @OUTPUT : value - value of variable @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Similar to routine ncvarput1, but the calling routine specifies the form in which data is passed (datatype), as well as the sign. The datatype can only be a numeric type. If the variable in the file is of type NC_CHAR, then an error is returned. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int mivarput1(int cdfid, int varid, long mindex[], nc_type datatype, const char *sign, void *value) { int status; long count[MAX_VAR_DIMS]; MI_SAVE_ROUTINE_NAME("mivarput1"); status = MI_varaccess(MI_PRIV_PUT, cdfid, varid, mindex, miset_coords(MAX_VAR_DIMS, 1L, count), datatype, MI_get_sign_from_string(datatype, sign), value, NULL, NULL); if (status < 0) { milog_message(MI_MSG_WRITEVAR, varid); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : miset_coords @INPUT : nvals - number of values in coordinate vector to set value - value to which coordinates should be set @OUTPUT : coords - coordinate vector @RETURNS : pointer to coords. @DESCRIPTION: Sets nvals entries of the vector coords to value. @METHOD : @GLOBALS : @CALLS : @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI long *miset_coords(int nvals, long value, long coords[]) { int i; MI_SAVE_ROUTINE_NAME("miset_coords"); for (i=0; iincdfid, ptr->invarid, start, count, var_buffer); if (status < 0) { MI_RETURN(MI_ERROR); } /* Put values to output variable */ status = ncvarput(ptr->outcdfid, ptr->outvarid, start, count, var_buffer); if (status < 0) { MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micopy_all_var_defs @INPUT : incdfid - input cdf file id outcdfid - output cdf file id nexclude - number of values in array excluded_vars excluded_vars - array of variable id's in incdfid that should not be copied @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) if an error occurs. @DESCRIPTION: Copies all variable definitions in file incdfid to file outcdfid (including attributes), excluding the variable id's listed in array excluded_vars. File outcdfid must be in define mode. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : August 3, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int micopy_all_var_defs(int incdfid, int outcdfid, int nexclude, int excluded_vars[]) { int num_vars; /* Number of variables in input file */ int varid; /* Variable id counter */ int i; int status; MI_SAVE_ROUTINE_NAME("micopy_all_var_defs"); /* Find out how many variables there are in the file and loop through them */ status = ncinquire(incdfid, NULL, &num_vars, NULL, NULL); if (status < 0) { MI_RETURN(MI_ERROR); } /* Loop through variables, copying them */ for (varid=0; varid=nexclude) { status = micopy_var_def(incdfid, varid, outcdfid); if (status < 0) { MI_RETURN(MI_ERROR); } } } /* Copy global attributes */ for (i=0; i=nexclude) { status = micopy_all_atts(incdfid, NC_GLOBAL, outcdfid, NC_GLOBAL); } MI_RETURN(status); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micopy_all_var_values @INPUT : incdfid - input cdf file id outcdfid - output cdf file id nexclude - number of values in array excluded_vars excluded_vars - array of variable id's in incdfid that should not be copied @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) if an error occurs. @DESCRIPTION: Copies all variable values in file incdfid to file outcdfid, excluding the variable id's listed in array excluded_vars. File outcdfid must be in data mode. Usually called after micopy_all_var_defs with the same arguments. If a variable to be copied is not defined properly in outcdfid, then an error occurs. @METHOD : @GLOBALS : @CALLS : NetCDF routines. @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int micopy_all_var_values(int incdfid, int outcdfid, int nexclude, int excluded_vars[]) { int num_vars; /* Number of variables in input file */ int varid; /* Variable id counter */ int outvarid; /* Output variable id */ char name[MAX_NC_NAME]; /*Variable name */ int i; int status; MI_SAVE_ROUTINE_NAME("micopy_all_var_values"); /* Find out how many variables there are in the file and loop through them */ status = ncinquire(incdfid, NULL, &num_vars, NULL, NULL); if (status < 0) { milog_message(MI_MSG_VARCOUNT); MI_RETURN(MI_ERROR); } /* Loop through variables, copying them */ for (varid=0; varid=nexclude) { /* Get the input variable's name */ status = ncvarinq(incdfid, varid, name, NULL, NULL, NULL, NULL); if (status < 0) { milog_message(MI_MSG_VARINQ); MI_RETURN(MI_ERROR); } /* Look for it in the output file */ outvarid = ncvarid(outcdfid, name); if (outvarid < 0) { milog_message(MI_MSG_OUTPUTVAR, name); MI_RETURN(MI_ERROR); } /* Copy the values */ status = micopy_var_values(incdfid, varid, outcdfid, outvarid); if (status < 0) { MI_RETURN(MI_ERROR); } } } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : micreate_tempfile @INPUT : void @OUTPUT : (none) @RETURNS : Pointer to filename (which must be freed), or NULL if an error occurs. @DESCRIPTION: Creates a temporary file (which is initially CLOSED). @METHOD : Unnecessarily convoluted, I suppose... See comments. @GLOBALS : @CALLS : Standard POSIX/UNIX routines @CREATED : 07-March-2003 by bert@bic.mni.mcgill.ca @MODIFIED : ---------------------------------------------------------------------------- */ /* Force P_tmpdir to be something reasonable. */ #if !defined(P_tmpdir) #define P_tmpdir "/var/tmp" #endif /* P_tmpdir not defined */ MNCAPI char * micreate_tempfile(void) { int tmp_fd=0; char *tmpfile_ptr; #if defined (HAVE_MKSTEMP) /* Best-case scenario (so far...) * mkstemp() creates a file immediately, minimizing the race * conditions that exist when using the other functions. These race * conditions can lead to small security holes (and large, annoying * GNU linker messages). * * The only catch is that mkstemp() does not automatically put the * file in the TMPDIR directory (or some other appropriate place). * So I more-or-less emulate that behavior here. */ const char pat_str[] = "/minc-XXXXXX"; char *tmpdir_ptr; if ((tmpdir_ptr = getenv("TMPDIR")) == NULL) { tmpdir_ptr = P_tmpdir; } tmpfile_ptr = malloc(strlen(tmpdir_ptr) + sizeof (pat_str)); if (tmpfile_ptr == NULL) { return (NULL); } strcpy(tmpfile_ptr, tmpdir_ptr); strcat(tmpfile_ptr, pat_str); tmp_fd = mkstemp(tmpfile_ptr); /* Creates the file if possible. */ #elif defined (HAVE_TEMPNAM) /* Second-best case. While not completely avoiding the race condition, * this approach should at least have the nice property of putting the * tempfile in the right directory (on IRIX and Linux, at least - on * some systems tempnam() may not consult the TMPDIR environment variable). */ tmpfile_ptr = tempnam(NULL, "minc-"); if (tmpfile_ptr == NULL) { return (NULL); } tmp_fd = open(tmpfile_ptr, O_CREAT | O_EXCL | O_RDWR, S_IWRITE | S_IREAD); #elif defined (HAVE_TMPNAM) /* Worst case. tmpnam() is apparently the worst of all possible worlds * here. It doesn't allow any way to force a particular directory, * and it doesn't avoid the race condition. But volume_io used it for * years, so I see no reason to disallow this case for systems that * might not define the above two functions (whether any such systems * exist is unclear to me). */ tmpfile_ptr = malloc(L_tmpnam + 1); if (tmpfile_ptr == NULL) { return (NULL); } if (tmpnam(tmpfile_ptr) == NULL) { free(tmpfile_ptr); return (NULL); } tmp_fd = open(tmpfile_ptr, O_CREAT | O_EXCL | O_RDWR, S_IWRITE | S_IREAD); #else #error "System defines neither mkstemp(), tempnam(), nor tmpnam()" #endif /* Neither HAVE_MKSTEMP, HAVE_TEMPNAM, or HAVE_TMPNAM defined. */ /* If we get here, tmp_fd should have been opened and the file * created. Now go ahead and close the file. */ if (tmp_fd >= 0) { close(tmp_fd); } else { free(tmpfile_ptr); tmpfile_ptr = NULL; } return (tmpfile_ptr); } /** Simple function to read a user's .mincrc file, if present. */ static int miread_cfg(const char *name, char *buffer, int maxlen) { FILE *fp; int result = 0; char *home_ptr = getenv("HOME"); char path[256]; if (home_ptr != NULL) { strncpy(path, home_ptr, sizeof(path) - 1); } else { path[0] = '\0'; } strcat(path, "/.mincrc"); if ((fp = fopen(path, "r")) != NULL) { while (fgets(buffer, maxlen, fp)) { if (buffer[0] == '#') { continue; } if (!strncasecmp(buffer, name, strlen(name))) { char *tmp = strchr(buffer, '='); if (tmp != NULL) { tmp++; while (isspace(*tmp)) { tmp++; } strncpy(buffer, tmp, maxlen); result = 1; break; } } } fclose(fp); } return (result); } int miget_cfg_bool(const char *name) { char buffer[128]; char *var_ptr; if ((var_ptr = getenv(name)) != NULL) { strncpy(buffer, var_ptr, sizeof (buffer) - 1); } else { if (!miread_cfg(name, buffer, sizeof (buffer))) { return (0); } } return (atoi(buffer) != 0); } int miget_cfg_int(const char *name) { char buffer[128]; char *var_ptr; if ((var_ptr = getenv(name)) != NULL) { strncpy(buffer, var_ptr, sizeof (buffer) - 1); } else { if (!miread_cfg(name, buffer, sizeof(buffer))) { return (0); } } return (ParseLong(buffer, NULL)); } char * miget_cfg_str(const char *name) { char buffer[256]; char *var_ptr; if ((var_ptr = getenv(name)) != NULL) { strncpy(buffer, var_ptr, sizeof(buffer) - 1); } else { if (!miread_cfg(name, buffer, sizeof(buffer))) { return (NULL); } } return (strdup(buffer)); } libminc-libminc-2-3-00/libsrc/read_file_names.c000066400000000000000000000054121257462267400213620ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "read_file_names.h" #define FILE_NAME_ALLOC_SIZE 10 #define FILE_PATH_MAX 2046 /* ----------------------------- MNI Header ----------------------------------- @NAME : read_file_names @INPUT : filelist - name of file from which to read names @OUTPUT : num_files - number of files read in @RETURNS : Pointer to a NULL-terminated array of file names @DESCRIPTION: Reads in a list of file names from file filelist or stdin if "-" is specified. Returns NULL if an error occurs. If no error occurs, then a pointer to an empty array is returned and num_files is zero. @METHOD : @GLOBALS : @CALLS : @CREATED : March 8, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char **read_file_names(char *filelist, int *num_files) { char **files; int array_size; int nfiles; FILE *fp; char line[FILE_PATH_MAX+1]; size_t length; /* Open the file */ if (strcmp(filelist, "-") == 0) { fp = stdin; } else { fp = fopen(filelist, "r"); if (fp == NULL) { (void) fprintf(stderr, "Error opening file \"%s\"\n", filelist); return NULL; } } /* Allocate an initial array and NULL-terminate it */ array_size = FILE_NAME_ALLOC_SIZE; files = malloc(sizeof(*files) * array_size); if (files == NULL) { (void) fprintf(stderr, "Error allocating memory\n"); (void) fclose(fp); return NULL; } nfiles = 0; files[nfiles] = NULL; /* Read in file names */ while (fgets(line, sizeof(line)/sizeof(line[0]), fp) != NULL) { /* Remove a trailing newline and check that there is a name */ length = strlen(line); if ((length > 0) && (line[length-1] == '\n')) { line[length-1] = '\0'; length--; } if (length == 0) continue; /* Make room for names if needed */ while (nfiles >= array_size-1) { array_size += FILE_NAME_ALLOC_SIZE; files = realloc(files, sizeof(*files) * array_size); if (files == NULL) { (void) fprintf(stderr, "Error allocating memory\n"); (void) fclose(fp); return NULL; } } /* Save the name, making sure that the list is NULL-terminated */ files[nfiles] = strdup(line); if (files[nfiles] == NULL) { (void) fprintf(stderr, "Error allocating memory\n"); free(files); fclose(fp); return NULL; } nfiles++; files[nfiles] = NULL; } /* Close the file */ (void) fclose(fp); /* Return the number of files */ *num_files = nfiles; return files; } libminc-libminc-2-3-00/libsrc/read_file_names.h000066400000000000000000000014211257462267400213630ustar00rootroot00000000000000 /* ----------------------------- MNI Header ----------------------------------- @NAME : read_file_names @INPUT : filelist - name of file from which to read names @OUTPUT : num_files - number of files read in @RETURNS : Pointer to a NULL-terminated array of file names @DESCRIPTION: Reads in a list of file names from file filelist or stdin if "-" is specified. Returns NULL if an error occurs. If no error occurs, then a pointer to an empty array is returned and num_files is zero. @METHOD : @GLOBALS : @CALLS : @CREATED : March 8, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ char **read_file_names(char *filelist, int *num_files); libminc-libminc-2-3-00/libsrc/restructure.c000066400000000000000000000135051257462267400206560ustar00rootroot00000000000000/** \file restructure.c * \brief In-place reordering of multidimensional arrays. * \author Bert Vincent * ************************************************************************/ #include #include #include "restructure.h" /** In-place array dimension restructuring. * * Based on Chris H.Q. Ding, "An Optimal Index Reshuffle Algorithm for * Multidimensional Arrays and its Applications for Parallel Architectures" * IEEE Transactions on Parallel and Distributed Systems, Vol.12, No.3, * March 2001, pp.306-315. * * I rewrote the algorithm in "C" an generalized it to N dimensions. * * Guaranteed to do the minimum number of memory moves, but requires * that we allocate a bitmap of nelem/8 bytes. The paper suggests * ways to eliminate the bitmap - I'll work on it. */ /** * Map a set of array coordinates to a linear offset in the array memory. */ static size_t index_to_offset(size_t ndims, const size_t sizes[], const size_t index[]) { size_t offset = index[0]; size_t i; for (i = 1; i < ndims; i++) { offset *= sizes[i]; offset += index[i]; } return (offset); } /** * Map a linear offset to a set of coordinates in a multidimensional array. */ static void offset_to_index(size_t ndims, const size_t sizes[], size_t offset, size_t index[]) { size_t i; for (i = ndims - 1; i > 0; i--) { index[i] = offset % sizes[i]; offset /= sizes[i]; } index[0] = offset; } /* Trivial bitmap test & set. */ #define BIT_TST(bm, i) (bm[(i) / 8] & (1 << ((i) % 8))) #define BIT_SET(bm, i) (bm[(i) / 8] |= (1 << ((i) % 8))) #ifndef MAX_ARRAY_DIMS #define MAX_ARRAY_DIMS 1000 #endif /** The main restructuring code. This code will reorganize data in * a multidimensional array "in place". */ void restructure_array(size_t ndims, /* Dimension count */ unsigned char *array, /* Raw data */ const size_t *lengths_perm, /* Permuted lengths */ size_t el_size, /* Element size, in bytes */ const int *map, /* Mapping array */ const int *dir) /* Direction array, in permuted order */ { size_t index[MAX_ARRAY_DIMS]; /* Raw indices */ size_t index_perm[MAX_ARRAY_DIMS]; /* Permuted indices */ size_t lengths[MAX_ARRAY_DIMS]; /* Raw (unpermuted) lengths */ unsigned char *temp; size_t offset_start; size_t offset_next; size_t offset; unsigned char *bitmap; size_t total; size_t i; if ((temp = malloc(el_size)) == NULL) { return; } /** * Permute the lengths from their "output" configuration back into * their "raw" or native order: **/ for (i = 0; i < ndims; i++) { lengths[map[i]] = lengths_perm[i]; } /** * Calculate the total size of the array, in elements. **/ total = 1; for (i = 0; i < ndims; i++) { total *= lengths[i]; } /** * Allocate a bitmap with enough space to hold one bit for each * element in the array. **/ bitmap = calloc((total + 8 - 1) / 8, 1); /* bit array */ if (bitmap == NULL) { free(temp); return; } for (offset_start = 0; offset_start < total; offset_start++) { /** * Look for an unset bit - that's where we start the next * cycle. **/ if (!BIT_TST(bitmap, offset_start)) { /** * Found a cycle we have not yet performed. **/ offset_next = -1; /* Initialize. */ #ifdef _DEBUG printf("%ld", offset_start); #endif /* DEBUG */ /** * Save the first element in this cycle. **/ memcpy(temp, array + (offset_start * el_size), el_size); /** * We've touched this location. **/ BIT_SET(bitmap, offset_start); offset = offset_start; /** * Do until the cycle repeats. **/ while (offset_next != offset_start) { /** * Compute the index from the offset and permuted length. **/ offset_to_index(ndims, lengths_perm, offset, index_perm); /** * Permute the index into the alternate arrangement. **/ for (i = 0; i < ndims; i++) { if (dir[i] < 0) { index[map[i]] = lengths[map[i]] - index_perm[i] - 1; } else { index[map[i]] = index_perm[i]; } } /** * Calculate the next offset from the permuted index. **/ offset_next = index_to_offset(ndims, lengths, index); #ifdef DEBUG if (offset_next >= total) { printf("Fatal - offset %ld out of bounds!\n", offset_next); printf("lengths %lld,%lld,%lld\n", lengths[0],lengths[1],lengths[2]); printf("index %lld,%lld,%lld\n", index_perm[0], index_perm[0], index_perm[2]); //TODO: report MEMORY error somehow exit(-1); } #endif /** * If we are not at the end of the cycle... **/ if (offset_next != offset_start) { /** * Note that we've touched a new location. **/ BIT_SET(bitmap, offset_next); #ifdef _DEBUG printf(" - %ld", offset_next); #endif /* DEBUG */ /** * Move from old to new location. **/ memcpy(array + (offset * el_size), array + (offset_next * el_size), el_size); /** * Advance offset to the next location in the cycle. **/ offset = offset_next; } } /** * Store the first value in the cycle, which we saved in * 'tmp', into the last offset in the cycle. **/ memcpy(array + (offset * el_size), temp, el_size); #ifdef _DEBUG printf("\n"); #endif /* DEBUG */ } } free(bitmap); /* Get rid of the bitmap. */ free(temp); } libminc-libminc-2-3-00/libsrc/restructure.h000066400000000000000000000006031257462267400206560ustar00rootroot00000000000000/* * \file restructure.h * \brief Just declares the prototype of restructure_array(). */ extern void restructure_array(size_t ndims, unsigned char *array, const size_t *lengths_perm, size_t el_size, const int *map, const int *dir); libminc-libminc-2-3-00/libsrc/strdup.c000066400000000000000000000030671257462267400176120ustar00rootroot00000000000000#include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : strdup @INPUT : string - string to duplicate @OUTPUT : (none) @RETURNS : Pointer to duplicate string or NULL if an error occurs @DESCRIPTION: Makes a duplicate of a string and returns a pointer to it. @METHOD : VAX CC rtl does not have strdup, so we provide it here to be included in minc.olb. @GLOBALS : @CALLS : @CREATED : June 18, 1993 (Peter Neelin) @MODIFIED : * $Log: strdup.c,v $ * Revision 6.1 1999-10-19 14:45:11 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:18 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:37:36 neelin * Pre-release * * Revision 1.2 93/08/11 12:06:30 neelin * Added RCS logging in source. * ---------------------------------------------------------------------------- */ char *strdup(const char *string) { int length; char *new_string; /* Get the string length */ length = strlen(string); /* Allocate space */ new_string = malloc((size_t) length+1); if (new_string == NULL) { return NULL; } /* Copy the string */ return strcpy(new_string, string); } libminc-libminc-2-3-00/libsrc/time_stamp.c000066400000000000000000000076261257462267400204400ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : time_stamp.c @DESCRIPTION: File containing routine to create a time stamp string. @METHOD : @CREATED : February 1, 1993 (Peter Neelin) @MODIFIED : * $Log: time_stamp.c,v $ * Revision 6.4 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.3 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.2 2004/10/15 13:46:51 bert * Minor changes for Windows compatibility * * Revision 6.1 2002/01/14 21:28:26 neelin * Moved nd_loop, voxel_loop, ParseArgv and time_stamp from ../progs/Proglib * in order to include them in the main minc library. * * Revision 6.2 1999/10/19 15:57:18 neelin * Fixed log message containing log substitution * * Revision 6.1 1999/10/19 14:45:14 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:41 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:41 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:50 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:35 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:34:30 neelin * Release of minc version 0.2 * * Revision 1.4 94/09/28 10:34:20 neelin * Pre-release * * Revision 1.3 93/08/04 13:03:56 neelin * Added RCS Log to keep track of modifications in source * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #ifdef HAVE_STRING_H #include #endif #include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : time_stamp @INPUT : argc - number of arguments argv - list of arguments @OUTPUT : @RETURNS : pointer to string containing time stamp. @DESCRIPTION: Function to produce a time stamp string for a program. Returns a string of the form "date > command". The command is simply the concatenation of argv elements. @METHOD : @GLOBALS : @CALLS : @CREATED : February 1, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ #include char *time_stamp(int argc, char *argv[]) { char *str, *the_time; size_t length, last; int i; static char separator[]={">>>"}; time_t timer; /* Get the time, overwriting newline */ timer = time(NULL); the_time = ctime(&timer); /* Get the total length of the string and allocate space */ length=strlen(the_time) + strlen(separator) + 2; for(i=0; i /* Undefine those things that get redefined in float.h */ #ifdef FLT_DIG #undef FLT_DIG #endif #ifdef DBL_DIG #undef DBL_DIG #endif #ifdef DBL_MIN #undef DBL_MIN #endif #ifdef DBL_MAX #undef DBL_MAX #endif #include libminc-libminc-2-3-00/libsrc/value_conversion.c000066400000000000000000000635011257462267400216510ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : value_conversion.c @DESCRIPTION: File of functions for converting values. These routines are for use by other MINC routines only. @METHOD : Routines included in this file : semiprivate : (public but destined only for this package) MI_varaccess MI_var_loop MI_get_sign_from_string MI_convert_type private : MI_get_sign MI_var_action @CREATED : July 27, 1992. (Peter Neelin, Montreal Neurological Institute) @MODIFIED : * $Log: value_conversion.c,v $ * Revision 6.8 2008-01-17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.7 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.6 2004/10/15 13:45:28 bert * Minor changes for Windows compatibility * * Revision 6.5 2004/04/27 15:49:51 bert * Use new logging, gettext preparation * * Revision 6.4 2003/11/14 16:52:24 stever * More last-minute fixes. * * Revision 6.3 2003/09/18 16:17:23 bert * Use fabs instead of ABS * * Revision 6.2 2001/04/17 18:40:14 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:12 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.1 1997/04/10 18:14:50 neelin * Fixed handling of invalid data when icv scale is zero. * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.2 1995/02/08 19:14:44 neelin * More changes for irix 5 lint. * * Revision 2.1 1995/02/08 19:01:06 neelin * Moved private function declarations from minc_routines.h to appropriate file. * * Revision 2.0 1994/09/28 10:38:21 neelin * Release of minc version 0.2 * * Revision 1.9 94/09/28 10:37:22 neelin * Pre-release * * Revision 1.8 93/11/05 09:18:08 neelin * Improved epsilon calculation for valid range checking. * * Revision 1.7 93/10/28 15:12:06 neelin * Fixed fillvalue checking stuff in MI_convert_type. * * Revision 1.6 93/10/28 10:19:16 neelin * Added an epsilon for fillvalue checking in routine MI_convert_type (for * reading through an icv). * * Revision 1.5 93/08/11 12:06:32 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include #include "type_limits.h" /* Private functions */ PRIVATE int MI_var_action(int ndims, long var_start[], long var_count[], long nvalues, void *var_buffer, void *caller_data); PRIVATE int MI_get_sign(nc_type datatype, int sign); /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_varaccess @INPUT : operation - either MI_PRIV_GET or MI_PRIV_PUT, indicating whether the routine should get or put data from/to a cdf file cdfid - cdf file id varid - variable id start - vector of coordinates of corner of hyperslab count - vector of edge lengths of hyperslab datatype - type that calling routine wants (one of the valid netcdf data types, excluding NC_CHAR) sign - sign that the calling routine wants (one of MI_PRIV_SIGNED, MI_PRIV_UNSIGNED, MI_PRIV_DEFAULT). bufsize_step - vector of buffer size steps wanted by caller (MI_var_loop will try, but no guarantees); if NULL, then 1 is assumed. For the first index that cannot be read in one piece, the allocated buffer will tend to have the count of as a multiple of the corresponding value in this vector. icvp - pointer to icv structure (image conversion variable) If NULL, then icvp->do_scale and icvp->do_dimconvert are assumed to be FALSE. icvp->do_scale - boolean indicating whether scaling should be done. If so, then outvalue = icvp->scale * (double) invalue + icvp->offset icvp->scale - (see do_scale) icvp->offset - (see do_scale) icvp->do_dimconvert - boolean indicating whether the dimension conversion routine should be called icvp->dimconvert_func - dimension conversion routine values - values to store in variable (for put) @OUTPUT : values - values to get from variable (for get) @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Routine to do work for getting/putting and converting the type of variable values. Similar to routine ncvarget/ ncvarput but the calling routine specifies the form in which data should be returned/passed (datatype), as well as the sign. The datatype can only be a numeric type. If the variable in the file is of type NC_CHAR, then an error is returned. Values can optionally be scaled (for image conversion routines) by setting icvp->do_scale to TRUE and using icvp->scale and icvp->offset. Dimensional conversion can be done be setting icvp->do_dimconvert to TRUE and passing a function to be called (icvp->dimconvert_func). @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ SEMIPRIVATE int MI_varaccess(int operation, int cdfid, int varid, long start[], long count[], nc_type datatype, int sign, void *values, int *bufsize_step, mi_icv_type *icvp) { mi_varaccess_type strc; /* Structure of values for functions */ int ndims; /* Number of variable dimensions */ char stringa[MI_MAX_ATTSTR_LEN]; /* String for attribute value */ char *string = stringa; int oldncopts; /* Save old value of ncopts */ MI_SAVE_ROUTINE_NAME("MI_varaccess"); /* Check to see if ivc structure was passed and set variables needed by this routine */ if (icvp == NULL) { strc.do_scale = FALSE; strc.do_dimconvert = FALSE; strc.do_fillvalue = FALSE; } else { strc.do_scale = icvp->do_scale; strc.do_dimconvert = icvp->do_dimconvert; strc.do_fillvalue = icvp->do_fillvalue; } /* Inquire about the variable */ MI_CHK_ERR(ncvarinq(cdfid, varid, NULL, &(strc.var_type), &ndims, NULL, NULL)) /* Check that the variable type is numeric */ if ((datatype==NC_CHAR) || (strc.var_type==NC_CHAR)) { milog_message(MI_MSG_VARNOTNUM); MI_RETURN(MI_ERROR); } /* Try to find out the sign of the variable using MIsigntype. To avoid programs dying unexpectedly, we must change ncopts, then restore it */ oldncopts = ncopts; ncopts = 0; string=miattgetstr(cdfid, varid, MIsigntype, MI_MAX_ATTSTR_LEN, string); ncopts = oldncopts; /* Get the signs */ strc.var_sign = MI_get_sign_from_string(strc.var_type, string); strc.call_sign = MI_get_sign(datatype, sign); /* Check to see if the type requested is the same as the variable type, the signs are the same and no dimension conversion is needed. If so, just get/put the values */ if ((datatype == strc.var_type) && (strc.call_sign == strc.var_sign) && !strc.do_scale && !strc.do_dimconvert && !strc.do_fillvalue) { switch (operation) { case MI_PRIV_GET: MI_CHK_ERR(ncvarget(cdfid, varid, start, count, values)) break; case MI_PRIV_PUT: MI_CHK_ERR(ncvarput(cdfid, varid, start, count, values)) break; default: milog_message(MI_MSG_BADOP); MI_RETURN(MI_ERROR); } MI_RETURN(MI_NOERROR); } /* Otherwise, we have to loop through data. Set up structure and call MI_var_loop */ strc.operation=operation; strc.cdfid=cdfid; strc.varid=varid; strc.call_type=datatype; strc.var_value_size=nctypelen(strc.var_type); strc.call_value_size=nctypelen(strc.call_type); strc.icvp=icvp; strc.start=start; strc.count=count; strc.values=values; MI_CHK_ERR( MI_var_loop(ndims, start, count, strc.var_value_size, bufsize_step, MI_MAX_VAR_BUFFER_SIZE, (void *) &strc, MI_var_action) ) MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_var_action @INPUT : ndims - number of dimensions var_start - coordinate vector of corner of hyperslab var_count - vector of edge lengths of hyperslab nvalues - number of values in hyperslab var_buffer - pointer to variable buffer caller_data - pointer to data from MI_varaccess @OUTPUT : (none) @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Buffer action routine to be called by MI_var_loop, for use by MI_varaccess. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 30, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_var_action(int ndims, long var_start[], long var_count[], long nvalues, void *var_buffer, void *caller_data) /* ARGSUSED */ { mi_varaccess_type *ptr; /* Pointer to data from MI_varaccess */ int status; /* Status returned by function call */ MI_SAVE_ROUTINE_NAME("MI_var_action"); ptr=(mi_varaccess_type *) caller_data; /* Get/put values and do conversions, etc. */ switch (ptr->operation) { case MI_PRIV_GET: status=ncvarget(ptr->cdfid, ptr->varid, var_start, var_count, var_buffer); if (status != MI_ERROR) { /* If doing dimension conversion, let dimconvert function do all the work, including type conversion */ if (!ptr->do_dimconvert) { status=MI_convert_type(nvalues, ptr->var_type, ptr->var_sign, var_buffer, ptr->call_type, ptr->call_sign, ptr->values, ptr->icvp); } else { status=(*(ptr->icvp->dimconvert_func))(ptr->operation, ptr->icvp, ptr->start, ptr->count, ptr->values, var_start, var_count, var_buffer); } } break; case MI_PRIV_PUT: /* If doing dimension conversion, let dimconvert function do all the work, including type conversion */ if (!ptr->do_dimconvert) { status=MI_convert_type(nvalues, ptr->call_type, ptr->call_sign, ptr->values, ptr->var_type, ptr->var_sign, var_buffer, ptr->icvp); } else { status=(*(ptr->icvp->dimconvert_func))(ptr->operation, ptr->icvp, ptr->start, ptr->count, ptr->values, var_start, var_count, var_buffer); } if (status != MI_ERROR) { status=ncvarput(ptr->cdfid, ptr->varid, var_start, var_count, var_buffer); } break; default: milog_message(MI_MSG_BADOP); status=MI_ERROR; } /* Check for an error */ MI_CHK_ERR(status) /* Increment the values pointer */ if (!ptr->do_dimconvert) { ptr->values = (void *) ((char *) ptr->values + nvalues*ptr->call_value_size); } MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_var_loop @INPUT : ndims - number of dimensions in variable start - vector of coordinates of corner of hyperslab count - vector of edge lengths of hyperslab value_size - size (in bytes) of each value to be buffered bufsize_step - vector of buffer size steps wanted by caller (MI_var_loop will try, but no guarantees); if NULL, then 1 is assumed. For the first index that cannot be read in one piece, the allocated buffer will tend to have the count of as a multiple of the corresponding value in this vector. max_buffer_size - maximum size (in bytes) of buffer caller_data - pointer to a structure of data to pass to functions action_func - function to do something with each buffer @OUTPUT : (none) @RETURNS : MI_ERROR (=-1) when an error occurs @DESCRIPTION: Routine to loop through a variable's indices, getting data into a buffer and doing something to it. A function pointer is passed that will perform these functions on each buffer. @METHOD : @GLOBALS : @CALLS : NetCDF and MINC routines @CREATED : July 29, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ SEMIPRIVATE int MI_var_loop(int ndims, long start[], long count[], int value_size, int *bufsize_step, long max_buffer_size, void *caller_data, int (*action_func) (int, long [], long [], long, void *, void *)) { long nvalues, newnvalues; /* Number of values in fastest varying dims. Note that any dimensional subscript variables should be long */ int firstdim; /* First dimension that doesn't fit in buffer */ long ntimes; /* Number of firstdim elements that fit in buf */ void *var_buffer; /* Pointer to buffer for variable data */ long var_count[MAX_VAR_DIMS]; /* Count, start and end coordinate */ long var_start[MAX_VAR_DIMS]; /* vectors for getting buffers */ long var_end[MAX_VAR_DIMS]; int i; /* Looping variable - only used for dimension number, not dimension subscript */ MI_SAVE_ROUTINE_NAME("MI_var_loop"); /* Find out how much space we need and then allocate a buffer. To do this we find out how many dimensions will fit in our maximum buffer size. firstdim is the index of the first dimension that won't fit. nvalues is the number of values in the first dimensions that do fit in the buffer. ntimes is the number of times that the first dimensions fit in the buffer. To make things simpler, dimension 0 is always considered to not fit, even if it does. */ nvalues=newnvalues=1; for (firstdim=ndims-1; firstdim>=1; firstdim--) { newnvalues *= count[firstdim]; if (newnvalues*value_size > max_buffer_size) break; nvalues = newnvalues; } if (firstdim<0) { /* Check for 0-dim variable */ firstdim=0; ntimes=1; } else { ntimes = MIN(MI_MAX_VAR_BUFFER_SIZE/(nvalues*value_size), count[firstdim]); /* Try to make ntimes an convenient multiple for the caller */ if ((ntimes != count[firstdim]) && (bufsize_step != NULL)) { ntimes = MAX(1, ntimes - (ntimes % bufsize_step[firstdim])); } } /* Allocate space for variable values */ if ((var_buffer = MALLOC(ntimes*nvalues*value_size, char)) == NULL) { milog_message(MI_MSG_OUTOFMEM); MI_RETURN(MI_ERROR); } /* Create a count variable for the var buffer, with 1s for dimensions that vary slower than firstdim and count[i] for dimensions that vary faster. Set a start variable for the var buffer, equal to start. Set an end variable for the var buffer. */ if (ndims <= 0) { /* Handle zero-dimension variable */ var_start[0]=0; var_end[0]=1; var_count[0]=1; } for (i=0; ifirstdim) ? count[i] : (i==firstdim) ? ntimes : 1; var_start[i] = start[i]; var_end[i] = start[i] + count[i]; } /* Loop through the dimensions, copying buffers, etc. Exit when the slowest varying dimension reaches its limit. */ while (var_start[0] < var_end[0]) { var_count[firstdim] = MIN(ntimes, var_end[firstdim] - var_start[firstdim]); /* Do the stuff on the buffer */ if ((*action_func)(ndims, var_start, var_count, var_count[firstdim]*nvalues, var_buffer, caller_data) == MI_ERROR) { FREE(var_buffer); MI_RETURN_ERROR(MI_ERROR); } /* Increment the start counters */ var_start[firstdim] += var_count[firstdim]; i=firstdim; while ( (i>0) && (var_start[i] >= var_end[i])) { var_start[i] = start[i]; i--; var_start[i]++; } } /* Free the buffer and return */ FREE(var_buffer); MI_RETURN(MI_NOERROR); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_get_sign_from_string @INPUT : type - type of value sign - sign of value (one of MI_EMPTY_STRING, MI_SIGNED or MI_UNSIGNED) @OUTPUT : (none) @RETURNS : either MI_PRIV_SIGNED or MI_PRIV_UNSIGNED @DESCRIPTION: Converts sign string to either MI_PRIV_SIGNED or MI_PRIV_UNSIGNED, as appropriate, by calling MI_get_sign. @METHOD : @GLOBALS : (none) @CALLS : MI_get_sign @CREATED : July 30, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ SEMIPRIVATE int MI_get_sign_from_string(nc_type datatype, const char *sign) { MI_SAVE_ROUTINE_NAME("MI_get_sign_from_string"); MI_RETURN(MI_get_sign(datatype, (sign == NULL) || (*sign == '\0') ? MI_PRIV_DEFSIGN : (STRINGS_EQUAL(sign, MI_SIGNED)) ? MI_PRIV_SIGNED : (STRINGS_EQUAL(sign, MI_UNSIGNED)) ? MI_PRIV_UNSIGNED : MI_PRIV_DEFSIGN)); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_get_sign @INPUT : type - type of value sign - sign of value (one of MI_PRIV_DEFSIGN, MI_PRIV_SIGNED or MI_PRIV_UNSIGNED) @OUTPUT : (none) @RETURNS : either MI_PRIV_SIGNED or MI_PRIV_UNSIGNED @DESCRIPTION: Converts sign variable to either MI_PRIV_SIGNED or MI_PRIV_UNSIGNED, as appropriate, if its value is MI_PRIV_DEFSIGN, otherwise the value of sign is returned as is. The default signs are byte : unsigned short : signed int : signed float : signed double : signed @METHOD : @GLOBALS : @CALLS : @CREATED : July 27, 1992 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int MI_get_sign(nc_type datatype, int sign) { MI_SAVE_ROUTINE_NAME("MI_get_sign"); MI_RETURN( ((datatype==NC_FLOAT) || (datatype==NC_DOUBLE)) ? MI_PRIV_SIGNED : ((sign==MI_PRIV_SIGNED) || (sign==MI_PRIV_UNSIGNED)) ? sign : (datatype==NC_BYTE) ? MI_PRIV_UNSIGNED : (datatype==NC_SHORT) ? MI_PRIV_SIGNED : (datatype==NC_INT) ? MI_PRIV_SIGNED : MI_PRIV_SIGNED ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : MI_convert_type @INPUT : number_of_values - number of values to copy intype - type of input values insign - sign of input values (one of MI_PRIV_DEFSIGN, MI_PRIV_SIGNED or MI_PRIV_UNSIGNED) invalues - vector of values outtype - type of output values outsign - sign of output values icvp - pointer to icv structure (if NULL, then icvp->do_scale is assumed to be FALSE) icvp->do_scale - boolean indicating whether scaling should be done. If so, then outvalue = icvp->scale * (double) invalue + icvp->offset icvp->scale - (see do_scale) icvp->offset - (see do_scale) @OUTPUT : outvalues - output values @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Converts the invalues to outvalues according to their type. Types must be numeric. Values out of range are truncated to the nearest value in range. The sign of integer values is given by insign and outsign, which must have values MI_PRIV_DEFSIGN, MI_PRIV_SIGNED or MI_PRIV_UNSIGNED. If it is MI_PRIV_DEFSIGN then the default signs are used (from MI_get_sign) : byte : unsigned short : signed int : signed Note that if a conversion must take place, then all input values are converted to double. Values can be scaled through icvp->scale and icvp->offset by setting icvp->do_scale to TRUE. @METHOD : @GLOBALS : @CALLS : @CREATED : July 27, 1992 (Peter Neelin) @MODIFIED : August 28, 1992 (P.N.) - replaced type conversions with macros ---------------------------------------------------------------------------- */ SEMIPRIVATE int MI_convert_type(long number_of_values, nc_type intype, int insign, void *invalues, nc_type outtype, int outsign, void *outvalues, mi_icv_type *icvp) { int inincr, outincr; /* Pointer increments for arrays */ int insgn, outsgn; /* Signs for input and output */ long i; double dvalue=0.0; /* Temporary double for conversion */ void *inptr, *outptr; /* Pointers to input and output values */ int do_scale; /* Should scaling be done? */ int do_fillvalue; /* Should fillvalue checking be done? */ double fillvalue; /* Value to fill with */ double dmax, dmin; /* Range of legal values */ double epsilon; /* Epsilon for legal values comparisons */ MI_SAVE_ROUTINE_NAME("MI_convert_type"); /* Check to see if icv structure was passed and set variables needed */ if (icvp == NULL) { do_scale=FALSE; do_fillvalue = FALSE; dmax = dmin = 0.0; fillvalue = 0.0; } else { do_scale=icvp->do_scale; do_fillvalue=icvp->do_fillvalue; fillvalue = icvp->user_fillvalue; dmax = icvp->fill_valid_max; dmin = icvp->fill_valid_min; epsilon = (dmax - dmin) * FILLVALUE_EPSILON; epsilon = fabs(epsilon); dmax += epsilon; dmin -= epsilon; } /* Check the types and get their size */ if ((intype==NC_CHAR) || (outtype==NC_CHAR)) { milog_message(MI_MSG_VARNOTNUM); MI_RETURN(MI_ERROR); } if (((inincr =nctypelen(intype ))==MI_ERROR) || ((outincr=nctypelen(outtype))==MI_ERROR)) { MI_RETURN(MI_ERROR); } /* Get the sign of input and output values */ insgn = MI_get_sign(intype, insign); outsgn = MI_get_sign(outtype, outsign); /* Check to see if a conversion needs to be made. If not, just copy the memory */ if ((intype==outtype) && (insgn==outsgn) && !do_scale && !do_fillvalue) { (void) memcpy(outvalues, invalues, (size_t) number_of_values*inincr); } /* Otherwise, loop through */ else { /* Step through values */ inptr=invalues; outptr=outvalues; for (i=0 ; i dmax))) { dvalue = fillvalue; } else if (do_scale) { dvalue = icvp->scale * dvalue + icvp->offset; } /* Truncate if necessary and assign the value */ {MI_FROM_DOUBLE(dvalue, outtype, outsgn, outptr)} inptr = (void *) ((char *)inptr + inincr); outptr = (void *) ((char *)outptr + outincr); } /* End of for loop */ } /* End of else */ MI_RETURN(MI_NOERROR); } libminc-libminc-2-3-00/libsrc/voxel_loop.c000066400000000000000000003756131257462267400204700ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : voxel_loop.c @DESCRIPTION: Routines to loop through a file doing an operation on a single voxel. @METHOD : @GLOBALS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : * $Log: voxel_loop.c,v $ * Revision 6.14 2010-03-02 23:24:40 rotor * * libsrc/hdf_convenience.c: removed spurious debug output * * libsrc/minc.h: replaced MAX_NC_OPEN with 32 * * libsrc/voxel_loop.c: replaced MAX_NC_OPEN with MI_MAX_NUM_ICV * * Revision 6.13 2010-03-02 12:23:14 rotor * * ported HDF calls to 1.8.x * * Makefile.am: updated for minccmp * * Revision 6.12 2008/01/17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.11 2008/01/13 09:38:54 stever * Avoid compiler warnings about functions and variables that are defined * but not used. Remove some such functions and variables, * conditionalize some, and move static declarations out of header files * into C files. * * Revision 6.10 2008/01/13 04:30:28 stever * Add braces around static initializers. * * Revision 6.9 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.8 2006/04/09 15:40:25 bert * Minor change * * Revision 6.7 2005/08/26 21:04:58 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.6 2004/11/01 22:23:14 bert * Get rid of minc_def.h, use standard MALLOC() macro * * Revision 6.5 2004/10/15 13:46:52 bert * Minor changes for Windows compatibility * * Revision 6.4 2004/04/27 15:43:29 bert * Support MINC 2.0 format * * Revision 6.3 2003/11/14 16:52:24 stever * More last-minute fixes. * * Revision 6.2 2003/09/18 16:49:46 bert * Use fabs instead of ABS * * Revision 6.1 2002/01/14 21:28:26 neelin * Moved nd_loop, voxel_loop, ParseArgv and time_stamp from ../progs/Proglib * in order to include them in the main minc library. * * Revision 6.9 2002/01/14 20:02:39 neelin * Force the input buffers to have a minimum size so that large images do * not force excessive reading of the input file. * * Revision 6.8 2001/11/28 18:39:16 neelin * Added get_info_vxoel_index to allow users to get the full multi-dimensional * file index of the current voxel. * Modifications to allow arg_string to be NULL. * * Revision 6.7 2001/09/18 15:32:27 neelin * Create image variable last to allow big images and to fix compatibility * problems with 2.3 and 3.x. * * Revision 6.6 2001/08/16 16:41:32 neelin * Added library functions to handle reading of datatype, sign and valid range, * plus writing of valid range and setting of default ranges. These functions * properly handle differences between valid_range type and image type. Such * difference can cause valid data to appear as invalid when double to float * conversion causes rounding in the wrong direction (out of range). * Modified voxel_loop, volume_io and programs to use these functions. * * Revision 6.5 2001/08/16 13:32:27 neelin * Partial fix for valid_range of different type from image (problems * arising from double to float conversion/rounding). NOT COMPLETE. * * Revision 6.4 2001/04/24 13:38:40 neelin * Replaced NC_NAT with MI_ORIGINAL_TYPE. * * Revision 6.3 2001/04/17 18:40:15 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.2 2000/09/19 14:36:05 neelin * Added ability for caller to specify functions for allocating and freeing * voxel buffers used in loop. This is particularly useful for embedding * the voxel_loop code in other programs, such as Python, which manage memory * in their own way. * * Revision 6.1 1999/10/19 14:45:15 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:41 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:41 neelin * Release of minc version 0.5 * * Revision 4.2 1997/06/20 13:58:35 neelin * Fixed bug: when doing accumulation with no output file and with * 4D input (or more), had problem setting input start vector. This broke * mincconcat for 4D input files. * * Revision 4.1 1997/05/22 12:41:40 neelin * Loosened up checking of start coordinates so that we look at error * relative to the total extent of the volume (nelements*step) instead of * relative to start value (which may be close to zero). * * Revision 4.0 1997/05/07 20:00:50 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:35 neelin * Release of minc version 0.3 * * Revision 1.6 1995/05/11 12:31:29 neelin * Removed error messages from ncattdel. * * Revision 1.5 1995/05/02 16:05:32 neelin * Fixed bug in handling more than 30 files (needed to detach from icv). * Added more checking of dimensions. * Fixed bug in allocation of space for global max/min. * * Revision 1.4 1995/05/01 20:04:50 neelin * Fixed memory leak - not freeing global_minimum/maximum. * * Revision 1.3 1995/03/21 15:33:07 neelin * Changed call to voxel_function to always use proper vector length and * set num_voxels to the number of voxels, not multiplying by vector length. * * Revision 1.2 1995/03/21 14:06:39 neelin * Improved interface and added lots of functionality (much for the benefit * of mincconcat). * * Revision 1.1 94/12/14 10:17:19 neelin * Initial revision * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include "minc_private.h" #include #include #include "voxel_loop.h" #include "nd_loop.h" /* Minimum number of voxels to put in a buffer. If this is too small, then for large images excessive reading can result. If it is too large, then for large images too much memory will be used. */ #define MIN_VOXELS_IN_BUFFER 1024 /* Default ncopts values for error handling */ #define NC_OPTS_VAL NC_VERBOSE | NC_FATAL /* Epsilon for coordinate comparisons */ #define COORD_EPSILON (FLT_EPSILON * 10.0) /* Typedefs */ typedef struct Loopfile_Info Loopfile_Info; /* Structure definitions */ struct Loop_Info { int current_file; int current_index; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; long dimvoxels[MAX_VAR_DIMS]; /* Number of voxels skipped by a step of one in each dimension */ Loopfile_Info *loopfile_info; }; struct Loop_Options { int verbose; int clobber; nc_type datatype; int is_signed; double valid_range[2]; int max_open_files; int check_all_input_dim_info; int convert_input_to_scalar; int output_vector_size; /* 0 = same as input size */ int input_mincid; long total_copy_space; char *loop_dimension; int num_all_inputs; VoxelInputFileFunction input_file_function; VoxelOutputFileFunction output_file_function; int copy_all_header_info; int do_accumulate; int num_extra_buffers; VoxelStartFunction start_function; VoxelFinishFunction finish_function; VoxelFunction voxel_function; void *caller_data; Loop_Info *loop_info; int is_floating_type; AllocateBufferFunction allocate_buffer_function; #if MINC2 int v2format; #endif /* MINC2 */ }; struct Loopfile_Info { int cflags; /* creation flags */ int num_input_files; int num_output_files; char **input_files; char **output_files; int input_all_open; int output_all_open; int *input_mincid; int *output_mincid; int *input_icvid; int *output_icvid; int current_input_file_number; int current_output_file_number; int headers_only; int want_headers_only; int sequential_access; int can_open_all_input; }; /* Function prototypes */ PRIVATE int get_loop_dim_size(int inmincid, Loop_Options *loop_options); PRIVATE void translate_input_coords(int inmincid, long chunk_cur[], long input_cur[], long chunk_curcount[], long input_curcount[], int *loop_dim_index, Loop_Options *loop_options); PRIVATE void check_input_files(Loop_Options *loop_options, Loopfile_Info *loopfile_info); PRIVATE int input_image_varinq(int mincid, int imgid, char *name, nc_type *datatype, int *ndims, int dim[], int *natts, Loop_Options *loop_options); PRIVATE void get_dim_info(int mincid, int *ndims, long size[], char dimname[][MAX_NC_NAME], double start[], double step[], double dircos[][3], int is_regular[], Loop_Options *loop_options); PRIVATE void setup_output_files(Loop_Options *loop_options, Loopfile_Info *loopfile_info, char *arg_string); PRIVATE long get_vector_length(int mincid, Loop_Options *loop_options); PRIVATE void setup_variables(int inmincid, int outmincid, int output_curfile, char *arg_string, Loop_Options *loop_options); PRIVATE void update_history(int mincid, char *arg_string); PRIVATE void setup_icvs(Loop_Options *loop_options, Loopfile_Info *loopfile_info); PRIVATE void do_voxel_loop(Loop_Options *loop_options, Loopfile_Info *loopfile_info); PRIVATE void setup_looping(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int *ndims, long block_start[], long block_end[], long block_incr[], long *block_num_voxels, long chunk_incr[], long *chunk_num_voxels); PRIVATE void initialize_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int *ifile, int *dim_index, int *dummy_index); PRIVATE int finish_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int ifile, int dim_index, int dummy_index); PRIVATE void increment_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int *ifile, int *dim_index, int *dummy_index); PRIVATE Loopfile_Info *initialize_loopfile_info(int num_input_files, char *input_files[], int num_output_files, char *output_files[], Loop_Options *loop_options); PRIVATE void cleanup_loopfile_info(Loopfile_Info *loopfile_info); PRIVATE int get_input_numfiles(Loopfile_Info *loopfile_info); PRIVATE int get_output_numfiles(Loopfile_Info *loopfile_info); PRIVATE char *get_input_filename(Loopfile_Info *loopfile_info, int file_num); PRIVATE void set_input_headers_only(Loopfile_Info *loopfile_info, int headers_only); PRIVATE void set_input_sequential(Loopfile_Info *loopfile_info, int sequential_access); PRIVATE int get_input_mincid(Loopfile_Info *loopfile_info, int file_num); PRIVATE int get_output_mincid(Loopfile_Info *loopfile_info, int file_num); PRIVATE int create_output_file(Loopfile_Info *loopfile_info, int file_num); PRIVATE int get_input_icvid(Loopfile_Info *loopfile_info, int file_num); PRIVATE int get_output_icvid(Loopfile_Info *loopfile_info, int file_num); PRIVATE int create_input_icvid(Loopfile_Info *loopfile_info, int file_num); PRIVATE int create_output_icvid(Loopfile_Info *loopfile_info, int file_num); PRIVATE Loop_Info *create_loop_info(void); PRIVATE void initialize_loop_info(Loop_Info *loop_info); PRIVATE void free_loop_info(Loop_Info *loop_info); PRIVATE void set_info_shape(Loop_Info *loop_info, long start[], long count[]); PRIVATE void set_info_current_file(Loop_Info *loop_info, int current_file); PRIVATE void set_info_current_index(Loop_Info *loop_info, int current_index); PRIVATE void set_info_loopfile_info(Loop_Info *loop_info, Loopfile_Info *loopfile_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : voxel_loop @INPUT : num_input_files - number of input files. input_files - array of names of input files. num_output_files - number of output files. output_files - array of names of output files. arg_string - string for history. loop_options - pointer to structure containing loop options. voxel_function - user function to process a group of voxels. See description in header file. caller_data - data that will be passed to voxel_function @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to loop through the voxels of a file and call a function to operate on each voxel. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void voxel_loop(int num_input_files, char *input_files[], int num_output_files, char *output_files[], char *arg_string, Loop_Options *loop_options, VoxelFunction voxel_function, void *caller_data) { Loopfile_Info *loopfile_info; int need_to_free_loop_options; int old_ncopts; //(void)fprintf(stderr, "About to loop, max_buffer is %d\n", loop_options->total_copy_space); /* Save ncopts and set it to default value */ old_ncopts = ncopts; ncopts = NC_OPTS_VAL; /* Check that there is at least one input file */ if (num_input_files < 1) { (void) fprintf(stderr, "There must be at least one input file.\n"); exit(EXIT_FAILURE); } if (num_output_files < 0) { (void) fprintf(stderr, "Negative number of output files!\n"); exit(EXIT_FAILURE); } /* Initialize loop options if needed */ need_to_free_loop_options = FALSE; if (loop_options == NULL) { loop_options = create_loop_options(); need_to_free_loop_options = TRUE; } loop_options->voxel_function = voxel_function; loop_options->caller_data = caller_data; /* Make sure that Loop_Info structure is initialized */ initialize_loop_info(loop_options->loop_info); /* Initialize looping info */ loopfile_info = initialize_loopfile_info(num_input_files, input_files, num_output_files, output_files, loop_options); /* Check that input files match */ set_input_headers_only(loopfile_info, TRUE); check_input_files(loop_options, loopfile_info); set_input_headers_only(loopfile_info, FALSE); /* Set up variables in output file */ setup_output_files(loop_options, loopfile_info, arg_string); /* Setup icv's */ setup_icvs(loop_options, loopfile_info); /* Loop through the voxels */ do_voxel_loop(loop_options, loopfile_info); /* Clean up looping info */ cleanup_loopfile_info(loopfile_info); /* Free loop options if needed */ if (need_to_free_loop_options) { free_loop_options(loop_options); } /* Restore ncopts */ ncopts = old_ncopts; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_loop_dim_size @INPUT : inmincid - input minc id loop_options - Options for loops @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to get the size of the looping dimension for the given input file @METHOD : @GLOBALS : @CALLS : @CREATED : January 24, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_loop_dim_size(int inmincid, Loop_Options *loop_options) { int dimid; long dim_length; int ndims, dim[MAX_VAR_DIMS]; int found; int idim; /* Look for dimension */ dimid = MI_ERROR; if (loop_options->loop_dimension != NULL) { ncopts = 0; dimid = ncdimid(inmincid, loop_options->loop_dimension); ncopts = NC_OPTS_VAL; } if (dimid == MI_ERROR) return 1; (void) ncdiminq(inmincid, dimid, NULL, &dim_length); /* Get image variable info */ (void) ncvarinq(inmincid, ncvarid(inmincid, MIimage), NULL, NULL, &ndims, dim, NULL); /* Check to see if the dimension subscripts the image */ found = FALSE; for (idim=0; idim < ndims; idim++) { if (dimid == dim[idim]) found = TRUE; } /* Return appropriate value */ if (found) return dim_length; else return 1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : translate_input_coords @INPUT : inmincid - input minc id chunk_cur - start for current chunk chunk_curcount - count for current chunk loop_options - Options for loops @OUTPUT : input_cur - start for input file input_curcount - count for input file loop_dim_index - index in input_cur for looping dimension @RETURNS : (nothing) @DESCRIPTION: Routine to translate the input hyperslab coords from those excluding the looping dimension to ones appropriate for the input file. Also returns the index (offset in input_cur) for the looping dimension (so that the caller can modify this index). @METHOD : @GLOBALS : @CALLS : @CREATED : January 24, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void translate_input_coords(int inmincid, long chunk_cur[], long input_cur[], long chunk_curcount[], long input_curcount[], int *loop_dim_index, Loop_Options *loop_options) { int dimid; int ndims, dim[MAX_VAR_DIMS]; int idim, jdim; /* Look for dimension */ dimid = MI_ERROR; if (loop_options->loop_dimension != NULL) { ncopts = 0; dimid = ncdimid(inmincid, loop_options->loop_dimension); ncopts = NC_OPTS_VAL; } /* Get image variable info */ (void) ncvarinq(inmincid, ncvarid(inmincid, MIimage), NULL, NULL, &ndims, dim, NULL); /* Copy the hyperslab coordinates and get the index */ *loop_dim_index = ndims; jdim = 0; for (idim=0; idim < ndims; idim++) { if (dimid != dim[idim]) { input_cur[idim] = chunk_cur[jdim]; input_curcount[idim] = chunk_curcount[jdim]; jdim++; } else { input_cur[idim] = 0; input_curcount[idim] = 1; *loop_dim_index = idim; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_input_files @INPUT : loop_options - Options for loops loopfile_info - Information describing looping stuff and files @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to check input files for consistency. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void check_input_files(Loop_Options *loop_options, Loopfile_Info *loopfile_info) { int ifile, idim, jdim; int first_ndims, ndims; int input_mincid; long first_size[MAX_VAR_DIMS], size[MAX_VAR_DIMS]; char first_dimname[MAX_VAR_DIMS][MAX_NC_NAME]; char dimname[MAX_VAR_DIMS][MAX_NC_NAME]; double extent; double first_start[MAX_VAR_DIMS], start[MAX_VAR_DIMS]; double first_step[MAX_VAR_DIMS], step[MAX_VAR_DIMS]; double start_diff, step_diff; double first_dircos[MAX_VAR_DIMS][3], dircos[MAX_VAR_DIMS][3]; double dircos_diff, dircos_cumdiff; int first_is_regular[MAX_VAR_DIMS], is_regular[MAX_VAR_DIMS]; /* Keep track of number of inputs (files and looping dimension) */ loop_options->num_all_inputs = 0; /* Loop over files. For the first file, we only get the dimension info, we don't check it. */ for (ifile = 0; ifile < get_input_numfiles(loopfile_info); ifile++) { /* Get mincid */ input_mincid = get_input_mincid(loopfile_info, ifile); /* Add up number of inputs */ loop_options->num_all_inputs += get_loop_dim_size(input_mincid, loop_options); /* Get dimension information for this file */ if (ifile == 0) { get_dim_info(input_mincid, &first_ndims, first_size, first_dimname, first_start, first_step, first_dircos, first_is_regular, loop_options); } else { get_dim_info(input_mincid, &ndims, size, dimname, start, step, dircos, is_regular, loop_options); /* Check number of dimensions */ if (ndims != first_ndims) { (void) fprintf(stderr, "Files %s and %s have different numbers of dimensions\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0)); exit(EXIT_FAILURE); } /* Loop over dimensions */ for (idim = 0; idim < first_ndims; idim++) { /* Check dimension sizes */ if (size[idim] != first_size[idim]) { (void) fprintf(stderr, "Files %s and %s have different sizes of dimensions\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0)); exit(EXIT_FAILURE); } /* Check optional dimension stuff */ if (loop_options->check_all_input_dim_info) { /* Check names */ if (strcmp(dimname[idim], first_dimname[idim]) != 0) { (void) fprintf(stderr, "Files %s and %s have different dimension names\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0)); exit(EXIT_FAILURE); } /* Check coordinates */ start_diff = start[idim] - first_start[idim]; extent = ((double) first_size[idim]) * first_step[idim]; if (extent != 0.0) start_diff /= extent; step_diff = step[idim] - first_step[idim]; if (first_step[idim] != 0.0) step_diff /= first_step[idim]; dircos_cumdiff = 0.0; for (jdim=0; jdim < 3; jdim++) { dircos_diff = first_dircos[idim][jdim] - dircos[idim][jdim]; if (first_dircos[idim][jdim] != 0.0) dircos_diff /= first_dircos[idim][jdim]; dircos_cumdiff += fabs(dircos_diff); } if (fabs(start_diff) > COORD_EPSILON) { (void) fprintf(stderr, "Files %s and %s have different start coordinates (%s)\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0), first_dimname[idim]); exit(EXIT_FAILURE); } if (fabs(step_diff) > COORD_EPSILON) { (void) fprintf(stderr, "Files %s and %s have different step coordinates (%s)\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0), first_dimname[idim]); exit(EXIT_FAILURE); } if (dircos_cumdiff > COORD_EPSILON) { (void) fprintf(stderr, "Files %s and %s have different direction cosines (%s)\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0), first_dimname[idim]); exit(EXIT_FAILURE); } if (first_is_regular[idim] != is_regular[idim]) { (void) fprintf(stderr, "Files %s and %s have different coordinate spacings (%s)\n", get_input_filename(loopfile_info,ifile), get_input_filename(loopfile_info,0), first_dimname[idim]); exit(EXIT_FAILURE); } } /* If check all dimension info */ } /* End of loop over dimensions */ } /* End of if ifile == 0 else */ /* Call the user's function if needed */ if (loop_options->input_file_function != NULL) { set_info_current_file(loop_options->loop_info, ifile); set_info_loopfile_info(loop_options->loop_info, loopfile_info); loop_options->input_file_function(loop_options->caller_data, input_mincid, ifile, loop_options->loop_info); set_info_loopfile_info(loop_options->loop_info, NULL); } } /* End of loop over files */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_image_varinq @INPUT : mincid - Minc id of file imgid - id of image variable loop_options - structure containing looping options @OUTPUT : name - name of variable datatype - type of variable ndims - number of dimensions dim - array of dimension ids natts - number of attributes @RETURNS : MI_ERROR if an error occurs @DESCRIPTION: Routine to call ncvarinq for the image variable, suppressing the dimension specified in loop_options. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int input_image_varinq(int mincid, int imgid, char *name, nc_type *datatype, int *ndims, int dim[], int *natts, Loop_Options *loop_options) { int dimid; int old_ncopts; int idim, jdim; int status; int nimgdims; char dimname[MAX_NC_NAME]; /* Get loop dimension id */ dimid = MI_ERROR; if (loop_options->loop_dimension != NULL) { old_ncopts = ncopts; ncopts = 0; dimid = ncdimid(mincid, loop_options->loop_dimension); ncopts = old_ncopts; } /* Call ncvarinq. If there is no loop dim, or an error occurred, then return. */ status = ncvarinq(mincid, imgid, name, datatype, ndims, dim, natts); if ((dimid == MI_ERROR) || (status == MI_ERROR)) return status; /* Get number of image dimensions */ nimgdims = 2; if (*ndims > 0) { (void) ncdiminq(mincid, dim[*ndims-1], dimname, NULL); if (strcmp(dimname, MIvector_dimension) == 0) nimgdims++; } /* Look for the loop dimension */ jdim = 0; for (idim=0; idim < *ndims; idim++) { if (dim[idim] != dimid) { dim[jdim] = dim[idim]; jdim++; } else if (idim >= *ndims-nimgdims) { (void) fprintf(stderr, "Don't use an image dimension as a loop dimension.\n"); exit(EXIT_FAILURE); } } /* Save number of dimensions */ *ndims = jdim; return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dim_info @INPUT : mincid - Minc id of file @OUTPUT : ndims - number of dimensions size - array of sizes of dimensions dimname - array of dimension names start - array of starts for dimensions step - array of steps for dimensions dircos - array of direction cosines is_regular - array of flags indicating whether dimension is regularly spaced or not loop_options - looping options @RETURNS : (nothing) @DESCRIPTION: Routine to get dimension information for a file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void get_dim_info(int mincid, int *ndims, long size[], char dimname[][MAX_NC_NAME], double start[], double step[], double dircos[][3], int is_regular[], Loop_Options *loop_options) { int imgid, varid; int idim, jdim; int dim[MAX_VAR_DIMS]; int att_length; char string[MAX_NC_NAME]; char *thename; int old_ncopts; enum {XAXIS, YAXIS, ZAXIS, OAXIS} dimtype; static double default_dircos[][3] = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } }; char regstring[MI_MAX_ATTSTR_LEN]; /* Get image variable info */ imgid = ncvarid(mincid, MIimage); (void) input_image_varinq(mincid, imgid, NULL, NULL, ndims, dim, NULL, loop_options); /* Loop through dimensions */ for (idim=0; idim < *ndims; idim++) { if (dimname == NULL) thename = string; else thename = dimname[idim]; (void) ncdiminq(mincid, dim[idim], thename, &size[idim]); /* Set default coordinate info */ if (start != NULL) start[idim] = 0.0; if (step != NULL) step[idim] = 1.0; if (dircos != NULL) { if ((strcmp(thename, MIxspace) == 0) || (strcmp(thename, MIxfrequency) == 0)) dimtype = XAXIS; else if ((strcmp(thename, MIyspace) == 0) || (strcmp(thename, MIyfrequency) == 0)) dimtype = YAXIS; else if ((strcmp(thename, MIzspace) == 0) || (strcmp(thename, MIzfrequency) == 0)) dimtype = ZAXIS; else dimtype = OAXIS; for (jdim=0; jdim < 3; jdim++) dircos[idim][jdim] = default_dircos[dimtype][jdim]; } if (is_regular != NULL) is_regular[idim] = TRUE; /* Get the coordinate info */ old_ncopts = ncopts; ncopts = 0; varid = ncvarid(mincid, thename); if (varid != MI_ERROR) { if (start != NULL) (void) miattget1(mincid, varid, MIstart, NC_DOUBLE, &start[idim]); if (step != NULL) (void) miattget1(mincid, varid, MIstep, NC_DOUBLE, &step[idim]); if (dircos != NULL) (void) miattget(mincid, varid, MIdirection_cosines, NC_DOUBLE, 3, dircos[idim], &att_length); if (is_regular != NULL) { if (miattgetstr(mincid, varid, MIspacing, sizeof(regstring), regstring) != NULL) { if (strcmp(regstring, MI_REGULAR) == 0) is_regular[idim] = TRUE; else if (strcmp(regstring, MI_IRREGULAR) == 0) is_regular[idim] = FALSE; } } } ncopts = old_ncopts; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_output_files @INPUT : loop_options - Options controlling looping loopfile_info - Looping information arg_string - string for history @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to setup the the output files @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void setup_output_files(Loop_Options *loop_options, Loopfile_Info *loopfile_info, char *arg_string) { int inmincid, outmincid; int ifile; /* Get mincid for first input file */ inmincid = get_input_mincid(loopfile_info, 0); /* Create output files */ for (ifile=0; ifile < get_output_numfiles(loopfile_info); ifile++) { outmincid = create_output_file(loopfile_info, ifile); setup_variables(inmincid, outmincid, ifile, arg_string, loop_options); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_vector_length @INPUT : mincid - minc file id loop_options - looping options (if NULL, uses ncvarinq) @OUTPUT : (none) @RETURNS : Length of vector dimension or zero if no such dimension. @DESCRIPTION: Routine to get the length of the vector dimension in a minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE long get_vector_length(int mincid, Loop_Options *loop_options) { int imgid; int ndims; int dim[MAX_VAR_DIMS]; char dimname[MAX_NC_NAME]; long vector_length; /* Get image variable id */ imgid = ncvarid(mincid, MIimage); /* Get the image dimension info */ if (loop_options != NULL) { (void) input_image_varinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL, loop_options); } else { (void) ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); } /* Check for vector dimension */ (void) ncdiminq(mincid, dim[ndims-1], dimname, &vector_length); if ((strcmp(dimname, MIvector_dimension) != 0) || (ndims <= 2)) { vector_length = 0; } return vector_length; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_variables @INPUT : inmincid - input minc file id outmincid - output minc file id output_curfile - current output file number (counting from zero) arg_string - string for history loop_options - options controlling loop behaviour @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to setup the variables in the output file. @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void setup_variables(int inmincid, int outmincid, int output_curfile, char *arg_string, Loop_Options *loop_options) { int inimgid, outimgid, maxid, minid, varid; int indim[MAX_VAR_DIMS], outdim[MAX_VAR_DIMS]; nc_type datatype; int idim, odim, ivar, in_ndims, out_ndims, in_nimgdims, out_nimgdims; char dimname[MAX_NC_NAME + 1]; int nvars, varlist[MAX_VAR_DIMS*2]; long dimlength; int input_vector_length; int changing_vector_dim; double valid_range[2]; /* Get image variable id for input file */ inimgid = ncvarid(inmincid, MIimage); /* Get the list of dimensions subscripting the image variable */ (void) input_image_varinq(inmincid, inimgid, NULL, &datatype, &in_ndims, indim, NULL, loop_options); /* Get the length of the input vector dimension */ input_vector_length = get_vector_length(inmincid, loop_options); if (loop_options->convert_input_to_scalar && (input_vector_length > 0)) { input_vector_length = 0; in_ndims--; } /* Get the number of image dimensions in the input file */ in_nimgdims = (input_vector_length == 0 ? 2 : 3); /* Are we changing the length of the vector dimension (or removing it?). For an output vector size of 1, we don't want an output vector dimension. */ changing_vector_dim = ((loop_options->output_vector_size != 0) && (loop_options->output_vector_size != input_vector_length)); if (loop_options->output_vector_size == 1) changing_vector_dim = (input_vector_length != 0); /* Work out number of output dimensions and image dimensions */ out_ndims = in_ndims; out_nimgdims = in_nimgdims; if (changing_vector_dim && (input_vector_length == 0)) { out_ndims++; out_nimgdims++; } else if (changing_vector_dim && (loop_options->output_vector_size <= 1)) { out_ndims--; out_nimgdims--; } /* Set up the output minc file */ /* Loop, creating output dimensions */ odim = 0; nvars = 0; for (idim=0; idim < in_ndims; idim++) { /* Check for a change in vector dimension length */ if ((idim != in_ndims-1) || (input_vector_length == 0) || !changing_vector_dim) { /* Copy the dimension */ (void) ncdiminq(inmincid, indim[idim], dimname, &dimlength); outdim[odim] = ncdimdef(outmincid, dimname, dimlength); /* Copy the dimension variables if we are not copying the whole header */ if (!loop_options->copy_all_header_info) { ncopts = 0; for (ivar=0; ivar < 2; ivar++) { if (ivar == 1) (void) strncat(dimname, "_width", MAX_NC_NAME); varlist[nvars] = ncvarid(inmincid, dimname); if (varlist[nvars] != MI_ERROR) { (void) micopy_var_def(inmincid, varlist[nvars], outmincid); nvars++; } } ncopts = NC_OPTS_VAL; } odim++; } } /* Create the output vector dimension if needed */ if (changing_vector_dim && (loop_options->output_vector_size > 1)) { outdim[odim] = ncdimdef(outmincid, MIvector_dimension, (long) loop_options->output_vector_size); } /* Copy other variables in file, if appropriate */ if (loop_options->copy_all_header_info) { nvars = 0; ncopts = 0; varlist[nvars] = inimgid; if (varlist[nvars] != MI_ERROR) nvars++; varlist[nvars] = ncvarid(inmincid, MIimagemax); if (varlist[nvars] != MI_ERROR) nvars++; varlist[nvars] = ncvarid(inmincid, MIimagemin); if (varlist[nvars] != MI_ERROR) nvars++; if (loop_options->loop_dimension != NULL) { (void) strncpy(dimname, loop_options->loop_dimension, MAX_NC_NAME); varlist[nvars] = ncvarid(inmincid, dimname); if (varlist[nvars] != MI_ERROR) nvars++; (void) strncat(dimname, "_width", MAX_NC_NAME); varlist[nvars] = ncvarid(inmincid, dimname); if (varlist[nvars] != MI_ERROR) nvars++; } (void) micopy_all_var_defs(inmincid, outmincid, nvars, varlist); ncopts = NC_OPTS_VAL; } /* Add the time stamp to the history */ update_history(outmincid, arg_string); /* Call the user's function if needed */ if (loop_options->output_file_function != NULL) { set_info_current_file(loop_options->loop_info, 0); loop_options->output_file_function(loop_options->caller_data, outmincid, output_curfile, loop_options->loop_info); } /* Create the image-min/max variables */ maxid = micreate_std_variable(outmincid, MIimagemax, NC_DOUBLE, out_ndims-out_nimgdims, outdim); minid = micreate_std_variable(outmincid, MIimagemin, NC_DOUBLE, out_ndims-out_nimgdims, outdim); ncopts = 0; (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemax), outmincid, maxid); (void) micopy_all_atts(inmincid, ncvarid(inmincid, MIimagemin), outmincid, minid); ncopts = NC_OPTS_VAL; /* Create the image variable */ if (loop_options->datatype != MI_ORIGINAL_TYPE) { datatype = loop_options->datatype; } loop_options->is_floating_type = ((datatype == NC_FLOAT) || (datatype == NC_DOUBLE)); outimgid = micreate_std_variable(outmincid, MIimage, datatype, out_ndims, outdim); (void) micopy_all_atts(inmincid, inimgid, outmincid, outimgid); if (loop_options->is_floating_type) { ncopts = 0; (void) ncattdel(outmincid, outimgid, MIsigntype); ncopts = NC_OPTS_VAL; valid_range[0] = 0; valid_range[1] = 1; (void) miset_valid_range(outmincid, outimgid, valid_range); } else if (loop_options->datatype != MI_ORIGINAL_TYPE) { if (loop_options->is_signed) (void) miattputstr(outmincid, outimgid, MIsigntype, MI_SIGNED); else (void) miattputstr(outmincid, outimgid, MIsigntype, MI_UNSIGNED); if ((loop_options->valid_range[1] > loop_options->valid_range[0])) { (void) miset_valid_range(outmincid, outimgid, loop_options->valid_range); } else { ncopts = 0; (void) ncattdel(outmincid, outimgid, MIvalid_range); ncopts = NC_OPTS_VAL; } } (void) miattputstr(outmincid, outimgid, MIcomplete, MI_FALSE); /* Put the file in data mode */ (void) ncsetfill(outmincid, NC_NOFILL); (void) ncendef(outmincid); /* Copy over variable values */ ncopts = 0; if (loop_options->copy_all_header_info) { (void) micopy_all_var_values(inmincid, outmincid, nvars, varlist); } else { for (ivar=0; ivar < nvars; ivar++) { (void) ncvarinq(inmincid, varlist[ivar], dimname, NULL, NULL, NULL, NULL); varid = ncvarid(outmincid, dimname); if (varid != MI_ERROR) { (void) micopy_var_values(inmincid, varlist[ivar], outmincid, varid); } } } ncopts = NC_OPTS_VAL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_history @INPUT : mincid - id of output minc file arg_string - string giving list of arguments @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to update the history global variable in the output minc file @METHOD : @GLOBALS : @CALLS : @CREATED : August 26, 1993 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void update_history(int mincid, char *arg_string) { nc_type datatype; int att_length; char *string; if (arg_string == NULL) return; /* Get the history attribute length */ ncopts=0; if ((ncattinq(mincid, NC_GLOBAL, MIhistory, &datatype, &att_length) == MI_ERROR) || (datatype != NC_CHAR)) att_length = 0; att_length += strlen(arg_string) + 1; /* Allocate a string and get the old history */ string = MALLOC(att_length, char); string[0] = '\0'; (void) miattgetstr(mincid, NC_GLOBAL, MIhistory, att_length, string); ncopts = NC_OPTS_VAL; /* Add the new command and put the new history. */ (void) strcat(string, arg_string); (void) miattputstr(mincid, NC_GLOBAL, MIhistory, string); FREE(string); } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_icvs @INPUT : loop_options - loop option info loopfile_info - looping information @OUTPUT : (nothing) @RETURNS : (nothing) @DESCRIPTION: Routine to set up the input and output icv's. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void setup_icvs(Loop_Options *loop_options, Loopfile_Info *loopfile_info) { int ifile; int icvid; /* Loop through input icv's, setting their values. Attaching is done by get_input_icvid. */ for (ifile=0; ifile < get_input_numfiles(loopfile_info); ifile++) { icvid = create_input_icvid(loopfile_info, ifile); (void) miicv_setint(icvid, MI_ICV_TYPE, NC_DOUBLE); (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_DO_FILLVALUE, TRUE); if (loop_options->convert_input_to_scalar) { (void) miicv_setint(icvid, MI_ICV_DO_DIM_CONV, TRUE); (void) miicv_setint(icvid, MI_ICV_DO_SCALAR, TRUE); (void) miicv_setint(icvid, MI_ICV_XDIM_DIR, MI_ICV_ANYDIR); (void) miicv_setint(icvid, MI_ICV_YDIM_DIR, MI_ICV_ANYDIR); (void) miicv_setint(icvid, MI_ICV_ZDIM_DIR, MI_ICV_ANYDIR); (void) miicv_setint(icvid, MI_ICV_KEEP_ASPECT, FALSE); } } /* Loop through output icv's, setting their values. Attaching is done by get_input_icvid. */ for (ifile=0; ifile < get_output_numfiles(loopfile_info); ifile++) { icvid = create_output_icvid(loopfile_info, ifile); (void) miicv_setint(icvid, MI_ICV_TYPE, NC_DOUBLE); (void) miicv_setint(icvid, MI_ICV_DO_NORM, TRUE); (void) miicv_setint(icvid, MI_ICV_USER_NORM, TRUE); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : do_voxel_loop @INPUT : loop_options - user options for looping loopfile_info - information on files used in loop @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to loop through the voxels and do something to each one @METHOD : @GLOBALS : @CALLS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : November 30, 1994 (P.N.) ---------------------------------------------------------------------------- */ PRIVATE void do_voxel_loop(Loop_Options *loop_options, Loopfile_Info *loopfile_info) { long block_start[MAX_VAR_DIMS], block_end[MAX_VAR_DIMS]; long block_incr[MAX_VAR_DIMS]; long block_cur[MAX_VAR_DIMS], block_curcount[MAX_VAR_DIMS]; long chunk_start[MAX_VAR_DIMS], chunk_end[MAX_VAR_DIMS]; long chunk_incr[MAX_VAR_DIMS]; long chunk_cur[MAX_VAR_DIMS], chunk_curcount[MAX_VAR_DIMS]; long input_cur[MAX_VAR_DIMS], input_curcount[MAX_VAR_DIMS]; long firstfile_cur[MAX_VAR_DIMS], firstfile_curcount[MAX_VAR_DIMS]; double **input_buffers, **output_buffers, **extra_buffers; double **results_buffers; long chunk_num_voxels, block_num_voxels, ivox; int outmincid, imgid, maxid, minid; double *data, minimum, maximum, valid_range[2]; double *global_minimum, *global_maximum; int ifile, ofile, ibuff, ndims, idim; int num_output_files; int num_input_buffers, num_output_buffers, num_extra_buffers; int input_vector_length, output_vector_length; int modify_vector_count; int current_input; int input_icvid, input_mincid; int loop_dim_index; int dim_index; int outer_file_loop; int dummy_index; int input_curfile; nc_type file_datatype; /* Get number of files, buffers, etc. */ num_output_files = get_output_numfiles(loopfile_info); num_input_buffers = (loop_options->do_accumulate ? 1 : loop_options->num_all_inputs); num_extra_buffers = loop_options->num_extra_buffers; num_output_buffers = num_output_files + num_extra_buffers; input_vector_length = get_vector_length(get_input_mincid(loopfile_info, 0), loop_options); if ((input_vector_length == 0) || (loop_options->convert_input_to_scalar)) input_vector_length = 1; if (num_output_files > 0) { output_vector_length = get_vector_length(get_output_mincid(loopfile_info, 0), NULL); if (output_vector_length == 0) output_vector_length = 1; } else output_vector_length = 1; modify_vector_count = (input_vector_length != output_vector_length); /* Initialize all of the counters to reasonable values */ (void) miset_coords(MAX_VAR_DIMS, 0, block_start); (void) miset_coords(MAX_VAR_DIMS, 0, block_end); (void) miset_coords(MAX_VAR_DIMS, 0, block_incr); (void) miset_coords(MAX_VAR_DIMS, 0, block_cur); (void) miset_coords(MAX_VAR_DIMS, 0, block_curcount); (void) miset_coords(MAX_VAR_DIMS, 0, chunk_start); (void) miset_coords(MAX_VAR_DIMS, 0, chunk_end); (void) miset_coords(MAX_VAR_DIMS, 0, chunk_incr); (void) miset_coords(MAX_VAR_DIMS, 0, chunk_cur); (void) miset_coords(MAX_VAR_DIMS, 0, chunk_curcount); (void) miset_coords(MAX_VAR_DIMS, 0, input_cur); (void) miset_coords(MAX_VAR_DIMS, 0, input_curcount); (void) miset_coords(MAX_VAR_DIMS, 0, firstfile_cur); (void) miset_coords(MAX_VAR_DIMS, 0, firstfile_curcount); /* Get block and chunk looping information */ setup_looping(loop_options, loopfile_info, &ndims, block_start, block_end, block_incr, &block_num_voxels, chunk_incr, &chunk_num_voxels); /* Allocate space for buffers */ if (loop_options->allocate_buffer_function != NULL) { loop_options->allocate_buffer_function (loop_options->caller_data, TRUE, num_input_buffers, chunk_num_voxels, input_vector_length, &input_buffers, num_output_files, block_num_voxels, output_vector_length, &output_buffers, num_extra_buffers, chunk_num_voxels, output_vector_length, &extra_buffers, loop_options->loop_info); } else { /* Allocate input buffers */ input_buffers = MALLOC(num_input_buffers, double *); for (ibuff=0; ibuff < num_input_buffers; ibuff++) { input_buffers[ibuff] = MALLOC(chunk_num_voxels * input_vector_length, double); } /* Allocate output buffers */ if (num_output_files > 0) { output_buffers = MALLOC(num_output_files, double *); for (ibuff=0; ibuff < num_output_files; ibuff++) { output_buffers[ibuff] = MALLOC(block_num_voxels * output_vector_length, double); } } /* Allocate extra buffers */ if (num_extra_buffers > 0) { extra_buffers = MALLOC(num_extra_buffers, double *); for (ibuff=0; ibuff < num_extra_buffers; ibuff++) { extra_buffers[ibuff] = MALLOC(chunk_num_voxels * output_vector_length, double); } } } /* Set up the results pointers */ if (num_output_buffers > 0) { results_buffers = MALLOC(num_output_buffers, double *); for (ibuff=0; ibuff < num_output_buffers; ibuff++) { if (ibuff < num_output_files) { results_buffers[ibuff] = output_buffers[ibuff]; } else { results_buffers[ibuff] = extra_buffers[ibuff-num_output_files]; } } } /* Initialize global min and max */ if (num_output_files > 0) { global_minimum = MALLOC(num_output_files, double); global_maximum = MALLOC(num_output_files, double); for (ofile=0; ofile < num_output_files; ofile++) { global_minimum[ofile] = DBL_MAX; global_maximum[ofile] = -DBL_MAX; } } else { global_minimum = NULL; global_maximum = NULL; } /* Initialize loop info - just to be safe */ initialize_loop_info(loop_options->loop_info); /* Print log message */ if (loop_options->verbose) { (void) printf("Processing:"); (void) fflush(stdout); } /* Outer loop over files, if appropriate */ outer_file_loop = (loop_options->do_accumulate && (num_output_buffers <= 0)); for (initialize_file_and_index(loop_options, loopfile_info, outer_file_loop, &ifile, &dim_index, &dummy_index); finish_file_and_index(loop_options, loopfile_info, outer_file_loop, ifile, dim_index, dummy_index); increment_file_and_index(loop_options, loopfile_info, outer_file_loop, &ifile, &dim_index, &dummy_index)) { /* Loop through blocks (image-max/min do not vary over blocks) */ nd_begin_looping(block_start, block_cur, ndims); while (!nd_end_of_loop(block_cur, block_end, ndims)) { nd_update_current_count(block_cur, block_incr, block_end, block_curcount, ndims); /* Set results_buffers to beginning of output buffers */ for (ofile=0; ofile < num_output_files; ofile++) { results_buffers[ofile] = output_buffers[ofile]; } /* Loop through chunks (space for input buffers) */ for (idim=0; idim < ndims; idim++) { chunk_start[idim] = block_cur[idim]; chunk_end[idim] = block_cur[idim] + block_curcount[idim]; } nd_begin_looping(chunk_start, chunk_cur, ndims); while (!nd_end_of_loop(chunk_cur, chunk_end, ndims)) { nd_update_current_count(chunk_cur, chunk_incr, chunk_end, chunk_curcount, ndims); /* Print log message */ if (loop_options->verbose) { (void) printf("."); (void) fflush(stdout); } /* Calculate number of voxels in a chunk */ chunk_num_voxels = 1; for (idim=0; idim < ndims; idim++) chunk_num_voxels *= chunk_curcount[idim]; chunk_num_voxels /= input_vector_length; /* Translate start and count for file and save in loop_info */ if (outer_file_loop) input_curfile = ifile; else input_curfile = 0; input_mincid = get_input_mincid(loopfile_info, input_curfile); translate_input_coords(input_mincid, chunk_cur, firstfile_cur, chunk_curcount, firstfile_curcount, &loop_dim_index, loop_options); /* Save start and count and file and index in loop_info */ set_info_shape(loop_options->loop_info, firstfile_cur, firstfile_curcount); set_info_current_file(loop_options->loop_info, 0); set_info_current_index(loop_options->loop_info, 0); /* Initialize results buffers if necessary */ if (loop_options->do_accumulate) { if (loop_options->start_function != NULL) { loop_options->start_function (loop_options->caller_data, chunk_num_voxels, num_output_buffers, output_vector_length, results_buffers, loop_options->loop_info); } } /* Get the input buffers and accumulate them if needed */ current_input = 0; for (initialize_file_and_index(loop_options, loopfile_info, !outer_file_loop, &ifile, &dim_index, &dummy_index); finish_file_and_index(loop_options, loopfile_info, !outer_file_loop, ifile, dim_index, dummy_index); increment_file_and_index(loop_options, loopfile_info, !outer_file_loop, &ifile, &dim_index, &dummy_index)) { /* Get input icvid and mincid and translate coords for file. We need to do this each time in case we have an outer file loop. */ input_icvid = get_input_icvid(loopfile_info, ifile); (void) miicv_inqint(input_icvid, MI_ICV_CDFID, &input_mincid); translate_input_coords(input_mincid, chunk_cur, input_cur, chunk_curcount, input_curcount, &loop_dim_index, loop_options); /* Read buffer */ ibuff = (loop_options->do_accumulate ? 0 : current_input); input_cur[loop_dim_index] = dim_index; (void) miicv_get(input_icvid, input_cur, input_curcount, input_buffers[ibuff]); if (loop_options->do_accumulate) { set_info_shape(loop_options->loop_info, input_cur, input_curcount); set_info_current_file(loop_options->loop_info, ifile); set_info_current_index(loop_options->loop_info, dim_index); set_info_loopfile_info(loop_options->loop_info, loopfile_info); loop_options->voxel_function(loop_options->caller_data, chunk_num_voxels, num_input_buffers, input_vector_length, input_buffers, num_output_buffers, output_vector_length, results_buffers, loop_options->loop_info); set_info_loopfile_info(loop_options->loop_info, NULL); } current_input++; } /* Inner loop over files and dimension index */ /* Do something with the buffers or finish accumulation */ set_info_shape(loop_options->loop_info, firstfile_cur, firstfile_curcount); set_info_current_file(loop_options->loop_info, 0); set_info_current_index(loop_options->loop_info, 0); if (loop_options->do_accumulate) { if (loop_options->finish_function != NULL) { loop_options->finish_function(loop_options->caller_data, chunk_num_voxels, num_output_buffers, output_vector_length, results_buffers, loop_options->loop_info); } } else { loop_options->voxel_function(loop_options->caller_data, chunk_num_voxels, num_input_buffers, input_vector_length, input_buffers, num_output_buffers, output_vector_length, results_buffers, loop_options->loop_info); } /* Increment results_buffers through output buffers */ for (ofile=0; ofile < num_output_files; ofile++) { results_buffers[ofile] += chunk_num_voxels * output_vector_length; } nd_increment_loop(chunk_cur, chunk_start, chunk_incr, chunk_end, ndims); } /* End of loop through chunks */ /* Write out output buffers */ for (ofile=0; ofile < num_output_files; ofile++) { outmincid = get_output_mincid(loopfile_info, ofile); maxid = ncvarid(outmincid, MIimagemax); minid = ncvarid(outmincid, MIimagemin); data = output_buffers[ofile]; /* Find the max and min */ minimum = DBL_MAX; maximum = -DBL_MAX; for (ivox=0; ivox < block_num_voxels*output_vector_length; ivox++) { if (data[ivox] != -DBL_MAX) { if (data[ivox] < minimum) minimum = data[ivox]; if (data[ivox] > maximum) maximum = data[ivox]; } } if ((minimum == DBL_MAX) && (maximum == -DBL_MAX)) { minimum = 0.0; maximum = 0.0; } /* Save global min and max */ if (minimum < global_minimum[ofile]) global_minimum[ofile] = minimum; if (maximum > global_maximum[ofile]) global_maximum[ofile] = maximum; /* Write out the max and min */ (void) mivarput1(outmincid, maxid, block_cur, NC_DOUBLE, NULL, &maximum); (void) mivarput1(outmincid, minid, block_cur, NC_DOUBLE, NULL, &minimum); /* Write out the values */ if (modify_vector_count) block_curcount[ndims-1] = output_vector_length; (void) miicv_put(get_output_icvid(loopfile_info, ofile), block_cur, block_curcount, data); } /* End of loop through output files */ nd_increment_loop(block_cur, block_start, block_incr, block_end, ndims); } /* End of loop through chunks */ } /* End of outer loop through files and dimension indices */ /* Data has been completely written */ for (ofile=0; ofile < num_output_files; ofile++) { outmincid = get_output_mincid(loopfile_info, ofile); imgid = ncvarid(outmincid, MIimage); (void) miattputstr(outmincid, imgid, MIcomplete, MI_TRUE); if (loop_options->is_floating_type) { if ((global_minimum[ofile] == DBL_MAX) && (global_maximum[ofile] == -DBL_MAX)) { global_minimum[ofile] = 0.0; global_maximum[ofile] = 0.0; } valid_range[0] = global_minimum[ofile]; valid_range[1] = global_maximum[ofile]; /* Force truncation of valid_range to match float image */ if ((ncvarinq(outmincid, imgid, NULL, &file_datatype, NULL, NULL, NULL) != MI_ERROR) && (file_datatype == NC_FLOAT)) { valid_range[0] = (float) valid_range[0]; valid_range[1] = (float) valid_range[1]; } (void) miset_valid_range(outmincid, imgid, valid_range); } } /* Print log message */ if (loop_options->verbose) { (void) printf("Done\n"); (void) fflush(stdout); } /* Free results pointer array, but not its buffers, since these were allocate as output_buffers and extra_buffers */ if (num_output_buffers > 0) { FREE(results_buffers); } /* Free the buffers */ if (loop_options->allocate_buffer_function != NULL) { loop_options->allocate_buffer_function (loop_options->caller_data, FALSE, num_input_buffers, chunk_num_voxels, input_vector_length, &input_buffers, num_output_files, block_num_voxels, output_vector_length, &output_buffers, num_extra_buffers, chunk_num_voxels, output_vector_length, &extra_buffers, loop_options->loop_info); } else { /* Free input buffers */ for (ibuff=0; ibuff < num_input_buffers; ibuff++) { FREE(input_buffers[ibuff]); } FREE(input_buffers); /* Free output buffers */ if (num_output_files > 0) { for (ibuff=0; ibuff < num_output_files; ibuff++) { FREE(output_buffers[ibuff]); } FREE(output_buffers); } /* Free extra buffers */ if (num_extra_buffers > 0) { for (ibuff=0; ibuff < num_extra_buffers; ibuff++) { FREE(extra_buffers[ibuff]); } FREE(extra_buffers); } } /* Free max and min arrays */ if (num_output_files > 0) { FREE(global_minimum); FREE(global_maximum); } return; } /* ----------------------------- MNI Header ----------------------------------- @NAME : setup_looping @INPUT : loop_options - users options controlling looping loopfile_info - information on files @OUTPUT : ndims - number of dimensions block_start - vector specifying start of block block_end - end of block block_incr - increment for stepping through blocks block_num_voxels - number of voxels in block chunk_incr - increment for stepping through chunks chunk_num_voxels - number of voxels in chunk @RETURNS : (nothing) @DESCRIPTION: Routine to set up vectors giving blocks and chunks through which we will loop. @METHOD : @GLOBALS : @CALLS : @CREATED : December 2, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void setup_looping(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int *ndims, long block_start[], long block_end[], long block_incr[], long *block_num_voxels, long chunk_incr[], long *chunk_num_voxels) { int inmincid; int total_ndims, scalar_ndims, idim; int input_vector_length, output_vector_length; int num_input_buffers; int vector_data; int nimgdims; long size[MAX_VAR_DIMS]; long max_voxels_in_buffer; /* Get input mincid */ inmincid = get_input_mincid(loopfile_info, 0); /* Get number of dimensions and their sizes */ get_dim_info(inmincid, &total_ndims, size, NULL, NULL, NULL, NULL, NULL, loop_options); /* Get vector lengths */ input_vector_length = get_vector_length(inmincid, loop_options); if (get_output_numfiles(loopfile_info) > 0) output_vector_length = get_vector_length(get_output_mincid(loopfile_info, 0), NULL); else output_vector_length = 0; /* Check for vector data and whether we are adding a dimension */ vector_data = ((input_vector_length > 0) || (output_vector_length > 0)); if ((input_vector_length == 0) && (output_vector_length > 0)) { total_ndims++; size[total_ndims-1] = 1; } scalar_ndims = (vector_data ? total_ndims - 1 : total_ndims); /* Get number of image dimensions */ nimgdims = (vector_data ? 3 : 2); /* Set vector lengths properly */ if (input_vector_length <= 0) input_vector_length = 1; if (output_vector_length <= 0) output_vector_length = 1; /* Set vectors */ *block_num_voxels = 1; for (idim=0; idim < total_ndims; idim++) { block_start[idim] = 0; block_end[idim] = size[idim]; if (idim < total_ndims - nimgdims) block_incr[idim] = 1; else block_incr[idim] = size[idim]; *block_num_voxels *= block_incr[idim]; chunk_incr[idim] = 1; } if (vector_data) { *block_num_voxels /= input_vector_length; idim = total_ndims-1; chunk_incr[idim] = block_incr[idim]; } /* Figure out chunk size. Enforce a minimum chunk size. */ *chunk_num_voxels = 1; num_input_buffers = (loop_options->do_accumulate ? 1 : loop_options->num_all_inputs); max_voxels_in_buffer = (loop_options->total_copy_space/((long) sizeof(double)) - get_output_numfiles(loopfile_info) * *block_num_voxels * output_vector_length) / (num_input_buffers * input_vector_length + loop_options->num_extra_buffers * output_vector_length); if (max_voxels_in_buffer < MIN_VOXELS_IN_BUFFER) { max_voxels_in_buffer = MIN_VOXELS_IN_BUFFER; } if (max_voxels_in_buffer > 0) { for (idim=scalar_ndims-1; idim >= 0; idim--) { chunk_incr[idim] = max_voxels_in_buffer / *chunk_num_voxels; if (chunk_incr[idim] > block_incr[idim]) chunk_incr[idim] = block_incr[idim]; *chunk_num_voxels *= chunk_incr[idim]; } } /* Set ndims */ *ndims = total_ndims; } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_file_and_index @INPUT : loop_options - users options controlling looping loopfile_info - information on files do_loop - TRUE if looping stuff should really be done @OUTPUT : ifile - file counter dim_index - dimension index counter dummy_index - counter used when do_loop is FALSE @RETURNS : (nothing) @DESCRIPTION: Routine to initialize the file and index loop. These three routines allow looping through files and dimension indices at two levels. If do_loop is TRUE, then normal looping is done (increment dim_index fastest, then ifile). If do_loop is FALSE, then only one loop is performed (using dummy_index). @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void initialize_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int *ifile, int *dim_index, int *dummy_index) /* ARGSUSED */ { if (do_loop) { *ifile = 0; *dim_index = 0; } else { *dummy_index = 0; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : finish_file_and_index @INPUT : loop_options - users options controlling looping loopfile_info - information on files do_loop - TRUE if looping stuff should really be done ifile - file counter dim_index - dimension index counter dummy_index - counter used when do_loop is FALSE @RETURNS : TRUE while there are more buffers to process. @DESCRIPTION: Routine to test for the end of the file and index loop. These three routines allow looping through files and dimension indices at two levels. If do_loop is TRUE, then normal looping is done (increment dim_index fastest, then ifile). If do_loop is FALSE, then only one loop is performed (using dummy_index). @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int finish_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int ifile, int dim_index, int dummy_index) /* ARGSUSED */ { if (do_loop) { return (ifile < get_input_numfiles(loopfile_info)); } else { return (dummy_index < 1); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : increment_file_and_index @INPUT : loop_options - users options controlling looping loopfile_info - information on files do_loop - TRUE if looping stuff should really be done @OUTPUT : ifile - file counter dim_index - dimension index counter dummy_index - counter used when do_loop is FALSE @RETURNS : (nothing) @DESCRIPTION: Routine to increment the file and index loop. These three routines allow looping through files and dimension indices at two levels. If do_loop is TRUE, then normal looping is done (increment dim_index fastest, then ifile). If do_loop is FALSE, then only one loop is performed (using dummy_index). Note that dummy_index is not touched if do_loop is TRUE. @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void increment_file_and_index(Loop_Options *loop_options, Loopfile_Info *loopfile_info, int do_loop, int *ifile, int *dim_index, int *dummy_index) { int input_mincid; if (do_loop) { (*dim_index)++; input_mincid = get_input_mincid(loopfile_info, *ifile); if (*dim_index >= get_loop_dim_size(input_mincid, loop_options)) { *dim_index = 0; (*ifile)++; } } else { (*dummy_index)++; } } /* ------------ Routines controlling looping over files ------------ */ /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_loopfile_info @INPUT : num_input_files - number of input files input_files - list of input file names num_output_files - list of output file names output_files - list of output file names loop_options - user options for looping @OUTPUT : (none) @RETURNS : pointer to Loopfile_Info structure @DESCRIPTION: Routine to set up looping information for these files. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE Loopfile_Info *initialize_loopfile_info(int num_input_files, char *input_files[], int num_output_files, char *output_files[], Loop_Options *loop_options) { int num_free_files, num_files, ifile; Loopfile_Info *loopfile_info; /* Allocate structure */ loopfile_info = MALLOC(1, Loopfile_Info); /* Save clobber info */ if (loop_options->clobber) { loopfile_info->cflags = NC_CLOBBER; } else { loopfile_info->cflags = NC_NOCLOBBER; } #if MINC2 if (loop_options->v2format) { loopfile_info->cflags |= MI2_CREATE_V2; } #endif /* MINC2 */ /* Save number of input and output files */ loopfile_info->num_input_files = num_input_files; loopfile_info->num_output_files = num_output_files; /* Save input file names (just copy pointers, not strings) */ if (num_input_files > 0) { loopfile_info->input_files = MALLOC(num_input_files, char *); for (ifile=0; ifile < num_input_files; ifile++) loopfile_info->input_files[ifile] = input_files[ifile]; } else loopfile_info->input_files = NULL; /* Save output file names (just copy pointers, not strings) */ if (num_output_files > 0) { loopfile_info->output_files = MALLOC(num_output_files, char *); for (ifile=0; ifile < num_output_files; ifile++) loopfile_info->output_files[ifile] = output_files[ifile]; } else loopfile_info->output_files = NULL; /* Keep track of number of files that we can open */ num_free_files = loop_options->max_open_files; if (num_free_files > MI_MAX_NUM_ICV) num_free_files = MI_MAX_NUM_ICV; /* Check to see if we can open output files (we must leave room for one input file) */ if (num_output_files < num_free_files-1) { loopfile_info->output_all_open = TRUE; num_files = num_output_files; } else { loopfile_info->output_all_open = FALSE; num_files = 1; } num_free_files -= num_files; loopfile_info->output_mincid = MALLOC(num_files, int); loopfile_info->output_icvid = MALLOC(num_files, int); for (ifile=0; ifile < num_files; ifile++) { loopfile_info->output_mincid[ifile] = MI_ERROR; loopfile_info->output_icvid[ifile] = MI_ERROR; } loopfile_info->current_input_file_number = -1; /* Check whether sequential access would be better */ loopfile_info->sequential_access = (loop_options->do_accumulate && ((num_output_files + loop_options->num_extra_buffers) <= 0)); /* Check to see if we can open input files */ if (num_input_files < num_free_files) { loopfile_info->can_open_all_input = TRUE; num_files = num_input_files; } else { loopfile_info->can_open_all_input = FALSE; num_files = 1; } num_free_files -= num_files; loopfile_info->input_mincid = MALLOC(num_files, int); loopfile_info->input_icvid = MALLOC(num_files, int); for (ifile=0; ifile < num_files; ifile++) { loopfile_info->input_mincid[ifile] = MI_ERROR; loopfile_info->input_icvid[ifile] = MI_ERROR; } loopfile_info->current_output_file_number = -1; /* Check for an already open input file */ if (loop_options->input_mincid != MI_ERROR) { loopfile_info->input_mincid[0] = loop_options->input_mincid; loopfile_info->current_input_file_number = 0; } /* Check whether we want to open all input files */ loopfile_info->input_all_open = (! loopfile_info->sequential_access) && loopfile_info->can_open_all_input; /* Set default for expanding compressed files */ loopfile_info->headers_only = FALSE; loopfile_info->want_headers_only = FALSE; /* Return the loopfile_info structure */ return loopfile_info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_loopfile_info @INPUT : loopfile_info - looping information @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to clean up looping information. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void cleanup_loopfile_info(Loopfile_Info *loopfile_info) { int num_files, ifile; /* Close input files and free icv's */ if (loopfile_info->can_open_all_input) num_files = loopfile_info->num_input_files; else num_files = 1; for (ifile=0; ifile < num_files; ifile++) { if (loopfile_info->input_icvid[ifile] != MI_ERROR) (void) miicv_free(loopfile_info->input_icvid[ifile]); if (loopfile_info->input_mincid[ifile] != MI_ERROR) (void) miclose(loopfile_info->input_mincid[ifile]); } /* Close output files and free icv's */ if (loopfile_info->output_all_open) num_files = loopfile_info->num_output_files; else num_files = 1; for (ifile=0; ifile < num_files; ifile++) { (void) miicv_free(loopfile_info->output_icvid[ifile]); (void) miclose(loopfile_info->output_mincid[ifile]); } /* Free input arrays */ if (loopfile_info->input_files != NULL) FREE(loopfile_info->input_files); if (loopfile_info->input_mincid != NULL) FREE(loopfile_info->input_mincid); if (loopfile_info->input_icvid != NULL) FREE(loopfile_info->input_icvid); /* Free output arrays */ if (loopfile_info->output_files != NULL) FREE(loopfile_info->output_files); if (loopfile_info->output_mincid != NULL) FREE(loopfile_info->output_mincid); if (loopfile_info->output_icvid != NULL) FREE(loopfile_info->output_icvid); /* Free the structure */ FREE(loopfile_info); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_input_numfiles @INPUT : loopfile_info - looping information @OUTPUT : (none) @RETURNS : Number of input files @DESCRIPTION: Routine to get the number of input files. @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_input_numfiles(Loopfile_Info *loopfile_info) { return loopfile_info->num_input_files; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_output_numfiles @INPUT : loopfile_info - looping information @OUTPUT : (none) @RETURNS : Number of output files @DESCRIPTION: Routine to get the number of output files. @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_output_numfiles(Loopfile_Info *loopfile_info) { return loopfile_info->num_output_files; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_input_filename @INPUT : loopfile_info - looping information file_num - input file number @OUTPUT : (none) @RETURNS : Pointer to string containing input file name for file ifile. @DESCRIPTION: Routine to get name of an input file. @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE char *get_input_filename(Loopfile_Info *loopfile_info, int file_num) { /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_input_files)) { (void) fprintf(stderr, "Bad input file number %d\n", file_num); exit(EXIT_FAILURE); } return loopfile_info->input_files[file_num]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_input_headers_only @INPUT : loopfile_info - looping information headers_only - TRUE if we only need input headers, FALSE if we need the whole file. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to modify the Loopfile_Info structure so that in future we get either whole minc files or only the headers. The change is only made if it makes sense (ie. we are processing files sequentially). @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_input_headers_only(Loopfile_Info *loopfile_info, int headers_only) { int num_files, ifile; int icvid, mincid; /* Change the indication that we want to have headers only */ loopfile_info->want_headers_only = headers_only; /* If headers_only is not changing, don't do anything */ if ((headers_only && loopfile_info->headers_only) || (!headers_only && !loopfile_info->headers_only)) { return; } /* Check to see if it makes sense to change to headers only */ if (headers_only && loopfile_info->input_all_open) return; /* Change the value */ loopfile_info->headers_only = headers_only; /* If we are going to headers_only == FALSE, then loop through icv's and files, making sure that they are detached and closed (we will need to re-open them */ if (!loopfile_info->headers_only) { num_files = (loopfile_info->can_open_all_input ? loopfile_info->num_input_files : 1); for (ifile=0; ifile < num_files; ifile++) { icvid = loopfile_info->input_icvid[ifile]; mincid = MI_ERROR; if (icvid != MI_ERROR) { (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); if (mincid != MI_ERROR) { (void) miicv_detach(icvid); (void) miclose(mincid); } } if ((loopfile_info->input_mincid[ifile] != MI_ERROR) && (loopfile_info->input_mincid[ifile] != mincid)) { (void) miclose(loopfile_info->input_mincid[ifile]); } loopfile_info->input_mincid[ifile] = MI_ERROR; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_input_sequential @INPUT : loopfile_info - looping information sequential_access - TRUE if we want to open only one file at a time, FALSE otherwise. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to modify the Loopfile_Info structure so that files are opened one at a time. @METHOD : @GLOBALS : @CALLS : @CREATED : March 1, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_input_sequential(Loopfile_Info *loopfile_info, int sequential_access) { int old_input_all_open; int ifile, num_files; int mincid = MI_ERROR, icvid; int current_input_file_number; /* Set flag for sequential access */ loopfile_info->sequential_access = sequential_access; /* Change status of input_all_open */ old_input_all_open = loopfile_info->input_all_open; loopfile_info->input_all_open = (! loopfile_info->sequential_access) && loopfile_info->can_open_all_input; /* Check if input_all_open has changed */ if (!old_input_all_open && loopfile_info->input_all_open) { current_input_file_number = loopfile_info->current_input_file_number; if (current_input_file_number >= 0) { mincid = loopfile_info->input_mincid[0]; loopfile_info->input_mincid[0] = MI_ERROR; loopfile_info->input_mincid[current_input_file_number] = mincid; } } else if (old_input_all_open && !loopfile_info->input_all_open) { if (loopfile_info->can_open_all_input) num_files = loopfile_info->num_input_files; else num_files = 1; for (ifile=0; ifile < num_files; ifile++) { icvid = loopfile_info->input_icvid[ifile]; if (icvid != MI_ERROR) { (void) miicv_inqint(icvid, MI_ICV_CDFID, &mincid); (void) miicv_detach(icvid); if (mincid != MI_ERROR) { (void) miclose(mincid); } } if ((loopfile_info->input_mincid[ifile] != MI_ERROR) && (loopfile_info->input_mincid[ifile] != mincid)) (void) miclose(loopfile_info->input_mincid[ifile]); loopfile_info->input_mincid[ifile] = MI_ERROR; } } /* Call set_input_headers_only in case want_headers_only is different from headers_only */ set_input_headers_only(loopfile_info, loopfile_info->want_headers_only); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_input_mincid @INPUT : loopfile_info - looping information file_num - input file number @OUTPUT : (none) @RETURNS : Id of minc file @DESCRIPTION: Routine to get the minc id for an input file. The file number corresponds to the file's position in the input_files list (counting from zero). @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_input_mincid(Loopfile_Info *loopfile_info, int file_num) { int index; int created_tempfile; char *filename; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_input_files)) { (void) fprintf(stderr, "Bad input file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not */ if (loopfile_info->input_all_open) { index = file_num; } else { index = 0; if ((loopfile_info->input_mincid[index] != MI_ERROR) && (loopfile_info->current_input_file_number != file_num)) { if (loopfile_info->input_icvid[index] != MI_ERROR) (void) miicv_detach(loopfile_info->input_icvid[index]); (void) miclose(loopfile_info->input_mincid[index]); loopfile_info->input_mincid[index] = MI_ERROR; } loopfile_info->current_input_file_number = file_num; } /* Open the file if it hasn't been already */ if (loopfile_info->input_mincid[index] == MI_ERROR) { filename = miexpand_file(loopfile_info->input_files[file_num], NULL, loopfile_info->headers_only, &created_tempfile); if (!filename) { fprintf(stderr, "Could not expand file \"%s\"!\n", loopfile_info->input_files[file_num]); exit(EXIT_FAILURE); } loopfile_info->input_mincid[index] = miopen(filename, NC_NOWRITE); if (created_tempfile) { (void) remove(filename); } FREE(filename); } return loopfile_info->input_mincid[index]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_output_mincid @INPUT : loopfile_info - looping information file_num - output file number @OUTPUT : (none) @RETURNS : Id of minc file @DESCRIPTION: Routine to get the minc id for an output file. The file number corresponds to the file's position in the output_files list (counting from zero). @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_output_mincid(Loopfile_Info *loopfile_info, int file_num) { int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_output_files)) { (void) fprintf(stderr, "Bad output file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not */ if (loopfile_info->output_all_open) { index = file_num; } else { index = 0; if ((loopfile_info->output_mincid[index] != MI_ERROR) && (loopfile_info->current_output_file_number != file_num)) { if (loopfile_info->output_icvid[index] != MI_ERROR) (void) miicv_detach(loopfile_info->output_icvid[index]); (void) miclose(loopfile_info->output_mincid[index]); loopfile_info->output_mincid[index] = MI_ERROR; } loopfile_info->current_output_file_number = file_num; } /* Open the file if it hasn't been already */ if (loopfile_info->output_mincid[index] == MI_ERROR) { loopfile_info->output_mincid[index] = miopen(loopfile_info->output_files[file_num], NC_WRITE); } return loopfile_info->output_mincid[index]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_output_file @INPUT : loopfile_info - looping information file_num - output file number @OUTPUT : (none) @RETURNS : Id of minc file @DESCRIPTION: Routine to create an output file. The file number corresponds to the file's position in the output_files list (counting from zero). @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int create_output_file(Loopfile_Info *loopfile_info, int file_num) { int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_output_files)) { (void) fprintf(stderr, "Bad output file number %d for create.\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not */ if (loopfile_info->output_all_open) { index = file_num; } else { index = 0; if ((loopfile_info->output_mincid[index] != MI_ERROR) && (loopfile_info->current_output_file_number != file_num)) { (void) miclose(loopfile_info->output_mincid[index]); loopfile_info->output_mincid[index] = MI_ERROR; } loopfile_info->current_output_file_number = file_num; } /* Create the file */ if (loopfile_info->output_mincid[index] != MI_ERROR) { (void) fprintf(stderr, "File %s has already been created\n", loopfile_info->output_files[file_num]); exit(EXIT_FAILURE); } loopfile_info->output_mincid[index] = micreate(loopfile_info->output_files[file_num], loopfile_info->cflags); return loopfile_info->output_mincid[index]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_input_icvid @INPUT : loopfile_info - looping information file_num - input file number @OUTPUT : (none) @RETURNS : Id of icv for the specified file @DESCRIPTION: Routine to get the icv id for an input file. The file number corresponds to the file's position in the input_files list (counting from zero). @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_input_icvid(Loopfile_Info *loopfile_info, int file_num) { int mincid, icv_mincid, icvid; int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_input_files)) { (void) fprintf(stderr, "Bad input file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not - get the correct index */ if (loopfile_info->can_open_all_input) { index = file_num; } else { index = 0; } /* Check to see if the icv is attached to the correct minc file. If not, re-attach it. */ icvid = loopfile_info->input_icvid[index]; mincid = get_input_mincid(loopfile_info, file_num); if (icvid != MI_ERROR) (void) miicv_inqint(icvid, MI_ICV_CDFID, &icv_mincid); else icv_mincid = MI_ERROR; if (mincid != icv_mincid) { (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); } return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_output_icvid @INPUT : loopfile_info - looping information file_num - output file number @OUTPUT : (none) @RETURNS : Id of icv for the specified file @DESCRIPTION: Routine to get the icv id for an output file. The file number corresponds to the file's position in the output_files list (counting from zero). @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int get_output_icvid(Loopfile_Info *loopfile_info, int file_num) { int mincid, icv_mincid, icvid; int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_output_files)) { (void) fprintf(stderr, "Bad output file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not - get the correct index */ if (loopfile_info->output_all_open) { index = file_num; } else { index = 0; } /* Check to see if the icv is attached to the correct minc file. If not, re-attach it. */ icvid = loopfile_info->output_icvid[index]; mincid = get_output_mincid(loopfile_info, file_num); if (icvid != MI_ERROR) (void) miicv_inqint(icvid, MI_ICV_CDFID, &icv_mincid); else icv_mincid = MI_ERROR; if (mincid != icv_mincid) { (void) miicv_attach(icvid, mincid, ncvarid(mincid, MIimage)); } return icvid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_input_icvid @INPUT : loopfile_info - looping information file_num - input file number @OUTPUT : (none) @RETURNS : Id of icv for the specified file @DESCRIPTION: Routine to create the icv id for an input file. The file number corresponds to the file's position in the input_files list (counting from zero). If the icv already exists, just return it. If there are too many files, then only one icv will be used. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int create_input_icvid(Loopfile_Info *loopfile_info, int file_num) { int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_input_files)) { (void) fprintf(stderr, "Bad input file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not - get the correct index */ if (loopfile_info->can_open_all_input) { index = file_num; } else { index = 0; } /* Check to see if icv exists - if not create it */ if (loopfile_info->input_icvid[index] == MI_ERROR) { loopfile_info->input_icvid[index] = miicv_create(); } return loopfile_info->input_icvid[index]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_output_icvid @INPUT : loopfile_info - looping information file_num - output file number @OUTPUT : (none) @RETURNS : Id of icv for the specified file @DESCRIPTION: Routine to create the icv id for an output file. The file number corresponds to the file's position in the output_files list (counting from zero). If the icv already exists, just return it. If there are too many files, then only one icv will be used. @METHOD : @GLOBALS : @CALLS : @CREATED : November 30, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE int create_output_icvid(Loopfile_Info *loopfile_info, int file_num) { int index; /* Check for bad file_num */ if ((file_num < 0) || (file_num >= loopfile_info->num_output_files)) { (void) fprintf(stderr, "Bad output file number %d\n", file_num); exit(EXIT_FAILURE); } /* Check to see if all files are open or not - get the correct index */ if (loopfile_info->output_all_open) { index = file_num; } else { index = 0; } /* Check to see if icv exists - if not create it */ if (loopfile_info->output_icvid[index] == MI_ERROR) { loopfile_info->output_icvid[index] = miicv_create(); } return loopfile_info->output_icvid[index]; } /* ------------ Routines to set loop options ------------ */ /* ----------------------------- MNI Header ----------------------------------- @NAME : create_loop_options @INPUT : (none) @OUTPUT : (none) @RETURNS : Pointer to Loop_Options structure @DESCRIPTION: Routine to create and initialize the loop options structure. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI Loop_Options *create_loop_options(void) { Loop_Options *loop_options; /* Allocate structure */ loop_options = MALLOC(1, Loop_Options); /* Fill in the defaults */ loop_options->clobber = FALSE; loop_options->verbose = TRUE; loop_options->datatype = MI_ORIGINAL_TYPE; loop_options->is_signed = TRUE; loop_options->valid_range[0] = 0.0; loop_options->valid_range[1] = 0.0; loop_options->max_open_files = MI_MAX_NUM_ICV - 2; loop_options->check_all_input_dim_info = TRUE; loop_options->convert_input_to_scalar = FALSE; loop_options->output_vector_size = 0; loop_options->input_mincid = MI_ERROR; loop_options->total_copy_space = miget_cfg_int(MICFG_MAXBUF) * 1024; if(loop_options->total_copy_space == 0){ loop_options->total_copy_space = MI2_DEF_BUFF_SIZE * 1024; } loop_options->loop_dimension = NULL; loop_options->num_all_inputs = 0; loop_options->input_file_function = NULL; loop_options->output_file_function = NULL; loop_options->copy_all_header_info = TRUE; loop_options->do_accumulate = FALSE; loop_options->num_extra_buffers = 0; loop_options->start_function = NULL; loop_options->finish_function = NULL; loop_options->voxel_function = NULL; loop_options->caller_data = NULL; loop_options->loop_info = create_loop_info(); loop_options->allocate_buffer_function = NULL; #if MINC2 loop_options->v2format = FALSE; /* Use MINC 2.0 file format (HDF5)? */ #endif /* MINC2 */ /* Return the structure pointer */ return loop_options; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_loop_options @INPUT : loop_options - pointer to structure to cleanup @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to cleanup and free the Loop_Options structure @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void free_loop_options(Loop_Options *loop_options) { free_loop_info(loop_options->loop_info); if (loop_options->loop_dimension != NULL) FREE(loop_options->loop_dimension); FREE(loop_options); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_clobber @INPUT : loop_options - user options for looping verbose - TRUE if output files should be clobbered @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn output clobber on or off @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_clobber(Loop_Options *loop_options, int clobber) { loop_options->clobber = clobber; } #if MINC2 /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_v2format @INPUT : loop_options - user options for looping v2format - TRUE if output files should use MINC 2.0 format @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn output clobber on or off @METHOD : @GLOBALS : @CALLS : @CREATED : October 8, 2003 (Bert Vincent) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_v2format(Loop_Options *loop_options, int v2format) { loop_options->v2format = v2format; } #endif /* MINC2 */ /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_verbose @INPUT : loop_options - user options for looping verbose - TRUE if logging should be done. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn logging on or off. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_verbose(Loop_Options *loop_options, int verbose) { loop_options->verbose = verbose; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_datatype @INPUT : loop_options - user options for looping datatype - NetCDF datatype for output (MI_ORIGINAL_TYPE means use input type) is_signed - TRUE if type is signed valid_min - valid minimum for type (if valid_min >= valid_max, then defaults are used) valid_max - valid maximum for type @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set the output file datatype, sign and range. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_datatype(Loop_Options *loop_options, nc_type datatype, int is_signed, double valid_min, double valid_max) { loop_options->datatype = datatype; loop_options->is_signed = is_signed; loop_options->valid_range[0] = valid_min; loop_options->valid_range[1] = valid_max; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_max_open_files @INPUT : loop_options - user options for looping max_open_files - maximum number of open files allowed (between 1 and MI_MAX_NUM_ICV) @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set the maximum number of open minc files. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_max_open_files(Loop_Options *loop_options, int max_open_files) { if ((max_open_files <= 0) || (max_open_files > MI_MAX_NUM_ICV)) { (void) fprintf(stderr, "Bad number of files %d in set_loop_max_open_files\n", max_open_files); exit(EXIT_FAILURE); } loop_options->max_open_files = max_open_files; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_check_dim_info @INPUT : loop_options - user options for looping check_dim_info - TRUE if all dimension information in input files should be check, FALSE otherwise @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn dimension info checking on or off. @METHOD : @GLOBALS : @CALLS : @CREATED : March 16, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_check_dim_info(Loop_Options *loop_options, int check_dim_info) { loop_options->check_all_input_dim_info = check_dim_info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_convert_input_to_scalar @INPUT : loop_options - user options for looping convert_input_to_scalar - TRUE if input should be converted to scalar values @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to allow input to be converted to scalar values @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_convert_input_to_scalar(Loop_Options *loop_options, int convert_input_to_scalar) { loop_options->convert_input_to_scalar = convert_input_to_scalar; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_output_vector_size @INPUT : loop_options - user options for looping output_vector_size - length of vector dimension for output. 0 means keep the size the same as the input. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn set the length of the vector dimension for output. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_output_vector_size(Loop_Options *loop_options, int output_vector_size) { if (output_vector_size < 0) { (void) fprintf(stderr, "Bad vector size %d in set_loop_output_vector_size\n", output_vector_size); exit(EXIT_FAILURE); } loop_options->output_vector_size = output_vector_size; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_first_input_mincid @INPUT : loop_options - user options for looping input_mincid - id of first minc file (already opened). @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn allow the user to pass in an already opened minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_first_input_mincid(Loop_Options *loop_options, int input_mincid) { loop_options->input_mincid = input_mincid; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_buffer_size @INPUT : loop_options - user options for looping buffer_size - maximum amount of buffer space to use (in bytes). @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn set a limit on the amount of buffer space used. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_buffer_size(Loop_Options *loop_options, long buffer_size) { if (buffer_size <= 0) { (void) fprintf(stderr, "Bad buffer size %d in set_loop_buffer_size\n", (int) buffer_size); } loop_options->total_copy_space = buffer_size; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_dimension @INPUT : loop_options - user options for looping dimension_name - name of dimension that is treated like a series of input files. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to allow a dimension to be treated like a series of input files (one buffer per dimension element per file). @METHOD : @GLOBALS : @CALLS : @CREATED : January 24, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_dimension(Loop_Options *loop_options, char *dimension_name) { if (loop_options->loop_dimension != NULL) FREE(loop_options->loop_dimension); if ((dimension_name == NULL) || ((int)strlen(dimension_name) <= 0)) { loop_options->loop_dimension = NULL; } else { loop_options->loop_dimension = strdup(dimension_name); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_input_file_function @INPUT : loop_options - user options for looping input_file_function - function to be called for each input file so that the user can extract any extract information. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to allow the user to define a function to be called for each input file before processing is done. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_input_file_function (Loop_Options *loop_options, VoxelInputFileFunction input_file_function) { loop_options->input_file_function = input_file_function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_output_file_function @INPUT : loop_options - user options for looping output_file_function - function to be called for each output file so that the user can modify the header (file is in define mode). @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to allow the user to define a function to be called for each output file so that things can be added to the header. The file will be in define mode and should remain that way. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_output_file_function (Loop_Options *loop_options, VoxelOutputFileFunction output_file_function) { loop_options->output_file_function = output_file_function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_copy_all_header @INPUT : loop_options - user options for looping copy_all_header - TRUE if all header info should be copied to output file @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn copying of all header info off. @METHOD : @GLOBALS : @CALLS : @CREATED : March 13, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_copy_all_header(Loop_Options *loop_options, int copy_all_header) { loop_options->copy_all_header_info = copy_all_header; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_accumulate @INPUT : loop_options - user options for looping do_accumulation - TRUE if accumulating should be done, FALSE otherwise. num_extra_buffers - number of extra buffers to allocate. start_function - function to be called before looping with all output and extra buffers as arguments. NULL means don't call any function. finish_function - function to be called after looping with all output and extra buffers as arguments. NULL means don't call any function. @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to turn allow an accumulation mode of looping. Instead of loading all input files and then calling the voxel routine, the voxel routine is called after for each input file. The user can provide a start_function that is called to initialize the output buffers and a finish_function that is called to finish calculations. The user can also ask for extra buffers. These are treated like output buffers (at the end of the list) but are not written out. @METHOD : @GLOBALS : @CALLS : @CREATED : December 6, 1994 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void set_loop_accumulate(Loop_Options *loop_options, int do_accumulation, int num_extra_buffers, VoxelStartFunction start_function, VoxelFinishFunction finish_function) { loop_options->do_accumulate = do_accumulation; /* Turning off accumulation */ if (!do_accumulation) { loop_options->num_extra_buffers = 0; loop_options->start_function = NULL; loop_options->finish_function = NULL; } /* Turning on accumulation */ else { if (num_extra_buffers < 0) { (void) fprintf(stderr, "Bad num_extra_buffers %d in set_loop_accumulate\n", num_extra_buffers); exit(EXIT_FAILURE); } loop_options->do_accumulate = TRUE; loop_options->num_extra_buffers = num_extra_buffers; loop_options->start_function = start_function; loop_options->finish_function = finish_function; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_loop_allocate_buffer_function @INPUT : loop_options - user options for looping allocate_buffer_function - user function that allocates and frees input_data @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to set the function that allocates and frees input_data @METHOD : @GLOBALS : @CALLS : @CREATED : Aug 29, 2000 (J. Taylor) @MODIFIED : Sept. 19, 2000 (P. Neelin) ---------------------------------------------------------------------------- */ MNCAPI void set_loop_allocate_buffer_function(Loop_Options *loop_options, AllocateBufferFunction allocate_buffer_function) { loop_options->allocate_buffer_function = allocate_buffer_function; } /* ------------ Routines to set and get loop info ------------ */ /* ----------------------------- MNI Header ----------------------------------- @NAME : create_loop_info @INPUT : (none) @OUTPUT : (none) @RETURNS : Pointer to Loop_Info structure @DESCRIPTION: Routine to create and initialize the loop info structure. @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE Loop_Info *create_loop_info(void) { Loop_Info *loop_info; /* Allocate structure */ loop_info = MALLOC(1, Loop_Info); /* Fill in the defaults */ initialize_loop_info(loop_info); /* Return the structure pointer */ return loop_info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_loop_info @INPUT : loop_info - pointer to Loop_Info structure @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Routine to initialize the loop info structure. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void initialize_loop_info(Loop_Info *loop_info) { int idim; /* Fill in the defaults */ loop_info->current_file = 0; loop_info->current_index = 0; for (idim=0; idim < MAX_VAR_DIMS; idim++) { loop_info->start[idim] = 0; loop_info->count[idim] = 0; } loop_info->loopfile_info = NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_loop_info @INPUT : loop_info - pointer to structure to cleanup @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to cleanup and free the Loop_Info structure @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void free_loop_info(Loop_Info *loop_info) { FREE(loop_info); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_info_shape @INPUT : loop_info - info structure pointer start - start of hyperslab count - count of hyperslab @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to set the hyperslab shape (start and count) @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_info_shape(Loop_Info *loop_info, long start[], long count[]) { int idim; long size; /* Save the shape of the current chunk */ for (idim=0; idim < MAX_VAR_DIMS; idim++) { loop_info->start[idim] = start[idim]; loop_info->count[idim] = count[idim]; } /* Figure out how many voxels will be skipped by a step of one in each dimension */ loop_info->dimvoxels[MAX_VAR_DIMS-1] = 1; for (idim=MAX_VAR_DIMS-2; idim >= 0; idim--) { size = loop_info->count[idim+1]; if (size <= 0) size = 1; loop_info->dimvoxels[idim] = size * loop_info->dimvoxels[idim+1]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_shape @INPUT : loop_info - info structure pointer ndims - number of dimensions to copy (uses MAX_VAR_DIMS if ndims == 0) @OUTPUT : start - start of hyperslab count - count of hyperslab @RETURNS : (none) @DESCRIPTION: Routine to get the current hyperslab shape (start and count) @METHOD : @GLOBALS : @CALLS : @CREATED : January 20, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void get_info_shape(Loop_Info *loop_info, int ndims, long start[], long count[]) { int idim; if ((ndims <= 0) || (ndims > MAX_VAR_DIMS)) ndims = MAX_VAR_DIMS; for (idim=0; idim < ndims; idim++) { start[idim] = loop_info->start[idim]; count[idim] = loop_info->count[idim]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_voxel_index @INPUT : loop_info - info structure pointer subscript - 1-D voxel index into data buffers of voxel function ndims - number of dimensions in index array (uses MAX_VAR_DIMS if ndims == 0) @OUTPUT : index - multi-dimensional voxel index into file @RETURNS : (none) @DESCRIPTION: Routine to get the current voxel index. @METHOD : @GLOBALS : @CALLS : @CREATED : November 28, 2001 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI void get_info_voxel_index(Loop_Info *loop_info, long subscript, int ndims, long index[]) { int idim; long this_index; /* Check the array size */ if ((ndims <= 0) || (ndims > MAX_VAR_DIMS)) ndims = MAX_VAR_DIMS; /* Convert the 1-D subscript into a multi-dim index and add it to the start index of the chunk */ for (idim=0; idim < ndims; idim++) { this_index = subscript / loop_info->dimvoxels[idim]; index[idim] = loop_info->start[idim] + this_index; subscript -= this_index * loop_info->dimvoxels[idim]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_info_current_file @INPUT : loop_info - info structure pointer current_file - number of current input file (for accumulation) @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to set the current input file number. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_info_current_file(Loop_Info *loop_info, int current_file) { loop_info->current_file = current_file; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_current_file @INPUT : loop_info - info structure pointer @OUTPUT : (none) @RETURNS : Number of current input file (for accumulating over files) @DESCRIPTION: Routine to get the current input file number. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int get_info_current_file(Loop_Info *loop_info) { return loop_info->current_file; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_current_mincid @INPUT : loop_info - info structure pointer @OUTPUT : (none) @RETURNS : Minc id for current input file (for accumulating over files) @DESCRIPTION: Routine to get the current input file mincid. @METHOD : @GLOBALS : @CALLS : @CREATED : March 8, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int get_info_current_mincid(Loop_Info *loop_info) { if (loop_info->loopfile_info == NULL) return MI_ERROR; return get_input_mincid(loop_info->loopfile_info, loop_info->current_file); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_info_current_index @INPUT : loop_info - info structure pointer current_index - number of current dimension index (for accumulation over files and dimension) @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to set the current dimension index. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_info_current_index(Loop_Info *loop_info, int current_index) { loop_info->current_index = current_index; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_current_index @INPUT : loop_info - info structure pointer @OUTPUT : (none) @RETURNS : Number of current dimension index (for accumulating over files) @DESCRIPTION: Routine to get the current dimension index. @METHOD : @GLOBALS : @CALLS : @CREATED : February 28, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int get_info_current_index(Loop_Info *loop_info) { return loop_info->current_index; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_info_loopfile_info @INPUT : loop_info - info structure pointer loopfile_info - loopfile info structure pointer @OUTPUT : (none) @RETURNS : (none) @DESCRIPTION: Routine to set the loopfile info structure for future queries @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ PRIVATE void set_info_loopfile_info(Loop_Info *loop_info, Loopfile_Info *loopfile_info) { loop_info->loopfile_info = loopfile_info; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_info_whole_file @INPUT : loop_info - info structure pointer @OUTPUT : (none) @RETURNS : Id of current minc file @DESCRIPTION: Routine to change minc file handling to get the whole input file, not just the header (should be called from within the input_file_function). @METHOD : @GLOBALS : @CALLS : @CREATED : March 7, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ MNCAPI int get_info_whole_file(Loop_Info *loop_info) { Loopfile_Info *loopfile_info; /* Check for NULL pointer */ if (loop_info->loopfile_info == NULL) return MI_ERROR; loopfile_info = loop_info->loopfile_info; /* Make the input non-sequential (hold the files open if possible), and ask for whole files */ set_input_sequential(loopfile_info, FALSE); set_input_headers_only(loopfile_info, FALSE); /* Return the current minc file id */ if (loop_info->current_file >= 0) return get_input_mincid(loopfile_info, loop_info->current_file); else return MI_ERROR; } libminc-libminc-2-3-00/libsrc/voxel_loop.h000066400000000000000000000352301257462267400204610ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : voxel_loop.h @DESCRIPTION: Header file for voxel_loop.c @GLOBALS : @CREATED : January 10, 1994 (Peter Neelin) @MODIFIED : * $Log: voxel_loop.h,v $ * Revision 6.4 2005-08-26 21:04:58 bert * Use #if rather than #ifdef with MINC2 symbol * * Revision 6.3 2004/10/15 13:46:52 bert * Minor changes for Windows compatibility * * Revision 6.2 2004/04/27 15:43:04 bert * Added set_loop_v2format() * * Revision 6.1 2002/01/14 21:28:26 neelin * Moved nd_loop, voxel_loop, ParseArgv and time_stamp from ../progs/Proglib * in order to include them in the main minc library. * * Revision 6.3 2001/11/28 18:39:17 neelin * Added get_info_vxoel_index to allow users to get the full multi-dimensional * file index of the current voxel. * Modifications to allow arg_string to be NULL. * * Revision 6.2 2000/09/19 14:36:05 neelin * Added ability for caller to specify functions for allocating and freeing * voxel buffers used in loop. This is particularly useful for embedding * the voxel_loop code in other programs, such as Python, which manage memory * in their own way. * * Revision 6.1 1999/10/19 14:45:16 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:23:41 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:24:41 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:00:50 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:31:35 neelin * Release of minc version 0.3 * * Revision 1.3 1995/03/21 15:33:07 neelin * Changed call to voxel_function to always use proper vector length and * set num_voxels to the number of voxels, not multiplying by vector length. * * Revision 1.2 1995/03/21 14:06:39 neelin * Improved interface and added lots of functionality (much for the benefit * of mincconcat). * * Revision 1.1 94/12/14 10:18:21 neelin * Initial revision * * Revision 2.0 94/09/28 10:36:28 neelin * Release of minc version 0.2 * * Revision 1.3 94/09/28 10:36:22 neelin * Pre-release * * Revision 1.2 94/01/12 10:19:19 neelin * Added logging. Turned off filling. Added miclose for files. * * Revision 1.1 94/01/11 15:16:09 neelin * Initial revision * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ /* Includes */ #include "minc.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Incomplete structure typedefs */ typedef struct Loop_Info Loop_Info; typedef struct Loop_Options Loop_Options; /* User function typedefs */ /* ----------------------------- MNI Header ----------------------------------- @NAME : VoxelFunction @INPUT : caller_data - pointer to client data. num_voxels - number of voxels to process. Note that the total number of input values is num_voxels * input_vector_length. num_input_buffers - number of input buffers to handle on this call - either total number of input files or 1 (for accumulating over files). input_vector_length - length of input vector. input_data - array of pointers to input buffers (1 for each input file, unless we are accumulating). num_output_buffers - number of output buffers to handle on this call - will be the total number of output files unless we are accumulating over files (see set_loop_accumulate). output_vector_length - length of output vector == input_vector_length or as set by set_loop_output_vector_size. loop_info - pointer that can be passed to functions returning looping information @OUTPUT : output_data - array of pointers to output buffers. Set values to -DBL_MAX to represent illegal, out-of-range values. If extra buffers are requested by set_loop_accumulate, they will follow the output buffers. @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to process data. ---------------------------------------------------------------------------- */ typedef void (*VoxelFunction) (void *caller_data, long num_voxels, int num_input_buffers, int input_vector_length, double *input_data[], int num_output_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : VoxelInputFileFunction @INPUT : caller_data - pointer to client data. input_mincid - mincid for current input file input_curfile - current input file number (count from zero) loop_info - pointer that can be passed to functions returning looping information @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to get information for each input file. ---------------------------------------------------------------------------- */ typedef void (*VoxelInputFileFunction) (void *caller_data, int input_mincid, int input_curfile, Loop_Info *loop_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : VoxelOutputFileFunction @INPUT : caller_data - pointer to client data. output_mincid - mincid for current output file output_curfile - current output file number (count from zero) loop_info - pointer that can be passed to functions returning looping information @OUTPUT : (none) @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to modify the header of an output file (in define mode) ---------------------------------------------------------------------------- */ typedef void (*VoxelOutputFileFunction) (void *caller_data, int output_mincid, int output_curfile, Loop_Info *loop_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : VoxelStartFunction @INPUT : caller_data - pointer to client data. num_voxels - number of voxels to process. output_num_buffers - number of output buffers to handle on this call - will be the total number of output files plus the number of extra buffers requested by set_loop_accumulate. output_vector_length - length of output vector = 1 or as set by set_loop_output_vector_size. loop_info - pointer that can be passed to functions returning looping information @OUTPUT : output_data - array of pointers to output buffers. If extra buffers are requested by set_loop_accumulate, they will follow the output buffers. @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to initialize data processing when accumulating over files (specified when calling set_loop_accumulate). ---------------------------------------------------------------------------- */ typedef void (*VoxelStartFunction) (void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : VoxelFinishFunction @INPUT : caller_data - pointer to client data. num_voxels - number of voxels to process. output_num_buffers - number of output buffers to handle on this call - will be the total number of output files plus the number of extra buffers requested by set_loop_accumulate. output_vector_length - length of output vector = 1 or as set by set_loop_output_vector_size. loop_info - pointer that can be passed to functions returning looping information @OUTPUT : output_data - array of pointers to output buffers. If extra buffers are requested by set_loop_accumulate, they will follow the output buffers. @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to finish data processing when accumulating over files (specified when calling set_loop_accumulate). ---------------------------------------------------------------------------- */ typedef void (*VoxelFinishFunction) (void *caller_data, long num_voxels, int output_num_buffers, int output_vector_length, double *output_data[], Loop_Info *loop_info); /* ----------------------------- MNI Header ----------------------------------- @NAME : AllocateBufferFunction @INPUT : caller_data - pointer to client data. do_allocations - if TRUE, allocate data, if FALSE, free data num_input_buffers - number of input buffers to allocate num_input_voxels - number of voxels in each input buffer input_vector_length - number of values per input voxel num_output_buffers - number of output buffers to allocate num_output_voxels - number of voxels in each output buffer output_vector_length - number of values per output voxel num_extra_buffers - number of working buffers to allocate num_extra_voxels - number of voxels in each extra buffer extra_vector_length - number of values per extra voxel loop_info - pointer that can be passed to functions returning looping information input_buffers, output_buffers, extra_buffers - if do_allocation is FALSE, then arrays of pointers to buffers that need to be freed. The pointer arrays should also be freed. @OUTPUT : input_buffers, output_buffers, extra_buffers - if do_allocation is TRUE, then these arrays of buffers should be allocated (both the array of pointers and the buffers). The pointer array should have length num_xxx_buffers and each buffer should have length num_xxx_voxels*xxx_vector_length and be of type double. @RETURNS : (nothing) @DESCRIPTION: Typedef for function called by voxel_loop to allocate and free buffers. ---------------------------------------------------------------------------- */ typedef void (*AllocateBufferFunction) (void *caller_data, int do_allocation, int num_input_buffers, int num_input_voxels, int input_vector_length, double ***input_buffers, int num_output_buffers, int num_output_voxels, int output_vector_length, double ***output_buffers, int num_extra_buffers, int num_extra_voxels, int extra_vector_length, double ***extra_buffers, Loop_Info *loop_info); /* Function declarations */ MNCAPI void voxel_loop(int num_input_files, char *input_files[], int num_output_files, char *output_files[], char *arg_string, Loop_Options *loop_options, VoxelFunction voxel_function, void *caller_data); MNCAPI Loop_Options *create_loop_options(void); MNCAPI void free_loop_options(Loop_Options *loop_options); MNCAPI void set_loop_clobber(Loop_Options *loop_options, int clobber); #if MINC2 MNCAPI void set_loop_v2format(Loop_Options *loop_options, int use_v2_format); #endif /* MINC2 */ MNCAPI void set_loop_verbose(Loop_Options *loop_options, int verbose); MNCAPI void set_loop_datatype(Loop_Options *loop_options, nc_type datatype, int is_signed, double valid_min, double valid_max); MNCAPI void set_loop_max_open_files(Loop_Options *loop_options, int max_open_files); MNCAPI void set_loop_check_dim_info(Loop_Options *loop_options, int check_dim_info); MNCAPI void set_loop_convert_input_to_scalar(Loop_Options *loop_options, int convert_input_to_scalar); MNCAPI void set_loop_output_vector_size(Loop_Options *loop_options, int output_vector_size); MNCAPI void set_loop_first_input_mincid(Loop_Options *loop_options, int input_mincid); MNCAPI void set_loop_buffer_size(Loop_Options *loop_options, long buffer_size); MNCAPI void set_loop_dimension(Loop_Options *loop_options, char *dimension_name); MNCAPI void set_loop_input_file_function (Loop_Options *loop_options, VoxelInputFileFunction input_file_function); MNCAPI void set_loop_output_file_function (Loop_Options *loop_options, VoxelOutputFileFunction output_file_function); MNCAPI void set_loop_copy_all_header(Loop_Options *loop_options, int copy_all_header); MNCAPI void set_loop_accumulate(Loop_Options *loop_options, int do_accumulation, int num_extra_buffers, VoxelStartFunction start_function, VoxelFinishFunction finish_function); MNCAPI void set_loop_allocate_buffer_function(Loop_Options *loop_options, AllocateBufferFunction allocate_buffer_function); MNCAPI void get_info_shape(Loop_Info *loop_info, int ndims, long start[], long count[]); MNCAPI void get_info_voxel_index(Loop_Info *loop_info, long subscript, int ndims, long index[]); MNCAPI int get_info_current_file(Loop_Info *loop_info); MNCAPI int get_info_current_mincid(Loop_Info *loop_info); MNCAPI int get_info_current_index(Loop_Info *loop_info); MNCAPI int get_info_whole_file(Loop_Info *loop_info); #ifdef __cplusplus } #endif /* __cplusplus */ libminc-libminc-2-3-00/libsrc2/000077500000000000000000000000001257462267400162015ustar00rootroot00000000000000libminc-libminc-2-3-00/libsrc2/convert.c000066400000000000000000000411221257462267400200250ustar00rootroot00000000000000/** \file convert.c * \brief MINC 2.0 Coordinate and Voxel Conversion Functions * \author Bert Vincent * * Functions to convert "real" valued data to and from "voxel" valued * data, and to convert coordinates between "voxel" and "world" systems. */ /** * \defgroup mi2Cvt MINC 2.0 Coordinate and Voxel Conversion Functions */ #include #include #include #include #include "minc2.h" #include "minc2_private.h" /** Convert values between real (scaled) values and voxel (unscaled) * values. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file, whereas the "real" value is the * value at the given location after scaling has been applied. * * The \a coords parameter specifies the location at which the * conversion is performed. This is needed because MINC supports * per-slice scaling, therefore a conversion performed at one location * may differ from that performed at another location. * * \param volume A volume handle * \param coords The position for which to perform the conversion. * \param ncoords The length of the \a coords array. * \param real_value The original real value, to be converted to voxel. * \param voxel_value_ptr A pointer to the converted voxel value. * \ingroup mi2Cvt */ int miconvert_real_to_voxel(mihandle_t volume, const misize_t coords[], size_t ncoords, double real_value, double *voxel_value_ptr ) { int result = MI_NOERROR; double valid_min, valid_max; double slice_min, slice_max; double voxel_range, voxel_offset; double real_range, real_offset; /* get valid min/max, image min/max */ miget_volume_valid_range(volume, &valid_max, &valid_min); /* get image min/max */ miget_slice_range(volume, coords, ncoords, &slice_max, &slice_min); /* Calculate the actual conversion. */ voxel_offset = valid_min; real_offset = slice_min; voxel_range = valid_max - valid_min; real_range = slice_max - slice_min; real_value = (real_value - real_offset) / real_range; *voxel_value_ptr = (real_value * voxel_range) + voxel_offset; return (result); } /** Convert values between real (scaled) values and voxel (unscaled) * values. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file, whereas the "real" value is the * value at the given location after scaling has been applied. * * The \a coords parameter specifies the location at which the * conversion is performed. This is needed because MINC supports * per-slice scaling, therefore a conversion performed at one location * may differ from that performed at another location. * * \param volume A volume handle * \param coords The position for which to perform the conversion. * \param ncoords The length of the \a coords array. * \param voxel_value The original voxel value, to be converted to real. * \param real_value_ptr A pointer to the converted real value. * \ingroup mi2Cvt */ int miconvert_voxel_to_real(mihandle_t volume, const misize_t coords[], int ncoords, double voxel_value, double *real_value_ptr) { int result = MI_NOERROR; double valid_min, valid_max; double slice_min, slice_max; double voxel_range, voxel_offset; double real_range, real_offset; /* get valid min/max, image min/max */ miget_volume_valid_range(volume, &valid_max, &valid_min); /* get image min/max */ miget_slice_range(volume, coords, ncoords, &slice_max, &slice_min); /* Calculate the actual conversion. */ voxel_offset = valid_min; real_offset = slice_min; voxel_range = valid_max - valid_min; real_range = slice_max - slice_min; voxel_value = (voxel_value - voxel_offset) / voxel_range; *real_value_ptr = (voxel_value * real_range) + real_offset; return (result); } /** \internal */ static void mireorder_voxel_to_xyz(mihandle_t volume, const double voxel[], double xyz[MI2_3D], midimclass_t dimclass) { int i, axis; for (i = 0; i < volume->number_of_dims; i++) { midimhandle_t hdim = volume->dim_handles[i]; axis = hdim->world_index; if ( axis >= 0 && dimclass == hdim->dim_class) { xyz[axis] = voxel[i]; } } } /** \internal */ static void mireorder_xyz_to_voxel(mihandle_t volume, const double xyz[MI2_3D], double voxel[], midimclass_t dimclass) { int i, axis; for (i = 0; i < volume->number_of_dims; i++) { midimhandle_t hdim = volume->dim_handles[i]; axis = hdim->world_index; if ( axis >= 0 && dimclass == hdim->dim_class) { voxel[i] = xyz[axis]; } } } /** Converts an N-dimensional spatial position in voxel coordinates into a * 3-dimensional spatial position in world coordinates. * * The returned world coordinate vector is in a standardized order, with * the X position first (at index 0), followed by the Y and Z coordinates. * The voxel coordinate vector is in the native order appropriate to the * file. * * \ingroup mi2Cvt */ int miconvert_voxel_to_world(mihandle_t volume, const double voxel[], double world[MI2_3D]) { double temp[MI2_3D]; mireorder_voxel_to_xyz(volume, voxel, temp, MI_DIMCLASS_SPATIAL); mitransform_coord(world, volume->v2w_transform, temp); return (MI_NOERROR); } /** Converts a 3-dimensional spatial position in world coordinates into a * N-dimensional spatial position in voxel coordinates. * * The input world coordinate vector is in a standardized order, with * the X position first (at index 0), followed by the Y and Z coordinates. * The voxel coordinate vector is in the native order appropriate to the * file. * * \ingroup mi2Cvt */ int miconvert_world_to_voxel(mihandle_t volume, const double world[MI2_3D], double voxel[]) { double temp[MI2_3D]; int i; for (i = 0; i < volume->number_of_dims; i++) { voxel[i] = 0.0; } mitransform_coord(temp, volume->w2v_transform, world); mireorder_xyz_to_voxel(volume, temp, voxel, MI_DIMCLASS_SPATIAL); return (MI_NOERROR); } /** This function retrieves the real values of a position in the * MINC volume. The "real" value is the value at the given location * after scaling has been applied. * * \param volume A volume handle * \param coords The voxel position to retrieve * \param ndims The number of values in the \a coords array * \param value_ptr Pointer to a double variable to hold the returned value. * * \ingroup mi2Cvt */ int miget_real_value(mihandle_t volume, const misize_t coords[], int ndims, double *value_ptr) { double voxel; int result; result = miget_voxel_value(volume, coords, ndims, &voxel); if (result != MI_NOERROR) { return (result); } miconvert_voxel_to_real(volume, coords, ndims, voxel, value_ptr); return (MI_NOERROR); } /** This function sets the real value of a position in the MINC * volume. The "real" value is the value at the given location * after scaling has been applied. * * \param volume A volume handle * \param coords The voxel position to retrieve * \param ndims The number of values in the \a coords array * \param value The value to save at this location. * * \ingroup mi2Cvt */ int miset_real_value(mihandle_t volume, const misize_t coords[], int ndims, double value) { double voxel; if ((volume->mode & MI2_OPEN_RDWR) == 0) { //TODO: report that file is not open properly return (MI_ERROR); } miconvert_real_to_voxel(volume, coords, ndims, value, &voxel); return (miset_voxel_value(volume, coords, ndims, voxel)); } /** * This function calculates the start values for the volume dimensions, * assuming that the spatial origin is relocated to the given world * coordinate. * * \ingroup mi2Cvt */ int miconvert_world_origin_to_start( mihandle_t volume, double world[3], double starts[3]) { fprintf(stderr, "miconvert_world_origin_to_start: Not implemented.\n"); return (MI_NOERROR); } /** * This function calculates the start values for the volume dimensions, * assuming that the spatial origin is relocated to the given world * coordinate. * * \ingroup mi2Cvt */ int miconvert_spatial_frequency_origin_to_start( mihandle_t volume, double world[3], double starts[3]) { fprintf(stderr, "miconvert_spatial_frequency_origin_to_start: Not implemented.\n"); return (MI_NOERROR); } static double dot_vectors(int n, double v1[], double v2[]) { int i; double d; d = 0.0; for ( i = 0; i < n; i++ ) { d += v1[i] * v2[i]; } return ( d ); } static int solve_linear_system( int n, double **coefs, double *values, double *solution) { int i; for ( i = 0; i < n; i++ ) { solution[i] = values[i]; } return (scaled_maximal_pivoting_gaussian_elimination_real(n, coefs, 1, &solution )); } static void convert_transform_origin_to_starts(mihandle_t hvol, double origin[], double starts[] ) { int axis; int which[MI2_3D]; int n_axes, i, j; double o_dot_c, c_dot_c; double x_dot_x, x_dot_y, x_dot_v, y_dot_y, y_dot_v, bottom; double **matrix, solution[MI2_3D]; for ( i = 0; i < hvol->number_of_dims; i++) { starts[i] = 0.0; } /*--- get the list of valid axes (which) */ n_axes = 0; for ( i = 0; i < hvol->number_of_dims; i++ ) { axis = hvol->dim_handles[i]->world_index; if ( axis >= 0 ) { which[axis] = i; ++n_axes; } } /*--- get the starts: computed differently for 1, 2, or 3 axes */ switch (n_axes) { case 1: o_dot_c = dot_vectors(MI2_3D, origin, hvol->dim_handles[which[0]]->direction_cosines); c_dot_c = dot_vectors(MI2_3D, hvol->dim_handles[which[0]]->direction_cosines, hvol->dim_handles[which[0]]->direction_cosines); if ( c_dot_c != 0.0 ) { starts[which[0]] = o_dot_c / c_dot_c; } break; case 2: x_dot_x = dot_vectors(MI2_3D, hvol->dim_handles[which[0]]->direction_cosines, hvol->dim_handles[which[0]]->direction_cosines ); x_dot_v = dot_vectors(MI2_3D, hvol->dim_handles[which[0]]->direction_cosines, origin ); x_dot_y = dot_vectors(MI2_3D, hvol->dim_handles[which[0]]->direction_cosines, hvol->dim_handles[which[1]]->direction_cosines ); y_dot_y = dot_vectors(MI2_3D, hvol->dim_handles[which[1]]->direction_cosines, hvol->dim_handles[which[1]]->direction_cosines ); y_dot_v = dot_vectors(MI2_3D, hvol->dim_handles[which[1]]->direction_cosines, origin ); bottom = x_dot_x * y_dot_y - x_dot_y * x_dot_y; if ( bottom != 0.0 ) { starts[which[0]] = (x_dot_v * y_dot_y - x_dot_y * y_dot_v) / bottom; starts[which[1]] = (y_dot_v * x_dot_x - x_dot_y * x_dot_v) / bottom; } break; case 3: /*--- this is the usual case, solve the equations to find what starts give the desired origin */ matrix = alloc2d(MI2_3D, MI2_3D); for ( i = 0; i < MI2_3D; i++) { for ( j = 0; j < hvol->number_of_dims; j++ ) { matrix[i][j] = hvol->dim_handles[j]->direction_cosines[i]; } } if ( solve_linear_system( MI2_3D, matrix, origin, solution ) ) { starts[which[0]] = solution[0]; starts[which[1]] = solution[1]; starts[which[2]] = solution[2]; } free2d(MI2_3D, matrix); break; } } /** * This function sets the world coordinates of the point (0,0,0) in voxel * coordinates. This changes the constant offset of the two coordinate * systems. * * \ingroup mi2Cvt */ int miset_world_origin(mihandle_t volume, /**< A volume handle */ double world[MI2_3D]) /**< The world coordinates of voxel origin */ { double starts[MI2_3D]; int i; convert_transform_origin_to_starts(volume, world, starts); for (i = 0; i < volume->number_of_dims; i++) { midimhandle_t hdim = volume->dim_handles[i]; if (hdim->dim_class == MI_DIMCLASS_SPATIAL || hdim->dim_class == MI_DIMCLASS_SFREQUENCY) { hdim->start = starts[hdim->world_index]; } } /* Get the voxel to world transform for the volume */ miget_voxel_to_world(volume, volume->v2w_transform); /* Calculate the inverse transform */ miinvert_transform(volume->v2w_transform, volume->w2v_transform); return (MI_NOERROR); } /** * This function sets the world coordinates of the point (0,0,0) in voxel * coordinates. This changes the constant offset of the two coordinate * systems. * * \ingroup mi2Cvt */ int miset_spatial_frequency_origin(mihandle_t volume, double world[3]) { if ((volume->mode & MI2_OPEN_RDWR) == 0) { return (MI_ERROR); } fprintf(stderr, "miset_spatial_frequency_origin: Not implemented.\n"); return (MI_NOERROR); } /** This function retrieves the voxel values of a position in the * MINC volume. The voxel value is the unscaled value, and corresponds * to the value actually stored in the file. * * \ingroup mi2Cvt */ int miget_voxel_value(mihandle_t volume, const misize_t coords[], int ndims, double *voxel_ptr) { int result; misize_t count[MI2_MAX_VAR_DIMS]; int i; for (i = 0; i < volume->number_of_dims; i++) { count[i] = 1; } result = miget_voxel_value_hyperslab(volume, MI_TYPE_DOUBLE, coords, count, voxel_ptr); return (result); } /** This function sets the voxel value of a position in the MINC * volume. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file. * * \ingroup mi2Cvt */ int miset_voxel_value(mihandle_t volume, const misize_t coords[], int ndims, double voxel) { int result; misize_t count[MI2_MAX_VAR_DIMS]; int i; if ((volume->mode & MI2_OPEN_RDWR) == 0) { return (MI_ERROR); } for (i = 0; i < ndims; i++) { count[i] = 1; } result = miset_voxel_value_hyperslab(volume, MI_TYPE_DOUBLE, coords, count, &voxel); return (result); } int miget_volume_real_range(mihandle_t volume, double real_range[]) { hid_t spc_id; int n; double *buffer; int i; /* First find the real minimum. */ spc_id = H5Dget_space(volume->imin_id); n = (int) H5Sget_simple_extent_npoints(spc_id); H5Sclose(spc_id); buffer = malloc(n * sizeof(double)); if (buffer == NULL) { return (MI_ERROR); } H5Dread(volume->imin_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); real_range[0] = FLT_MAX; for (i = 0; i < n; i++) { if (buffer[i] < real_range[0]) { real_range[0] = buffer[i]; } } free(buffer); /* Now find the maximum. */ spc_id = H5Dget_space(volume->imax_id); n = (int) H5Sget_simple_extent_npoints(spc_id); H5Sclose(spc_id); buffer = malloc(n * sizeof(double)); if (buffer == NULL) { return (MI_ERROR); } H5Dread(volume->imax_id, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer); real_range[1] = FLT_MIN; for (i = 0; i < n; i++) { if (buffer[i] > real_range[1]) { real_range[1] = buffer[i]; } } free(buffer); return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/datatype.c000066400000000000000000000054541257462267400201700ustar00rootroot00000000000000/** \file datatype.c * \brief MINC 2.0 data type/space functions ************************************************************************/ #include #include #include "minc2.h" #include "minc2_private.h" /** Return the data class of a volume (See miclass_t). */ int miget_data_class ( mihandle_t volume, miclass_t *volume_class ) { *volume_class = volume->volume_class; return ( MI_NOERROR ); } /** Return the data type of a volume (See mitype_t). */ int miget_data_type ( mihandle_t volume, mitype_t *data_type ) { *data_type = volume->volume_type; return ( MI_NOERROR ); } /** Return the byte size of the voxel datatytpe */ int miget_data_type_size ( mihandle_t volume, misize_t *voxel_size ) { hid_t grp_id; hid_t dset_id; hid_t type_id; hid_t file_id = volume->hdf_id; grp_id = midescend_path ( file_id, MI_FULLIMAGE_PATH ); if ( grp_id < 0 ) { return ( MI_ERROR ); } dset_id = H5Dopen1 ( grp_id, "image" ); if ( dset_id < 0 ) { return ( MI_ERROR ); } type_id = H5Dget_type ( dset_id ); if ( type_id < 0 ) { return ( MI_ERROR ); } *voxel_size = H5Tget_size ( type_id ); H5Tclose ( type_id ); H5Dclose ( dset_id ); H5Gclose ( grp_id ); return ( MI_NOERROR ); } /** Return the minc space type, name should be freed after use */ int miget_space_name ( mihandle_t volume, char **name ) { size_t length = 0; int result = MI_ERROR; int i; /* This is the order of the search for candidates for the space type. The reason for this is complication is to permit support for older-style MINC files which associate the spacetype with individual dimensions. */ static const char *path_list[] = { MI_ROOT_PATH "/" MI_INFO_NAME, MI_ROOT_PATH "/dimensions/xspace", MI_ROOT_PATH "/dimensions/yspace", MI_ROOT_PATH "/dimensions/zspace", NULL }; /* Search for the spacetype attribute in all available paths. */ for ( i = 0; path_list[i] != 0; i++ ) { result = miget_attr_length ( volume, path_list[i], "spacetype", &length ); if ( result == MI_NOERROR ) { break; } } if ( result != MI_NOERROR ) { /* Nothing found, so use the default. */ length = strlen ( MI_NATIVE ); *name = malloc ( length + 1 ); strcpy ( *name, MI_NATIVE ); } else { *name = malloc ( length + 1 ); result = miget_attr_values ( volume, MI_TYPE_STRING, path_list[i], "spacetype", length, *name ); } return ( result ); } /** * Set minc space type */ int miset_space_name ( mihandle_t volume, const char *name ) { return miset_attr_values ( volume, MI_TYPE_STRING, MI_ROOT_PATH "/" MI_INFO_NAME, "spacetype", strlen ( name ), name ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/dimension.c000066400000000000000000001352411257462267400203400ustar00rootroot00000000000000/** \file dimension.c * \brief MINC 2.0 "dimension" Functions * \author Leila Baghdadi * * Functions to create, destroy, and manipulate MINC dimension objects. * All functions return MI_NOERROR upon success and MI_ERROR on failure. ************************************************************************/ #define _GNU_SOURCE 1 #include /*#include */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "minc2.h" #include "minc2_private.h" /** Figure out whether a dimension is associated with a volume. \param dimension The dimension handle. \param volume A pointer to the volume handle. * * This method returns the volume handle associated with a given dimension * or an error if the specified handle is not associated with the volume. * \ingroup mi2Dim */ int miget_volume_from_dimension ( midimhandle_t dimension, mihandle_t *volume ) { if ( dimension == NULL ) { return ( MI_ERROR ); } if ( dimension->volume_handle != NULL ) { *volume = dimension->volume_handle; } else { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** Create a copy of a given dimension. \param dim_ptr The dimension handle of the dimension to copy. \param new_dim_ptr A pointer to the dimension handle of the copied dimension. * * This method creates a copy of the specified dimension and returns the handle * to the copied dimension or error on failure. * \ingroup mi2Dim */ int micopy_dimension ( midimhandle_t dim_ptr, midimhandle_t *new_dim_ptr ) { misize_t i; midimhandle_t handle; if ( dim_ptr == NULL ) { return ( MI_ERROR ); } /* Allocate storage for the structure */ handle = ( midimhandle_t ) malloc ( sizeof ( struct midimension ) ); if ( handle == NULL ) { return ( MI_ERROR ); } handle->attr = dim_ptr->attr; handle->dim_class = dim_ptr->dim_class; /* Copy direction cosines */ handle->direction_cosines[MI2_X] = dim_ptr->direction_cosines[0]; handle->direction_cosines[MI2_Y] = dim_ptr->direction_cosines[1]; handle->direction_cosines[MI2_Z] = dim_ptr->direction_cosines[2]; switch ( dim_ptr->flipping_order ) { case MI_FILE_ORDER: handle->flipping_order = MI_FILE_ORDER; break; case MI_COUNTER_FILE_ORDER: handle->flipping_order = MI_COUNTER_FILE_ORDER; break; default: free(handle); return ( MI_ERROR ); } handle->name = strdup ( dim_ptr->name ); handle->length = dim_ptr->length; if ( dim_ptr->offsets != NULL ) { handle->offsets = ( double * ) malloc ( dim_ptr->length * sizeof ( double ) ); if ( handle->offsets == NULL ) { free(handle); return ( MI_ERROR ); } for ( i = 0; i < dim_ptr->length; i++ ) { handle->offsets[i] = dim_ptr->offsets[i]; } } else { handle->offsets = NULL; } // check to make sure start and step are defined! if ( dim_ptr->step != 0 ) { handle->start = dim_ptr->start; handle->step = dim_ptr->step; } else { handle->step = 0.0; } //check to make sure string is not empty or null if ( dim_ptr->units == NULL || *dim_ptr->units == '\0' ) { handle->units = strdup ( "mm" ); } else { handle->units = strdup ( dim_ptr->units ); } handle->width = dim_ptr->width; if ( dim_ptr->widths != NULL ) { handle->widths = ( double * ) malloc ( dim_ptr->length * sizeof ( double ) ); if ( handle->widths == NULL ) { return ( MI_ERROR ); } for ( i = 0; i < dim_ptr->length; i++ ) { handle->widths[i] = dim_ptr->widths[i]; } } else { handle->widths = NULL; } if ( dim_ptr->comments == NULL ) { handle->comments = NULL; } else { handle->comments = strdup ( dim_ptr->comments ); } handle->volume_handle = dim_ptr->volume_handle; *new_dim_ptr = handle; return ( MI_NOERROR ); } /** Define a new dimension in a MINC volume. \param name A pointer to the string specifying the dimension name. \param class The class of the dimension. \param attr The attribute of the dimension. \param length The size of the dimension. \param new_dim_ptr A pointer to the dimension handle. * * This function defines a dimension that can be used in the definition * of a new MINC volume (see the create_volume function). The name may * be an arbitrary string of up to 128 alphanumeric characters. Any of * the "standard" names retained from MINC 1.0 retain their default * behaviors: MIxspace, MIyspace, and MIzspace default to spatial * dimensions, and MItime default to be a time dimension. MItfrequency * is a temporal frequency axis, and MIxfrequency, MIyfrequency, and * MIzfrequency are spatial frequency axes. Any other name may be used. * * When initially defined, a regularly-sampled dimension will have a * "start" value of zero, and a "separation" or "step" value of 1.0. An * irregular dimension will be initialized with all offsets equal to * zero. * * The size of the dimension may range from 0 to 2^32, which should provide * enough range to represent detail on the order of 10 Angstroms in * typical medical imaging applications. * * For the detailed defintion of \a class and \a type refer to the MINC 2.0 API * definition. * \ingroup mi2Dim */ int micreate_dimension(const char *name, midimclass_t dimclass, midimattr_t attr, misize_t length, midimhandle_t *new_dim_ptr) { midimhandle_t handle; misize_t i; /* Allocate space for the new dimension */ handle = ( midimhandle_t ) malloc ( sizeof ( struct midimension ) ); if ( handle == NULL ) { return ( MI_ERROR ); } /* Duplicate the dimension name */ handle->name = strdup ( name ); /* Set the dimension comment to NULL unless otherwise */ handle->comments = NULL; switch ( dimclass ) { case MI_DIMCLASS_SPATIAL: handle->dim_class = MI_DIMCLASS_SPATIAL; if ( strcmp ( name, MIxspace ) == 0 ) { handle->direction_cosines[MI2_X] = 1.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 0.0; handle->comments = strdup ( "X increases from patient left to right" ); } else if ( strcmp ( name, MIyspace ) == 0 ) { handle->direction_cosines[MI2_X] = 0.0; handle->direction_cosines[MI2_Y] = 1.0; handle->direction_cosines[MI2_Z] = 0.0; handle->comments = strdup ( "Y increases from patient posterior to anterior" ); } else if ( strcmp ( name, MIzspace ) == 0 ) { handle->direction_cosines[MI2_X] = 0.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 1.0; handle->comments = strdup ( "Z increases from patient inferior to superior" ); } else { handle->direction_cosines[MI2_X] = 1.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 0.0; handle->comments = NULL; } break; case MI_DIMCLASS_TIME: handle->dim_class = MI_DIMCLASS_TIME; break; case MI_DIMCLASS_SFREQUENCY: handle->dim_class = MI_DIMCLASS_SFREQUENCY; if ( strcmp ( name, "xfrequency" ) == 0 ) { handle->direction_cosines[MI2_X] = 1.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 0.0; } else if ( strcmp ( name, "yfrequency" ) == 0 ) { handle->direction_cosines[MI2_X] = 0.0; handle->direction_cosines[MI2_Y] = 1.0; handle->direction_cosines[MI2_Z] = 0.0; } else if ( strcmp ( name, "zfrequency" ) == 0 ) { handle->direction_cosines[MI2_X] = 0.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 1.0; } else { handle->direction_cosines[MI2_X] = 1.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 0.0; } break; case MI_DIMCLASS_TFREQUENCY: handle->dim_class = MI_DIMCLASS_TFREQUENCY; break; case MI_DIMCLASS_USER: handle->dim_class = MI_DIMCLASS_USER; break; case MI_DIMCLASS_RECORD: handle->dim_class = MI_DIMCLASS_RECORD; break; case MI_DIMCLASS_ANY: default: free(handle); return MI_ERROR; } handle->offsets = NULL; handle->attr = attr; if ( attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) { handle->widths = ( double * ) malloc ( length * sizeof ( double ) ); for ( i = 0; i < length; i++ ) { handle->widths[i] = 1.0; } } else { handle->widths = NULL; } // do not set start and step if vector_dimension present if ( strcmp ( name, MIvector_dimension ) ) { handle->start = 0.0; handle->step = 1.0; } else { handle->step = 0.0; } handle->width = 1.0; handle->flipping_order = MI_FILE_ORDER; if ( dimclass != MI_DIMCLASS_SPATIAL && dimclass != MI_DIMCLASS_SFREQUENCY ) { handle->direction_cosines[MI2_X] = 1.0; handle->direction_cosines[MI2_Y] = 0.0; handle->direction_cosines[MI2_Z] = 0.0; } handle->length = length; handle->units = strdup ( "mm" ); /* volume_handle is the only NULL value once the dimension is created. */ handle->volume_handle = NULL; *new_dim_ptr = handle; return ( MI_NOERROR ); } /** Delete the dimension definition. \param dim_prt The dimension handle. * * Note: The original document stated that a dimension has to be * associated with a given volume before it can be deleted. This * feature was erased from the document and not considered here. * \ingroup mi2Dim */ int mifree_dimension_handle ( midimhandle_t dim_ptr ) { if ( dim_ptr == NULL ) { return ( MI_ERROR ); } if ( dim_ptr->name != NULL ) { free ( dim_ptr->name ); } if ( dim_ptr->offsets != NULL ) { free ( dim_ptr->offsets ); } if ( dim_ptr->units != NULL ) { free ( dim_ptr->units ); } if ( dim_ptr->widths != NULL ) { free ( dim_ptr->widths ); } if( dim_ptr->comments != NULL ) { free( dim_ptr->comments ); } free ( dim_ptr ); return ( MI_NOERROR ); } /** Retrieve the list of dimensions defined in a MINC volume, * with the same class \a class and attribute \a attr. * \param volume The volume handle. * \param class The class of the dimensions. * \param attr The attribute of the dimensions. * \param order The order of the dimension (file or apparent). * \param array_length The number of dimension to be retrieved. * \param dimensions An array of dimension handles to be retrieved. * * This function is used to retrieve an array of dimension handles for a * MINC volume. It will place the handles of the first "array_length" * dimensions into the "dimensions[]" array, returning only those dimension * whose characteristics match the "class" and "attr" parameters. * The miorder_t is an enumerated type flag which determines whether the * dimension order is determined by the file or by the apparent order set by * the user. This function will fail if the user has not set the apparent * dimension order by calling either of * (miset_apparent_dimension_order(_by_name)) * before calling this function with MI_DIMORDER_APPARENT flag. * \ingroup mi2Dim */ int miget_volume_dimensions ( mihandle_t volume, midimclass_t class, midimattr_t attr, miorder_t order, int array_length, midimhandle_t dimensions[] ) { hsize_t number_of_dims, i = 0, max_dims; int num_ret_dims = 0; if ( volume == NULL ) { return ( MI_ERROR ); } /* make sure the user has set the apparernt order before * calling this function with MI_DIMORDER_APPARENT */ if ( order == MI_DIMORDER_APPARENT && volume->dim_indices == NULL ) { return ( MI_ERROR ); } number_of_dims = volume->number_of_dims; if ( (hsize_t)( array_length ) > number_of_dims ) { max_dims = number_of_dims; } else { max_dims = array_length; } /* Go through each dimension separately and figure out which one has a matching class and attribute. if the user has set the apparent order use the dim_indices to search the dimensions otherwise use dim_handles */ for ( i = 0; i < max_dims; i++ ) { midimhandle_t hdim; if ( order == MI_DIMORDER_APPARENT ) { hdim = volume->dim_handles[volume->dim_indices[i]]; } else { hdim = volume->dim_handles[i]; } if ( class == MI_DIMCLASS_ANY || class == hdim->dim_class ) { if ( attr == MI_DIMATTR_ALL || hdim->attr == attr ) { dimensions[num_ret_dims++] = hdim; } } } return ( num_ret_dims ); } /** * Set apparent dimension order, based on an array of dimensions. You * may also set the dimension order by the name of the dimension, see * miset_apparent_dimension_order_by_name(). * \param volume The volume handle. * \param array_length The number of dimensions to be sorted. * \param dimensions An "ordered" array of dimension handles. * * This method sets an apparent dimension order. The user can sort the * dimensions in any desired order. If the user specifies fewer dimensions * than the existing ones, then they are assumed to be added to the last. * \ingroup mi2Dim */ int miset_apparent_dimension_order ( mihandle_t volume, int array_length, midimhandle_t dimensions[] ) { int diff; int i = 0, j = 0, k = 0; if ( volume == NULL || array_length <= 0 ) { return ( MI_ERROR ); } /* If array_length was more than the number of dimensions the rest of the given dimensions will be ignored. */ diff = volume->number_of_dims - array_length; if ( diff < 0 ) { diff = 0; } /* Allocated space for dimensions indices, if not already done. */ if ( volume->dim_indices == NULL ) { volume->dim_indices = ( int * ) malloc ( volume->number_of_dims * sizeof ( int ) ); memset ( volume->dim_indices, -1, sizeof ( volume->number_of_dims ) ); } if ( diff > 0 ) { while ( i < volume->number_of_dims && k < diff ) { for ( j = 0; j < array_length; j++ ) { if ( volume->dim_handles[i] == dimensions[j] ) { break; } } if ( j == array_length ) { volume->dim_indices[k] = i; k++; } i++; } } for ( i = 0; i < volume->number_of_dims; i++ ) { for ( j = 0; j < array_length; j++ ) { if ( volume->dim_handles[i] == dimensions[j] ) { volume->dim_indices[j + diff] = i; break; } } } /* printf(" apparent dimension order \n"); for(i=0; i < volume->number_of_dims; i++) { printf(" %d ", volume->dim_indices[i]); } printf("\n"); */ return ( MI_NOERROR ); } /** * Set apparent dimension order by name. * \param volume The volume handle. * \param array_length The number of dimensions to be sorted. * \param names An "ordered" array of dimension names. * * This method sets an apparent dimension order by dimension name. Note that * all dimension names must be different or an error occurs. * \ingroup mi2Dim */ int miset_apparent_dimension_order_by_name ( mihandle_t volume, int array_length, char **names ) { int diff; int i = 0, j = 0, k = 0; if ( volume == NULL ) { return ( MI_ERROR ); } if ( names == NULL || array_length <= 0 ) { /* Reset the dimension ordering */ if ( volume->dim_indices != NULL ) { free ( volume->dim_indices ); volume->dim_indices = NULL; } return ( MI_NOERROR ); } /* Note that all dimension names must be different or an error occurs. */ for ( i = 0; i < array_length; i++ ) { for ( j = i + 1; j < array_length; j++ ) { if ( strcmp ( names[i], names[j] ) == 0 ) { return ( MI_ERROR ); } } } /* If array_length was more than the number of dimensions the rest of the given dimensions will be ignored. */ diff = volume->number_of_dims - array_length; if ( diff < 0 ) { diff = 0; } /* Allocated space for dimensions indices, if not already done. */ if ( volume->dim_indices == NULL ) { volume->dim_indices = ( int * ) malloc ( volume->number_of_dims * sizeof ( int ) ); memset ( volume->dim_indices, -1, sizeof ( volume->number_of_dims ) ); } i = 0; j = 0; k = 0; if ( diff > 0 ) { while ( i < volume->number_of_dims && k < diff ) { for ( j = 0; j < array_length; j++ ) { if ( strcmp ( volume->dim_handles[i]->name, names[j] ) ) { break; } } if ( j == 3 ) { volume->dim_indices[k] = i; k++; } i++; } } for ( i = 0; i < volume->number_of_dims; i++ ) { for ( j = 0; j < array_length; j++ ) { if ( !strcmp ( volume->dim_handles[i]->name, names[j] ) ) { volume->dim_indices[j + diff] = i; break; } } } return ( MI_NOERROR ); } /** *Set the record flag and add a record dimension to the volume * dimensions so the volume would appear to have an extra dimension. * \param volume The volume handle. * \param record_flag The flag to determine whether there exist a record dimension * in the volume. * * This method causes a volume to appear to have a record dimension. The record * dimension will be set to uniform and flat (i.e., the volume will appear to have * an extra dimension) * \ingroup mi2Dim */ int miset_apparent_record_dimension_flag ( mihandle_t volume, int record_flag ) { midimhandle_t handle; if ( volume == NULL ) { return ( MI_ERROR ); } /* Allocate space for the dimension */ handle = ( midimhandle_t ) malloc ( sizeof ( struct midimension ) ); if ( handle == NULL ) { return ( MI_ERROR ); } handle->dim_class = MI_DIMCLASS_RECORD; handle->volume_handle = volume; volume->dim_handles[volume->number_of_dims] = handle; /* Add one to the number of dimensions so the volume will appear to have (n+1) dimensions */ volume->number_of_dims++; return ( MI_NOERROR ); } /** * Get the apparent order of voxels (i.e., the order that voxel indices increase/decrease) * \param dimension The dimension handle * \param flipping_order The order of voxels. * \param sign The sign of the step value. * * This method gets the apparent order of voxels for the specified dimension * and the sign of the step values. * \ingroup mi2Dim */ int miget_dimension_apparent_voxel_order ( midimhandle_t dimension, miflipping_t *flipping_order, miflipping_t *sign ) { if ( dimension == NULL ) { return ( MI_ERROR ); } switch ( dimension->flipping_order ) { case MI_FILE_ORDER: *flipping_order = MI_FILE_ORDER; if ( dimension->step > 0 ) { *sign = MI_POSITIVE; } else { *sign = MI_NEGATIVE; } break; case MI_COUNTER_FILE_ORDER: *flipping_order = MI_COUNTER_FILE_ORDER; if ( dimension->step > 0 ) { *sign = MI_NEGATIVE; } else { *sign = MI_POSITIVE; } break; case MI_POSITIVE: *sign = MI_POSITIVE; if ( dimension->step > 0 ) { *flipping_order = MI_FILE_ORDER; } else { *flipping_order = MI_COUNTER_FILE_ORDER; } break; case MI_NEGATIVE: *sign = MI_NEGATIVE; if ( dimension->step > 0 ) { *flipping_order = MI_COUNTER_FILE_ORDER; } else { *flipping_order = MI_FILE_ORDER; } break; default: return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * Set the apparent order of voxels. * \param dimension The dimension handle. * \param flipping_order The order of voxels. * * This method sets the apparent order of voxels for the specified dimension. * For the detailed description of voxel order refer to the MINC 2.0 API definition. * \ingroup mi2Dim */ int miset_dimension_apparent_voxel_order ( midimhandle_t dimension, miflipping_t flipping_order ) { if ( dimension == NULL ) { return ( MI_ERROR ); } switch ( flipping_order ) { case MI_FILE_ORDER: dimension->flipping_order = MI_FILE_ORDER; break; case MI_COUNTER_FILE_ORDER: dimension->flipping_order = MI_COUNTER_FILE_ORDER; break; case MI_POSITIVE: dimension->flipping_order = MI_POSITIVE; break; case MI_NEGATIVE: dimension->flipping_order = MI_NEGATIVE; break; default: return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * Get the class of a MINC dimension. * \param dimension The dimension handle. * \param class A pointer to the dimension class. * * The "class" of a MINC dimension defines the general type of a dimension, * whether it is a spatial dimension, a time dimension, or a frequency dimension * as transformed from either space or time. User-defined dimension are also * permitted, with no default handling assumed. Finally, a record can be specified * as a dimension. * \ingroup mi2Dim */ int miget_dimension_class ( midimhandle_t dimension, midimclass_t *class ) { if ( dimension == NULL ) { return ( MI_ERROR ); } switch ( dimension->dim_class ) { case MI_DIMCLASS_ANY: *class = MI_DIMCLASS_ANY; break; case MI_DIMCLASS_SPATIAL: *class = MI_DIMCLASS_SPATIAL; break; case MI_DIMCLASS_TIME: *class = MI_DIMCLASS_TIME; break; case MI_DIMCLASS_SFREQUENCY: *class = MI_DIMCLASS_SFREQUENCY; break; case MI_DIMCLASS_TFREQUENCY: *class = MI_DIMCLASS_TFREQUENCY; break; case MI_DIMCLASS_USER: *class = MI_DIMCLASS_USER; break; case MI_DIMCLASS_RECORD: *class = MI_DIMCLASS_RECORD; break; default: return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * Set the class of a MINC dimension. * \param dimension The dimension handle. * \param class The dimension class. * * Refer to miget_dimension_class(). * \ingroup mi2Dim */ int miset_dimension_class ( midimhandle_t dimension, midimclass_t class ) { if ( dimension == NULL ) { return ( MI_ERROR ); } switch ( class ) { case MI_DIMCLASS_ANY: dimension->dim_class = MI_DIMCLASS_ANY; break; case MI_DIMCLASS_SPATIAL: dimension->dim_class = MI_DIMCLASS_SPATIAL; break; case MI_DIMCLASS_TIME: dimension->dim_class = MI_DIMCLASS_TIME; break; case MI_DIMCLASS_SFREQUENCY: dimension->dim_class = MI_DIMCLASS_SFREQUENCY; break; case MI_DIMCLASS_TFREQUENCY: dimension->dim_class = MI_DIMCLASS_TFREQUENCY; break; case MI_DIMCLASS_USER: dimension->dim_class = MI_DIMCLASS_USER; break; case MI_DIMCLASS_RECORD: dimension->dim_class = MI_DIMCLASS_RECORD; break; default: return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * Get the direction cosine vector of a given SPATIAL dimension. * \param dimension The dimension handle. * \param direction_cosines An array of direction_cosines(i.e., vector determining the direction cosine). * * Spatial dimension in MINC volumes may be associated with a vector of direction * cosines which define the precise orientation of the axis relative to "true" * x, y, or z coordinates. * \ingroup mi2Dim */ int miget_dimension_cosines ( midimhandle_t dimension, double direction_cosines[3] ) { if ( dimension == NULL || ( dimension->dim_class != MI_DIMCLASS_SPATIAL && dimension->dim_class != MI_DIMCLASS_SFREQUENCY ) ) { return ( MI_ERROR ); } direction_cosines[0] = dimension->direction_cosines[0]; direction_cosines[1] = dimension->direction_cosines[1]; direction_cosines[2] = dimension->direction_cosines[2]; return ( MI_NOERROR ); } /** * Set the direction cosine vector for a given SPATIAL dimension. * \param dimension The dimension handle. * \param direction_cosines An array of direction_cosines(i.e., vector determining the direction cosine). * * Refer to miget_dimension_cosines(). * \ingroup mi2Dim */ int miset_dimension_cosines ( midimhandle_t dimension, const double direction_cosines[3] ) { if ( dimension == NULL || ( dimension->dim_class != MI_DIMCLASS_SPATIAL && dimension->dim_class != MI_DIMCLASS_SFREQUENCY ) ) { return ( MI_ERROR ); } dimension->direction_cosines[0] = direction_cosines[0]; dimension->direction_cosines[1] = direction_cosines[1]; dimension->direction_cosines[2] = direction_cosines[2]; return ( MI_NOERROR ); } /** * Get the comments attribute for a given dimension. * \param dimension The dimension handle. * \param comments_ptr A string pointer for the comments. * * Get and Set the dimension description. Note that the spatial dimensions * (xspace, yspace, zspace) are initialized according to minc1 description. * All other dimensions will have an empty description unless set by the user. * The string pointer returned in \a *comments_ptr must be freed by the caller. * \ingroup mi2Dim */ int miget_dimension_description ( midimhandle_t dimension, char **comments_ptr ) { if ( dimension == NULL ) { return ( MI_ERROR ); } *comments_ptr = strdup ( dimension->comments ); return ( MI_NOERROR ); } /** * Set the comments attribute for a given dimension. * \param dimension The dimension handle. * \param comments_ptr A pointer for the comments. * * Refer to miget_dimension_description(). * \ingroup mi2Dim */ int miset_dimension_description ( midimhandle_t dimension, const char *comments ) { if ( dimension == NULL || comments == NULL ) { return ( MI_ERROR ); } if ( ( strlen ( comments ) + 1 ) <= MI2_CHAR_LENGTH ) { dimension->comments = strdup ( comments ); } else { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * Get the identifier (name) of a MINC dimension. * \param dimension The dimension handle. * \param name_ptr A string pointer for returning the dimension name, should be released with free * * Retrieves the name of the given dimension. * \ingroup mi2Dim */ int miget_dimension_name ( midimhandle_t dimension, char **name_ptr ) { if ( dimension == NULL ) { return ( MI_ERROR ); } *name_ptr = strdup ( dimension->name ); return ( MI_NOERROR ); } /** * Set the identifier (name) of a given MINC dimension. * \param dimension The dimension handle. * \param name_ptr A pointer for the dimension name. * * Rename the given dimension. * \ingroup mi2Dim */ int miset_dimension_name ( midimhandle_t dimension, const char *name ) { if ( dimension == NULL || name == NULL ) { return ( MI_ERROR ); } if ( ( strlen ( name ) + 1 ) > MI2_CHAR_LENGTH ) { return ( MI_ERROR ); } /* Free the existing dimension name. */ if ( dimension->name != NULL ) { free ( dimension->name ); } dimension->name = strdup ( name ); return ( MI_NOERROR ); } /** * Get the untransformed world coordinates of points along a MINC dimension. * \param dimension The dimension handle. * \param array_length The number of dimensions. * \param start_position The position in which to retrieve the offsets. * \param offsets The array of offsets to be returned. * * Get or Set the dimension offsets, that is, the * absolute world coordinates of each sampled point along the dimension. * * The caller may retrieve up to "array_length" values, starting at the * integer index "start_position". Thus an arbitrary contiguous subset * of the dimension's offsets may be retrieved or stored. An error is * returned if the "start_position" exceeds the total size of the * dimension. If the value of "start_position" is legal, but the sum of * "start_position" and "array_length" exceeds the size of the dimension, * the function will get or set offsets up to the size of the dimension. * Any extra positions in the offsets[] array will be ignored. * \ingroup mi2Dim */ int miget_dimension_offsets( midimhandle_t dimension, misize_t array_length, misize_t start_position, double offsets[] ) { misize_t end_position; misize_t i, j; if ( dimension == NULL || start_position > dimension->length ) { return ( MI_ERROR ); } if ( ( start_position + array_length ) > dimension->length ) { end_position = dimension->length; } else { end_position = start_position + array_length; } if ( dimension->offsets == NULL ) { for ( i = start_position, j = 0; i < end_position ; i++, j++ ) { /* For regularly sampled dimensions, the step value * is added to each value to get the next value. */ offsets[j] = dimension->start + ( i * dimension->step ); } } else { for ( i = start_position, j = 0; i < end_position ; i++, j++ ) { offsets[j] = dimension->offsets[i]; } } return ( MI_NOERROR ); } /** * Set the absolute world coordinates of points along a MINC dimension. * \param dimension The dimension handle. * \param array_length The number of dimensions. * \param start_position The position in which to retrieve the offsets. * \param offsets The array of offsets to be set. * * Refer to miget_dimension_offsets(). * \ingroup mi2Dim */ int miset_dimension_offsets ( midimhandle_t dimension, misize_t array_length, misize_t start_position, const double offsets[] ) { misize_t end_position; misize_t i, j; /* Check to see whether the dimension is regularly sampled. */ if ( dimension == NULL || ( dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) == 0 || start_position > dimension->length ) { return ( MI_ERROR ); } if ( ( start_position + array_length ) > dimension->length ) { end_position = dimension->length; } else { end_position = start_position + array_length; } /* Allocate space for the offsets if not already done. */ if ( dimension->offsets == NULL ) { dimension->offsets = ( double * ) malloc ( dimension->length * sizeof ( double ) ); } for ( i = start_position, j = 0; i < end_position; i++, j++ ) { dimension->offsets[i] = offsets[j] ; } return ( MI_NOERROR ); } /** * Get the sampling flag for a MINC dimension. * \param dimension The dimension handle. * \param sampling_flag The flag to determine regular/irregular sampling dimensions. * * This flag is true (non-zero) if the dimension is sampled at regular * intervals, and false if the dimension is sampled irregularly. * If a dimension has regular sampling, the miget_dimension_separation() * may be used to retrieve the sampling interval, and the * miget_dimension_start() may be used to retrieve the origin * value along the axis. * * If a dimension has irregular sampling, the miget_dimension_offsets() * may be used to retrieve the positions of each sample along that axis. * \ingroup mi2Dim */ int miget_dimension_sampling_flag ( midimhandle_t dimension, miboolean_t *sampling_flag ) { if ( dimension == NULL ) { return ( MI_ERROR ); } *sampling_flag = ( dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) != 0; return ( MI_NOERROR ); } /** * Set the sampling flag for a MINC dimension. * \param dimension The dimension handle. * \param sampling_flag The flag to determine regular/irregular sampling dimensions. * * Refer to miget_dimension_sampling_flag(). * \ingroup mi2Dim */ int miset_dimension_sampling_flag ( midimhandle_t dimension, miboolean_t sampling_flag ) { if ( dimension == NULL ) { return ( MI_ERROR ); } if ( sampling_flag ) { dimension->attr |= MI_DIMATTR_NOT_REGULARLY_SAMPLED; dimension->attr &= ~MI_DIMATTR_REGULARLY_SAMPLED; } else { dimension->attr &= ~MI_DIMATTR_NOT_REGULARLY_SAMPLED; dimension->attr |= MI_DIMATTR_REGULARLY_SAMPLED; } return ( MI_NOERROR ); } /** * Get the constant sampling interval (step) for a single dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param separation_ptr The Pointer to the dimension sampling interval (step). * * Gets or sets the constant sampling interval defined on a regularly-sampled * dimension. While it is legal to call these functions for an irregularly- * sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_separation ( midimhandle_t dimension, mivoxel_order_t voxel_order, double *separation_ptr ) { if ( dimension == NULL || dimension->step == 0 ) { return ( MI_ERROR ); } if ( voxel_order == MI_ORDER_FILE ) { *separation_ptr = dimension->step; } else { if ( dimension->flipping_order == MI_COUNTER_FILE_ORDER ) { *separation_ptr = -dimension->step; } else if ( dimension->flipping_order == MI_POSITIVE ) { if ( dimension->step > 0 ) { *separation_ptr = dimension->step; } else { *separation_ptr = -dimension->step; } } else if ( dimension->flipping_order == MI_NEGATIVE ) { if ( dimension->step < 0 ) { *separation_ptr = dimension->step; } else { *separation_ptr = -dimension->step; } } else { *separation_ptr = dimension->step; } } return ( MI_NOERROR ); } /** * Set the sampling interval (step) for a single dimension. * \param dimension The dimension handle. * \param separation The dimension sampling interval (step). * * Refer to miget_dimension_separation(). * \ingroup mi2Dim */ int miset_dimension_separation ( midimhandle_t dimension, double separation ) { /* file-order of voxels is assumed. */ if ( dimension == NULL || dimension->step == 0 ) { return ( MI_ERROR ); } dimension->step = separation; /* If not explicitly set, the width will be assumed to be equal to the dimension's step size. */ //dimension->width = separation; return ( MI_NOERROR ); } /** * Get the sampling interval (STEP) for a list of dimensions. * \param dimensions An array of dimension handles. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of dimensions in the dimesions array. * \param separations An array of dimensions sampling intervals (step) values. * * Get or Set the scalar separation (sampling interval) * associated with each of the dimensions in the input "dimensions[]" * array. The "array_length" parameter specifies the size of both the * input and output arrays. While it is legal to call these functions for * an irregularly-sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_separations ( const midimhandle_t dimensions[], mivoxel_order_t voxel_order, misize_t array_length, double separations[] ) { misize_t i; for ( i = 0; i < array_length; i++ ) { miget_dimension_separation ( dimensions[i], voxel_order, &separations[i] ); } return ( MI_NOERROR ); } /** * Set the sampling interval (STEP) for a list of dimensions. * \param dimensions An array of dimension handles. * \param array_length The number of dimensions in the dimesions array. * \param separations An array of dimensions sampling intervals (step) values. * * Refer to miget_dimension_separations(). * \ingroup mi2Dim */ int miset_dimension_separations ( const midimhandle_t dimensions[], misize_t array_length, const double separations[] ) { misize_t i; for ( i = 0; i < array_length; i++ ) { miset_dimension_separation ( dimensions[i], separations[i] ); } return ( MI_NOERROR ); } /** * Get the length of a MINC dimension. * \param dimension The dimension handle. * \param size_ptr A pointer to the dimension size. * * Get or Set the size (or length) of a MINC 2 dimension * object used in creating a new volume. The size of a dimension * associated with an existing volume cannot be changed. * \ingroup mi2Dim */ int miget_dimension_size ( midimhandle_t dimension, misize_t *size_ptr ) { if ( dimension == NULL ) { return ( MI_ERROR ); } *size_ptr = dimension->length; return ( MI_NOERROR ); } /** * Set the length of a MINC dimension if not associated with a volume. * \param dimension The dimension handle. * \param size The size of the dimension. * * Refer to miget_dimension_size(). * \ingroup mi2Dim */ int miset_dimension_size ( midimhandle_t dimension, misize_t size ) { /* Check whether the dimension is associated with a volume. */ if ( dimension == NULL || dimension->volume_handle != NULL ) { return ( MI_ERROR ); } dimension->length = size; return ( MI_NOERROR ); } /** * Retrieve the length of all dimensions in dimensions array. * \param dimensions An array of dimension handles. * \param array_length The number of dimensions in the dimensions array * \param sizes An array of dimension sizes. * * This function will copy the lengths of each of the dimensions listed in the * "dimensions[]" array into the "sizes[]" array. The parameter "array_length" * specifies the length of both of the arrays. * \ingroup mi2Dim */ int miget_dimension_sizes ( const midimhandle_t dimensions[], misize_t array_length, misize_t sizes[] ) { misize_t i; for ( i = 0; i < array_length; i++ ) { miget_dimension_size ( dimensions[i], &sizes[i] ); } return ( MI_NOERROR ); } /** * Get the start value of a MINC dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param start_ptr A pointer to the start value. * * Get or set the origin of the dimension in world * coordinates. While a "start" value may be legally associated with any * dimension, it is considered meaningless when associated with an * irregularly sampled dimension. * \ingroup mi2Dim */ int miget_dimension_start ( midimhandle_t dimension, mivoxel_order_t voxel_order, double *start_ptr ) { /* If voxel_order is set to apparent file order (i.e., 1) start = start + step * (n-1) */ if ( dimension == NULL || dimension->step == 0 ) { return ( MI_ERROR ); } if ( voxel_order == MI_ORDER_FILE ) { *start_ptr = dimension->start; } else { // L.B March 16/2011, Properly reflect voxel ordering if ( dimension->flipping_order == MI_COUNTER_FILE_ORDER ) { *start_ptr = dimension->start + ( dimension->step * ( dimension->length - 1 ) ); } else if ( dimension->flipping_order == MI_POSITIVE ) { if ( dimension->step > 0 ) { *start_ptr = dimension->start; } else { *start_ptr = dimension->start + ( dimension->step * ( dimension->length - 1 ) ); } } else if ( dimension->flipping_order == MI_NEGATIVE ) { if ( dimension->step < 0 ) { *start_ptr = dimension->start; } else { *start_ptr = dimension->start + ( dimension->step * ( dimension->length - 1 ) ); } } } return ( MI_NOERROR ); } /** * Set the start of a MINC dimension. * \param dimension The dimension handle. * \param start The start of the dimension. * * Refer to miget_dimension_start(). * \ingroup mi2Dim */ int miset_dimension_start ( midimhandle_t dimension, double start ) { if ( dimension == NULL || dimension->step == 0 ) { return ( MI_ERROR ); } dimension->start = start; return ( MI_NOERROR ); } /** * Get the start values for MINC dimensions in dimensions array. * \param dimensions The array of dimension handles. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of dimensions in the dimensions array. * \param starts The array of dimension starts. * * Get or Set the start value for an array of * regularly-sampled dimensions. The start value defines the origin of * that dimension. While it is legal to call these functions for an * irregularly-sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_starts ( const midimhandle_t dimensions[], mivoxel_order_t voxel_order, misize_t array_length, double starts[] ) { misize_t i; for ( i = 0; i < array_length; i++ ) { miget_dimension_start ( dimensions[i], voxel_order, &starts[i] ); } return ( MI_NOERROR ); } /** * Set the start values for MINC dimensions in dimensions array. * \param dimensions The array of dimension handles. * \param array_length The number of dimensions in the dimensions array. * \param starts The array of dimension starts. * * Refer to miget_dimension_starts(). * \ingroup mi2Dim */ int miset_dimension_starts ( const midimhandle_t dimensions[], misize_t array_length, const double starts[] ) { misize_t i; for ( i = 0; i < array_length; i++ ) { miset_dimension_start ( dimensions[i], starts[i] ); } return ( MI_NOERROR ); } /** * Get the unit string for a MINC dimension. * \param dimension The dimension handle. * \param units_ptr A string pointer to the dimension units. * * Retrieves the units of the given dimension, * The caller must free the string returned by this function. * \ingroup mi2Dim */ int miget_dimension_units ( midimhandle_t dimension, char **units_ptr ) { if ( dimension == NULL || units_ptr == NULL ) { return ( MI_ERROR ); } *units_ptr = strdup ( dimension->units ); return ( MI_NOERROR ); } /** * Set the unit string for a MINC dimension. * \param dimension The dimension handle. * \param units A pointer to the dimension units. * * Set the units for an existing dimension. * The new string must be no greater than 128 characters in length, * including the trailing zero byte. * \ingroup mi2Dim */ int miset_dimension_units ( midimhandle_t dimension, const char *units ) { if ( dimension == NULL || units == NULL ) { return ( MI_ERROR ); } if ( strlen ( units ) + 1 > MI2_CHAR_LENGTH ) { return ( MI_ERROR ); } dimension->units = strdup ( units ); return ( MI_NOERROR ); } /** * Get A single full-width half-maximum value from a * regularly sampled dimension. * \param dimension The dimension handle. * \param width_ptr A pointer to the FWHM value. * * Get or Set the dimension width, that is, the * full-width half-maximum values of each sampled point along the dimension. * These functions are used to set a constant width for regularly-sampled * dimensions. * \ingroup mi2Dim */ int miget_dimension_width ( midimhandle_t dimension, double *width_ptr ) { if ( dimension == NULL || ( dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) != 0 ) { return ( MI_ERROR ); } *width_ptr = dimension->width; return ( MI_NOERROR ); } /** * Set the A single full-width half-maximum value for a * regularly sampled dimension. * \param dimension The dimension handle. * \param width The FWHM value. * * Refer to miget_dimension_width(). * \ingroup mi2Dim */ int miset_dimension_width ( midimhandle_t dimension, double width ) { if ( dimension == NULL || ( dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) != 0 ) { return ( MI_ERROR ); } /* Check to make sure width value is positive. */ if ( width < 0 ) { dimension->width = -width; } else { dimension->width = width; } return ( MI_NOERROR ); } /** * Get the full-width half-maximum value for points along an * irregularly sampled dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of width in the widths array. * \param start_position Index for starting position. * \param widths An array of width values to be retrieved. * * Get or Set the dimension widths, that is, the * full-width half-maximum values of each sampled point along the * dimension. * The caller may retrieve up to "array_length" values, starting at the * integer index "start_position". Thus an arbitrary contiguous subset * of the dimension's widths may be retrieved or stored. An error is * returned if the "start_position" exceeds the total size of the * dimension. If the value of "start_position" is legal, but the sum of * "start_position" and "array_length" exceeds the size of the dimension, * the function will get or set widths up to the size of the dimension. * Any extra positions in the widths[] array will be ignored. * \ingroup mi2Dim */ int miget_dimension_widths ( midimhandle_t dimension, mivoxel_order_t voxel_order, misize_t array_length, misize_t start_position, double widths[] ) { unsigned long diff; misize_t i, j = 0; if ( dimension == NULL || start_position > dimension->length ) { return ( MI_ERROR ); } if ( ( start_position + array_length ) > dimension->length ) { diff = dimension->length; } else { diff = array_length; } /* Allocate space for the widths array TODO: This is suspicious, didn't we just pass a pointer to this array? */ /* widths = ( double * ) malloc ( diff * sizeof ( double ) ); */ /* Check to see whether the dimension is regularly sampled. */ if ( start_position == 0 ) { diff--; } if ( dimension->widths == NULL ) { for ( i = start_position; i <= diff; i++ ) { widths[j] = dimension->width; j++; } } else { /* If the apparent order is requested, the widths are returned REVERSE (flip) order. */ if ( voxel_order == 0 ) { for ( i = start_position; i <= diff; i++ ) { widths[j] = dimension->widths[i]; j++; } } else { for ( i = diff; i >= start_position; i-- ) { widths[j] = dimension->widths[i]; j++; } } } return MI_NOERROR; } /** * Set the full-width half-maximum value for points along an * irregularly sampled dimension. * \param dimension The dimension handle. * \param array_length The number of width in the widths array. * \param start_position Index for starting position. * \param widths An array of width values to be set. * * Refer to miget_dimension_widths(). * \ingroup mi2Dim */ int miset_dimension_widths ( midimhandle_t dimension, misize_t array_length, misize_t start_position, const double widths[] ) { misize_t diff; misize_t i, j = 0; /* Check to see whether the dimension is regularly sampled. */ if ( dimension == NULL || ( dimension->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED ) == 0 || start_position > dimension->length ) { return ( MI_ERROR ); } if ( ( start_position + array_length ) > dimension->length ) { diff = dimension->length; } else { diff = array_length; } /* Allocate space for widths array if not already done */ if ( dimension->widths == NULL ) { dimension->widths = ( double * ) malloc ( dimension->length * sizeof ( double ) ); } if ( start_position == 0 ) { diff--; } for ( i = start_position; i <= diff; i++ ) { if ( widths[i] < 0 ) { dimension->widths[i] = -1 * widths[j]; } else { dimension->widths[i] = widths[j]; } j++; } return ( MI_NOERROR ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/free.c000066400000000000000000000013331257462267400172660ustar00rootroot00000000000000/** \file free.c * \brief MINC 2.0 "free" functions ************************************************************************/ #include "minc2.h" #include /** * Free space allocated for string storage by a MINC function. * \param name_ptr A pointer to the space to be freed. */ int mifree_name(char *name_ptr) { if (name_ptr == NULL) { return (MI_ERROR); } free(name_ptr); return (MI_NOERROR); } /** * Free list of names * not certain we really need this... */ int mifree_names(char **name_pptr) { if (name_pptr == NULL) { return (MI_ERROR); } while (*name_pptr != NULL) { free(*name_pptr++); } return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/grpattr.c000066400000000000000000000627541257462267400200460ustar00rootroot00000000000000/** * \file grpattr.c * \brief MINC 2.0 group/attribute functions * \author Bert Vincent and Leila Baghdadi * * Functions to manipulate attributes and groups. ************************************************************************/ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "minc2.h" #include "minc2_private.h" #define MILIST_MAX_PATH 256 #define MILIST_RECURSE 0x0001 struct milistframe { struct milistframe *next; hid_t grp_id; unsigned int att_idx; int grp_idx; char relpath[MILIST_MAX_PATH]; }; struct milistdata { int flags; char *name_ptr; int name_len; struct milistframe *frame_ptr; /* recursive frame */ }; /** Start listing the objects in a group. */ int milist_start ( mihandle_t vol, const char *path, int flags, milisthandle_t *handle ) { hid_t grp_id; char fullpath[256]; struct milistdata *data; struct milistframe *frame; strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1); grp_id = midescend_path ( vol->hdf_id, fullpath ); if ( grp_id < 0 ) { return ( MI_ERROR ); } data = ( struct milistdata * ) malloc ( sizeof ( struct milistdata ) ); if ( data == NULL ) { return ( MI_ERROR ); } frame = ( struct milistframe * ) malloc ( sizeof ( struct milistframe ) ); frame->next = NULL; frame->grp_id = grp_id; frame->att_idx = 0; frame->grp_idx = 0; strcpy ( frame->relpath, path ); data->frame_ptr = frame; data->flags = flags; *handle = data; return ( MI_NOERROR ); } static int milist_recursion ( milisthandle_t handle, char *path ) { struct milistdata *data = ( struct milistdata * ) handle; herr_t r; struct milistframe *frame; for ( ;; ) { H5E_BEGIN_TRY { r = H5Gget_objtype_by_idx ( data->frame_ptr->grp_id, data->frame_ptr->grp_idx ); } H5E_END_TRY; r--; if ( r < 0 ) { /* End of this group, need to pop the frame. */ frame = data->frame_ptr->next; if ( H5Iget_type ( data->frame_ptr->grp_id ) == H5I_GROUP ) { H5Gclose ( data->frame_ptr->grp_id ); } else { H5Dclose ( data->frame_ptr->grp_id ); } free ( data->frame_ptr ); data->frame_ptr = frame; if ( frame == NULL ) { return ( MI_ERROR ); } } else { data->frame_ptr->grp_idx++; /* If the object is a group, we need to recurse into it. */ r = 1; if ( r == H5G_GROUP ) { char tmp[256]; size_t l; H5Gget_objname_by_idx ( data->frame_ptr->grp_id, data->frame_ptr->grp_idx - 1, tmp, sizeof ( tmp ) ); frame = malloc ( sizeof ( struct milistframe ) ); if ( frame == NULL ) { return ( MI_ERROR ); } frame->grp_idx = 0; frame->att_idx = 0; frame->grp_id = H5Gopen1 ( data->frame_ptr->grp_id, tmp ); strcpy ( frame->relpath, data->frame_ptr->relpath ); l = strlen ( frame->relpath ); if ( l > 0 && frame->relpath[l - 1] != '/' ) { strcat ( frame->relpath, "/" ); } strcat ( frame->relpath, tmp ); /* Start working on the next frame. */ frame->next = data->frame_ptr; data->frame_ptr = frame; strncpy ( path, data->frame_ptr->relpath, MILIST_MAX_PATH ); break; /* Out of inner for(;;) */ } } } return ( MI_NOERROR ); } static herr_t milist_attr_op ( hid_t loc_id, const char *attr_name, void *op_data ) { struct milistdata *data = ( struct milistdata * ) op_data; (void)loc_id;/*to remove unused variable warning*/ strncpy ( data->name_ptr, attr_name, data->name_len ); return ( 1 ); } /** Iterate through attributes */ int milist_attr_next ( mihandle_t vol, milisthandle_t handle, char *path, int maxpath, char *name, int maxname ) { struct milistdata *data = ( struct milistdata * ) handle; herr_t r; data->name_ptr = name; data->name_len = maxname; for ( ;; ) { H5E_BEGIN_TRY { r = H5Aiterate1 ( data->frame_ptr->grp_id, &data->frame_ptr->att_idx, milist_attr_op, data ); } H5E_END_TRY; if ( r > 0 ) { strncpy ( path, data->frame_ptr->relpath, maxpath ); return ( MI_NOERROR ); } else { if ( data->flags & MILIST_RECURSE ) { return milist_recursion ( handle, path ); } else { return ( MI_ERROR ); } } } return ( MI_NOERROR ); } /** Finish listing attributes or groups */ int milist_finish ( milisthandle_t handle ) { hid_t tmp_id; struct milistdata *data = ( struct milistdata * ) handle; struct milistframe *frame; if ( data == NULL ) { return ( MI_ERROR ); } while ( ( frame = data->frame_ptr ) != NULL ) { data->frame_ptr = frame->next; //H5Gclose(frame->grp_id); H5E_BEGIN_TRY { tmp_id = H5Gclose ( frame->grp_id ); if ( tmp_id < 0 ) { tmp_id = H5Dclose ( frame->grp_id ); } } H5E_END_TRY; free ( frame ); } free ( data ); return ( MI_NOERROR ); } static herr_t milist_grp_op ( hid_t loc_id, const char *name, void *op_data ) { struct milistdata *data = ( struct milistdata * ) op_data; H5G_stat_t statbuf; H5Gget_objinfo ( loc_id, name, FALSE, &statbuf ); if ( statbuf.type == H5G_GROUP ) { size_t l; l = strlen ( data->frame_ptr->relpath ); if ( l > 0 && data->frame_ptr->relpath[l - 1] != '/' ) { strcat ( data->frame_ptr->relpath, "/" ); } } strcat ( data->frame_ptr->relpath, name ); return ( 1 ); } /** Get the group at given path */ int milist_grp_next ( milisthandle_t handle, char *path, int maxpath ) { struct milistdata *data = ( struct milistdata * ) handle; herr_t r; if ( ! ( data->flags & MILIST_RECURSE ) ) { char fullpath[256]; char tmp[256]; strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) - 1 ); strncat ( fullpath, data->frame_ptr->relpath, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); strcpy ( tmp, data->frame_ptr->relpath ); H5E_BEGIN_TRY { r = H5Giterate ( data->frame_ptr->grp_id, fullpath, &data->frame_ptr->grp_idx, milist_grp_op, data ); } H5E_END_TRY; if ( r > 0 ) { strncpy ( path, data->frame_ptr->relpath, maxpath ); strncpy ( data->frame_ptr->relpath, tmp, maxpath ); return ( MI_NOERROR ); } else { return ( MI_ERROR ); } } else if ( data->flags & MILIST_RECURSE ) { r = milist_recursion ( handle, path ); if ( r == MI_ERROR ) { return ( MI_ERROR ); } } else { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** Create a group at "path" using "name". */ int micreate_group ( mihandle_t vol, const char *path, const char *name ) { hid_t hdf_file; hid_t hdf_grp; hid_t hdf_new_grp; hid_t hdf_gpid; char fullpath[256]; /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return ( MI_ERROR ); } strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { return ( MI_ERROR ); } H5E_BEGIN_TRY { /* See if the group already exists. If so, just return. */ hdf_new_grp = H5Gopen1 ( hdf_grp, name ); if ( hdf_new_grp >= 0 ) { H5Gclose ( hdf_new_grp ); return ( MI_NOERROR ); } /* Actually create the requested group. */ hdf_gpid = H5Pcreate (H5P_GROUP_CREATE); H5Pset_attr_phase_change (hdf_gpid, 0, 0); hdf_new_grp = H5Gcreate2 ( hdf_grp, name, H5P_DEFAULT, hdf_gpid, H5P_DEFAULT ); if ( hdf_new_grp < 0 ) { return ( MI_ERROR ); } } H5E_END_TRY; /* Close the handles we created. */ H5Pclose ( hdf_gpid ); H5Gclose ( hdf_new_grp ); H5Gclose ( hdf_grp ); return ( MI_NOERROR ); } /** Delete the named attribute. */ int midelete_attr ( mihandle_t vol, const char *path, const char *name ) { hid_t tmp_id; hid_t hdf_file; hid_t hdf_grp; herr_t hdf_result; char fullpath[256]; /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return ( MI_ERROR ); } strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { return ( MI_ERROR ); } /* Delete the attribute from the path. */ hdf_result = H5Adelete ( hdf_grp, name ); if ( hdf_result < 0 ) { return ( MI_ERROR ); } /* Close the handles we created (PROPERLY!) */ H5E_BEGIN_TRY { tmp_id = H5Gclose ( hdf_grp ); if ( tmp_id < 0 ) { tmp_id = H5Dclose ( hdf_grp ); } } H5E_END_TRY; return ( MI_NOERROR ); } /** Delete the subgroup \a name from the group \a path */ int midelete_group ( mihandle_t vol, const char *path, const char *name ) { hid_t hdf_file; hid_t hdf_grp; herr_t hdf_result; char fullpath[256]; /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return ( MI_ERROR ); } strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { return ( MI_ERROR ); } H5E_BEGIN_TRY { /* Delete the group (or any object, really) from the path. */ hdf_result = H5Gunlink ( hdf_grp, name ); if ( hdf_result < 0 ) { hdf_result = MI_ERROR; } else { hdf_result = MI_NOERROR; } } H5E_END_TRY; /* Close the handles we created. */ H5Gclose ( hdf_grp ); return ( hdf_result ); } /** Get the length of a attribute */ int miget_attr_length ( mihandle_t vol, const char *path, const char *name, size_t *length ) { hid_t hdf_file=-1; hid_t hdf_grp=-1; hid_t hdf_attr=-1; hsize_t hdf_dims[1]; /* TODO: symbolic constant for "1" here? */ hid_t hdf_space=-1; hid_t hdf_type=-1; char fullpath[256]; int status = MI_ERROR; /* Guilty until proven innocent */ /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"HDF file is not open"); } if ( (!strcmp ( name, "history" ) || !strcmp(name,"ident") || !strcmp(name,"minc_version")) && ( *path==0 || !strcmp(path,"/")) ) { strncpy ( fullpath, MI_ROOT_PATH "/" , sizeof ( fullpath ) ); } else { strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); } if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) goto cleanup; H5E_BEGIN_TRY { hdf_attr = H5Aopen_name ( hdf_grp, name ); } H5E_END_TRY; if( hdf_attr<0 ) goto cleanup; if((hdf_space = H5Aget_space ( hdf_attr ))<0) goto cleanup; if((hdf_type = H5Aget_type ( hdf_attr ))<0) goto cleanup; switch ( H5Sget_simple_extent_ndims ( hdf_space ) ) { case 0: /* Scalar */ /* String types need to return the length of the string. */ if ( H5Tget_class ( hdf_type ) == H5T_STRING ) { *length = H5Tget_size ( hdf_type ); } else { *length = 1; } break; case 1: H5Sget_simple_extent_dims ( hdf_space, hdf_dims, NULL ); *length = hdf_dims[0]; break; default: /* For now, we allow only scalars and vectors. No multidimensional * arrays for MINC 2.0 attributes. */ MI_LOG_ERROR(MI2_MSG_GENERIC,"Only scalars and vectors are supported"); goto cleanup; } status=MI_NOERROR; cleanup: if(hdf_type >= 0 ) H5Tclose ( hdf_type ); if(hdf_space >= 0) H5Sclose ( hdf_space ); if(hdf_attr >= 0 ) H5Aclose ( hdf_attr ); if ( hdf_grp >= 0 ) { /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_grp ) == H5I_GROUP ) { H5Gclose ( hdf_grp ); } else { H5Dclose ( hdf_grp ); } } return status; } /** Get the type of an attribute. */ int miget_attr_type ( mihandle_t vol, const char *path, const char *name, mitype_t *data_type ) { hid_t hdf_file=-1; hid_t hdf_grp=-1; hid_t hdf_attr=-1; hid_t hdf_type=-1; char fullpath[256]; int status = MI_ERROR; /* Guilty until proven innocent */ /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"HDF file is not open"); } if ( (!strcmp ( name, "history" ) || !strcmp(name,"ident") || !strcmp(name,"minc_version")) && ( *path==0 || !strcmp(path,"/")) ) { strncpy ( fullpath, MI_ROOT_PATH "/" , sizeof ( fullpath ) ); } else { strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); } if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { goto cleanup; } H5E_BEGIN_TRY { hdf_attr = H5Aopen_name ( hdf_grp, name ); } H5E_END_TRY; if( hdf_attr<0 ) goto cleanup; if( (hdf_type = H5Aget_type ( hdf_attr ))<0 ) goto cleanup; switch ( H5Tget_class ( hdf_type ) ) { case H5T_FLOAT: if ( H5Tget_size ( hdf_type ) == sizeof ( float ) ) { *data_type = MI_TYPE_FLOAT; } else { *data_type = MI_TYPE_DOUBLE; } break; case H5T_STRING: *data_type = MI_TYPE_STRING; break; case H5T_INTEGER: *data_type = MI_TYPE_INT; break; default: goto cleanup; } status = MI_NOERROR; cleanup: if( hdf_type >= 0 ) H5Tclose ( hdf_type ); if( hdf_attr >= 0 ) H5Aclose ( hdf_attr ); if ( hdf_grp >= 0 ) { /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_grp ) == H5I_GROUP ) { H5Gclose ( hdf_grp ); } else { H5Dclose ( hdf_grp ); } } return status; } /** Copy all attribute given a path */ int micopy_attr ( mihandle_t vol, const char *path, mihandle_t new_vol ) { milisthandle_t hlist = NULL; mitype_t data_type = MI_TYPE_UNKNOWN; char namebuf[256]; char pathbuf[256]; char valstr[128]; int r; size_t length; /*TODO: make sure path size does not exceed 256 somehow*/ r = milist_start ( vol, path, 1, &hlist ); if ( r == MI_NOERROR ) { while ( milist_attr_next ( vol, hlist, pathbuf, sizeof ( pathbuf ), namebuf, sizeof ( namebuf ) ) == MI_NOERROR ) { /*TODO: Add error checking here*/ miget_attr_type ( vol, pathbuf, namebuf, &data_type ); miget_attr_length ( vol, pathbuf, namebuf, &length ); switch ( data_type ) { case MI_TYPE_STRING: if(lengthhdf_id; if ( hdf_file < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"HDF file is not open"); } if ( (!strcmp ( name, "history" ) || !strcmp(name,"ident") || !strcmp(name,"minc_version")) && ( *path==0 || !strcmp(path,"/")) ) { strncpy ( fullpath, MI_ROOT_PATH "/" , sizeof ( fullpath ) ); } else { strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); } if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { goto cleanup; } H5E_BEGIN_TRY { hdf_attr = H5Aopen_name ( hdf_grp, name ); } H5E_END_TRY; if( hdf_attr<0 ) goto cleanup; switch ( data_type ) { case MI_TYPE_INT: mtyp_id = H5Tcopy ( H5T_NATIVE_INT ); break; case MI_TYPE_FLOAT: mtyp_id = H5Tcopy ( H5T_NATIVE_FLOAT ); break; case MI_TYPE_DOUBLE: mtyp_id = H5Tcopy ( H5T_NATIVE_DOUBLE ); break; case MI_TYPE_STRING: mtyp_id = H5Tcopy ( H5T_C_S1 ); H5Tset_size ( mtyp_id, length ); break; default: goto cleanup; } if( (hdf_space = H5Aget_space ( hdf_attr )) < 0 ) goto cleanup; if( (hdf_type = H5Aget_type ( hdf_attr )) <0 ) goto cleanup; /* If we're retrieving a vector, make certain the length passed into this * function is sufficient. */ switch ( H5Sget_simple_extent_ndims ( hdf_space ) ) { case 0: /* Scalar */ /* String types need to return the length of the string. */ if ( H5Tget_class ( hdf_type ) == H5T_STRING ) { hdf_attr_size=H5Tget_size ( hdf_type ); } else { hdf_attr_size = 1; } break; case 1: H5Sget_simple_extent_dims ( hdf_space, &hdf_attr_size, NULL ); break; default: /* For now, we allow only scalars and vectors. No multidimensional * arrays for MINC 2.0 attributes. */ MI_LOG_ERROR(MI2_MSG_GENERIC,"Only scalars and vectors are supported"); goto cleanup; } if ( length < hdf_attr_size ) { /* Not enough space in the output vector */ fprintf(stderr,"Requested size:%d needed size:%d\n",(int)length,(int)hdf_attr_size); goto cleanup; } if ( H5Aread ( hdf_attr, mtyp_id, values ) <0 ) goto cleanup; /* make sure string is zero terminated if there is enough space in the input buffer */ if( data_type == MI_TYPE_STRING && length > hdf_attr_size ) ( ( char * ) values ) [hdf_attr_size+1] = '\0'; status = MI_NOERROR; cleanup: if( hdf_space >= 0 ) H5Sclose ( hdf_space ); if( hdf_type >= 0 ) H5Tclose ( hdf_type ); if( hdf_attr >= 0 ) H5Aclose ( hdf_attr ); if( mtyp_id >= 0 ) H5Tclose ( mtyp_id ); if ( hdf_grp >= 0 ) { /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_grp ) == H5I_GROUP ) { H5Gclose ( hdf_grp ); } else { H5Dclose ( hdf_grp ); } } return status; } /** Set the values of an attribute. */ int miset_attr_values ( mihandle_t vol, mitype_t data_type, const char *path, const char *name, size_t length, const void *values ) { hid_t hdf_file=-1; hid_t hdf_grp=-1; int result; char fullpath[256]; hid_t tmp_id; char *std_name; char *pch; size_t i, slength; int status = MI_ERROR; /* Guilty until proven innocent */ /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"HDF file is not open"); } if ( (!strcmp ( name, "history" ) || !strcmp(name,"ident") || !strcmp(name,"minc_version")) && ( *path==0 || !strcmp(path,"/")) ) { strncpy ( fullpath, MI_ROOT_PATH "/" , sizeof ( fullpath ) ); } else { strncpy ( fullpath, MI_ROOT_PATH "/" MI_INFO_NAME, sizeof ( fullpath ) ); } if ( *path != '/' && *path!=0 ) { strncat ( fullpath, "/", sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); } strncat ( fullpath, path, sizeof ( fullpath ) - strlen ( fullpath ) - 1 ); /* find last occurance of '/' */ pch = strrchr ( path, '/' ); if ( pch != NULL ) { slength = strlen ( path ) - ( pch - path ); std_name = malloc ( slength + 1 ); for ( i = 0; i < slength; i++ ) std_name[i] = path[pch - path + 1 + i]; std_name[slength] = '\0'; } else { std_name = malloc ( strlen ( path ) + 1 ); strcpy ( std_name, path ); } /* might need to create standard dataset first*/ if ( !strcmp ( std_name, "acquisition" ) || !strcmp ( std_name, "patient" ) || !strcmp ( std_name, "study" ) ) { H5E_BEGIN_TRY { tmp_id = H5Dopen1 ( hdf_file, fullpath ); if ( tmp_id < 0 ) { create_standard_dataset ( hdf_file, std_name ); } else { H5Dclose ( tmp_id ); } } H5E_END_TRY; } else { H5E_BEGIN_TRY { tmp_id = H5Dopen1 ( hdf_file, fullpath ); if ( tmp_id < 0 ) { create_dataset ( hdf_file, std_name ); } else { H5Dclose ( tmp_id ); } } H5E_END_TRY; } free ( std_name ); /* Search through the path, descending into each group encountered. */ hdf_grp = midescend_path ( hdf_file, fullpath ); if ( hdf_grp < 0 ) { goto cleanup; } result = miset_attr_at_loc ( hdf_grp, name, data_type, length, values ); if ( result < 0 ) { goto cleanup; } status=MI_NOERROR; cleanup: /* added the following instead H5Gclose(hdf_grp) */ if ( hdf_grp >= 0 ) { /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_grp ) == H5I_GROUP ) { H5Gclose ( hdf_grp ); } else { H5Dclose ( hdf_grp ); } } return status; } /** Add global history attribute */ int miadd_history_attr ( mihandle_t vol, size_t length, const void *values ) { int result; hid_t hdf_file; hid_t hdf_grp; hid_t tmp_id; /* Get a handle to the actual HDF file */ hdf_file = vol->hdf_id; if ( hdf_file < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"HDF file is not open"); } hdf_grp = midescend_path ( hdf_file, MI_ROOT_PATH ); if ( hdf_grp < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"midescend_path fail"); } result = miset_attr_at_loc ( hdf_grp, "history", MI_TYPE_STRING, length, values ); if ( result < 0 ) { return ( MI_ERROR ); } /* added the following instead H5Gclose(hdf_grp) */ H5E_BEGIN_TRY { tmp_id = H5Gclose ( hdf_grp ); if ( tmp_id < 0 ) { tmp_id = H5Dclose ( hdf_grp ); } } H5E_END_TRY; return ( MI_NOERROR ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/hyper.c000066400000000000000000001301151257462267400174750ustar00rootroot00000000000000/** \file hyper.c * \brief MINC 2.0 Hyperslab Functions * \author Bert Vincent * * Functions to manipulate hyperslabs of volume image data. ************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif //HAVE_CONFIG_H #include #include #include #include #include "minc2.h" #include "minc2_private.h" #include "restructure.h" #define MIRW_OP_READ 1 #define MIRW_OP_WRITE 2 /** Calculates and returns the number of bytes required to store the * hyperslab specified by the \a n_dimensions and the * \a count parameters, using hdf type id */ void miget_hyperslab_size_hdf(hid_t hdf_type_id, int n_dimensions, const hsize_t count[], misize_t *size_ptr) { size_t voxel_size; misize_t temp; int i; voxel_size = H5Tget_size(hdf_type_id); temp = 1; for (i = 0; i < n_dimensions; i++) { temp *= count[i]; } *size_ptr = (temp * voxel_size); } /** Calculates and returns the number of bytes required to store the * hyperslab specified by the \a n_dimensions and the * \a count parameters. */ int miget_hyperslab_size(mitype_t volume_data_type, /**< Data type of a voxel. */ int n_dimensions, /**< Dimensionality */ const hsize_t count[], /**< Dimension lengths */ misize_t *size_ptr) /**< Returned byte count */ { hid_t type_id; type_id = mitype_to_hdftype(volume_data_type, TRUE); if (type_id < 0) { return (MI_ERROR); } miget_hyperslab_size_hdf(type_id,n_dimensions,count,size_ptr); H5Tclose(type_id); return (MI_NOERROR); } /** "semiprivate" function for translating coordinates. */ int mitranslate_hyperslab_origin(mihandle_t volume, const misize_t* start, const misize_t* count, hsize_t* hdf_start, hsize_t* hdf_count, int* dir) /* direction vector in file order */ { int n_different = 0; int file_i; int ndims = volume->number_of_dims; int j; for(j=0; jdim_indices != NULL) { /* Have to swap indices */ user_i = volume->dim_indices[file_i]; if (user_i != file_i) { n_different++; } } else { user_i = file_i; } hdim = volume->dim_handles[user_i]; switch (hdim->flipping_order) { case MI_FILE_ORDER: hdf_start[user_i] = start[file_i]; dir[file_i] = 1; /* Set direction positive */ break; case MI_COUNTER_FILE_ORDER: hdf_start[user_i] = hdim->length - start[file_i] - count[file_i]; dir[file_i] = -1; /* Set direction negative */ n_different++; break; case MI_POSITIVE: if (hdim->step >= 0.0) { /* Positive? */ hdf_start[user_i] = start[file_i]; dir[file_i] = 1; /* Set direction positive */ } else { hdf_start[user_i] = hdim->length - start[file_i] - count[file_i]; dir[file_i] = -1; /* Set direction negative */ n_different++; } break; case MI_NEGATIVE: if (hdim->step < 0.0) { /* Negative? */ hdf_start[user_i] = start[file_i]; dir[file_i] = 1; /* Set direction positive */ } else { hdf_start[user_i] = hdim->length - start[file_i] - count[file_i]; dir[file_i] = -1; /* Set direction negative */ n_different++; } break; } hdf_count[user_i] = count[file_i]; } return (n_different); } /** Read/write a hyperslab of data. This is the simplified function * which performs no value conversion. It is much more efficient than * mirw_hyperslab_icv() */ static int mirw_hyperslab_raw(int opcode, mihandle_t volume, mitype_t midatatype, const misize_t start[], const misize_t count[], void *buffer) { hid_t dset_id = -1; hid_t mspc_id = -1; hid_t fspc_id = -1; hid_t type_id = -1; int result = MI_ERROR; hsize_t hdf_start[MI2_MAX_VAR_DIMS]; hsize_t hdf_count[MI2_MAX_VAR_DIMS]; int dir[MI2_MAX_VAR_DIMS]; /* Direction vector in file order */ int ndims; int n_different = 0; misize_t buffer_size; void *temp_buffer=NULL; char path[MI2_MAX_PATH]; size_t icount[MI2_MAX_VAR_DIMS]; /* Disallow write operations to anything but the highest resolution. */ if (opcode == MIRW_OP_WRITE && volume->selected_resolution != 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to write to a volume thumbnail"); } sprintf(path, MI_ROOT_PATH "/image/%d/image", volume->selected_resolution); /*printf("Using:%s\n",path);*/ /* Open the dataset with the specified path */ MI_CHECK_HDF_CALL(dset_id = H5Dopen1(volume->hdf_id, path),"H5Dopen1"); if (dset_id < 0) { return (MI_ERROR); } MI_CHECK_HDF_CALL(fspc_id = H5Dget_space(dset_id),"H5Dget_space"); if (fspc_id < 0) { /*TODO: report can't get dataset*/ goto cleanup; } MI_CHECK_HDF_CALL(fspc_id = H5Dget_space(dset_id),"H5Dget_space"); if (fspc_id < 0) { goto cleanup; } if (midatatype == MI_TYPE_UNKNOWN) { type_id = H5Tcopy(volume->mtype_id); } else { type_id = mitype_to_hdftype(midatatype, TRUE); } ndims = volume->number_of_dims; if (ndims == 0) { /* A scalar volume is possible but extremely unlikely, not to * mention useless! */ mspc_id = H5Screate(H5S_SCALAR); } else { n_different = mitranslate_hyperslab_origin(volume, start, count, hdf_start, hdf_count, dir); MI_CHECK_HDF_CALL(mspc_id = H5Screate_simple(ndims, hdf_count, NULL),"H5Screate_simple"); if (mspc_id < 0) { goto cleanup; } } MI_CHECK_HDF_CALL(result = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, hdf_start, NULL, hdf_count, NULL),"H5Sselect_hyperslab"); if (result < 0) { goto cleanup; } miget_hyperslab_size_hdf(type_id,ndims,hdf_count,&buffer_size); if (opcode == MIRW_OP_READ) { MI_CHECK_HDF_CALL(result = H5Dread(dset_id, type_id, mspc_id, fspc_id, H5P_DEFAULT,buffer),"H5Dread"); /* Restructure the array after reading the data in file orientation. */ if (n_different != 0) { int i; for (i = 0; i < ndims; i++) { icount[i] = count[i]; } restructure_array(ndims, buffer, icount, H5Tget_size(type_id), volume->dim_indices, dir); } } else { volume->is_dirty = TRUE; /* Mark as modified. */ /* Restructure array before writing to file. * TODO: use temporary buffer for that! */ if (n_different != 0) { int idir[MI2_MAX_VAR_DIMS]; int imap[MI2_MAX_VAR_DIMS]; int i; /* Invert before calling */ for (i = 0; i < ndims; i++) { icount[volume->dim_indices[i]] = count[i]; idir[volume->dim_indices[i]] = dir[i]; // this one was correct the original way imap[volume->dim_indices[i]] = i; } /*Use temporary array to preserve input data*/ temp_buffer=malloc(buffer_size); if(temp_buffer==NULL) { /*TODO: report memory error*/ result=MI_ERROR; goto cleanup; } memcpy(temp_buffer,buffer,buffer_size); restructure_array(ndims, temp_buffer, icount, H5Tget_size(type_id), imap, idir); MI_CHECK_HDF_CALL(result = H5Dwrite(dset_id, type_id, mspc_id, fspc_id, H5P_DEFAULT, temp_buffer),"H5Dwrite"); } else { MI_CHECK_HDF_CALL(result = H5Dwrite(dset_id, type_id, mspc_id, fspc_id, H5P_DEFAULT, buffer),"H5Dwrite"); } } cleanup: if (type_id >= 0) { H5Tclose(type_id); } if (mspc_id >= 0) { H5Sclose(mspc_id); } if (fspc_id >= 0) { H5Sclose(fspc_id); } if ( dset_id >=0 ) { H5Dclose(dset_id); } if ( temp_buffer!= NULL) { free( temp_buffer ); } return (result); } #define APPLY_DESCALING(type_in,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,voxel_min,voxel_max) \ { \ hsize_t _i,_j;\ type_in *_buffer=(type_in *)buffer;\ for(_i=0;_iselected_resolution != 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to write to a volume thumbnail"); } sprintf(path, MI_ROOT_PATH "/image/%d/image", volume->selected_resolution); /*printf("Using:%s\n",path);*/ /* Open the dataset with the specified path */ MI_CHECK_HDF_CALL(dset_id = H5Dopen1(volume->hdf_id, path),"H5Dopen1"); if (dset_id < 0) { return (MI_ERROR); } MI_CHECK_HDF_CALL(fspc_id = H5Dget_space(dset_id),"H5Dget_space"); if (fspc_id < 0) { goto cleanup; } buffer_type_id = mitype_to_hdftype(buffer_data_type, TRUE); if(buffer_type_id<0) { goto cleanup; } ndims = volume->number_of_dims; if (ndims == 0) { /* A scalar volume is possible but extremely unlikely, not to * mention useless! */ mspc_id = H5Screate(H5S_SCALAR); hdf_count[0]=1; } else { n_different = mitranslate_hyperslab_origin(volume, start, count, hdf_start, hdf_count, dir); mspc_id = H5Screate_simple(ndims, hdf_count, NULL); if (mspc_id < 0) { fprintf(stderr,"H5Screate_simple: Fail %s:%d\n",__FILE__,__LINE__); goto cleanup; } } miget_hyperslab_size_hdf(buffer_type_id, ndims, hdf_count, &buffer_size); MI_CHECK_HDF_CALL(result = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, hdf_start, NULL, hdf_count, NULL),"H5Sselect_hyperslab"); if (result < 0) { goto cleanup; } if((result=miget_volume_valid_range( volume, &volume_valid_max, &volume_valid_min))<0) { goto cleanup; } #ifdef _DEBUG printf("mirw_hyperslab_icv:Volume:%lx valid_max:%f valid_min:%f scaling:%d n_different:%d\n",(long int)(volume),volume_valid_max,volume_valid_min,volume->has_slice_scaling,n_different); #endif if(volume->has_slice_scaling) { hid_t image_max_fspc_id; hid_t image_min_fspc_id; hid_t scaling_mspc_id; total_number_of_slices=1; image_slice_length=1; scaling_needed=1; image_max_fspc_id=H5Dget_space(volume->imax_id); image_min_fspc_id=H5Dget_space(volume->imin_id); if ( image_max_fspc_id < 0 ) { /*Report error that image-max is not found!*/ return ( MI_ERROR ); } slice_ndims = H5Sget_simple_extent_ndims ( image_max_fspc_id ); if(slice_ndims<0) { /*TODO: report read error somehow*/ fprintf(stderr,"H5Sget_simple_extent_ndims: Fail %s:%d\n",__FILE__,__LINE__); goto cleanup; } if ( (hsize_t)slice_ndims > ndims ) { /*Can this really happen?*/ slice_ndims = ndims; } for ( j = 0; j < slice_ndims; j++ ) { image_slice_count[j] = hdf_count[j]; image_slice_start[j] = hdf_start[j]; if(hdf_count[j]>1) /*avoid zero sized dimensions?*/ total_number_of_slices*=hdf_count[j]; } for (i = slice_ndims; i < ndims; i++ ) { if(hdf_count[i]>1) /*avoid zero sized dimensions?*/ image_slice_length*=hdf_count[i]; image_slice_count[i] = 0; image_slice_start[i] = 0; } image_slice_max_buffer=malloc(total_number_of_slices*sizeof(double)); if(!image_slice_max_buffer) { result=MI_ERROR; MI_LOG_ERROR(MI2_MSG_OUTOFMEM,total_number_of_slices*sizeof(double)); goto cleanup; } image_slice_min_buffer=malloc(total_number_of_slices*sizeof(double)); if(!image_slice_min_buffer) { result=MI_ERROR; MI_LOG_ERROR(MI2_MSG_OUTOFMEM,total_number_of_slices*sizeof(double)); goto cleanup; } scaling_mspc_id = H5Screate_simple(slice_ndims, image_slice_count, NULL); if( (result=H5Sselect_hyperslab(image_max_fspc_id, H5S_SELECT_SET, image_slice_start, NULL, image_slice_count, NULL))>=0 ) { if((result=H5Dread(volume->imax_id, H5T_NATIVE_DOUBLE, scaling_mspc_id, image_max_fspc_id, H5P_DEFAULT,image_slice_max_buffer))<0) { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Dread"); goto cleanup; } } else { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Sselect_hyperslab"); goto cleanup; } if((result=H5Sselect_hyperslab(image_min_fspc_id, H5S_SELECT_SET, image_slice_start, NULL, image_slice_count, NULL))>=0 ) { if((result=H5Dread(volume->imin_id, H5T_NATIVE_DOUBLE, scaling_mspc_id, image_min_fspc_id, H5P_DEFAULT,image_slice_min_buffer))<0) { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Dread"); goto cleanup; } } else { /*TODO: report read error somehow*/ MI_LOG_ERROR(MI2_MSG_HDF5,"H5Sselect_hyperslab"); goto cleanup; } H5Sclose(scaling_mspc_id); H5Sclose(image_max_fspc_id); } else { slice_ndims=0; total_number_of_slices=1; image_slice_max_buffer=malloc(sizeof(double)); image_slice_min_buffer=malloc(sizeof(double)); miget_volume_range(volume,image_slice_max_buffer,image_slice_min_buffer); image_slice_length=1; /*it produces unity scaling*/ scaling_needed=(*image_slice_max_buffer!=volume_valid_max) || (*image_slice_min_buffer!=volume_valid_min); for (i = 0; i < ndims; i++) { image_slice_length *= hdf_count[i]; } #ifdef _DEBUG printf("mirw_hyperslab_icv:Real max:%f min:%f\n",*image_slice_max_buffer,*image_slice_min_buffer); #endif } //A hack to disable interslice scaling when it is not needed according to MINC1 specs if( volume->volume_type==MI_TYPE_FLOAT || volume->volume_type==MI_TYPE_DOUBLE || volume->volume_type==MI_TYPE_FCOMPLEX || volume->volume_type==MI_TYPE_DCOMPLEX ) { scaling_needed=0; } #ifdef _DEBUG printf("mirw_hyperslab_icv:Slice_ndim:%d total_number_of_slices:%d image_slice_length:%d scaling_needed:%d\n",slice_ndims,total_number_of_slices,image_slice_length,scaling_needed); #endif if (opcode == MIRW_OP_READ) { MI_CHECK_HDF_CALL(result = H5Dread(dset_id, buffer_type_id, mspc_id, fspc_id, H5P_DEFAULT, buffer),"H5Dread"); if(result<0) { goto cleanup; } if(scaling_needed) { switch(buffer_data_type) { case MI_TYPE_FLOAT: #ifdef _DEBUG printf("Descaling float\n"); #endif APPLY_DESCALING(float,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_DOUBLE: #ifdef _DEBUG printf("Descaling double\n"); #endif APPLY_DESCALING(double,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_INT: #ifdef _DEBUG printf("Descaling int\n"); #endif APPLY_DESCALING(int,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_UINT: #ifdef _DEBUG printf("Descaling uint\n"); #endif APPLY_DESCALING(unsigned int,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_SHORT: #ifdef _DEBUG printf("Descaling short\n"); #endif APPLY_DESCALING(short,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_USHORT: #ifdef _DEBUG printf("Descaling ushort\n"); #endif APPLY_DESCALING(unsigned short,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_BYTE: #ifdef _DEBUG printf("Descaling byte\n"); #endif APPLY_DESCALING(char,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_UBYTE: #ifdef _DEBUG printf("Descaling ubyte\n"); #endif APPLY_DESCALING(unsigned char,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; default: /*TODO: report unsupported conversion*/ result=MI_ERROR; goto cleanup; } } else { #ifdef _DEBUG printf("Descaling not needed!\n"); #endif } if (n_different != 0 ) { for (i = 0; i < ndims; i++) { icount[i] = count[i]; } restructure_array(ndims, buffer, icount, H5Tget_size(buffer_type_id),volume->dim_indices, dir); /*TODO: check if we managed to restructure the array*/ result=0; } } else { /*opcode != MIRW_OP_READ*/ volume->is_dirty = TRUE; /* Mark as modified. */ if (n_different != 0 ) { /* Invert before calling */ for (i = 0; i < ndims; i++) { icount[volume->dim_indices[i]] = count[i]; idir[volume->dim_indices[i]] = dir[i]; /* this one was correct the original way*/ imap[volume->dim_indices[i]] = i; } } if(scaling_needed || n_different != 0) { /*create temporary copy, to be destroyed*/ temp_buffer=malloc(buffer_size); if(!temp_buffer) { MI_LOG_ERROR(MI2_MSG_OUTOFMEM,buffer_size); result=MI_ERROR; /*TODO: error code?*/ goto cleanup; } memcpy(temp_buffer,buffer,buffer_size); if (n_different != 0 ) restructure_array(ndims, temp_buffer, icount, H5Tget_size(buffer_type_id), imap, idir); if(scaling_needed) { switch(buffer_data_type) { case MI_TYPE_FLOAT: #ifdef _DEBUG printf("scaling float\n"); #endif APPLY_SCALING(float,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_DOUBLE: #ifdef _DEBUG printf("scaling double\n"); #endif APPLY_SCALING(double,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_INT: #ifdef _DEBUG printf("scaling int\n"); #endif APPLY_SCALING(int,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_UINT: #ifdef _DEBUG printf("scaling unsigned int\n"); #endif APPLY_SCALING(unsigned int,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_SHORT: #ifdef _DEBUG printf("scaling short\n"); #endif APPLY_SCALING(short,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_USHORT: #ifdef _DEBUG printf("scaling unsigned short\n"); #endif APPLY_SCALING(unsigned short,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_BYTE: #ifdef _DEBUG printf("scaling char\n"); #endif APPLY_SCALING(char,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; case MI_TYPE_UBYTE: #ifdef _DEBUG printf("scaling unsigned char\n"); #endif APPLY_SCALING(unsigned char,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max); break; default: /*TODO: report unsupported conversion*/ result=MI_ERROR; goto cleanup; } } MI_CHECK_HDF_CALL(result = H5Dwrite(dset_id, buffer_type_id, mspc_id, fspc_id, H5P_DEFAULT, temp_buffer),"H5Dwrite"); } else { MI_CHECK_HDF_CALL(result = H5Dwrite(dset_id, buffer_type_id, mspc_id, fspc_id, H5P_DEFAULT, buffer),"H5Dwrite"); } if(result<0) { goto cleanup; } } cleanup: if (buffer_type_id >= 0) { H5Tclose(buffer_type_id); } if (mspc_id >= 0) { H5Sclose(mspc_id); } if (fspc_id >= 0) { H5Sclose(fspc_id); } if ( dset_id >=0 ) { H5Dclose(dset_id); } if(temp_buffer!=NULL) { free(temp_buffer); } if(image_slice_min_buffer!=NULL) { free(image_slice_min_buffer); } if(image_slice_max_buffer!=NULL) { free(image_slice_max_buffer); } return (result); } #define APPLY_DESCALING_NORM(type_out,buffer_in,buffer_out,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,voxel_min,voxel_max,data_min,data_max,norm_min,norm_max) \ { \ hsize_t _i,_j;\ double voxel_offset=voxel_min;\ double voxel_range=voxel_max-voxel_min;\ double norm_offset=norm_min;\ double norm_range=(double)norm_max-(double)norm_min;\ double data_offset=data_min;\ double data_range=(double)data_max-(double)data_min;\ type_out *_buffer_out=(type_out *)buffer_out;\ double *_buffer_in=(double *)buffer_in; \ for(_i=0;_i=1.0)?norm_max:(rint(_temp*norm_range)+norm_offset); \ *_buffer_out=(type_out)(_temp);\ _buffer_in++;\ _buffer_out++;\ }\ } #define APPLY_SCALING_NORM(type_in,buffer_in,buffer_out,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,voxel_min,voxel_max,data_min,data_max,norm_min,norm_max) \ { \ hsize_t _i,_j;\ double voxel_range=voxel_max-voxel_min;\ double voxel_offset=voxel_min;\ double norm_offset=norm_min;\ double norm_range=(double)norm_max-(double)norm_min;\ double data_offset=data_min;\ double data_range=(double)data_max-(double)data_min;\ type_in *_buffer_in=(type_in *)buffer_in;\ double *_buffer_out=buffer_out;\ for(_i=0;_iselected_resolution != 0) { /*TODO: report error that we are not dealing with the rihgt image here*/ return (MI_ERROR); } sprintf(path, MI_ROOT_PATH "/image/%d/image", volume->selected_resolution); /* Open the dataset with the specified path */ MI_CHECK_HDF_CALL(dset_id = H5Dopen1(volume->hdf_id, path),"H5Dopen1"); if (dset_id < 0) { return (MI_ERROR); } MI_CHECK_HDF_CALL(fspc_id = H5Dget_space(dset_id),"H5Dget_space"); if (fspc_id < 0) { /*TODO: report can't get dataset*/ goto cleanup; } buffer_type_id = mitype_to_hdftype(buffer_data_type,TRUE); if(buffer_type_id<0) { goto cleanup; } MI_CHECK_HDF_CALL(volume_type_id = H5Tcopy ( H5T_NATIVE_DOUBLE ),"H5Tcopy"); if(volume_type_id<0) { fprintf(stderr,"H5Tcopy: Fail %s:%d\n",__FILE__,__LINE__); goto cleanup; } ndims = volume->number_of_dims; if (ndims == 0) { /* A scalar volume is possible but extremely unlikely, not to * mention useless! */ mspc_id = H5Screate(H5S_SCALAR); } else { n_different = mitranslate_hyperslab_origin(volume,start,count, hdf_start,hdf_count,dir); MI_CHECK_HDF_CALL(mspc_id = H5Screate_simple(ndims, hdf_count, NULL),"H5Screate_simple"); if (mspc_id < 0) { goto cleanup; } } miget_hyperslab_size_hdf(volume_type_id,ndims,hdf_count,&buffer_size); miget_hyperslab_size_hdf(buffer_type_id,ndims,hdf_count,&input_buffer_size); MI_CHECK_HDF_CALL(result = H5Sselect_hyperslab(fspc_id, H5S_SELECT_SET, hdf_start, NULL, hdf_count, NULL),"H5Sselect_hyperslab"); if (result < 0) { goto cleanup; } miget_volume_valid_range( volume, &volume_valid_max, &volume_valid_min); #ifdef _DEBUG printf("mirw_hyperslab_normalized:Volume:%x valid_max:%f valid_min:%f scaling:%d\n",volume,volume_valid_max,volume_valid_min,volume->has_slice_scaling); #endif if(volume->has_slice_scaling && !(volume->volume_type==MI_TYPE_FLOAT || volume->volume_type==MI_TYPE_DOUBLE || volume->volume_type==MI_TYPE_FCOMPLEX || volume->volume_type==MI_TYPE_DCOMPLEX) ) { hid_t image_max_fspc_id; hid_t image_min_fspc_id; hid_t scaling_mspc_id; total_number_of_slices=1; image_slice_length=1; MI_CHECK_HDF_CALL(image_max_fspc_id=H5Dget_space(volume->imax_id),"H5Dget_space"); MI_CHECK_HDF_CALL(image_min_fspc_id=H5Dget_space(volume->imin_id),"H5Dget_space"); if ( image_max_fspc_id < 0 || image_min_fspc_id<0 ) { result=MI_ERROR; goto cleanup; } MI_CHECK_HDF_CALL(slice_ndims = H5Sget_simple_extent_ndims ( image_max_fspc_id ),"H5Sget_simple_extent_ndims"); if(slice_ndims<0) { goto cleanup; } if ( (hsize_t)slice_ndims > ndims ) { /*Can this really happen?*/ slice_ndims = ndims; } for ( j = 0; j < slice_ndims; j++ ) { image_slice_count[j] = hdf_count[j]; image_slice_start[j] = hdf_start[j]; if(hdf_count[j]>1) /*avoid zero sized dimensions?*/ total_number_of_slices*=hdf_count[j]; } for (i = slice_ndims; i < ndims; i++ ) { if(hdf_count[i]>1) /*avoid zero sized dimensions?*/ image_slice_length*=hdf_count[i]; image_slice_count[i] = 0; image_slice_start[i] = 0; } image_slice_max_buffer=malloc(total_number_of_slices*sizeof(double)); image_slice_min_buffer=malloc(total_number_of_slices*sizeof(double)); /*TODO check for allocation failure ?*/ MI_CHECK_HDF_CALL(scaling_mspc_id = H5Screate_simple(slice_ndims, image_slice_count, NULL),"H5Screate_simple"); if( (result=H5Sselect_hyperslab(image_max_fspc_id, H5S_SELECT_SET, image_slice_start, NULL, image_slice_count, NULL))>=0 ) { if( ( result=H5Dread(volume->imax_id, H5T_NATIVE_DOUBLE, scaling_mspc_id, image_max_fspc_id, H5P_DEFAULT,image_slice_max_buffer))<0) { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Dread"); goto cleanup; } } else { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Sselect_hyperslab"); goto cleanup; } if( (result=H5Sselect_hyperslab(image_min_fspc_id, H5S_SELECT_SET, image_slice_start, NULL, image_slice_count, NULL))>=0 ) { if( (result=H5Dread(volume->imin_id, H5T_NATIVE_DOUBLE, scaling_mspc_id, image_min_fspc_id, H5P_DEFAULT,image_slice_min_buffer))<0) { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Dread"); goto cleanup; } } else { MI_LOG_ERROR(MI2_MSG_HDF5,"H5Sselect_hyperslab"); goto cleanup; } H5Sclose(scaling_mspc_id); H5Sclose(image_max_fspc_id); } else { slice_ndims=0; total_number_of_slices=1; image_slice_max_buffer=malloc(sizeof(double)); image_slice_min_buffer=malloc(sizeof(double)); miget_volume_range( volume,image_slice_max_buffer,image_slice_min_buffer ); image_slice_length=1; for (i = 0; i < ndims; i++) { image_slice_length *= hdf_count[i]; } #ifdef _DEBUG printf("mirw_hyperslab_normalized:Real max:%f min:%f\n",*image_slice_max_buffer,*image_slice_min_buffer); #endif } #ifdef _DEBUG printf("mirw_hyperslab_normalized:Slice_ndim:%d total_number_of_slices:%d image_slice_length:%d\n",slice_ndims,total_number_of_slices,image_slice_length); printf("mirw_hyperslab_normalized:data min:%f data max:%f buffer_data_type:%d\n",data_min,data_max,buffer_data_type); #endif /*Allocate temporary Buffer*/ temp_buffer=(double*)malloc(buffer_size); if(!temp_buffer) { MI_LOG_ERROR(MI2_MSG_OUTOFMEM,buffer_size); result=MI_ERROR; goto cleanup; } if (opcode == MIRW_OP_READ) { MI_CHECK_HDF_CALL(result = H5Dread(dset_id, volume_type_id, mspc_id, fspc_id, H5P_DEFAULT, temp_buffer),"H5Dread"); if(result<0) { goto cleanup; } /*WARNING: floating point types will be normalized between 0.0 and 1.0*/ switch(buffer_data_type) { case MI_TYPE_FLOAT: APPLY_DESCALING_NORM(float,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0.0f,1.0f); break; case MI_TYPE_DOUBLE: APPLY_DESCALING_NORM(double,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0.0,1.0); break; case MI_TYPE_INT: APPLY_DESCALING_NORM(int,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,INT_MIN,INT_MAX); break; case MI_TYPE_UINT: APPLY_DESCALING_NORM(unsigned int,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,UINT_MAX); break; case MI_TYPE_SHORT: APPLY_DESCALING_NORM(short,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,SHRT_MIN,SHRT_MAX); break; case MI_TYPE_USHORT: APPLY_DESCALING_NORM(unsigned short,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,USHRT_MAX); break; case MI_TYPE_BYTE: APPLY_DESCALING_NORM(char,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,SCHAR_MIN,SCHAR_MAX); break; case MI_TYPE_UBYTE: APPLY_DESCALING_NORM(unsigned char,temp_buffer,buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,UCHAR_MAX); break; default: /*TODO: report unsupported conversion*/ result=MI_ERROR; goto cleanup; } if (n_different != 0 ) { for (i = 0; i < ndims; i++) { icount[i] = count[i]; } restructure_array(ndims, buffer, icount, H5Tget_size(buffer_type_id),volume->dim_indices, dir); /*TODO: check if we managed to restructure the array*/ result=0; } } else { /*opcode != MIRW_OP_READ*/ void *temp_buffer2; volume->is_dirty = TRUE; /* Mark as modified. */ if (n_different != 0 ) { /* Invert before calling */ for (i = 0; i < ndims; i++) { icount[volume->dim_indices[i]] = count[i]; idir[volume->dim_indices[i]] = dir[i]; /* this one was correct the original way*/ imap[volume->dim_indices[i]] = i; } } /*create temporary copy, to be destroyed*/ temp_buffer2=malloc(input_buffer_size); if(!temp_buffer2) { MI_LOG_ERROR(MI2_MSG_OUTOFMEM,input_buffer_size); result=MI_ERROR; /*TODO: error code?*/ goto cleanup; } memcpy(temp_buffer2,buffer,input_buffer_size); if (n_different != 0 ) restructure_array(ndims, temp_buffer2, icount, H5Tget_size(buffer_type_id), imap, idir); switch(buffer_data_type) { case MI_TYPE_FLOAT: APPLY_SCALING_NORM(float,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0.0,1.0); break; case MI_TYPE_DOUBLE: APPLY_SCALING_NORM(double,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0.0,1.0); break; case MI_TYPE_INT: APPLY_SCALING_NORM(int,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,(double)INT_MIN,(double)INT_MAX); break; case MI_TYPE_UINT: APPLY_SCALING_NORM(unsigned int,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,UINT_MAX); break; case MI_TYPE_SHORT: APPLY_SCALING_NORM(short,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,SHRT_MIN,SHRT_MAX); break; case MI_TYPE_USHORT: APPLY_SCALING_NORM(unsigned short,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,USHRT_MAX); break; case MI_TYPE_BYTE: APPLY_SCALING_NORM(char,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,SCHAR_MIN,SCHAR_MAX); break; case MI_TYPE_UBYTE: APPLY_SCALING_NORM(unsigned char,temp_buffer2,temp_buffer,image_slice_length,total_number_of_slices,image_slice_min_buffer,image_slice_max_buffer,volume_valid_min,volume_valid_max,data_min,data_max,0,UCHAR_MAX); break; default: /*TODO: report unsupported conversion*/ free(temp_buffer2); result=MI_ERROR; goto cleanup; } free(temp_buffer2); MI_CHECK_HDF_CALL(result = H5Dwrite(dset_id, volume_type_id, mspc_id, fspc_id, H5P_DEFAULT, temp_buffer),"H5Dwrite"); if(result<0) { goto cleanup; } } cleanup: if (volume_type_id >= 0) { H5Tclose(volume_type_id); } if (buffer_type_id >= 0) { H5Tclose(buffer_type_id); } if (mspc_id >= 0) { H5Sclose(mspc_id); } if (fspc_id >= 0) { H5Sclose(fspc_id); } if ( dset_id >=0 ) { H5Dclose(dset_id); } if(temp_buffer!=NULL) { free(temp_buffer); } if(image_slice_min_buffer!=NULL) { free(image_slice_min_buffer); } if(image_slice_max_buffer!=NULL) { free(image_slice_max_buffer); } return (result); } /** Reads the real values in the volume from the interval min through * max, mapped to the maximum representable range for the requested * data type. Float types is mapped to 0.0 1.0 */ int miget_hyperslab_normalized(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], double data_min, double data_max, void *buffer) { return mirw_hyperslab_normalized(MIRW_OP_READ, volume, buffer_data_type, start, count, data_min, data_max, buffer); } /** Writes the real values in the volume from the interval min through * max, mapped to the maximum representable range for the requested * data type. Float types is mapped to 0.0 1.0 */ int miset_hyperslab_normalized(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], double data_min, double data_max, void *buffer) { return mirw_hyperslab_normalized(MIRW_OP_WRITE, volume, buffer_data_type, start, count, data_min, data_max, buffer); } /** Get a hyperslab from the file, * converting voxel values into real values */ int miget_hyperslab_with_icv(mihandle_t volume, /**< A MINC 2.0 volume handle */ mitype_t buffer_data_type, /**< Output datatype */ const misize_t start[], /**< Start coordinates */ const misize_t count[], /**< Lengths of edges */ void *buffer) /**< Output memory buffer */ { return mirw_hyperslab_icv(MIRW_OP_READ, volume, buffer_data_type, start, count,buffer); } /** Write a hyperslab to the file, converting real values into voxel values */ int miset_hyperslab_with_icv(mihandle_t volume, /**< A MINC 2.0 volume handle */ mitype_t buffer_data_type, /**< Output datatype */ const misize_t start[], /**< Start coordinates */ const misize_t count[], /**< Lengths of edges */ void *buffer) /**< Output memory buffer */ { return mirw_hyperslab_icv(MIRW_OP_WRITE,volume,buffer_data_type,start,count,buffer); } /** Read a hyperslab from the file into the preallocated buffer, * converting from the stored "voxel" data range to the desired * "real" (float or double) data range. */ int miget_real_value_hyperslab(mihandle_t volume, /**< A MINC 2.0 volume handle */ mitype_t buffer_data_type, /**< Output datatype */ const misize_t start[], /**< Start coordinates */ const misize_t count[], /**< Lengths of edges */ void *buffer) /**< Output memory buffer */ { return mirw_hyperslab_icv(MIRW_OP_READ, volume, buffer_data_type, start, count, (void *) buffer); } /** Write a hyperslab to the file from the preallocated buffer, * converting from the stored "voxel" data range to the desired * "real" (float or double) data range. */ int miset_real_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer) { return mirw_hyperslab_icv(MIRW_OP_WRITE, volume, buffer_data_type, start, count, (void *) buffer); } /** Read a hyperslab from the file into the preallocated buffer, * with no range conversions or normalization. Type conversions will * be performed if necessary. */ int miget_voxel_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer) { return mirw_hyperslab_raw(MIRW_OP_READ, volume, buffer_data_type, start, count, buffer); } /** Write a hyperslab to the file from the preallocated buffer, * with no range conversions or normalization. Type conversions will * be performed if necessary. */ int miset_voxel_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer) { return mirw_hyperslab_raw(MIRW_OP_WRITE, volume, buffer_data_type, start, count, (void *) buffer); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/label.c000066400000000000000000000131711257462267400174270ustar00rootroot00000000000000 /** * \file label.c * \brief MINC 2.0 Label functions * \author Bert Vincent * * This small set of three functions are intended to allow for the * definition of labeled, or enumerated, volumes. * * Labeled volumes must have been created with the class MI_CLASS_LABEL, * and with any integer subtype. * ************************************************************************/ #include #include #include "minc2.h" #include "minc2_private.h" #define MI_LABEL_MAX 128 static int miswap2(unsigned short tmp) { unsigned char *x = (unsigned char *) &tmp; unsigned char t = x[0]; x[0] = x[1]; x[1] = t; return (tmp); } static int miswap4(unsigned int tmp) { unsigned char *x = (unsigned char *) &tmp; unsigned char t = x[0]; x[0] = x[3]; x[3] = t; t = x[1]; x[1] = x[2]; x[2] = t; return (tmp); } /** * This function associates a label name with an integer value for the given * volume. Functions which read and write voxel values will read/write * in integer values, and must call miget_label_name() to discover the * descriptive text string which corresponds to the integer value. */ int midefine_label(mihandle_t volume, int value, const char *name) { int result; if (volume == NULL || name == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to use null volume or variable"); } if (strlen(name) > MI_LABEL_MAX) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Label name is too long"); } if (volume->volume_class != MI_CLASS_LABEL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume class is not label"); } if (volume->ftype_id <= 0 || volume->mtype_id <= 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume not initialized"); } MI_CHECK_HDF_CALL_RET(result = H5Tenum_insert(volume->mtype_id, name, &value),"H5Tenum_insert"); /* We might have to swap these values before adding them to * the file type. * * COOL! the whole purpose of HDF5 being machine independent is defeated here! */ if (H5Tget_order(volume->ftype_id) != H5Tget_order(volume->mtype_id)) { switch (H5Tget_size(volume->ftype_id)) { case 2: value = miswap2((unsigned short) value); break; case 4: value = miswap4((unsigned int) value); break; } } MI_CHECK_HDF_CALL_RET(result = H5Tenum_insert(volume->ftype_id, name, &value),"H5Tenum_insert"); return (MI_NOERROR); } /** * For a labelled volume, this function retrieves the text name * associated with a given integer value. * * The name pointer returned must be freed by calling mifree_name(). */ int miget_label_name(mihandle_t volume, int value, char **name) { int result; if (volume == NULL || name == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to use null volume or variable"); } if (volume->volume_class != MI_CLASS_LABEL) { MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume class is not label"); } if (volume->mtype_id <= 0) { MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume is not initialized"); } *name = malloc(MI_LABEL_MAX); if (*name == NULL) { return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,MI_LABEL_MAX); } H5E_BEGIN_TRY { result = H5Tenum_nameof(volume->mtype_id, &value, *name, MI_LABEL_MAX); } H5E_END_TRY; MI_CHECK_HDF_CALL_RET(result,"H5Tenum_nameof"); return (MI_NOERROR); } /** * This function is the inverse of miget_label_name(). It is called to determine * what integer value, if any, corresponds to the given text string. */ int miget_label_value(mihandle_t volume, const char *name, int *value_ptr) { int result; if (volume == NULL || name == NULL || value_ptr == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to use null volume or variable"); } if (volume->volume_class != MI_CLASS_LABEL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume class is not label"); } if (volume->mtype_id <= 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume is not initialized"); } H5E_BEGIN_TRY { result = H5Tenum_valueof(volume->mtype_id, name, value_ptr); } H5E_END_TRY; MI_CHECK_HDF_CALL_RET(result,"H5Tenum_valueof"); return (MI_NOERROR); } /** * This function returns the number of defined labels, if any, or zero. */ int miget_number_of_defined_labels(mihandle_t volume, int *number_of_labels) { int result; if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to use null volume"); } if (volume->volume_class != MI_CLASS_LABEL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume class is not label"); } if (volume->mtype_id <= 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume is not initialized"); } H5E_BEGIN_TRY { result = H5Tget_nmembers(volume->mtype_id); } H5E_END_TRY; MI_CHECK_HDF_CALL_RET(result,"H5Tget_nmembers"); *number_of_labels = result; return (MI_NOERROR); } /** * This function returns the label value associated with an index (0,1,...) */ int miget_label_value_by_index(mihandle_t volume, int idx, int *value) { int result; if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to use null volume"); } if (volume->volume_class != MI_CLASS_LABEL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume class is not label"); } if (volume->mtype_id <= 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Volume is not initialized"); } H5E_BEGIN_TRY { result = H5Tget_member_value(volume->mtype_id,idx,value); } H5E_END_TRY; MI_CHECK_HDF_CALL_RET(result,"H5Tget_member_value"); return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/m2util.c000066400000000000000000001477201257462267400175740ustar00rootroot00000000000000/** \file m2util.c * \brief MINC 2.0 Private utility functions * \author Leila Baghdadi, Bert Vincent * ************************************************************************/ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "minc2.h" #include "minc2_private.h" #ifdef _MSC_VER double rint(double v) { return floor(v+0.5); } #endif /*! Convert a MINC 2 datatype into a HDF5 datatype. Actually returns a copy * of the datatype, so the returned value must be explicitly freed with a * call to H5Tclose(). * \param mitype The MINC 2 data type to convert * \param is_native Convert to native type if TRUE */ hid_t mitype_to_hdftype ( mitype_t mitype, int is_native ) { hid_t type_id; if ( is_native ) { /* Native types are in the byte-ordering of the native system. * They are appropriate for the "in-memory" types for data. */ switch ( mitype ) { case MI_TYPE_BYTE: type_id = H5Tcopy ( H5T_NATIVE_SCHAR ); break; case MI_TYPE_SHORT: type_id = H5Tcopy ( H5T_NATIVE_SHORT ); break; case MI_TYPE_INT: type_id = H5Tcopy ( H5T_NATIVE_INT ); break; case MI_TYPE_FLOAT: type_id = H5Tcopy ( H5T_NATIVE_FLOAT ); break; case MI_TYPE_DOUBLE: type_id = H5Tcopy ( H5T_NATIVE_DOUBLE ); break; case MI_TYPE_UBYTE: type_id = H5Tcopy ( H5T_NATIVE_UCHAR ); break; case MI_TYPE_USHORT: type_id = H5Tcopy ( H5T_NATIVE_USHORT ); break; case MI_TYPE_UINT: type_id = H5Tcopy ( H5T_NATIVE_UINT ); break; case MI_TYPE_SCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 4 ); H5Tinsert ( type_id, "real", 0, H5T_NATIVE_SHORT ); H5Tinsert ( type_id, "imag", 2, H5T_NATIVE_SHORT ); break; case MI_TYPE_ICOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 8 ); H5Tinsert ( type_id, "real", 0, H5T_NATIVE_INT ); H5Tinsert ( type_id, "imag", 4, H5T_NATIVE_INT ); break; case MI_TYPE_FCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 8 ); H5Tinsert ( type_id, "real", 0, H5T_NATIVE_FLOAT ); H5Tinsert ( type_id, "imag", 4, H5T_NATIVE_FLOAT ); break; case MI_TYPE_DCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 16 ); H5Tinsert ( type_id, "real", 0, H5T_NATIVE_DOUBLE ); H5Tinsert ( type_id, "imag", 8, H5T_NATIVE_DOUBLE ); break; default: type_id = H5Tcopy ( mitype ); /* It is a standard HDF type handle? */ break; } } else { /* The non-native types are standardized to be in * "little-endian" form. That's an arbitrary decision which * could certainly be debated. */ switch ( mitype ) { case MI_TYPE_BYTE: type_id = H5Tcopy ( H5T_STD_I8LE ); break; case MI_TYPE_SHORT: type_id = H5Tcopy ( H5T_STD_I16LE ); break; case MI_TYPE_INT: type_id = H5Tcopy ( H5T_STD_I32LE ); break; case MI_TYPE_FLOAT: type_id = H5Tcopy ( H5T_IEEE_F32LE ); break; case MI_TYPE_DOUBLE: type_id = H5Tcopy ( H5T_IEEE_F64LE ); break; case MI_TYPE_UBYTE: type_id = H5Tcopy ( H5T_STD_U8LE ); break; case MI_TYPE_USHORT: type_id = H5Tcopy ( H5T_STD_U16LE ); break; case MI_TYPE_UINT: type_id = H5Tcopy ( H5T_STD_U32LE ); break; case MI_TYPE_SCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 4 ); H5Tinsert ( type_id, "real", 0, H5T_STD_I16LE ); H5Tinsert ( type_id, "imag", 2, H5T_STD_I16LE ); break; case MI_TYPE_ICOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 8 ); H5Tinsert ( type_id, "real", 0, H5T_STD_I32LE ); H5Tinsert ( type_id, "imag", 4, H5T_STD_I32LE ); break; case MI_TYPE_FCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 8 ); H5Tinsert ( type_id, "real", 0, H5T_IEEE_F32LE ); H5Tinsert ( type_id, "imag", 4, H5T_IEEE_F32LE ); break; case MI_TYPE_DCOMPLEX: type_id = H5Tcreate ( H5T_COMPOUND, 16 ); H5Tinsert ( type_id, "real", 0, H5T_IEEE_F64LE ); H5Tinsert ( type_id, "imag", 8, H5T_IEEE_F64LE ); break; default: type_id = H5Tcopy ( mitype ); /* It is a standard HDF type handle? */ break; } } return ( type_id ); } /** get byte size of a specific data type */ int mitype_len ( mitype_t mitype ) { switch ( mitype ) { case MI_TYPE_BYTE: case MI_TYPE_UBYTE: return 1; case MI_TYPE_USHORT: case MI_TYPE_SHORT: return 2; case MI_TYPE_INT: case MI_TYPE_UINT: return 4; case MI_TYPE_FLOAT: return 4; case MI_TYPE_DOUBLE: return 8; case MI_TYPE_SCOMPLEX: return 2 * 2; case MI_TYPE_ICOMPLEX: return 2 * 4; case MI_TYPE_FCOMPLEX: return 2 * 4; case MI_TYPE_DCOMPLEX: return 2 * 8; default: break; } fprintf ( stderr, _ ( "Unknown type %d" ), ( int ) mitype ); return ( -1 ); } /** get string identifying if datatype is signed */ const char * mitype_sign ( mitype_t mitype ) { switch ( mitype ) { case MI_TYPE_BYTE: case MI_TYPE_SHORT: case MI_TYPE_INT: case MI_TYPE_FLOAT: case MI_TYPE_DOUBLE: case MI_TYPE_SCOMPLEX: case MI_TYPE_ICOMPLEX: case MI_TYPE_FCOMPLEX: case MI_TYPE_DCOMPLEX: return MI_SIGNED; case MI_TYPE_UBYTE: case MI_TYPE_USHORT: case MI_TYPE_UINT: return MI_UNSIGNED; default: break; } fprintf ( stderr, _ ( "Unknown type %d" ), ( int ) mitype ); return MI_SIGNED; } /*! Convert a MINC 2 datatype into a NetCDF datatype. * \param mitype The MINC 2 data type to convert * \param is_signed Set to TRUE if the data type is signed, FALSE if unsigned. */ /* TODO:CNV */ #if 0 int mitype_to_nctype ( mitype_t mitype, int *is_signed ) { int nctype; *is_signed = 1; /* Assume signed by default. */ switch ( mitype ) { case MI_TYPE_BYTE: nctype = NC_BYTE; break; case MI_TYPE_SHORT: nctype = NC_SHORT; break; case MI_TYPE_INT: nctype = NC_INT; break; case MI_TYPE_FLOAT: nctype = NC_FLOAT; break; case MI_TYPE_DOUBLE: nctype = NC_DOUBLE; break; case MI_TYPE_UBYTE: nctype = NC_BYTE; *is_signed = 0; break; case MI_TYPE_USHORT: nctype = NC_SHORT; *is_signed = 0; break; case MI_TYPE_UINT: nctype = NC_INT; *is_signed = 1; break; default: nctype = -1; /* ERROR!! */ break; } return ( nctype ); } #endif /*TODO:CNV*/ /*! Return the group or dataset ID of the last item in a "path", * specified like a UNIX pathname /black/white/red etc. * \param file_id The HDF5 handle of the file (or group) at which to start * the search. * \param path A string consisting of a slash-separated list of * HDF5 groupnames */ hid_t midescend_path ( hid_t file_id, const char *path ) { hid_t tmp_id; /* Put H5E_BEGIN_TRY/H5E_END_TRY around this to avoid the overzealous * automatic error reporting of HDF5. */ H5E_BEGIN_TRY { tmp_id = H5Dopen1 ( file_id, path ); /* If the dataset open fails, try opening the object as a group. */ if ( tmp_id < 0 ) { tmp_id = H5Gopen1 ( file_id, path ); } } H5E_END_TRY; return ( tmp_id ); } int miset_attr_at_loc ( hid_t hdf_loc, const char *name, mitype_t data_type, size_t length, const void *values ) { hid_t ftyp_id=-1; hid_t mtyp_id=-1; hid_t spc_id=-1; hid_t hdf_attr=-1; hsize_t hdf_len; int status=MI_ERROR; H5E_BEGIN_TRY { /* Delete attribute if it already exists. */ H5Adelete ( hdf_loc, name ); } H5E_END_TRY; switch ( data_type ) { case MI_TYPE_INT: ftyp_id = H5Tcopy ( H5T_STD_I32LE ); mtyp_id = H5Tcopy ( H5T_NATIVE_INT ); break; case MI_TYPE_FLOAT: ftyp_id = H5Tcopy ( H5T_IEEE_F32LE ); mtyp_id = H5Tcopy ( H5T_NATIVE_FLOAT ); break; case MI_TYPE_DOUBLE: ftyp_id = H5Tcopy ( H5T_IEEE_F64LE ); mtyp_id = H5Tcopy ( H5T_NATIVE_DOUBLE ); break; case MI_TYPE_STRING: ftyp_id = H5Tcopy ( H5T_C_S1 ); H5Tset_size ( ftyp_id, length ); mtyp_id = H5Tcopy ( ftyp_id ); length = 1; /* Apparent length is one. */ break; default: return MI_LOG_ERROR(MI2_MSG_GENERIC,"Unsupported attribute type"); } if ( length == 1 ) { if( (spc_id = H5Screate ( H5S_SCALAR ))<0) goto cleanup; } else { hdf_len = ( hsize_t ) length; if( (spc_id = H5Screate_simple ( 1, &hdf_len, NULL ))<0 ) goto cleanup; } if((hdf_attr = H5Acreate2 ( hdf_loc, name, ftyp_id, spc_id, H5P_DEFAULT, H5P_DEFAULT ))<0) goto cleanup; if(H5Awrite ( hdf_attr, mtyp_id, values ) < 0) goto cleanup; status=MI_NOERROR; cleanup: if(hdf_attr >=0 ) H5Aclose ( hdf_attr ); if(ftyp_id >=0 ) H5Tclose ( ftyp_id ); if(mtyp_id >=0 ) H5Tclose ( mtyp_id ); if(spc_id >=0 ) H5Sclose ( spc_id ); return status; } /** Set an attribute from a minc file */ int miset_attribute ( mihandle_t volume, const char *path, const char *name, mitype_t data_type, size_t length, const void *values ) { hid_t hdf_file; hid_t hdf_loc; /* Get a handle to the actual HDF file */ hdf_file = volume->hdf_id; if ( hdf_file < 0 ) { return ( MI_ERROR ); } /* Search through the path, descending into each group encountered. */ hdf_loc = midescend_path ( hdf_file, path ); if ( hdf_loc < 0 ) { return ( MI_ERROR ); } miset_attr_at_loc ( hdf_loc, name, data_type, length, values ); /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_loc ) == H5I_GROUP ) { H5Gclose ( hdf_loc ); } else { H5Dclose ( hdf_loc ); } return ( MI_NOERROR ); } /** Get a double attribute from a minc file */ int miget_attribute ( mihandle_t volume, const char *path, const char *name, mitype_t data_type, size_t length, void *values ) { hid_t hdf_file; hid_t hdf_loc; hid_t mtyp_id = -1; /* Parameter type */ hid_t spc_id = -1; hid_t hdf_attr = -1; int status = MI_ERROR; /* Guilty until proven innocent */ /* Get a handle to the actual HDF file */ hdf_file = volume->hdf_id; if ( hdf_file < 0 ) { return ( MI_ERROR ); } /* Find the group or dataset referenced by the path. */ hdf_loc = midescend_path ( hdf_file, path ); if ( hdf_loc < 0 ) { return ( MI_ERROR ); } H5E_BEGIN_TRY { hdf_attr = H5Aopen_name ( hdf_loc, name ); } H5E_END_TRY; if ( hdf_attr < 0 ) { goto cleanup; } switch ( data_type ) { case MI_TYPE_INT: mtyp_id = H5Tcopy ( H5T_NATIVE_INT ); break; case MI_TYPE_UINT: mtyp_id = H5Tcopy ( H5T_NATIVE_UINT ); break; case MI_TYPE_FLOAT: mtyp_id = H5Tcopy ( H5T_NATIVE_FLOAT ); break; case MI_TYPE_DOUBLE: mtyp_id = H5Tcopy ( H5T_NATIVE_DOUBLE ); break; case MI_TYPE_STRING: mtyp_id = H5Tcopy ( H5T_C_S1 ); H5Tset_size ( mtyp_id, length ); break; default: goto cleanup; } spc_id = H5Aget_space ( hdf_attr ); if ( spc_id < 0 ) { goto cleanup; } /* If we're retrieving a vector, make certain the length passed into this * function is sufficient. */ if ( H5Sget_simple_extent_ndims ( spc_id ) == 1 ) { hsize_t hdf_dims[1]; H5Sget_simple_extent_dims ( spc_id, hdf_dims, NULL ); if ( length < hdf_dims[0] ) { goto cleanup; } } if ( H5Aread ( hdf_attr, mtyp_id, values ) < 0 ) { goto cleanup; } status = MI_NOERROR; /* We succeeded! */ /* Be certain that the string is null-terminated. */ if ( data_type == MI_TYPE_STRING ) { hid_t atype; /* Attribute type */ size_t alength; atype = H5Aget_type ( hdf_attr ); alength = H5Tget_size ( atype ); ( ( char * ) values ) [alength] = '\0'; H5Tclose ( atype ); } cleanup: if ( hdf_attr >= 0 ) { H5Aclose ( hdf_attr ); } if ( mtyp_id >= 0 ) { H5Tclose ( mtyp_id ); } if ( spc_id >= 0 ) { H5Sclose ( spc_id ); } if ( hdf_loc >= 0 ) { /* The hdf_loc identifier could be a group or a dataset. */ if ( H5Iget_type ( hdf_loc ) == H5I_GROUP ) { H5Gclose ( hdf_loc ); } else { H5Dclose ( hdf_loc ); } } return ( status ); } /* Get the mapping from spatial dimension - x, y, z - to file dimensions * and vice-versa. */ /*TODO: convert to MINC2 API?*/ #if 0 void mifind_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]) { int imgid; int dim[MI2_MAX_VAR_DIMS]; int idim, ndims, world_index; char dimname[MI2_MAX_DIM_NAME]; /* Set default values */ for (idim = 0; idim < 3; idim++) space_to_dim[idim] = -1; for (idim = 0; idim < MI2_MAX_VAR_DIMS; idim++) dim_to_space[idim] = -1; /* Get the dimension ids for the image variable */ imgid = ncvarid(mincid, MIimage); ncvarinq(mincid, imgid, NULL, NULL, &ndims, dim, NULL); /* Loop over them to find the spatial ones */ for (idim = 0; idim < ndims; idim++) { /* Get the name and check that this is a spatial dimension */ ncdiminq(mincid, dim[idim], dimname, NULL); if (!strcmp(dimname, MIxspace)) { world_index = MI2_X; } else if (!strcmp(dimname, MIyspace)) { world_index = MI2_Y; } else if (!strcmp(dimname, MIzspace)) { world_index = MI2_Z; } else { continue; } space_to_dim[world_index] = idim; dim_to_space[idim] = world_index; } } #endif /** Get the voxel to world transform (for column vectors) */ void miget_voxel_to_world ( mihandle_t volume, mi_lin_xfm_t voxel_to_world ) { int i; int j; int k; double dircos[MI2_3D]; double step; double start; /* Zero the matrix */ for ( i = 0; i < MI2_LIN_XFM_SIZE; i++ ) { for ( j = 0; j < MI2_LIN_XFM_SIZE; j++ ) { voxel_to_world[i][j] = 0.0; } voxel_to_world[i][i] = 1.0; } for ( j = 0; j < volume->number_of_dims; j++ ) { midimhandle_t hdim = volume->dim_handles[j]; if ( hdim->dim_class == MI_DIMCLASS_SPATIAL || hdim->dim_class == MI_DIMCLASS_SFREQUENCY ) { k = hdim->world_index; } else { continue; } start = hdim->start; step = hdim->step; dircos[0] = hdim->direction_cosines[0]; dircos[1] = hdim->direction_cosines[1]; dircos[2] = hdim->direction_cosines[2]; minormalize_vector ( dircos ); /* Put them in the matrix */ for ( i = 0; i < MI2_3D; i++ ) { voxel_to_world[i][k] = step * dircos[i]; voxel_to_world[i][MI2_3D] += start * dircos[i]; } } } /** Normalize a 3 element vector */ void minormalize_vector ( double vector[MI2_3D] ) { int i; double magnitude; magnitude = 0.0; for ( i = 0; i < MI2_3D; i++ ) { magnitude += ( vector[i] * vector[i] ); } magnitude = sqrt ( magnitude ); if ( magnitude > 0.0 ) { for ( i = 0; i < MI2_3D; i++ ) { vector[i] /= magnitude; } } } /** Transforms a coordinate through a linear transform */ void mitransform_coord ( double out_coord[], mi_lin_xfm_t transform, const double in_coord[] ) { int i, j; double in_homogeneous[MI2_3D + 1]; double out_homogeneous[MI2_3D + 1]; for ( i = 0; i < MI2_3D; i++ ) { in_homogeneous[i] = in_coord[i]; } in_homogeneous[MI2_3D] = 1.0; for ( i = 0; i < MI2_3D + 1; i++ ) { out_homogeneous[i] = 0.0; for ( j = 0; j < MI2_3D + 1; j++ ) { out_homogeneous[i] += transform[i][j] * in_homogeneous[j]; } } #if 0 printf ( "W = %f\n", out_homogeneous[3] ); #endif /* 0 */ for ( i = 0; i < MI2_3D; i++ ) { out_coord[i] = out_homogeneous[i]; } } /** For conversions from double to integer, rounding may be performed * by setting this variable to non-zero. * However, at present, no API is available to control this. */ static int rounding_enabled = FALSE; static void miswap8 ( unsigned char *tmp_ptr ) { unsigned char x; x = tmp_ptr[0]; tmp_ptr[0] = tmp_ptr[7]; tmp_ptr[7] = x; x = tmp_ptr[1]; tmp_ptr[1] = tmp_ptr[6]; tmp_ptr[6] = x; x = tmp_ptr[2]; tmp_ptr[2] = tmp_ptr[5]; tmp_ptr[5] = x; x = tmp_ptr[3]; tmp_ptr[3] = tmp_ptr[4]; tmp_ptr[4] = x; } static void miswap4 ( unsigned char *tmp_ptr ) { unsigned char x; x = tmp_ptr[0]; tmp_ptr[0] = tmp_ptr[3]; tmp_ptr[3] = x; x = tmp_ptr[1]; tmp_ptr[1] = tmp_ptr[2]; tmp_ptr[2] = x; } static void miswap2 ( unsigned char *tmp_ptr ) { unsigned char x; x = tmp_ptr[0]; tmp_ptr[0] = tmp_ptr[1]; tmp_ptr[1] = x; } /** Generic HDF5 integer-to-double converter. */ static herr_t mi2_int_to_dbl ( hid_t src_id, hid_t dst_id, H5T_cdata_t *cdata, size_t nelements, size_t buf_stride, size_t bkg_stride, void *buf_ptr, void *bkg_ptr, hid_t dset_xfer_plist ) { unsigned char *dst_ptr; unsigned char *src_ptr; size_t src_nb; size_t dst_nb; H5T_sign_t src_sg; double t; size_t dst_cnt; size_t src_cnt; int src_swap; int dst_swap; switch ( cdata->command ) { case H5T_CONV_INIT: cdata->need_bkg = H5T_BKG_NO; src_nb = H5Tget_size ( src_id ); if ( src_nb != 1 && src_nb != 2 && src_nb != 4 ) { return ( -1 ); } dst_nb = H5Tget_size ( dst_id ); if ( dst_nb != 8 ) { return ( -1 ); } break; case H5T_CONV_CONV: src_nb = H5Tget_size ( src_id ); src_sg = H5Tget_sign ( src_id ); dst_nb = H5Tget_size ( dst_id ); if ( buf_stride == 0 ) { dst_cnt = dst_nb; src_cnt = src_nb; } else { dst_cnt = buf_stride; src_cnt = buf_stride; } /* Convert starting from "far side" of buffer... (Hope this works!) */ dst_ptr = ( ( unsigned char * ) buf_ptr ) + ( ( nelements - 1 ) * dst_nb ); src_ptr = ( ( unsigned char * ) buf_ptr ) + ( ( nelements - 1 ) * src_nb ); if ( H5Tget_order ( H5T_NATIVE_INT ) != H5Tget_order ( src_id ) ) { src_swap = 1; } else { src_swap = 0; } if ( H5Tget_order ( H5T_NATIVE_DOUBLE ) != H5Tget_order ( dst_id ) ) { dst_swap = 1; } else { dst_swap = 0; } if ( src_sg == H5T_SGN_2 ) { switch ( src_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap4 ( src_ptr ); } t = * ( int * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap2 ( src_ptr ); } t = * ( short * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; case 1: while ( nelements-- > 0 ) { t = * ( char * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; default: /* Should not happen! */ break; } } else { switch ( src_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap4 ( src_ptr ); } t = * ( unsigned int * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap2 ( src_ptr ); } t = * ( unsigned short * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; case 1: while ( nelements-- > 0 ) { t = * ( unsigned char * ) src_ptr; * ( double * ) dst_ptr = t; if ( dst_swap ) { miswap8 ( dst_ptr ); } src_ptr -= src_cnt; dst_ptr -= dst_cnt; } break; default: /* Should not happen! */ break; } } break; case H5T_CONV_FREE: break; default: /* Unknown command */ return ( -1 ); } return ( 0 ); } /** Generic HDF5 double-to-integer converter. */ static herr_t mi2_dbl_to_int ( hid_t src_id, hid_t dst_id, H5T_cdata_t *cdata, size_t nelements, size_t buf_stride, size_t bkg_stride, void *buf_ptr, void *bkg_ptr, hid_t dset_xfer_plist ) { unsigned char *dst_ptr; unsigned char *src_ptr; size_t src_nb; size_t dst_nb; H5T_sign_t dst_sg; double t; size_t dst_cnt; size_t src_cnt; int src_swap; int dst_swap; switch ( cdata->command ) { case H5T_CONV_INIT: cdata->need_bkg = H5T_BKG_NO; /* Verify that we can handle this conversion. */ src_nb = H5Tget_size ( src_id ); if ( src_nb != 8 ) { return ( -1 ); } dst_nb = H5Tget_size ( dst_id ); if ( dst_nb != 4 && dst_nb != 2 && dst_nb != 1 ) { return ( -1 ); } break; case H5T_CONV_CONV: dst_nb = H5Tget_size ( dst_id ); dst_sg = H5Tget_sign ( dst_id ); src_nb = H5Tget_size ( src_id ); dst_ptr = ( unsigned char * ) buf_ptr; src_ptr = ( unsigned char * ) buf_ptr; if ( H5Tget_order ( H5T_NATIVE_DOUBLE ) != H5Tget_order ( src_id ) ) { src_swap = 1; } else { src_swap = 0; } if ( H5Tget_order ( H5T_NATIVE_INT ) != H5Tget_order ( dst_id ) ) { dst_swap = 1; } else { dst_swap = 0; } /* The logic of HDF5 seems to be that if a stride is specified, * both the source and destination pointers should advance by that * amount. This seems wrong to me, but I've examined the HDF5 sources * and that's what their own type converters do. */ if ( buf_stride == 0 ) { dst_cnt = dst_nb; src_cnt = src_nb; } else { dst_cnt = buf_stride; src_cnt = buf_stride; } if ( rounding_enabled ) { if ( dst_sg == H5T_SGN_2 ) { switch ( dst_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > INT_MAX ) { t = INT_MAX; } else if ( t < INT_MIN ) { t = INT_MIN; } * ( ( int * ) dst_ptr ) = ( int ) t; if ( dst_swap ) { miswap4 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > SHRT_MAX ) { t = SHRT_MAX; } else if ( t < SHRT_MIN ) { t = SHRT_MIN; } * ( ( short * ) dst_ptr ) = ( short ) t; if ( dst_swap ) { miswap2 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 1: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > CHAR_MAX ) { t = CHAR_MAX; } else if ( t < CHAR_MIN ) { t = CHAR_MIN; } * ( ( char * ) src_ptr ) = ( char ) t; dst_ptr += dst_cnt; src_ptr += src_cnt; } break; default: /* Can't handle this! */ break; } } else { switch ( dst_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > UINT_MAX ) { t = UINT_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned int * ) dst_ptr ) = ( unsigned int ) t; if ( dst_swap ) { miswap4 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > USHRT_MAX ) { t = USHRT_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned short * ) dst_ptr ) = ( unsigned short ) t; if ( dst_swap ) { miswap2 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 1: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = rint ( * ( double * ) src_ptr ); if ( t > UCHAR_MAX ) { t = UCHAR_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned char * ) dst_ptr ) = ( unsigned char ) t; dst_ptr += dst_cnt; src_ptr += src_cnt; } break; default: /* Can't handle any other values */ break; } } } else { if ( dst_sg == H5T_SGN_2 ) { switch ( dst_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > INT_MAX ) { t = INT_MAX; } else if ( t < INT_MIN ) { t = INT_MIN; } * ( ( int * ) dst_ptr ) = ( int ) t; if ( dst_swap ) { miswap4 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > SHRT_MAX ) { t = SHRT_MAX; } else if ( t < SHRT_MIN ) { t = SHRT_MIN; } * ( ( short * ) dst_ptr ) = ( short ) t; if ( dst_swap ) { miswap4 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 1: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > CHAR_MAX ) { t = CHAR_MAX; } else if ( t < CHAR_MIN ) { t = CHAR_MIN; } * ( ( char * ) src_ptr ) = ( char ) t; dst_ptr += dst_cnt; src_ptr += src_cnt; } break; default: /* Can't handle this! */ break; } } else { switch ( dst_nb ) { case 4: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > UINT_MAX ) { t = UINT_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned int * ) dst_ptr ) = ( unsigned int ) t; if ( dst_swap ) { miswap4 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 2: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > USHRT_MAX ) { t = USHRT_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned short * ) dst_ptr ) = ( unsigned short ) t; if ( dst_swap ) { miswap2 ( dst_ptr ); } dst_ptr += dst_cnt; src_ptr += src_cnt; } break; case 1: while ( nelements-- > 0 ) { if ( src_swap ) { miswap8 ( src_ptr ); } t = ( int ) ( * ( double * ) src_ptr ); if ( t > UCHAR_MAX ) { t = UCHAR_MAX; } else if ( t < 0 ) { t = 0; } * ( ( unsigned char * ) dst_ptr ) = ( unsigned char ) t; dst_ptr += dst_cnt; src_ptr += src_cnt; } break; default: /* Can't handle any other values */ break; } } } break; case H5T_CONV_FREE: break; default: /* Unknown command */ return ( -1 ); } return ( 0 ); } /** Initialize some critical pieces of the library. For now all this does is install the double-to-integer and integer-to-double conversion functions. */ void miinit ( void ) { MI_CHECK_HDF_CALL(H5Tregister ( H5T_PERS_SOFT, "i2d", H5T_NATIVE_INT, H5T_NATIVE_DOUBLE, mi2_int_to_dbl ),"H5Tregister") MI_CHECK_HDF_CALL(H5Tregister ( H5T_PERS_SOFT, "d2i", H5T_NATIVE_DOUBLE, H5T_NATIVE_INT, mi2_dbl_to_int ),"H5Tregister") } /** HDF5 type conversion function for converting an arbitrary integer type to * an arbitrary enumerated type. The beauty part of this is that it is * not necessary to actually perform any real conversion! */ static herr_t mi2_null_conv ( hid_t src_id, hid_t dst_id, H5T_cdata_t *cdata, size_t nelements, size_t buf_stride, size_t bkg_stride, void *buf_ptr, void *bkg_ptr, hid_t dset_xfer_plist ) { switch ( cdata->command ) { case H5T_CONV_INIT: break; case H5T_CONV_CONV: break; case H5T_CONV_FREE: break; default: /* Unknown command */ return ( -1 ); } return ( 0 ); } /** This function should be called when a labeled volume is created or opened in order to facilitate conversions from the integer to the enumerated type. */ void miinit_enum ( hid_t type_id ) { H5Tregister ( H5T_PERS_SOFT, "i2e", H5T_NATIVE_INT, type_id, mi2_null_conv ); H5Tregister ( H5T_PERS_SOFT, "e2i", type_id, H5T_NATIVE_INT, mi2_null_conv ); H5Tregister ( H5T_PERS_SOFT, "d2e", H5T_NATIVE_DOUBLE, type_id, mi2_dbl_to_int ); H5Tregister ( H5T_PERS_SOFT, "e2d", type_id, H5T_NATIVE_DOUBLE, mi2_int_to_dbl ); } int minc_create_thumbnail ( mihandle_t volume, int grp ) { char path[MI2_MAX_PATH]; hid_t grp_id; /* Don't handle negative or overly large numbers! */ if ( grp <= 0 || grp > MI2_MAX_RESOLUTION_GROUP ) { return ( MI_ERROR ); } sprintf ( path, MI_ROOT_PATH "/image/%d", grp ); grp_id = H5Gcreate2 ( volume->hdf_id, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT ); if ( grp_id < 0 ) { return ( MI_ERROR ); } H5Gclose ( grp_id ); return ( MI_NOERROR ); } /** Function to downsample a single slice of an image. * \param in_ptr the 3D input slice, scale x isize[1] x isize[2] * \param out_ptr the 2D output slice, osize[1] x osize[2] */ static void midownsample_slice ( double *in_ptr, double *out_ptr, hsize_t isize[], hsize_t osize[], int scale ) { hsize_t j, k; int x, y, z; double d; double total; total = (double) scale * (double) scale * (double) scale; /* These two loops iterate over all of the voxels in the 2D output * image. */ for ( j = 0; j < osize[1]; j++ ) { for ( k = 0; k < osize[2]; k++ ) { /* The three inner loops iterate all scale^3 * voxels in the input image which will be averaged * to form the output image. */ d = 0; for ( x = 0; x < scale; x++ ) { for ( y = 0; y < scale; y++ ) { for ( z = 0; z < scale; z++ ) { size_t x1, y1, z1; double t; x1 = x; y1 = y + ( j * scale ); z1 = z + ( k * scale ); t = in_ptr[ ( ( x1 * isize[1] ) + y1 ) * isize[2] + z1]; d += t; } } } d /= total; out_ptr[ ( j * osize[2] ) + k] = d; } } } /** Convert the hyperslab from real to voxel values, calculating and * returning the minimum and maximum real values for the slab. This * could form the basis for a public function one day, but for now it * is considered private. */ static void miconvert_hyperslab_to_voxel ( mihandle_t volume, hsize_t start[], hsize_t count[], double *slab_ptr, double *max_ptr, double *min_ptr ) { /* This code is not intended to be a general hyperslab-to-voxel * converter yet. That is why it is not public. */ double real_min, real_max; /* Minimum and maximum values */ hsize_t index; hsize_t total; double voxel_range, voxel_offset; double real_range, real_offset; int i; double tmp_val; real_min = DBL_MAX; real_max = -DBL_MAX; total = 1; for ( i = 0; i < volume->number_of_dims; i++ ) { total *= count[i]; } /* Find the global minimum and maximum for this hyperslab. */ for ( index = 0; index < total; index++ ) { tmp_val = slab_ptr[index]; if ( tmp_val > real_max ) { real_max = tmp_val; } if ( tmp_val < real_min ) { real_min = tmp_val; } } voxel_range = volume->valid_max - volume->valid_min; voxel_offset = volume->valid_min; real_range = real_max - real_min; real_offset = real_min; for ( index = 0; index < total; index++ ) { tmp_val = slab_ptr[index]; tmp_val = ( tmp_val - real_offset ) / real_range; tmp_val = ( tmp_val * voxel_range ) + voxel_offset; slab_ptr[index] = rint ( tmp_val ); } if ( min_ptr != NULL ) { *min_ptr = real_min; } if ( max_ptr != NULL ) { *max_ptr = real_max; } } /** Convert the hyperslab from voxel to real values. This could form * the basis for a public function one day, but for now it is * considered private. */ static void miconvert_hyperslab_to_real ( mihandle_t volume, hsize_t start[], hsize_t count[], double *slab_ptr ) { /* This code is not intended to be a general hyperslab-to-real * converter yet. That is why it is not public. */ double real_min, real_max; /* Minimum and maximum values */ hsize_t index; hsize_t total; double voxel_range, voxel_offset; double real_range, real_offset; int i; double tmp_val; misize_t pos[MI2_MAX_VAR_DIMS]; int r; total = 1; for ( i = 0; i < volume->number_of_dims; i++ ) { total *= count[i]; pos[i] = start[i]; } voxel_offset = volume->valid_min; voxel_range = volume->valid_max - volume->valid_min; /* Get the initial real minimum & maximum. */ r = miget_slice_range ( volume, pos, volume->number_of_dims, &real_max, &real_min ); if ( r == MI_ERROR ) { fprintf ( stderr, "Oops - failed to get slice range\n" ); } real_offset = real_min; real_range = real_max - real_min; for ( index = 0; index < total; index++ ) { /* Since this calculation may cross slice boundaries, I need to * grab the correct real minimum and maximum for the coordinates * I happen to be in at the time. * * This next loop attempts to keep track of the current position, * and reloads the minimum and maximum whenever we change any other * than the fastest-varying dimension. */ for ( i = volume->number_of_dims - 1; i >= 0; i-- ) { pos[i]++; if ( pos[i] >= count[i] ) { pos[i] = start[i]; r = miget_slice_range ( volume, pos, volume->number_of_dims, &real_max, &real_min ); if ( r == MI_ERROR ) { fprintf ( stderr, "Oops - failed to get slice range\n" ); } real_offset = real_min; real_range = real_max - real_min; } else { break; } } tmp_val = ( slab_ptr[index] - voxel_offset ) / voxel_range; slab_ptr[index] = ( tmp_val * real_range ) + real_offset; } } /** Update an individual thumbnail for the \a volume. Updates group * number \a ogrp from source group \a igrp. The whole image tree must * be rooted at \a loc_id. */ int minc_update_thumbnail ( mihandle_t volume, hid_t loc_id, int igrp, int ogrp ) { hsize_t isize[MI2_MAX_VAR_DIMS]; hsize_t osize[MI2_MAX_VAR_DIMS]; hsize_t count[MI2_MAX_VAR_DIMS]; hsize_t start[MI2_MAX_VAR_DIMS]; hid_t idst_id=-1; /* Input dataset */ hid_t odst_id=-1; /* Output dataset */ hid_t ifspc_id=-1; /* Input "file" dataspace */ hid_t ofspc_id=-1; /* Output "file" dataspace */ hid_t typ_id=-1; /* Type ID */ hid_t imspc_id=-1; /* Input memory dataspace */ hid_t omspc_id=-1; /* Output memory dataspace */ char path[MI2_MAX_PATH]; int ndims; /* Number of dimensions in the image */ int scale; int i; /* Generic loop counter */ double *in_ptr; double *out_ptr; hsize_t slice; size_t in_bytes; size_t out_bytes; double smax, smin; /* Slice minimum and maximum */ hid_t omax_id=-1; /* Output image-max dataset */ hid_t omin_id=-1; /* Output image-min dataset */ hid_t tfspc_id=-1; /* Dimensionality of image-max/image-min */ hid_t tmspc_id=-1; hid_t dcpl_id=-1; /* Dataset creation property list */ herr_t result=0; /* return value for various functions*/ miinit(); /* Check arguments for basic validity. */ if ( ogrp <= igrp ) { return ( MI_ERROR ); } /* Calculate scale factor (always a power of 2) */ for ( i = igrp, scale = 1; i < ogrp; i++, scale <<= 1 ) ; /* Open the input path. */ sprintf ( path, "%d/image", igrp ); idst_id = H5Dopen1 ( loc_id, path ); if ( idst_id < 0 ) { return ( MI_ERROR ); } /* Get the input type. */ typ_id = H5Dget_type ( idst_id ); /* Get the input dataspace. */ ifspc_id = H5Dget_space ( idst_id ); /* Get the input dataset creation property list */ dcpl_id = H5Dget_create_plist ( idst_id ); ndims = H5Sget_simple_extent_ndims ( ifspc_id ); H5Sget_simple_extent_dims ( ifspc_id, isize, NULL ); /* Calculate the size of the new thumbnail. */ for ( i = 0; i < ndims; i++ ) { osize[i] = isize[i] / scale; if ( osize[i] == 0 ) { /* Too small? */ return ( MI_ERROR ); } } /* Create dataspace for new resolution */ ofspc_id = H5Screate_simple ( ndims, osize, NULL ); sprintf ( path, "%d/image", ogrp ); H5E_BEGIN_TRY { odst_id = H5Dcreate1 ( loc_id, path, typ_id, ofspc_id, H5P_DEFAULT ); } H5E_END_TRY; if ( odst_id < 0 ) { odst_id = H5Dopen1 ( loc_id, path ); if ( odst_id < 0 ) { return ( MI_ERROR ); } } H5Pclose ( dcpl_id ); /* No longer needed. */ if ( volume->volume_class == MI_CLASS_REAL ) { /* TODO: This is a bit of a hack - I need a better way to get the * dimensionality of the source image-min and image-max. */ tfspc_id = H5Screate_simple ( 1, &osize[0], NULL ); /* Create a simple scalar dataspace. */ tmspc_id = H5Screate ( H5S_SCALAR ); sprintf ( path, "%d/image-max", ogrp ); H5E_BEGIN_TRY { omax_id = H5Dcreate1 ( loc_id, path, H5T_IEEE_F64LE, tfspc_id, H5P_DEFAULT ); } H5E_END_TRY; if ( omax_id < 0 ) { omax_id = H5Dopen1 ( loc_id, path ); } sprintf ( path, "%d/image-min", ogrp ); H5E_BEGIN_TRY { omin_id = H5Dcreate1 ( loc_id, path, H5T_IEEE_F64LE, tfspc_id, H5P_DEFAULT ); } H5E_END_TRY; if ( omin_id < 0 ) { omin_id = H5Dopen1 ( loc_id, path ); } } /* Calculate the input buffer size - scale slices. */ in_bytes = scale * isize[1] * isize[2] * sizeof ( double ); in_ptr = malloc ( in_bytes ); out_bytes = osize[1] * osize[2] * sizeof ( double ); out_ptr = malloc ( out_bytes ); count[0] = scale; count[1] = isize[1]; count[2] = isize[2]; imspc_id = H5Screate_simple ( ndims, count, NULL ); count[0] = 1; count[1] = osize[1]; count[2] = osize[2]; omspc_id = H5Screate_simple ( ndims, count, NULL ); /* * read image & TODO: convert to "real" range. */ for ( slice = 0; slice < osize[0]; slice++ ) { start[0] = slice * scale; start[1] = 0; start[2] = 0; count[0] = scale; count[1] = isize[1]; count[2] = isize[2]; MI_CHECK_HDF_CALL(result=H5Sselect_hyperslab ( ifspc_id, H5S_SELECT_SET, start, NULL, count, NULL ),"H5Sselect_hyperslab"); if(result>=0) { MI_CHECK_HDF_CALL(H5Dread ( idst_id, H5T_NATIVE_DOUBLE, imspc_id, ifspc_id, H5P_DEFAULT, in_ptr ),"H5Dread"); /* Scale slice from voxel to real values. */ miconvert_hyperslab_to_real ( volume, start, count, in_ptr ); midownsample_slice ( in_ptr, out_ptr, isize, osize, scale ); } start[0] = slice; start[1] = 0; start[2] = 0; count[0] = 1; count[1] = osize[1]; count[2] = osize[2]; MI_CHECK_HDF_CALL(result=H5Sselect_hyperslab ( ofspc_id, H5S_SELECT_SET, start, NULL, count, NULL ),"H5Sselect_hyperslab"); if(result>=0) { miconvert_hyperslab_to_voxel ( volume, start, count, out_ptr, &smax, &smin ); MI_CHECK_HDF_CALL(H5Dwrite ( odst_id, H5T_NATIVE_DOUBLE, omspc_id, ofspc_id, H5P_DEFAULT, out_ptr ),"H5Dwrite"); } if ( volume->volume_class == MI_CLASS_REAL ) { /* Select the right point in tfspc_id */ H5Sselect_elements ( tfspc_id, H5S_SELECT_SET, 1, &start[0] ); H5Dwrite ( omax_id, H5T_NATIVE_DOUBLE, tmspc_id, tfspc_id, H5P_DEFAULT, &smax ); H5Dwrite ( omin_id, H5T_NATIVE_DOUBLE, tmspc_id, tfspc_id, H5P_DEFAULT, &smin ); } } free ( in_ptr ); free ( out_ptr ); if(omax_id>0) H5Dclose ( omax_id ); if(omin_id>0) H5Dclose ( omin_id ); if(tfspc_id>0) H5Sclose ( tfspc_id ); if(tmspc_id>0) H5Sclose ( tmspc_id ); if(omspc_id>0) H5Sclose ( omspc_id ); if(imspc_id>0) H5Sclose ( imspc_id ); if(odst_id>0) H5Dclose ( odst_id ); if(typ_id>0) H5Tclose ( typ_id ); H5Sclose ( ofspc_id ); H5Sclose ( ifspc_id ); return ( MI_NOERROR ); } /** Cycle through and update each of the lower-resolution images in * the file. */ int minc_update_thumbnails ( mihandle_t volume ) { int grp_no, prv_grp_no; hid_t grp_id; hsize_t n; hsize_t i; char name[MI2_MAX_PATH]; grp_id = H5Gopen1 ( volume->hdf_id, MI_ROOT_PATH "/image" ); if ( grp_id < 0 ) { return ( MI_ERROR ); /* Error opening group. */ } if ( H5Gget_num_objs ( grp_id, &n ) < 0 ) { return ( MI_ERROR ); /* Error getting object count. */ } grp_no = -1; for ( i = 0; i < n; i++ ) { if ( H5Gget_objname_by_idx ( grp_id, i, name, MI2_MAX_PATH ) < 0 ) { return ( MI_ERROR ); } prv_grp_no = grp_no; grp_no = atoi ( name ); if ( grp_no != 0 ) { minc_update_thumbnail ( volume, grp_id, prv_grp_no, grp_no ); } } H5Gclose ( grp_id ); return ( MI_NOERROR ); } double * alloc1d ( int n ) { return ( ( double * ) malloc ( sizeof ( double ) * n ) ); } double ** alloc2d ( int n, int m ) { double **mat; int i; mat = ( double ** ) malloc ( n * sizeof ( double * ) ); if ( mat == NULL ) { return NULL; } for ( i = 0; i < n; i++ ) { mat[i] = ( double * ) malloc ( m * sizeof ( double ) ); if ( mat[i] == NULL ) { free(mat); return NULL; } } return ( mat ); } void free2d ( int n, double **mat ) { int i; for ( i = 0; i < n; i++ ) { free ( mat[i] ); } free ( mat ); } /** Common code to create either standard or non-standard MINC datasets * as required. Used by create_dataset and create_standard_dataset. * Note that in the normal course of operations, it is possible for * the dataset creation to fail (perhaps it already exists). As a * result we can't take the return value too seriously. * \param hdf_file An open HDF5 file handle. * \param name The dataset (variable) name to create. * \param is_std Non-zero if this is a MINC-standard dataset, to be decorated * with the full set of standard MINC attributes. */ static int create_new_dataset(hid_t hdf_file, const char *name, int is_std) { hid_t dataset_info; hid_t dataspace_info; hid_t grp_info; int result; grp_info = H5Gopen1 ( hdf_file, MI_ROOT_PATH "/" MI_INFO_NAME ); if ( grp_info < 0 ) { return ( MI_ERROR ); } dataspace_info = H5Screate ( H5S_SCALAR ); if ( dataspace_info < 0 ) { H5Gclose(grp_info); return ( MI_ERROR ); } dataset_info = H5Dcreate1 ( grp_info, name, H5T_STD_I32LE, dataspace_info, H5P_DEFAULT ); if ( dataset_info < 0 ) { H5Sclose(dataspace_info); H5Gclose(grp_info); return ( MI_ERROR ); } if (is_std) { result = add_standard_minc_attributes(hdf_file,dataset_info); } else { result = add_minimal_minc_attributes(hdf_file,dataset_info); } H5Dclose ( dataset_info ); H5Sclose ( dataspace_info ); H5Gclose ( grp_info ); return ( result ); } /** Function to create a NON-STANDARD dataset (e.g. other than * "acquisition", "patient", or "study"). For internal use only. */ int create_dataset ( hid_t hdf_file, const char *name ) { return create_new_dataset(hdf_file, name, FALSE); } /** Function to create a standard MINC dataset (acquisition, patient, study). * For internal use only. */ int create_standard_dataset ( hid_t hdf_file, const char *name ) { return create_new_dataset(hdf_file, name, TRUE); } int add_minimal_minc_attributes(hid_t hdf_file, hid_t dset_id) { int result; result = miset_attr_at_loc ( dset_id, MIvartype, MI_TYPE_STRING, strlen ( MI_GROUP ), MI_GROUP ); if ( result < 0 ) { return ( MI_ERROR ); } return result; } int add_standard_minc_attributes(hid_t hdf_file, hid_t dset_id) { int result; result = miset_attr_at_loc ( dset_id, MIvarid, MI_TYPE_STRING, strlen ( MI_STDVAR ), MI_STDVAR ); if ( result < 0 ) { return ( MI_ERROR ); } result = miset_attr_at_loc ( dset_id, MIvartype, MI_TYPE_STRING, strlen ( MI_GROUP ), MI_GROUP ); if ( result < 0 ) { return ( MI_ERROR ); } result = miset_attr_at_loc ( dset_id, MIversion, MI_TYPE_STRING, strlen ( MI_VERSION_2_0 ), MI_VERSION_2_0 ); if ( result < 0 ) { return ( MI_ERROR ); } return result; } /** Performs scaled maximal pivoting gaussian elimination as a numerically robust method to solve systems of linear equations. */ int scaled_maximal_pivoting_gaussian_elimination ( int n, int row[], double **a, int n_values, double **solution ) { int i, j, k, p, v, tmp; double *s, val, best_val, m, scale_factor; int success; s = alloc1d ( n ); for ( i = 0; i < n; i++ ) row[i] = i; for ( i = 0; i < n; i++ ) { s[i] = fabs ( a[i][0] ); for ( j = 1; j < n; j++ ) { if ( fabs ( a[i][j] ) > s[i] ) s[i] = fabs ( a[i][j] ); } if ( s[i] == 0.0 ) { free ( s ); return ( FALSE ); } } success = TRUE; for ( i = 0; i < n - 1; i++ ) { p = i; best_val = a[row[i]][i] / s[row[i]]; best_val = fabs ( best_val ); for ( j = i + 1; j < n; j++ ) { val = a[row[j]][i] / s[row[j]]; val = fabs ( val ); if ( val > best_val ) { best_val = val; p = j; } } if ( a[row[p]][i] == 0.0 ) { success = FALSE; break; } if ( i != p ) { tmp = row[i]; row[i] = row[p]; row[p] = tmp; } for ( j = i + 1; j < n; j++ ) { if ( a[row[i]][i] == 0.0 ) { success = FALSE; break; } m = a[row[j]][i] / a[row[i]][i]; for ( k = i + 1; k < n; k++ ) a[row[j]][k] -= m * a[row[i]][k]; for ( v = 0; v < n_values; v++ ) solution[row[j]][v] -= m * solution[row[i]][v]; } if ( !success ) break; } if ( success && a[row[n - 1]][n - 1] == 0.0 ) success = FALSE; if ( success ) { for ( i = n - 1; i >= 0; --i ) { for ( j = i + 1; j < n; j++ ) { scale_factor = a[row[i]][j]; for ( v = 0; v < n_values; v++ ) solution[row[i]][v] -= scale_factor * solution[row[j]][v]; } for ( v = 0; v < n_values; v++ ) solution[row[i]][v] /= a[row[i]][i]; } } free ( s ); return ( success ); } int scaled_maximal_pivoting_gaussian_elimination_real ( int n, double **coefs, int n_values, double **values ) { int i, j, v, *row; double **a, **solution; int success; row = ( int * ) alloc1d ( n ); /* Ugly and wasteful but OK for 1D array */ a = alloc2d ( n, n ); solution = alloc2d ( n, n_values ); for ( i = 0; i < n; i++ ) { for ( j = 0; j < n; j++ ) a[i][j] = coefs[i][j]; for ( v = 0; v < n_values; v++ ) solution[i][v] = values[v][i]; } success = scaled_maximal_pivoting_gaussian_elimination ( n, row, a, n_values, solution ); if ( success ) { for ( i = 0; i < n; i++ ) { for ( v = 0; v < n_values; v++ ) { values[v][i] = solution[row[i]][v]; } } } free2d ( n, a ); free2d ( n, solution ); free ( row ); return ( success ); } /** Computes the inverse of a square matrix. */ static int invert_4x4_matrix ( double matrix[4][4], /**< Input matrix */ double inverse[4][4] ) /**< Output (inverted) matrix */ { int result; int i, j; double **mtmp; double **itmp; mtmp = alloc2d ( 4, 4 ); itmp = alloc2d ( 4, 4 ); /* Start off with the identity matrix. */ for ( i = 0; i < 4; i++ ) { for ( j = 0; j < 4; j++ ) { itmp[i][j] = 0.0; mtmp[i][j] = matrix[i][j]; } itmp[i][i] = 1.0; } result = scaled_maximal_pivoting_gaussian_elimination_real ( 4, mtmp, 4, itmp ); if ( result ) { for ( i = 0; i < 4; i++ ) { for ( j = 0; j < 4; j++ ) { inverse[i][j] = itmp[j][i]; } } } free2d ( 4, mtmp ); free2d ( 4, itmp ); return ( result ? MI_NOERROR : MI_ERROR ); } /** Fills in the transform with the identity matrix. */ static void mimake_identity_transform ( mi_lin_xfm_t transform ) { int i, j; for ( i = 0; i < MI2_LIN_XFM_SIZE; i++ ) { for ( j = 0; j < MI2_LIN_XFM_SIZE; j++ ) { transform[i][j] = 0.0; } transform[i][i] = 1.0; } } /** Function for inverting a MINC linear transform. */ int miinvert_transform ( mi_lin_xfm_t transform, mi_lin_xfm_t inverse ) { int result; result = invert_4x4_matrix ( transform, inverse ); if ( result != MI_NOERROR ) { mimake_identity_transform ( inverse ); } return ( result ); } /** Function to read a scalar variable of HDF5 type \a type_id from the given * \a path relative to the HDF5 file or group \a loc_id. */ int miget_scalar ( hid_t loc_id, hid_t type_id, const char *path, void *data ) { hid_t dset_id; hid_t spc_id; int result = MI_ERROR; H5E_BEGIN_TRY { dset_id = H5Dopen1 ( loc_id, path ); } H5E_END_TRY; if ( dset_id >= 0 ) { spc_id = H5Dget_space ( dset_id ); if ( spc_id >= 0 ) { if ( H5Sget_simple_extent_ndims ( spc_id ) == 0 ) { if ( H5Dread ( dset_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data ) >= 0 ) { result = MI_NOERROR; } } H5Sclose ( spc_id ); } H5Dclose ( dset_id ); } return ( result ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/minc2.h000066400000000000000000000004011257462267400173550ustar00rootroot00000000000000/** \file minc2.h * \brief MINC 2.0 public constants, types, and definitions. */ #ifndef MINC2_H #define MINC2_H #include #include "minc2_defs.h" #include "minc2_structs.h" #include "minc2_error.h" #include "minc2_api.h" #endif /* MINC2_H */ libminc-libminc-2-3-00/libsrc2/minc2_api.h000066400000000000000000001523651257462267400202270ustar00rootroot00000000000000/** * \file minc2_api.h * MINC2 API FUNCTION DECLARATIONS **/ #ifndef MINC2_API_H #define MINC2_API_H #ifdef __cplusplus extern "C" { /* Hey, Mr. Compiler - this is "C" code! */ #endif /* __cplusplus defined */ /** \defgroup mi2Group ATTRIBUTE/GROUP FUNCTIONS */ /** Start listing the objects in a group. * \ingroup mi2Group */ int milist_start(mihandle_t vol, const char *path, int flags, milisthandle_t *handle); /** Iterate through attributes * \ingroup mi2Group */ int milist_attr_next(mihandle_t vol, milisthandle_t handle, char *path, int maxpath, char *name, int maxname); /** Finish listing attributes or groups * \ingroup mi2Group */ int milist_finish(milisthandle_t handle); /** Get the group at given path * \ingroup mi2Group */ int milist_grp_next(milisthandle_t handle, char *path, int maxpath); /** Create a group at "path" using "name". * \ingroup mi2Group */ int micreate_group(mihandle_t vol, const char *path, const char *name); /** Delete the named attribute. * \ingroup mi2Group */ int midelete_attr(mihandle_t vol, const char *path, const char *name); /** Delete the subgroup \a name from the group \a path * \ingroup mi2Group */ int midelete_group(mihandle_t vol, const char *path, const char *name); /** Delete the subgroup \a name from the group \a path * \ingroup mi2Group */ int miget_attr_length(mihandle_t vol, const char *path, const char *name, size_t *length); /** Get the type of an attribute. * \ingroup mi2Group */ int miget_attr_type(mihandle_t vol, const char *path, const char *name, mitype_t *data_type); /** Copy all attribute given a path * \ingroup mi2Group */ int micopy_attr(mihandle_t vol, const char *path, mihandle_t new_vol); /** Get the values of an attribute. * \ingroup mi2Group */ int miget_attr_values(mihandle_t vol, mitype_t data_type, const char *path, const char *name, size_t length, void *values); /** Set the values of an attribute. * \ingroup mi2Group */ int miset_attr_values(mihandle_t vol, mitype_t data_type, const char *path, const char *name, size_t length, const void *values); /** Add global history attribute * \ingroup mi2Group */ int miadd_history_attr(mihandle_t vol, size_t length, const void *values); /** \defgroup mi2Memory FREE FUNCTIONS */ /** * Free space allocated for string storage by a MINC function. * \param name_ptr A pointer to the space to be freed. * \ingroup mi2Memory */ int mifree_name(char *name_ptr); /** * Free list of names * not certain we really need this... * \ingroup mi2Memory */ int mifree_names(char **name_pptr); /** \defgroup mi2DataType DATA TYPE/SPACE FUNCTIONS */ /** Return the data class of a volume (See miclass_t). * \ingroup mi2DataType */ int miget_data_class(mihandle_t vol, miclass_t *volume_class); /** Return the data type of a volume (See mitype_t). * \ingroup mi2DataType */ int miget_data_type(mihandle_t vol, mitype_t *volume_data_type); /** Return the byte size of the voxel datatytpe * \ingroup mi2DataType */ int miget_data_type_size(mihandle_t vol, misize_t *voxel_size); /** Return the minc space type, name should be freed after use * \ingroup mi2DataType */ int miget_space_name(mihandle_t vol, char **name); /** * Set minc space type * \ingroup mi2DataType */ int miset_space_name(mihandle_t vol, const char *name); /** \ingroup mi2Dim DIMENSION FUNCTIONS */ /** * Figure out whether a dimension is associated with a volume. * \param dimension The dimension handle. * \param volume A pointer to the volume handle. * * This method returns the volume handle associated with a given dimension * or an error if the specified handle is not associated with the volume. * \ingroup mi2Dim */ int miget_volume_from_dimension(midimhandle_t dimension, mihandle_t *volume); /** * Create a copy of a given dimension. * \param dim_ptr The dimension handle of the dimension to copy. * \param new_dim_ptr A pointer to the dimension handle of the copied dimension. * * This method creates a copy of the specified dimension and returns the handle * to the copied dimension or error on failure. * \ingroup mi2Dim */ int micopy_dimension(midimhandle_t dim_ptr, midimhandle_t *new_dim_ptr); /** * Define a new dimension in a MINC volume. * \param name A pointer to the string specifying the dimension name. * \param dimclass The class of the dimension. * \param attr The attribute of the dimension. * \param length The size of the dimension. * \param new_dim_ptr A pointer to the dimension handle. * * This function defines a dimension that can be used in the definition * of a new MINC volume (see the create_volume function). The name may * be an arbitrary string of up to 128 alphanumeric characters. Any of * the "standard" names retained from MINC 1.0 retain their default * behaviors: MIxspace, MIyspace, and MIzspace default to spatial * dimensions, and MItime default to be a time dimension. MItfrequency * is a temporal frequency axis, and MIxfrequency, MIyfrequency, and * MIzfrequency are spatial frequency axes. Any other name may be used. * * When initially defined, a regularly-sampled dimension will have a * "start" value of zero, and a "separation" or "step" value of 1.0. An * irregular dimension will be initialized with all offsets equal to * zero. * * The size of the dimension may range from 0 to 2^32, which should provide * enough range to represent detail on the order of 10 Angstroms in * typical medical imaging applications. * * For the detailed defintion of \a class and \a type refer to the MINC 2.0 API * definition. * \ingroup mi2Dim */ int micreate_dimension(const char *name, midimclass_t dimclass, midimattr_t attr, misize_t length, midimhandle_t *new_dim_ptr); /** * Delete the dimension definition. * \param dim_ptr The dimension handle. * * Note: The original document stated that a dimension has to be * associated with a given volume before it can be deleted. This * feature was erased from the document and not considered here. * \ingroup mi2Dim */ int mifree_dimension_handle(midimhandle_t dim_ptr); /** Retrieve the list of dimensions defined in a MINC volume, * with the same class \a class and attribute \a attr. * \param volume The volume handle. * \param dimclass The class of the dimensions. * \param attr The attribute of the dimensions. * \param order The order of the dimension (file or apparent). * \param array_length The number of dimension to be retrieved. * \param dimensions An array of dimension handles to be retrieved. * * This function is used to retrieve an array of dimension handles for a * MINC volume. It will place the handles of the first "array_length" * dimensions into the "dimensions[]" array, returning only those dimension * whose characteristics match the "class" and "attr" parameters. * The miorder_t is an enumerated type flag which determines whether the * dimension order is determined by the file or by the apparent order set by * the user. This function will fail if the user has not set the apparent * dimension order by calling either of * (miset_apparent_dimension_order(_by_name)) * before calling this function with MI_DIMORDER_APPARENT flag. * \ingroup mi2Dim */ int miget_volume_dimensions(mihandle_t volume, midimclass_t dimclass, midimattr_t attr, miorder_t order, int array_length, midimhandle_t dimensions[]); /** * Set apparent dimension order, based on an array of dimensions. You * may also set the dimension order by the name of the dimension, see * miset_apparent_dimension_order_by_name(). * \param volume The volume handle. * \param array_length The number of dimensions to be sorted. * \param dimensions An "ordered" array of dimension handles. * * This method sets an apparent dimension order. The user can sort the * dimensions in any desired order. If the user specifies fewer dimensions * than the existing ones, then they are assumed to be added to the last. * \ingroup mi2Dim */ int miset_apparent_dimension_order(mihandle_t volume, int array_length, midimhandle_t dimensions[]); /** * Set apparent dimension order by name. * \param volume The volume handle. * \param array_length The number of dimensions to be sorted. * \param names An "ordered" array of dimension names. * * This method sets an apparent dimension order by dimension name. Note that * all dimension names must be different or an error occurs. * \ingroup mi2Dim */ int miset_apparent_dimension_order_by_name(mihandle_t volume, int array_length, char **names); /** *Set the record flag and add a record dimension to the volume * dimensions so the volume would appear to have an extra dimension. * \param volume The volume handle. * \param record_flag The flag to determine whether there exist a record dimension * in the volume. * * This method causes a volume to appear to have a record dimension. The record * dimension will be set to uniform and flat (i.e., the volume will appear to have * an extra dimension) * \ingroup mi2Dim */ int miset_apparent_record_dimension_flag(mihandle_t volume, int record_flag); /** * Get the apparent order of voxels (i.e., the order that voxel indices increase/decrease) * \param dimension The dimension handle * \param file_order The order of voxels. * \param sign The sign of the step value. * * This method gets the apparent order of voxels for the specified dimension * and the sign of the step values. * \ingroup mi2Dim */ int miget_dimension_apparent_voxel_order(midimhandle_t dimension, miflipping_t *file_order, miflipping_t *sign); /** * Set the apparent order of voxels. * \param dimension The dimension handle. * \param flipping_order The order of voxels. * * This method sets the apparent order of voxels for the specified dimension. * For the detailed description of voxel order refer to the MINC 2.0 API definition. * \ingroup mi2Dim */ int miset_dimension_apparent_voxel_order(midimhandle_t dimension, miflipping_t flipping_order); /** * Get the class of a MINC dimension. * \param dimension The dimension handle. * \param dimclass A pointer to the dimension class. * * The "class" of a MINC dimension defines the general type of a dimension, * whether it is a spatial dimension, a time dimension, or a frequency dimension * as transformed from either space or time. User-defined dimension are also * permitted, with no default handling assumed. Finally, a record can be specified * as a dimension. * \ingroup mi2Dim */ int miget_dimension_class(midimhandle_t dimension, midimclass_t *dimclass); /** * Set the class of a MINC dimension. * \param dimension The dimension handle. * \param dimclass The dimension class. * * Refer to miget_dimension_class(). * \ingroup mi2Dim */ int miset_dimension_class(midimhandle_t dimension, midimclass_t dimclass); /** * Get the direction cosine vector of a given SPATIAL dimension. * \param dimension The dimension handle. * \param direction_cosines An array of direction_cosines(i.e., vector determining the direction cosine). * * Spatial dimension in MINC volumes may be associated with a vector of direction * cosines which define the precise orientation of the axis relative to "true" * x, y, or z coordinates. * \ingroup mi2Dim */ int miget_dimension_cosines(midimhandle_t dimension, double direction_cosines[3]); /** * Set the direction cosine vector for a given SPATIAL dimension. * \param dimension The dimension handle. * \param direction_cosines An array of direction_cosines(i.e., vector determining the direction cosine). * * Refer to miget_dimension_cosines(). * \ingroup mi2Dim */ int miset_dimension_cosines(midimhandle_t dimension, const double direction_cosines[3]); /** * Set the comments attribute for a given dimension. * \param dimension The dimension handle. * \param comments A pointer for the comments. * * Refer to miget_dimension_description(). * \ingroup mi2Dim */ int miset_dimension_description(midimhandle_t dimension, const char *comments); /** * Get the comments attribute for a given dimension. * \param dimension The dimension handle. * \param comments_ptr A string pointer for the comments. * * Get and Set the dimension description. Note that the spatial dimensions * (xspace, yspace, zspace) are initialized according to minc1 description. * All other dimensions will have an empty description unless set by the user. * The string pointer returned in \a *comments_ptr must be freed by the caller. * \ingroup mi2Dim */ int miget_dimension_description(midimhandle_t dimension, char **comments_ptr); /** * Get the identifier (name) of a MINC dimension. * \param dimension The dimension handle. * \param name_ptr A string pointer for returning the dimension name. * * Retrieves the name of the given dimension. * \ingroup mi2Dim */ int miget_dimension_name(midimhandle_t dimension, char **name_ptr); /** * Set the identifier (name) of a given MINC dimension. * \param dimension The dimension handle. * \param name A pointer for the dimension name. * * Rename the given dimension. * \ingroup mi2Dim */ int miset_dimension_name(midimhandle_t dimension, const char *name); /** * Get the untransformed world coordinates of points along a MINC dimension. * \param dimension The dimension handle. * \param array_length The number of dimensions. * \param start_position The position in which to retrieve the offsets. * \param offsets The array of offsets to be returned. * * Get or Set the dimension offsets, that is, the * absolute world coordinates of each sampled point along the dimension. * * The caller may retrieve up to "array_length" values, starting at the * integer index "start_position". Thus an arbitrary contiguous subset * of the dimension's offsets may be retrieved or stored. An error is * returned if the "start_position" exceeds the total size of the * dimension. If the value of "start_position" is legal, but the sum of * "start_position" and "array_length" exceeds the size of the dimension, * the function will get or set offsets up to the size of the dimension. * Any extra positions in the offsets[] array will be ignored. * \ingroup mi2Dim */ int miget_dimension_offsets(midimhandle_t dimension, misize_t array_length, misize_t start_position, double offsets[]); /** * Set the absolute world coordinates of points along a MINC dimension. * \param dimension The dimension handle. * \param array_length The number of dimensions. * \param start_position The position in which to retrieve the offsets. * \param offsets The array of offsets to be set. * * Refer to miget_dimension_offsets(). * \ingroup mi2Dim */ int miset_dimension_offsets(midimhandle_t dimension, misize_t array_length, misize_t start_position, const double offsets[]); /** * Get the sampling flag for a MINC dimension. * \param dimension The dimension handle. * \param sampling_flag The flag to determine regular/irregular sampling dimensions. * * This flag is true (non-zero) if the dimension is sampled at regular * intervals, and false if the dimension is sampled irregularly. * If a dimension has regular sampling, the miget_dimension_separation() * may be used to retrieve the sampling interval, and the * miget_dimension_start() may be used to retrieve the origin * value along the axis. * * If a dimension has irregular sampling, the miget_dimension_offsets() * may be used to retrieve the positions of each sample along that axis. * \ingroup mi2Dim */ int miget_dimension_sampling_flag(midimhandle_t dimension, miboolean_t *sampling_flag); /** * Set the sampling flag for a MINC dimension. * \param dimension The dimension handle. * \param sampling_flag The flag to determine regular/irregular sampling dimensions. * * Refer to miget_dimension_sampling_flag(). * \ingroup mi2Dim */ int miset_dimension_sampling_flag(midimhandle_t dimension, miboolean_t sampling_flag); /** * Get the constant sampling interval (step) for a single dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param separation_ptr The Pointer to the dimension sampling interval (step). * * Gets or sets the constant sampling interval defined on a regularly-sampled * dimension. While it is legal to call these functions for an irregularly- * sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_separation(midimhandle_t dimension, mivoxel_order_t voxel_order, double *separation_ptr); /** * Set the sampling interval (step) for a single dimension. * \param dimension The dimension handle. * \param separation The dimension sampling interval (step). * * Refer to miget_dimension_separation(). * \ingroup mi2Dim */ int miset_dimension_separation(midimhandle_t dimension, double separation); /** * Get the sampling interval (STEP) for a list of dimensions. * \param dimensions An array of dimension handles. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of dimensions in the dimesions array. * \param separations An array of dimensions sampling intervals (step) values. * * Get or Set the scalar separation (sampling interval) * associated with each of the dimensions in the input "dimensions[]" * array. The "array_length" parameter specifies the size of both the * input and output arrays. While it is legal to call these functions for * an irregularly-sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_separations(const midimhandle_t dimensions[], mivoxel_order_t voxel_order, misize_t array_length, double separations[]); /** * Set the sampling interval (STEP) for a list of dimensions. * \param dimensions An array of dimension handles. * \param array_length The number of dimensions in the dimesions array. * \param separations An array of dimensions sampling intervals (step) values. * * Refer to miget_dimension_separations(). * \ingroup mi2Dim */ int miset_dimension_separations(const midimhandle_t dimensions[], misize_t array_length, const double separations[]); /** * Get the length of a MINC dimension. * \param dimension The dimension handle. * \param size_ptr A pointer to the dimension size. * * Get or Set the size (or length) of a MINC 2 dimension * object used in creating a new volume. The size of a dimension * associated with an existing volume cannot be changed. * \ingroup mi2Dim */ int miget_dimension_size(midimhandle_t dimension, misize_t *size_ptr); /** * Set the length of a MINC dimension if not associated with a volume. * \param dimension The dimension handle. * \param size The size of the dimension. * * Refer to miget_dimension_size(). * \ingroup mi2Dim */ int miset_dimension_size(midimhandle_t dimension, misize_t size); /** * Retrieve the length of all dimensions in dimensions array. * \param dimensions An array of dimension handles. * \param array_length The number of dimensions in the dimensions array * \param sizes An array of dimension sizes. * * This function will copy the lengths of each of the dimensions listed in the * "dimensions[]" array into the "sizes[]" array. The parameter "array_length" * specifies the length of both of the arrays. * \ingroup mi2Dim */ int miget_dimension_sizes(const midimhandle_t dimensions[], misize_t array_length, misize_t sizes[]); /** * Get the start value of a MINC dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param start_ptr A pointer to the start value. * * Get or set the origin of the dimension in world * coordinates. While a "start" value may be legally associated with any * dimension, it is considered meaningless when associated with an * irregularly sampled dimension. * \ingroup mi2Dim */ int miget_dimension_start(midimhandle_t dimension, mivoxel_order_t voxel_order, double *start_ptr); /** * Set the start of a MINC dimension. * \param dimension The dimension handle. * \param start_ptr The start of the dimension. * * Refer to miget_dimension_start(). * \ingroup mi2Dim */ int miset_dimension_start(midimhandle_t dimension, double start_ptr); /** * Get the start values for MINC dimensions in dimensions array. * \param dimensions The array of dimension handles. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of dimensions in the dimensions array. * \param starts The array of dimension starts. * * Get or Set the start value for an array of * regularly-sampled dimensions. The start value defines the origin of * that dimension. While it is legal to call these functions for an * irregularly-sampled dimension, the values will be ignored. * \ingroup mi2Dim */ int miget_dimension_starts(const midimhandle_t dimensions[], mivoxel_order_t voxel_order, misize_t array_length, double starts[]); /** * Set the start values for MINC dimensions in dimensions array. * \param dimensions The array of dimension handles. * \param array_length The number of dimensions in the dimensions array. * \param starts The array of dimension starts. * * Refer to miget_dimension_starts(). * \ingroup mi2Dim */ int miset_dimension_starts(const midimhandle_t dimensions[], misize_t array_length, const double starts[]); /** * Get the unit string for a MINC dimension. * \param dimension The dimension handle. * \param units_ptr A string pointer to the dimension units. * * Retrieves the units of the given dimension, * The caller must free the string returned by this function. * \ingroup mi2Dim */ int miget_dimension_units(midimhandle_t dimension, char **units_ptr); /** * Set the unit string for a MINC dimension. * \param dimension The dimension handle. * \param units A pointer to the dimension units. * * Set the units for an existing dimension. * The new string must be no greater than 128 characters in length, * including the trailing zero byte. * \ingroup mi2Dim */ int miset_dimension_units(midimhandle_t dimension, const char *units); /** * Get A single full-width half-maximum value from a * regularly sampled dimension. * \param dimension The dimension handle. * \param width_ptr A pointer to the FWHM value. * * Get or Set the dimension width, that is, the * full-width half-maximum values of each sampled point along the dimension. * These functions are used to set a constant width for regularly-sampled * dimensions. * \ingroup mi2Dim */ int miget_dimension_width(midimhandle_t dimension, double *width_ptr); /** * Set the A single full-width half-maximum value for a * regularly sampled dimension. * \param dimension The dimension handle. * \param width_ptr The FWHM value. * * Refer to miget_dimension_width(). * \ingroup mi2Dim */ int miset_dimension_width(midimhandle_t dimension, double width_ptr); /** * Get the full-width half-maximum value for points along an * irregularly sampled dimension. * \param dimension The dimension handle. * \param voxel_order The order in which the voxel indices increase/decrease. * \param array_length The number of width in the widths array. * \param start_position Index for starting position. * \param widths An array of width values to be retrieved. * * Get or Set the dimension widths, that is, the * full-width half-maximum values of each sampled point along the * dimension. * The caller may retrieve up to "array_length" values, starting at the * integer index "start_position". Thus an arbitrary contiguous subset * of the dimension's widths may be retrieved or stored. An error is * returned if the "start_position" exceeds the total size of the * dimension. If the value of "start_position" is legal, but the sum of * "start_position" and "array_length" exceeds the size of the dimension, * the function will get or set widths up to the size of the dimension. * Any extra positions in the widths[] array will be ignored. * \ingroup mi2Dim */ int miget_dimension_widths(midimhandle_t dimension, mivoxel_order_t voxel_order, misize_t array_length, misize_t start_position, double widths[]); /** * Set the full-width half-maximum value for points along an * irregularly sampled dimension. * \param dimension The dimension handle. * \param array_length The number of width in the widths array. * \param start_position Index for starting position. * \param widths An array of width values to be set. * * Refer to miget_dimension_widths(). * \ingroup mi2Dim */ int miset_dimension_widths(midimhandle_t dimension, misize_t array_length, misize_t start_position, const double widths[]); /* VOLUME FUNCTIONS */ /** Create a volume with the specified name, dimensions, type, class, volume properties and retrieve the volume handle. \ingroup mi2Vol */ int micreate_volume(const char *filename, int number_of_dimensions, midimhandle_t dimensions[], mitype_t volume_type, miclass_t volume_class, mivolumeprops_t create_props, mihandle_t *volume); /** Create the actual image for the volume. * Note that the image dataset muct be created in the hierarchy * before the image data can be added. * \ingroup mi2Vol */ int micreate_volume_image(mihandle_t volume); /** Return the number of dimensions associated with this volume. * \ingroup mi2Vol */ int miget_volume_dimension_count(mihandle_t volume, midimclass_t dimclass, midimattr_t attr, int *number_of_dimensions); /** Return the number of dimensions associated with this volume. * \ingroup mi2Vol */ int miget_slice_dimension_count(mihandle_t volume, midimclass_t dimclass, midimattr_t attr, int *number_of_dimensions); /** Returns the number of voxels in the volume. * \ingroup mi2Vol */ int miget_volume_voxel_count(mihandle_t volume, misize_t *number_of_voxels); /** Opens an existing MINC volume for read-only access if mode argument is * MI2_OPEN_READ, or read-write access if mode argument is MI2_OPEN_RDWR. * \ingroup mi2Vol */ int miopen_volume(const char *filename, int mode, mihandle_t *volume); /** Close an existing MINC volume. If the volume was newly created, * all changes will be written to disk. In all cases this function closes * the open volume and frees memory associated with the volume handle. * \ingroup mi2Vol */ int miclose_volume(mihandle_t volume); /** Function to get the volume's slice-scaling flag. */ int miget_slice_scaling_flag(mihandle_t volume, miboolean_t *slice_scaling_flag); /** Function to set the volume's slice-scaling flag. */ int miset_slice_scaling_flag(mihandle_t volume, miboolean_t slice_scaling_flag); /** \defgroup mi2VPrp VOLUME PROPERTIES FUNCTIONS */ /** Create a volume property list. The new list will be returned in the * \a props parameter. When the program is finished * using the property list it should call mifree_volume_props() to free the * memory associated with the list. * \param props A pointer to the returned volume properties handle. * \ingroup mi2VPrp */ int minew_volume_props(mivolumeprops_t *props); /** Destroy a volume property list. * \param props The volume property list to delete. * \ingroup mi2VPrp */ int mifree_volume_props(mivolumeprops_t props); /** Get a copy of the volume property list. When the program is finished * using the property list it should call mifree_volume_props() to free the * memory associated with the list. * \param vol A volume handle * \param props A pointer to the returned volume properties handle. * \ingroup mi2VPrp */ int miget_volume_props(mihandle_t vol, mivolumeprops_t *props); /** Set multi-resolution properties. The \a enable_flag determines * whether or not thumbnail images will be calculated at all. The \a * depth parameter determines the lowest-resolution image that will be * available. The full resolution image is considered to be image #0, * the half resolution image is image #1, the quarter-resolution image * is #2, etc. Therefore a \a depth value of 2 implies both the half * and quarter resolution thumbnails will be calculated and stored in * the file. * \param props A volume property list handle * \param enable_flag TRUE if multiresolution support should be enabled in * this file. * \param depth The maximum depth of multiresolution data * to support. * \ingroup mi2VPrp */ int miset_props_multi_resolution(mivolumeprops_t props, miboolean_t enable_flag, int depth); /** Get multi-resolution properties. Returns the value of the \a enable_flag * and \a depth parameters. * \param props A volume property list handle * \param enable_flag Pointer to a boolean which will be set to TRUE if * multiresolution has been enabled. * \param depth Pointer to a integer which will contain the maximum resolution * depth enabled if multiresolution is enabled. * \ingroup mi2VPrp */ int miget_props_multi_resolution(mivolumeprops_t props, miboolean_t *enable_flag, int *depth); /** Select a different resolution from a multi-resolution image. * \ingroup mi2VPrp */ int miselect_resolution(mihandle_t volume, int depth); /** Compute or recompute all resolution groups. * * \ingroup mi2VPrp */ int miflush_from_resolution(mihandle_t volume, int depth); /** Set compression type for a volume property list * Note that enabling compression will automatically * enable blocking with default parameters. * \param props A volume properties list * \param compression_type The type of compression to use (MI_COMPRESS_NONE * or MI_COMPRESS_ZLIB) * \ingroup mi2VPrp */ int miset_props_compression_type(mivolumeprops_t props, micompression_t compression_type); /** Get compression type for a volume property list * \param props A volume property list handle * \param compression_type A pointer to a variable to which the current * compression type will be assigned. * \ingroup mi2VPrp */ int miget_props_compression_type(mivolumeprops_t props, micompression_t *compression_type); /** Set zlib compression properties for a volume list. The \a zlib_level * parameter may range from 1 to 9, where higher numbers request that the * library attempt to use more memory (and possibly processing power) to * achieve the highest possible compression ratio. * * \param props A volume property list handle * \param zlib_level An integer specifying the desired compression level. * \ingroup mi2VPrp */ int miset_props_zlib_compression(mivolumeprops_t props, int zlib_level); /** Get zlib compression properties from a volume property list. * \param props A volume property list handle * \param zlib_level Pointer to an integer variable that will receive the * current compression level. * \ingroup mi2VPrp */ int miget_props_zlib_compression(mivolumeprops_t props, int *zlib_level); /** Set blocking structure properties for the volume * \param props A volume property list handle * \param edge_count The number of edges (dimensions) in a block * \param edge_lengths The lengths of the edges * \ingroup mi2VPrp */ int miset_props_blocking(mivolumeprops_t props, int edge_count, const int *edge_lengths); /** Get blocking structure properties for the volume * \param props The properties structure from which to get the information * \param edge_count Returns the number of edges (dimensions) in a block * \param edge_lengths The lengths of the edges * \param max_lengths The number of elements of the edge_lengths array * \ingroup mi2VPrp */ int miget_props_blocking(mivolumeprops_t props, int *edge_count, int *edge_lengths, int max_lengths); /** Set properties for uniform/nonuniform record dimension * \ingroup mi2VPrp */ int miset_props_record(mivolumeprops_t props, misize_t record_length, char *record_name); /** Set the template volume flag * \ingroup mi2VPrp */ int miset_props_template(mivolumeprops_t props, int template_flag); /** \defgroup mi2Slice SLICE/VOLUME SCALE FUNCTIONS */ /** * This function sets \a slice_max to the maximum real value of * voxels in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. * \ingroup mi2Slice */ int miget_slice_max(mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_max); /** * This function sets minimum real value of * values in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. * \ingroup mi2Slice */ int miset_slice_max(mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_max); /** * This function sets \a slice_min to the minimum real value of * voxels in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. * \ingroup mi2Slice */ int miget_slice_min(mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_min); /** * This function sets minimum real value of * values in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. * \ingroup mi2Slice */ int miset_slice_min(mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_min); /** * This function gets both the minimum and * maximum real value of voxels in the slice containing the coordinates * \a start_positions. The \a array_length may be less than or equal to * the number of dimensions in the volume, extra coordinates will be * ignored. Specifying too few coordinates will trigger an error. * Coordinates must always be specified in raw file order. * \ingroup mi2Slice */ int miget_slice_range(mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_max, double *slice_min); /** * This function the minimum and maximum real value of voxels in the * slice containing the coordinates \a start_positions. The \a * array_length may be less than or equal to the number of dimensions in * the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. Coordinates must always be * specified in raw file order. * \ingroup mi2Slice */ int miset_slice_range(mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_max, double slice_min); /** * This function returns the maximum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miget_volume_max(mihandle_t volume, double *slice_max); /** * This function sets the maximum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miset_volume_max(mihandle_t volume, double slice_max); /** * This function returns the minimum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miget_volume_min(mihandle_t volume, double *slice_min); /** * This function sets the minimum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miset_volume_min(mihandle_t volume, double slice_min); /** * This function retrieves the maximum and minimum real values of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miget_volume_range(mihandle_t volume, double *volume_max, double *volume_min); /** * This function sets the maximum and minimum real values of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. * \ingroup mi2Slice */ int miset_volume_range(mihandle_t volume, double volume_max, double volume_min); /** \defgroup mi2Hyper HYPERSLAB FUNCTIONS */ /** Calculates and returns the number of bytes required to store the * hyperslab specified by the \a n_dimensions and the * \a count parameters. * \ingroup mi2Hyper */ int miget_hyperslab_size(mitype_t volume_data_type, int n_dimensions, const hsize_t count[], misize_t *size_ptr); /** Calculates and returns the number of bytes required to store the * hyperslab specified by the \a n_dimensions and the * \a count parameters, using hdf type id * \ingroup mi2Hyper */ void miget_hyperslab_size_hdf(hid_t hdf_type_id, int n_dimensions, const hsize_t count[], misize_t *size_ptr); /** Reads the real values in the volume from the interval min through * max, mapped to the maximum representable range for the requested * data type. Float types is mapped to 0.0 1.0 * \ingroup mi2Hyper */ int miget_hyperslab_normalized(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], double min, double max, void *buffer); /** Writes the real values in the volume from the interval min through * max, mapped to the maximum representable range for the requested * data type. Float types is mapped to 0.0 1.0 * \ingroup mi2Hyper */ int miset_hyperslab_normalized(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], double min, double max, void *buffer); /** Get a hyperslab from the file, * converting voxel values into real values * \ingroup mi2Hyper */ int miget_hyperslab_with_icv(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** Write a hyperslab to the file, converting real values into voxel values * \ingroup mi2Hyper */ int miset_hyperslab_with_icv(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** Read a hyperslab from the file into the preallocated buffer, * converting from the stored "voxel" data range to the desired * "real" (float or double) data range, same as miget_hyperslab_with_icv * \ingroup mi2Hyper */ int miget_real_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** Write a hyperslab to the file from the preallocated buffer, * converting from the stored "voxel" data range to the desired * "real" (float or double) data range, same as miset_hyperslab_with_icv * \ingroup mi2Hyper */ int miset_real_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** Read a hyperslab from the file into the preallocated buffer, * with no range conversions or normalization. Type conversions will * be performed if necessary. * \ingroup mi2Hyper */ int miget_voxel_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** Write a hyperslab to the file from the preallocated buffer, * with no range conversions or normalization. Type conversions will * be performed if necessary. * \ingroup mi2Hyper */ int miset_voxel_value_hyperslab(mihandle_t volume, mitype_t buffer_data_type, const misize_t start[], const misize_t count[], void *buffer); /** \defgroup mi2Cvt CONVERT FUNCTIONS */ /** Convert values between real (scaled) values and voxel (unscaled) * values. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file, whereas the "real" value is the * value at the given location after scaling has been applied. * * The \a coords parameter specifies the location at which the * conversion is performed. This is needed because MINC supports * per-slice scaling, therefore a conversion performed at one location * may differ from that performed at another location. * * \param volume A volume handle * \param coords The position for which to perform the conversion. * \param ncoords The length of the \a coords array. * \param real_value The original real value, to be converted to voxel. * \param voxel_value_ptr A pointer to the converted voxel value. * \ingroup mi2Cvt */ int miconvert_real_to_voxel(mihandle_t volume, const misize_t coords[], size_t ncoords, double real_value, double *voxel_value_ptr); /** Convert values between real (scaled) values and voxel (unscaled) * values. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file, whereas the "real" value is the * value at the given location after scaling has been applied. * * The \a coords parameter specifies the location at which the * conversion is performed. This is needed because MINC supports * per-slice scaling, therefore a conversion performed at one location * may differ from that performed at another location. * * \param volume A volume handle * \param coords The position for which to perform the conversion. * \param ncoords The length of the \a coords array. * \param voxel_value The original voxel value, to be converted to real. * \param real_value_ptr A pointer to the converted real value. * \ingroup mi2Cvt */ int miconvert_voxel_to_real(mihandle_t volume, const misize_t coords[], int ncoords, double voxel_value, double *real_value_ptr); /** Converts an N-dimensional spatial position in voxel coordinates into a * 3-dimensional spatial position in world coordinates. * * The returned world coordinate vector is in a standardized order, with * the X position first (at index 0), followed by the Y and Z coordinates. * The voxel coordinate vector is in the native order appropriate to the * file. * * \ingroup mi2Cvt */ int miconvert_voxel_to_world(mihandle_t volume, const double voxel[], double world[]); /** Converts a 3-dimensional spatial position in world coordinates into a * N-dimensional spatial position in voxel coordinates. * * The input world coordinate vector is in a standardized order, with * the X position first (at index 0), followed by the Y and Z coordinates. * The voxel coordinate vector is in the native order appropriate to the * file. * * \ingroup mi2Cvt */ int miconvert_world_to_voxel(mihandle_t volume, const double world[], double voxel[]); /** * This function calculates the start values for the volume dimensions, * assuming that the spatial origin is relocated to the given world * coordinate. * * \ingroup mi2Cvt */ int miconvert_world_origin_to_start( mihandle_t volume, double world[], double starts[]); /** * This function calculates the start values for the volume dimensions, * assuming that the spatial origin is relocated to the given world * coordinate. * * \ingroup mi2Cvt */ int miconvert_spatial_frequency_origin_to_start( mihandle_t volume, double world[], double starts[]); /** * This function sets the world coordinates of the point (0,0,0) in voxel * coordinates. This changes the constant offset of the two coordinate * systems. * * \ingroup mi2Cvt */ int miset_spatial_frequency_origin(mihandle_t volume, double world[]); /** This function retrieves the real values of a position in the * MINC volume. The "real" value is the value at the given location * after scaling has been applied. * * \param volume A volume handle * \param coords The voxel position to retrieve * \param ndims The number of values in the \a coords array * \param value_ptr Pointer to a double variable to hold the returned value. * * \ingroup mi2Cvt */ int miget_real_value(mihandle_t volume, const misize_t coords[], int ndims, double *value_ptr); /** This function sets the real value of a position in the MINC * volume. The "real" value is the value at the given location * after scaling has been applied. * * \param volume A volume handle * \param coords The voxel position to retrieve * \param ndims The number of values in the \a coords array * \param value The value to save at this location. * * \ingroup mi2Cvt */ int miset_real_value(mihandle_t volume, const misize_t coords[], int ndims, double value); /** This function retrieves the voxel values of a position in the * MINC volume. The voxel value is the unscaled value, and corresponds * to the value actually stored in the file. * * \ingroup mi2Cvt */ int miget_voxel_value(mihandle_t volume, const misize_t coords[], int ndims, double *voxel_ptr); /** This function sets the voxel value of a position in the MINC * volume. The voxel value is the unscaled value, and corresponds to the * value actually stored in the file. * * \ingroup mi2Cvt */ int miset_voxel_value(mihandle_t volume, const misize_t coords[], int ndims, double voxel); /** Get the absolute minimum and maximum values of a volume. * * \ingroup mi2Cvt */ int miget_volume_real_range(mihandle_t volume, double real_range[2]); /** * This function sets the world coordinates of the point (0,0,0) in voxel * coordinates. This changes the constant offset of the two coordinate * systems. * * \ingroup mi2Cvt */ int miset_world_origin(mihandle_t volume, double origin[MI2_3D]); /* VALID functions */ /** This function gets the maximum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_max(mihandle_t volume, double *valid_max); /** This function sets the maximum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_max(mihandle_t volume, double valid_max); /** This function gets the minimum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_min(mihandle_t volume, double *valid_min); /** This function sets the minimum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_min(mihandle_t volume, double valid_min); /** This function gets the minimum and maximum valid value specific to the * data type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_range(mihandle_t volume, double *valid_max, double *valid_min); /** This function sets the minimum and maximum valid value specific to the * data type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_range(mihandle_t volume, double valid_max, double valid_min); /** \defgroup mi2Rec RECORD functions */ /** This method gets the name of the record dimension * TODO: set record name?? * \ingroup mi2Rec */ int miget_record_name(mihandle_t volume, char **name); /** This method gets the length (i.e., number of fields in the case of * uniform records and number of bytes for non_uniform ones) of the * record. * \ingroup mi2Rec */ int miget_record_length(mihandle_t volume, int *length); /** This method returns the field name for the given field index. Memory * for returned string is allocated on the heap and should be released using * mifree_name(). * \ingroup mi2Rec */ int miget_record_field_name(mihandle_t volume, int index, char **name); /** This method sets a field name for the volume record. The volume * must be of class "MI_CLASS_UNIFORM_RECORD". The size of record * type will be increased if necessary to accomodate the new field. * \ingroup mi2Rec */ int miset_record_field_name(mihandle_t volume, int index, const char *name); /** \ingroup mi2Label LABEL functions */ /** * This function associates a label name with an integer value for the given * volume. Functions which read and write voxel values will read/write * in integer values, and must call miget_label_name() to discover the * descriptive text string which corresponds to the integer value. * \ingroup mi2Label */ int midefine_label(mihandle_t volume, int value, const char *name); /** * For a labelled volume, this function retrieves the text name * associated with a given integer value. * * The name pointer returned must be freed by calling mifree_name(). * \ingroup mi2Label */ int miget_label_name(mihandle_t volume, int value, char **name); /** * This function is the inverse of miget_label_name(). It is called to determine * what integer value, if any, corresponds to the given text string. * \ingroup mi2Label */ int miget_label_value(mihandle_t volume, const char *name, int *value); /** * This function returns the number of defined labels, if any, or zero. * \ingroup mi2Label */ int miget_number_of_defined_labels(mihandle_t volume, int *number_of_labels); /** * This function returns the label value associated with an index (0,1,...) * \ingroup mi2Label */ int miget_label_value_by_index(mihandle_t volume, int idx, int *value); #ifdef __cplusplus } #endif /* __cplusplus defined */ #endif /*MINC2_API_H*/ // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/minc2_defs.h000066400000000000000000000351471257462267400203750ustar00rootroot00000000000000/** minc2 definitions*/ #ifndef MINC2_DEFS_H #define MINC2_DEFS_H #ifndef MNCAPI #if defined(_MSC_VER) /* If we are building on the Microsoft C compiler, we want to * explicitly import all public functions from the DLL */ #define MNCAPI __declspec(dllimport) #else #define MNCAPI #endif /* _MSC_VER not defined */ #endif /* MNCAPI not defined */ /************************************************************************ * CONSTANTS ************************************************************************/ /* Some useful constants */ #define MI_EMPTY_STRING "" /* Error flags */ #define MI_ERROR (-1) #define MI_NOERROR 0 /* Maximum length of standard attributes */ #define MI_MAX_ATTSTR_LEN 64 /* Number of spatial dimensions */ #define MI_NUM_SPACE_DIMS 3 /* Maximum number of image dimensions for image conversion */ /* Bert 10-Aug-2004 - MI_MAX_IMGDIMS used to be defined to be MAX_VAR_DIMS, * a constant defined in netcdf.h. For many years MAX_VAR_DIMS was 100, * but in netCDF 3.5.1 the value was changed to 512. * Unfortunately, the definitions of MI2_ICV_DIM_SIZE, MI2_ICV_DIM_STEP, * and MI2_ICV_DIM_START assume that MI_MAX_IMGDIMS is less than or * equal to 100. To avoid changing the MINC API, we have to define * MI_MAX_IMGDIMS to 100 here. Otherwise the miicv_inqdbl() function * will return bogus values for these ICV properties. */ #define MI_MAX_IMGDIMS 100 #define MI2_MAX_IMGDIMS 100 /* Epsilon for detecting fillvalues */ #define MI2_FILLVALUE_EPSILON (10.0 * FLT_EPSILON) /* NetCDF standard attributes */ #define MIunits "units" #define MIlong_name "long_name" #define MIvalid_range "valid_range" #define MIvalid_max "valid_max" #define MIvalid_min "valid_min" #define MI_FillValue "_FillValue" #define MItitle "title" #define MIhistory "history" /* General variable attributes */ #define MIvartype "vartype" #define MIvarid "varid" #define MIsigntype "signtype" #define MIparent "parent" #define MIchildren "children" #define MIcomments "comments" #define MIversion "version" /* General attribute constants */ /* Prefix for identifying a variable attribute pointer */ #define MI_VARATT_POINTER_PREFIX "--->" /* Separator for elements of MIchildren */ #define MI_CHILD_SEPARATOR "\n" /* MIvartype values */ #define MI_GROUP "group________" #define MI_DIMENSION "dimension____" #define MI_DIM_WIDTH "dim-width____" #define MI_VARATT "var_attribute" /* MIvarid value */ #define MI_STDVAR "MINC standard variable" /* MIsigntype values */ #define MI_SIGNED "signed__" #define MI_UNSIGNED "unsigned" /* MIversion value */ #define MI_VERSION_1_0 "MINC Version 1.0" #define MI_CURRENT_VERSION MI_VERSION_1_0 /* Generally useful values for boolean attributes */ #define MI_TRUE "true_" #define MI_FALSE "false" /* Dimension names and names of associated variables */ #define MIxspace "xspace" #define MIyspace "yspace" #define MIzspace "zspace" #define MItime "time" #define MItfrequency "tfrequency" #define MIxfrequency "xfrequency" #define MIyfrequency "yfrequency" #define MIzfrequency "zfrequency" #define MIvector_dimension "vector_dimension" #define MIxspace_width "xspace-width" #define MIyspace_width "yspace-width" #define MIzspace_width "zspace-width" #define MItime_width "time-width" #define MItfrequency_width "tfrequency-width" #define MIxfrequency_width "xfrequency-width" #define MIyfrequency_width "yfrequency-width" #define MIzfrequency_width "zfrequency-width" /* Dimension variable attribute names */ /* For dimension variables (MIspacing is also for dimension width vars) */ #define MIspacing "spacing" #define MIstep "step" #define MIstart "start" #define MIspacetype "spacetype" #define MIalignment "alignment" #define MIdirection_cosines "direction_cosines" /* For dimension width variables */ #define MIwidth "width" #define MIfiltertype "filtertype" /* Dimension attribute constants */ /* MIgridtype values */ #define MI_REGULAR "regular__" #define MI_IRREGULAR "irregular" /* MIspacetype values */ #define MI_NATIVE "native____" #define MI_TALAIRACH "talairach_" #define MI_CALLOSAL "callosal__" /* MIalignment values */ #define MI_START "start_" #define MI_CENTRE "centre" #define MI_END "end___" #define MI_CENTER MI_CENTRE /* MIfiltertype values */ #define MI_SQUARE "square____" #define MI_GAUSSIAN "gaussian__" #define MI_TRIANGULAR "triangular" /* The root variable */ #define MIrootvariable "rootvariable" /* The image variable and its attributes */ #define MIimage "image" #define MIimagemax "image-max" #define MIimagemin "image-min" #define MIcomplete "complete" /* The patient variable and its attributes */ #define MIpatient "patient" #define MIfull_name "full_name" #define MIother_names "other_names" #define MIidentification "identification" #define MIother_ids "other_ids" #define MIbirthdate "birthdate" #define MIsex "sex" #define MIage "age" #define MIweight "weight" #define MIsize "size" #define MIaddress "address" #define MIinsurance_id "insurance_id" /* Patient attribute constants */ #define MI_MALE "male__" #define MI_FEMALE "female" #define MI_OTHER "other_" /* The study variable and its attributes */ #define MIstudy "study" #define MIstart_time "start_time" #define MIstart_year "start_year" #define MIstart_month "start_month" #define MIstart_day "start_day" #define MIstart_hour "start_hour" #define MIstart_minute "start_minute" #define MIstart_seconds "start_seconds" #define MImodality "modality" #define MImanufacturer "manufacturer" #define MIdevice_model "device_model" #define MIinstitution "institution" #define MIdepartment "department" #define MIstation_id "station_id" #define MIreferring_physician "referring_physician" #define MIattending_physician "attending_physician" #define MIradiologist "radiologist" #define MIoperator "operator" #define MIadmitting_diagnosis "admitting_diagnosis" #define MIprocedure "procedure" #define MIstudy_id "study_id" /* Study attribute constants */ #define MI_PET "PET__" #define MI_SPECT "SPECT" #define MI_GAMMA "GAMMA" #define MI_MRI "MRI__" #define MI_MRS "MRS__" #define MI_MRA "MRA__" #define MI_CT "CT___" #define MI_DSA "DSA__" #define MI_DR "DR___" #define MI_LABEL "label" /* The acquisition variable and its attributes */ #define MIacquisition "acquisition" #define MIprotocol "protocol" #define MIscanning_sequence "scanning_sequence" #define MIrepetition_time "repetition_time" #define MIecho_time "echo_time" #define MIinversion_time "inversion_time" #define MInum_averages "num_averages" #define MIimaging_frequency "imaging_frequency" #define MIimaged_nucleus "imaged_nucleus" #define MIradionuclide "radionuclide" #define MIcontrast_agent "contrast_agent" #define MIradionuclide_halflife "radionuclide_halflife" #define MItracer "tracer" #define MIinjection_time "injection_time" #define MIinjection_year "injection_year" #define MIinjection_month "injection_month" #define MIinjection_day "injection_day" #define MIinjection_hour "injection_hour" #define MIinjection_minute "injection_minute" #define MIinjection_seconds "injection_seconds" #define MIinjection_length "injection_length" #define MIinjection_dose "injection_dose" #define MIdose_units "dose_units" #define MIinjection_volume "injection_volume" #define MIinjection_route "injection_route" /* Error codes. * Note that they must not conflict with NetCDF error codes since * they are stored in the same global variable. */ #define MI_ERR_NONNUMERIC 1331 /* Non-numeric type */ #define MI_ERR_NONCHAR 1332 /* Non-character type */ #define MI_ERR_NONSCALAR 1333 /* Non-scalar attribute */ #define MI_ERR_BADOP 1334 /* Bad operation for MI_varaccess */ #define MI_ERR_NOTPOINTER 1335 /* Attribute is not a pointer */ #define MI_ERR_BAD_STDVAR 1336 /* Not a standard variable */ #define MI_ERR_BADSUFFIX 1337 /* Bad dimension width suffix */ #define MI_ERR_NOICV 1338 /* Out of icv slots */ #define MI_ERR_BADICV 1339 /* Illegal icv identifier */ #define MI_ERR_BADPROP 1340 /* Unknown icv property */ #define MI_ERR_ICVATTACHED 1341 /* Tried to modify attached icv */ #define MI_ERR_TOOFEWDIMS 1342 /* Too few dimensions to be an image */ #define MI_ERR_ICVNOTATTACHED 1343 /* Tried to access an unattached icv */ #define MI_ERR_DIMSIZE 1344 /* Dimensions differ in size */ #define MI_ERR_ICV_INVCOORDS 1345 /* Invalid icv coordinates */ #define MI_ERR_WRONGNDIMS 1346 /* Too many dimensions for a dim var */ #define MI_ERR_BADMATCH 1347 /* Variables do not match for copy */ #define MI_ERR_MAXMIN_DIMS 1348 /* Imagemax/min variables vary over image dimensions */ #define MI_ERR_UNCOMPRESS 1349 /* Not able to uncompress file */ #ifndef MI_NOERROR /** Generic return code for successful operations. */ #define MI_NOERROR 0 #endif /* MI_NOERROR not defined */ #ifndef MI2_NOERROR /** Generic return code for successful operations. */ #define MI2_NOERROR MI_NOERROR #endif /* MI2_NOERROR not defined */ #ifndef MI_ERROR /** Generic return code for operations which fail for any reason. */ #define MI_ERROR (-1) #endif /* MI_ERROR not defined */ #ifndef MI2_ERROR /** Generic return code for operations which fail for any reason. */ #define MI2_ERROR MI_ERROR #endif /* MI2_ERROR not defined */ #define MI_NATIVE "native____" #define MI_TALAIRACH "talairach_" #define MI_CALLOSAL "callosal__" #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #ifndef FALSE #define FALSE 0 #endif /* FALSE */ /** World spatial coordinates should always have this structure. */ #define MI2_3D 3 #define MI2_X 0 #define MI2_Y 1 #define MI2_Z 2 /** Dimension attribute values. */ #define MI_DIMATTR_ALL 0 #define MI_DIMATTR_REGULARLY_SAMPLED 0x1 #define MI_DIMATTR_NOT_REGULARLY_SAMPLED 0x2 /** Maximum length of a standard string. */ #define MI2_CHAR_LENGTH 128 /** Maximum number of dimensions a variable can have. */ #define MI2_MAX_VAR_DIMS 100 /** * Maximum length of dimension name TODO: check in HDF5 documentation * */ #define MI2_MAX_DIM_NAME 256 #define MI2_CHUNK_SIZE 32 /* Length of chunk, per dimension */ #define MI2_DEFAULT_ZLIB_LEVEL 4 #define MI2_MAX_ZLIB_LEVEL 9 #define MI2_MAX_PATH 128 #define MI2_MAX_RESOLUTION_GROUP 16 #define MI2_OPEN_READ 0x0001 #define MI2_OPEN_RDWR 0x0002 #define MI_VERSION_2_0 "MINC Version 2.0" /** * MINC2 ICV * * */ #define MI2_PRIV_DEFSIGN 0 #define MI2_PRIV_SIGNED 1 #define MI2_PRIV_UNSIGNED 2 /* Operations for MI_varaccess */ #define MI2_PRIV_GET 10 #define MI2_PRIV_PUT 11 /* Constants for image conversion variable (icv) properties */ /* Maximum number of icv's allowed */ #define MI2_MAX_NUM_ICV 1000 /**< Currently, this is never used. */ /* Default max and min for normalization */ #define MI2_DEFAULT_MAX 1.0 #define MI2_DEFAULT_MIN 0.0 /* For converting data type */ #define MI2_ICV_TYPE 1 #define MI2_ICV_SIGN 2 #define MI2_ICV_DO_RANGE 3 #define MI2_ICV_VALID_MAX 4 #define MI2_ICV_VALID_MIN 5 /* For doing normalization */ #define MI2_ICV_DO_NORM 6 #define MI2_ICV_USER_NORM 7 #define MI2_ICV_IMAGE_MAX 8 #define MI2_ICV_IMAGE_MIN 9 /* Values actually used in normalization - read-only */ #define MI2_ICV_NORM_MAX 10 #define MI2_ICV_NORM_MIN 11 /* For doing dimension conversions */ #define MI2_ICV_DO_DIM_CONV 12 /* For converting vector fields to scalar */ #define MI2_ICV_DO_SCALAR 13 /* For flipping axis direction */ #define MI2_ICV_XDIM_DIR 14 #define MI2_ICV_YDIM_DIR 15 #define MI2_ICV_ZDIM_DIR 16 /* For changing size of first two dimensions (excluding MIvector_dimension) */ #define MI2_ICV_ADIM_SIZE 17 #define MI2_ICV_BDIM_SIZE 18 #define MI2_ICV_KEEP_ASPECT 19 /* The pixel size and location of first two dimensions (these are readonly) */ #define MI2_ICV_ADIM_STEP 20 #define MI2_ICV_BDIM_STEP 21 #define MI2_ICV_ADIM_START 22 #define MI2_ICV_BDIM_START 23 /* Number of image dimensions for dimension conversion */ #define MI2_ICV_NUM_IMGDIMS 24 /* Number of dimensions of image variable taking into account vector/scalar data (read-only property) */ #define MI2_ICV_NUM_DIMS 25 /* Id of file and image variable (read-only properties) */ #define MI2_ICV_CDFID 26 #define MI2_ICV_VARID 27 /* Names of MIimagemax and MIimagemin variables */ #define MI2_ICV_MAXVAR 28 #define MI2_ICV_MINVAR 29 /* For setting input values to a specified fillvalue */ #define MI2_ICV_DO_FILLVALUE 30 #define MI2_ICV_FILLVALUE 31 /* Image dimension properties. For each dimension, add the dimension number (counting from fastest to slowest). */ #define MI2_ICV_DIM_SIZE 1000 #define MI2_ICV_DIM_STEP 1100 #define MI2_ICV_DIM_START 1200 /* Constants that can be used as values for the above properties. */ /* Possible values for MI2_ICV_?DIM_DIR */ #define MI2_ICV_POSITIVE 1 #define MI2_ICV_NEGATIVE (-1) #define MI2_ICV_ANYDIR 0 /* Possible value for MI2_ICV_?DIM_SIZE */ #define MI2_ICV_ANYSIZE (-1) /** * Error handling macros * take from minc * */ #define MI2_SAVE_ROUTINE_NAME(name) MI2_save_routine_name(name) #define MI2_RETURN(value) \ return( MI2_return() ? (value) : (value) ) #define MI2_RETURN_ERROR(error) \ return( MI2_return_error() ? (error) : (error) ) #define MI2_LOG_PKG_ERROR2(p1,p2) MI2_log_pkg_error2(p1, p2) #define MI2_LOG_PKG_ERROR3(p1,p2,p3) MI2_log_pkg_error3(p1, p2, p3) #define MI2_LOG_SYS_ERROR1(p1) MI2_log_sys_error1(p1) #define MI2_CHK_ERR(expr) {if ((expr)<0) MI2_RETURN_ERROR(MI_ERROR);} #define MICFG_FORCE_V2 "MINC_FORCE_V2" #define MICFG_COMPRESS "MINC_COMPRESS" #define MICFG_CHUNKING "MINC_CHUNKING" #define MICFG_LOGFILE "MINC_LOGFILE" #define MICFG_LOGLEVEL "MINC_LOGLEVEL" #define MICFG_MAXBUF "MINC_MAX_FILE_BUFFER_KB" #define MICFG_MAXMEM "MINC_MAX_MEMORY_KB" /* DUMMY rootvariable ID */ #define MI2_ROOTVARIABLE_ID (NC_MAX_VARS + 1) /* Impossible value */ /* stupid macro which was never put to use*/ #ifndef _ #define _(x) x /* For future gettext */ #endif #endif /*MINC2_DEFS_H*/ // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/minc2_error.c000066400000000000000000000320721257462267400205720ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc2_error.c @DESCRIPTION: File containing routines to do error handling for MINC package. Should be called through macros in minc_private.h @GLOBALS : @CALLS : @CREATED : August 7, 1992 (Peter Neelin) @MODIFIED : * $Log: minc2_error.c,v $ * Revision 6.8 2009-01-20 11:58:13 rotor * * CMakeLists.txt: updated version * * Updated Changelog to include releases * * Warning cleanups below * * conversion/dcm2mnc/minc_file.c: fixed printf type * * conversion/dcm2mnc/siemens_to_dicom.c: fixed printf type * * conversion/ecattominc/machine_indep.c: added string.h and fixed * 2 fprintf missing format args * * conversion/micropet/upet2mnc.c: fixed two fprintf format args * * conversion/minctoecat/ecat_write.c: added string.h * * conversion/minctoecat/minctoecat.c: added missing argument to fprintf * * conversion/nifti1/mnc2nii.c: fixed incorrect printf type * * progs/mincview/invert_raw_image.c: added fwrite checking * * Revision 6.7 2008/04/11 05:15:00 rotor * * rewrote error code (Claude) to remove global defs that were * causing build problems with DYLIB on OSX * * Revision 6.6 2008/01/17 02:33:02 rotor * * removed all rcsids * * removed a bunch of ^L's that somehow crept in * * removed old (and outdated) BUGS file * * Revision 6.5 2008/01/12 19:08:14 stever * Add __attribute__ ((unused)) to all rcsid variables. * * Revision 6.4 2004/10/15 13:46:15 bert * Minor changes for Windows compatibility * * Revision 6.3 2004/04/27 15:47:25 bert * Move most message text into this file * * Revision 6.2 2001/04/17 18:40:13 neelin * Modifications to work with NetCDF 3.x * In particular, changed NC_LONG to NC_INT (and corresponding longs to ints). * Changed NC_UNSPECIFIED to NC_NAT. * A few fixes to the configure script. * * Revision 6.1 1999/10/19 14:45:09 neelin * Fixed Log subsitutions for CVS * * Revision 6.0 1997/09/12 13:24:54 neelin * Release of minc version 0.6 * * Revision 5.0 1997/08/21 13:25:53 neelin * Release of minc version 0.5 * * Revision 4.0 1997/05/07 20:07:52 neelin * Release of minc version 0.4 * * Revision 3.0 1995/05/15 19:33:12 neelin * Release of minc version 0.3 * * Revision 2.0 1994/09/28 10:38:04 neelin * Release of minc version 0.2 * * Revision 1.7 94/09/28 10:37:16 neelin * Pre-release * * Revision 1.6 93/08/11 12:06:24 neelin * Added RCS logging in source. * @COPYRIGHT : Copyright 1993 Peter Neelin, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif //HAVE_CONFIG_H #include #include #include #include #include #include #include "minc2.h" #include "minc2_private.h" struct mierror_entry { int level; char *msgfmt; }; /* MINC routine name variable, call depth counter (for keeping track of minc routines calling minc routines) and variable for keeping track of callers ncopts. All of these are for error logging. */ static char *minc_routine_name = "MINC2"; static int minc_call_depth = 0; static int minc_trash_var = 0; static struct mierror_entry mierror_table[] = { { MI2_MSG_ERROR, "Cannot uncompress the file" }, /* MI2_MSG_UNCMPFAIL */ { MI2_MSG_ERROR, "Can't write compressed file" }, /* MI2_MSG_NOWRITECMP */ { MI2_MSG_ERROR, "Unable to open file '%s'" }, /* MI2_MSG_OPENFILE */ { MI2_MSG_ERROR, "Unable to create file '%s'"}, /* MI2_MSG_CREATEFILE */ { MI2_MSG_ERROR, "Error closing file"}, /* MI2_MSG_CLOSEFILE */ { MI2_MSG_WARNING, "Attribute '%s' not found"}, /* MI2_MSG_FINDATTR */ { MI2_MSG_ERROR, "Attribute '%s' is non-numeric"}, /* MI2_MSG_ATTRNOTNUM */ { MI2_MSG_ERROR, "Can't read attribute '%s'"}, /* MI2_MSG_READATTR */ { MI2_MSG_FATAL, "No memory for attribute '%s'"}, /* MI2_MSG_NOMEMATTR */ { MI2_MSG_ERROR, "Conversion error for attribute '%s'"}, /* MI2_MSG_CONVATTR */ { MI2_MSG_ERROR, "Attribute '%s' is not a scalar"}, /* MI2_MSG_ATTRNOTSCALAR */ { MI2_MSG_ERROR, "Attribute '%s' is not a string"}, /* MI2_MSG_ATTRNOTSTR */ { MI2_MSG_ERROR, "Can't write attribute '%s'"}, /* MI2_MSG_WRITEATTR */ { MI2_MSG_ERROR, "Can't read variable ID# %d"}, /* MI2_MSG_READVAR */ { MI2_MSG_ERROR, "Can't write variable ID# %d"}, /* MI2_MSG_WRITEVAR */ { MI2_MSG_ERROR, "Can't find variable ID# %d"}, /* MI2_MSG_FINDVAR */ { MI2_MSG_ERROR, "Can't read attribute count"}, /* MI2_MSG_ATTRCOUNT */ { MI2_MSG_ERROR, "Can't read attribute name"}, /* MI2_MSG_ATTRNAME */ { MI2_MSG_ERROR, "Can't copy attribute '%s'"}, /* MI2_MSG_COPYATTR */ { MI2_MSG_ERROR, "Can't read variable information"}, /* MI2_MSG_VARINQ */ { MI2_MSG_ERROR, "Can't get unlimited dimension"}, /* MI2_MSG_UNLIMDIM */ { MI2_MSG_ERROR, "Can't get dimension information"}, /* MI2_MSG_DIMINQ */ { MI2_MSG_ERROR, "Variable already defined with different size"}, /* MI2_MSG_VARCONFLICT */ { MI2_MSG_ERROR, "Can't define dimension '%s'"}, /* MI2_MSG_DIMDEF */ { MI2_MSG_ERROR, "Can't define variable '%s'"}, /* MI2_MSG_VARDEF */ { MI2_MSG_ERROR, "Variables do not match for value copy"}, /* MI2_MSG_VARMISMATCH */ { MI2_MSG_ERROR, "Variables have dimensions of different size"}, /* MI2_MSG_VARDIFFSIZE */ { MI2_MSG_ERROR, "Can't read variable count"}, /* MI2_MSG_VARCOUNT */ { MI2_MSG_ERROR, "Variable '%s' not copied"}, /* MI2_MSG_OUTPUTVAR */ { MI2_MSG_ERROR, "Error copying variable"}, /* MI2_MSG_COPYVAR */ { MI2_MSG_ERROR, "Non-numeric datatype"}, /* MI2_MSG_VARNOTNUM */ { MI2_MSG_FATAL, "Can't allocate %d bytes"}, /* MI2_MSG_OUTOFMEM */ { MI2_MSG_ERROR, "Attribute '%s' is not a pointer"}, /* MI2_MSG_ATTRNOTPTR */ { MI2_MSG_ERROR, "Variable '%s' is not a standard MINC variable"}, /* MI2_MSG_VARNOTSTD */ { MI2_MSG_ERROR, "Bad dimension width suffix"}, /* MI2_MSG_DIMWIDTH */ { MI2_MSG_ERROR, "Imagemax/min dimensions vary over image dimensions"}, /* MI2_MSG_MAXMINVARY */ { MI2_MSG_FATAL, "Should not happen!"}, /* MI2_MSG_SNH */ { MI2_MSG_FATAL, "Unknown integer size %d"}, /* MI2_MSG_INTSIZE */ { MI2_MSG_FATAL, "Unknown float size %d"}, /* MI2_MSG_FLTSIZE */ { MI2_MSG_FATAL, "Unknown type class %d"}, /* MI2_MSG_TYPECLASS */ { MI2_MSG_ERROR, "Function '%s' not implemented"}, /* MI2_MSG_NOTIMPL */ { MI2_MSG_FATAL, "Unknown type %d"}, /* MI2_MSG_BADTYPE */ { MI2_MSG_ERROR, "Can't open dataset %s"}, /* MI2_MSG_OPENDSET */ { MI2_MSG_ERROR, "Can't read dataset %s"}, /* MI2_MSG_READDSET */ { MI2_MSG_ERROR, "Can't write dataset %s"}, /* MI2_MSG_WRITEDSET */ { MI2_MSG_ERROR, "Can't use more than %d dimensions"}, /* MI2_MSG_TOOMANYDIMS */ { MI2_MSG_ERROR, "Attempt to modify an attached image conversion variable"}, /* MI2_MSG_ICVATTACHED */ { MI2_MSG_ERROR, "Illegal ICV identifier"}, /* MI2_MSG_BADICV */ { MI2_MSG_ERROR, "Error setting ICV property: %s"}, /* MI2_MSG_BADPROP */ { MI2_MSG_ERROR, "ICV is not attached"}, /* MI2_MSG_ICVNOTATTACHED */ { MI2_MSG_ERROR, "Invalid ICV coordinates"}, /* MI2_MSG_ICVCOORDS */ { MI2_MSG_ERROR, "Illegal variable access operation" }, /* MI2_MSG_BADOP */ { MI2_MSG_ERROR, "HDF5 function %s failed" } , /*MI2_MSG_HDF5*/ { MI2_MSG_ERROR, "Error: %s"} , /*MI2_MSG_GENERIC*/ }; int MI2_save_routine_name ( char *name ) { /* no idea what peter was up to here */ /* minc_trash_var = (((minc_call_depth++)==0) ? MI_save_routine_name(name) : * MI_NOERROR)) */ if ( ( minc_call_depth++ ) == 0 ) { minc_routine_name = name; minc_trash_var = TRUE; } else { minc_trash_var = MI_NOERROR; } return ( TRUE ); } int MI2_return ( void ) { /* no idea what peter was up to here */ /* return( (((--minc_call_depth)!=0) || MI_return()) ? (value) : (value)) */ return ( ( ( --minc_call_depth ) != 0 ) || TRUE ); } int MI2_return_error ( void ) { /* no idea what peter was up to here */ /* return( (((--minc_call_depth)!=0) || MI_return_error()) ? (error) : (error)) */ if ( ( --minc_call_depth ) == 0 ) { MI2_log_pkg_error2 ( 0, "MINC package entry point" ); } return ( TRUE ); } void MI2_log_pkg_error2 ( int p1, char *p2 ) { ( void ) fprintf ( stderr, "%s: ", minc_routine_name ); ( void ) fprintf ( stderr, "%s", p2 ); ( void ) fputc ( '\n', stderr ); ( void ) fflush ( stderr ); } void MI2_log_pkg_error3 ( int p1, char *p2, char *p3 ) { ( void ) fprintf ( stderr, "%s: ", minc_routine_name ); ( void ) fprintf ( stderr, p2, p3 ); ( void ) fputc ( '\n', stderr ); ( void ) fflush ( stderr ); } void MI2_log_sys_error1 ( char *p1 ) { char *message; int errnum = errno; ( void ) fprintf ( stderr, "%s", minc_routine_name ); ( void ) fprintf ( stderr, "%s", p1 ); if ( errnum == 0 ) { ( void ) fputc ( '\n', stderr ); } else { message = strerror ( errnum ); if ( message == NULL ) message = "Unknown error"; ( void ) fprintf ( stderr, ": %s\n", message ); } ( void ) fflush ( stderr ); } /* By default, print all messages of severity error, or worse. */ static struct { int level; char prog[128]; FILE *fp; } _MI2_log = { MI2_MSG_ERROR, {""}, NULL }; /** Simple function to read a user's .mincrc file, if present. */ static int mi2read_cfg(const char *name, char *buffer, int maxlen) { FILE *fp; int result = 0; char *home_ptr = getenv("HOME"); char path[256]; if (home_ptr != NULL) { strcpy(path, home_ptr); } else { path[0] = '\0'; } strcat(path, "/.mincrc"); if ((fp = fopen(path, "r")) != NULL) { while (fgets(buffer, maxlen, fp)) { if (buffer[0] == '#') { continue; } if (!strncasecmp(buffer, name, strlen(name))) { char *tmp = strchr(buffer, '='); if (tmp != NULL) { tmp++; while (isspace(*tmp)) { tmp++; } strncpy(buffer, tmp, maxlen); result = 1; break; } } } fclose(fp); } return (result); } static int mi2get_cfg_int(const char *name) { char buffer[128]; char *var_ptr; if ((var_ptr = getenv(name)) != NULL) { strncpy(buffer, var_ptr, sizeof (buffer)); } else { if (!mi2read_cfg(name, buffer, sizeof(buffer))) { return (0); } } return (atoi(buffer)); } static char * mi2get_cfg_str(const char *name) { char buffer[256]; char *var_ptr; if ((var_ptr = getenv(name)) != NULL) { strncpy(buffer, var_ptr, sizeof(buffer)); } else { if (!mi2read_cfg(name, buffer, sizeof(buffer))) { return (NULL); } } return (strdup(buffer)); } void mi2log_init ( const char *name ) { char *fname_str = mi2get_cfg_str ( MICFG_LOGFILE ); int level = mi2get_cfg_int ( MICFG_LOGLEVEL ); if ( fname_str == NULL ) { _MI2_log.fp = stderr; } else if ( !strcmp ( fname_str, "stdout" ) || !strcmp ( fname_str, "-" ) ) { _MI2_log.fp = stdout; } else { if ( *fname_str == '+' ) { _MI2_log.fp = fopen ( fname_str + 1, "w+" ); } else { _MI2_log.fp = fopen ( fname_str, "w" ); } } if ( level != 0 ) { _MI2_log.level = level; } strncpy ( _MI2_log.prog, name, sizeof ( _MI2_log.prog ) ); if ( fname_str != NULL ) { free ( fname_str ); } } int mi2log_set_verbosity ( int lvl ) { int lvl_prev = _MI2_log.level; _MI2_log.level = lvl; return ( lvl_prev ); } int mi2log_message ( const char *file,int line, mi2msgcode_t code, ... ) { va_list ap; int lvl; const char *fmt; if ( _MI2_log.fp == NULL ) { _MI2_log.fp = stderr; } lvl = mierror_table[code - MI2_MSG_BASE].level; fmt = mierror_table[code - MI2_MSG_BASE].msgfmt; /* Log the message if the the message priority * is less than the configured priority. Always log fatal errors. */ if ( ( lvl <= _MI2_log.level ) || lvl == MI2_MSG_FATAL ) { if ( _MI2_log.prog[0] != '\0' ) { fprintf ( _MI2_log.fp, "%s:%d %s ", file, line, _MI2_log.prog ); } fprintf ( _MI2_log.fp, "%s:%d (from %s): ", file, line, minc_routine_name ); va_start ( ap, code ); vfprintf ( _MI2_log.fp, fmt, ap ); va_end ( ap ); fprintf ( _MI2_log.fp, "\n" ); fflush ( _MI2_log.fp ); } /* For fatal messages, give up and exit. */ if ( lvl == MI2_MSG_FATAL ) { /*exit ( -1 ); SORRRY, no exits in my programs!*/ return ( MI_ERROR ); /* Just for convenience */ } return ( MI_ERROR ); /* Just for convenience */ } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/minc2_error.h000066400000000000000000000067451257462267400206070ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : minc2_error.h @DESCRIPTION: File containing error codes for libminc package. @GLOBALS : @CALLS : @CREATED : 17 Feburary, 2004 (Robert Vincent) @MODIFIED : * * $Log: minc_error.h,v $ * Revision 6.4 2008-04-11 05:15:00 rotor * * rewrote error code (Claude) to remove global defs that were * causing build problems with DYLIB on OSX * * Revision 6.3 2004/12/03 21:52:35 bert * Minor changes for Windows build * * Revision 6.2 2004/10/15 13:46:15 bert * Minor changes for Windows compatibility * * Revision 6.1 2004/04/27 15:42:47 bert * Define MINC logging codes * * @COPYRIGHT : Copyright 2004 Robert Vincent, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifndef MINC2_ERROR_H #define MINC2_ERROR_H /* message levels */ #define MI2_MSG_FATAL 0 #define MI2_MSG_ERROR 1 #define MI2_MSG_WARNING 2 #define MI2_MSG_INFO 3 #define MI2_MSG_DEBUG 4 #define MI2_MSG_BASE (10000) typedef enum mi2msgcode { MI2_MSG_UNCMPFAIL = MI2_MSG_BASE, MI2_MSG_NOWRITECMP, MI2_MSG_OPENFILE, MI2_MSG_CREATEFILE, MI2_MSG_CLOSEFILE, MI2_MSG_FINDATTR, MI2_MSG_ATTRNOTNUM, MI2_MSG_READATTR, MI2_MSG_NOMEMATTR, MI2_MSG_CONVATTR, MI2_MSG_ATTRNOTSCALAR, MI2_MSG_ATTRNOTSTR, MI2_MSG_WRITEATTR, MI2_MSG_READVAR, MI2_MSG_WRITEVAR, MI2_MSG_FINDVAR, MI2_MSG_ATTRCOUNT, MI2_MSG_ATTRNAME, MI2_MSG_COPYATTR, MI2_MSG_VARINQ, MI2_MSG_UNLIMDIM, MI2_MSG_DIMINQ, MI2_MSG_VARCONFLICT, MI2_MSG_DIMDEF, MI2_MSG_VARDEF, MI2_MSG_VARMISMATCH, MI2_MSG_VARDIFFSIZE, MI2_MSG_VARCOUNT, MI2_MSG_OUTPUTVAR, MI2_MSG_COPYVAR, MI2_MSG_VARNOTNUM, MI2_MSG_OUTOFMEM, MI2_MSG_ATTRNOTPTR, MI2_MSG_VARNOTSTD, MI2_MSG_DIMWIDTH, MI2_MSG_MAXMINVARY, MI2_MSG_SNH, MI2_MSG_INTSIZE, MI2_MSG_FLTSIZE, MI2_MSG_TYPECLASS, MI2_MSG_NOTIMPL, MI2_MSG_BADTYPE, MI2_MSG_OPENDSET, MI2_MSG_READDSET, MI2_MSG_WRITEDSET, MI2_MSG_TOOMANYDIMS, MI2_MSG_ICVATTACHED, MI2_MSG_BADICV, MI2_MSG_BADPROP, MI2_MSG_ICVNOTATTACHED, MI2_MSG_ICVCOORDS, MI2_MSG_BADOP, MI2_MSG_HDF5, MI2_MSG_GENERIC } mi2msgcode_t; int mi2log_message(const char *file,int line, mi2msgcode_t code, ...); int MI2_save_routine_name(char *name); int MI2_return(void); int MI2_return_error(void); void MI2_log_pkg_error2(int p1, char *p2); void MI2_log_pkg_error3(int p1, char *p2, char *p3); void MI2_log_sys_error1(char *p1); void mi2log_init(const char *name); int mi2log_set_verbosity ( int lvl ); #define MI_LOG_ERROR(code,...) mi2log_message(__FILE__,__LINE__,code , ##__VA_ARGS__ ) #define MI_CHECK_HDF_CALL(var,call) {if((var)<0) MI_LOG_ERROR(MI2_MSG_HDF5,call);} #define MI_CHECK_HDF_CALL_RET(var,call) {if((var)<0) return MI_LOG_ERROR(MI2_MSG_HDF5,call);} #endif /* MINC2_ERROR_H */ libminc-libminc-2-3-00/libsrc2/minc2_private.h000066400000000000000000000160601257462267400211170ustar00rootroot00000000000000/** \internal * \file minc2_private.h * \brief MINC 2.0 private constants, types, and functions. */ #ifndef MINC2_PRIVATE_H #define MINC2_PRIVATE_H #include #include "minc2_structs.h" /** The root of all MINC 2.0 objects in the HDF5 hierarchy. */ #define MI_ROOT_PATH "/minc-2.0" #define MI_ROOT_COMMENT "Root of the MINC 2.0 data hierarchy" #define MI_DIMAGE_PATH "image" #define MI_INFO_NAME "info" #define MI_INFO_COMMENT "Group holding directly accessible attributes" #define MI_DIMENSIONS_PATH "dimensions" #define MI_DIMS_COMMENT "Group holding dimension variables" /** The fixed path to the full-resolution image data. */ #define MI_IMAGE_PATH MI_ROOT_PATH "/" MI_DIMAGE_PATH /** The fixed path to the full-resolution image data. */ #define MI_FULLIMAGE_PATH MI_IMAGE_PATH "/0" /** The fixed path to the dimension */ #define MI_FULLDIMENSIONS_PATH MI_ROOT_PATH "/dimensions" /** Size of a linear transform */ #define MI2_LIN_XFM_SIZE 4 /** Standard linear transform, a 4x4 matrix. */ typedef double mi_lin_xfm_t[MI2_LIN_XFM_SIZE][MI2_LIN_XFM_SIZE]; #ifdef _WIN32 typedef __int64 mi_i64_t; #else //_WIN32 typedef long long mi_i64_t; #endif //_WIN32 /** The fixed path to the dimension */ #define MI_FULLDIMENSIONS_PATH MI_ROOT_PATH "/dimensions" /** \internal * Volume properties */ struct mivolprops { miboolean_t enable_flag; /* enable multi-res */ int depth; /* multi-res depth */ micompression_t compression_type; int zlib_level; int edge_count; /* how many chunks */ int *edge_lengths; /* size of each chunk */ int max_lengths; misize_t record_length; char *record_name; int template_flag; }; /** \internal * Dimension handle */ struct midimension { midimattr_t attr; /* Dimension attributes */ midimclass_t dim_class; /* Dimension class */ double direction_cosines[3]; /* Direction cosines */ miflipping_t flipping_order; char *name; /* Dimension name */ double *offsets; /* Offsets (if irregular) */ double step; /* Step size */ misize_t length; /* Length */ double start; /* Start value */ char *units; /* Units string */ double width; /* Sample width (if regular) */ double *widths; /* Widths (if irregular) */ char *comments; /* Comment string */ mihandle_t volume_handle; /* Handle of associated volume */ short world_index; /* -1, MI2_X, MI2_Y, or MI2_Z */ }; /** \internal * Volume handle */ struct mivolume { hid_t hdf_id; miboolean_t has_slice_scaling; int number_of_dims; midimhandle_t *dim_handles; /* file order of dimensions */ int *dim_indices; /* apparent order of dimensions */ mitype_t volume_type; miclass_t volume_class; mivolumeprops_t create_props; double valid_min; /* Volume-wide valid min */ double valid_max; /* Volume-wide valid max */ mi_lin_xfm_t v2w_transform; /* Voxel-to-world transform */ mi_lin_xfm_t w2v_transform; /* World-to-voxel transform (inverse) */ int selected_resolution; /* The current resolution (0-N) */ int mode; /* Open mode */ hid_t ftype_id; /* File type ID of image. */ hid_t mtype_id; /* Memory type ID of image. */ hid_t plist_id; /* Image property list */ hid_t image_id; /* Dataset for image */ hid_t imax_id; /* Dataset for image-max */ hid_t imin_id; /* Dataset for image-min */ double scale_min; /* Global minimum */ double scale_max; /* Global maximum */ miboolean_t is_dirty; /* TRUE if data has been modified. */ }; /** * \internal * "semi-private" functions. ****************************************************************************/ /* From m2util.c */ hid_t midescend_path(hid_t file_id, const char *path); hid_t mitype_to_hdftype(mitype_t, int); int mitype_len ( mitype_t mitype ); const char * mitype_sign ( mitype_t mitype ); int mitype_to_nctype(mitype_t, int *is_signed); int miget_attribute(mihandle_t volume, const char *varpath, const char *attname, mitype_t data_type, size_t maxvals, void *values); int miset_attr_at_loc(hid_t hdf_loc, const char *attname, mitype_t data_type, size_t maxvals, const void *values); int miset_attribute(mihandle_t volume, const char *varpath, const char *attname, mitype_t data_type, size_t maxvals, const void *values); /*void mifind_spatial_dims(int mincid, int space_to_dim[], int dim_to_space[]);*/ void miget_voxel_to_world(mihandle_t volume, mi_lin_xfm_t voxel_to_world); void minormalize_vector(double vector[]); void mitransform_coord(double out_coord[], mi_lin_xfm_t transform, const double in_coord[]); int miinvert_transform(mi_lin_xfm_t transform, mi_lin_xfm_t inverse); void miinit(void); void miinit_enum(hid_t); int miget_scalar(hid_t loc_id, hid_t type_id, const char *path, void *data); int minc_create_thumbnail(mihandle_t volume, int grp); int minc_update_thumbnail(mihandle_t volume, hid_t loc_id, int igrp, int ogrp); int minc_update_thumbnails(mihandle_t volume); int scaled_maximal_pivoting_gaussian_elimination(int n, int row[], double **a, int n_values, double **solution ); int scaled_maximal_pivoting_gaussian_elimination_real(int n, double **coefs, int n_values, double **values ); double *alloc1d(int); double **alloc2d(int, int); void free2d(int, double **); /* m2util : creation of minc2 comformant datasets*/ int create_dataset(hid_t hdf_file, const char *path); int create_standard_dataset(hid_t hdf_file, const char *path); int add_minimal_minc_attributes(hid_t hdf_file, hid_t dset_id); int add_standard_minc_attributes(hid_t hdf_file, hid_t dset_id); /* From hyper.c */ int mitranslate_hyperslab_origin(mihandle_t volume, const misize_t* start, const misize_t* count, hsize_t* hdf_start, hsize_t* hdf_count, int* dir); /* From volume.c */ void misave_valid_range(mihandle_t volume); /* From valid.c*/ void miinit_default_range(mitype_t mitype, double *valid_max, double *valid_min); #ifdef _MSC_VER double rint(double v); #define snprintf _snprintf #define vsnprintf _vsnprintf #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif #endif /*MINC2_PRIVATE_H*/ libminc-libminc-2-3-00/libsrc2/minc2_structs.h000066400000000000000000000154511257462267400211570ustar00rootroot00000000000000/** internal minc2 data structures*/ #ifndef MINC2_STRUCTS_H #define MINC2_STRUCTS_H #include "H5public.h" /************************************************************************ * ENUMS, STRUCTS, and TYPEDEFS ************************************************************************/ /* These structure declarations exist to allow the following typedefs to * work. Since the details of these structures are not meant to be public, * the actual structure definitions are in minc2_private.h */ struct mivolprops; struct midimension; struct mivolume; /** \typedef mivolumeprops_t * Opaque pointer to volume properties. */ typedef struct mivolprops *mivolumeprops_t; /** \typedef midimhandle_t * Opaque pointer to a MINC dimension object. */ typedef struct midimension *midimhandle_t; /** \typedef mihandle_t * The mihandle_t is an opaque type that represents a MINC file object. */ typedef struct mivolume *mihandle_t; /** \typedef milisthandle_t * The milisthandle_t is an opaque type that represents a handle * to iterate through various properties of MINC file object. */ typedef void *milisthandle_t; /** * This typedef used to represent the type of an individual voxel as * stored by MINC 2.0. */ typedef enum { MI_TYPE_ORIGINAL = 0, /**< MI_ORIGINAL_TYPE */ MI_TYPE_BYTE = 1, /**< 8-bit signed integer */ MI_TYPE_SHORT = 3, /**< 16-bit signed integer */ MI_TYPE_INT = 4, /**< 32-bit signed integer */ MI_TYPE_FLOAT = 5, /**< 32-bit floating point */ MI_TYPE_DOUBLE = 6, /**< 64-bit floating point */ MI_TYPE_STRING = 7, /**< ASCII string */ MI_TYPE_UBYTE = 100, /**< 8-bit unsigned integer */ MI_TYPE_USHORT = 101, /**< 16-bit unsigned integer */ MI_TYPE_UINT = 102, /**< 32-bit unsigned integer */ MI_TYPE_SCOMPLEX = 1000, /**< 16-bit signed integer complex */ MI_TYPE_ICOMPLEX = 1001, /**< 32-bit signed integer complex */ MI_TYPE_FCOMPLEX = 1002, /**< 32-bit floating point complex */ MI_TYPE_DCOMPLEX = 1003, /**< 64-bit floating point complex */ MI_TYPE_UNKNOWN = -1 /**< when the type is a record */ } mitype_t; /** \typedef miclass_t * This typedef is used to represent the class of the MINC file. * * The class specifies the data's interpretation rather than its * storage format. For example, a floating point class implies * that the data may be stored as integers but must nonetheless be * scaled into a "real" range before any mathematical operations * are performed. A label class implies that the values of a voxel * should be considered to represent a symbol, and therefore many * operations on the voxels would be considered meaningless. */ typedef enum { MI_CLASS_REAL = 0, /**< Floating point (default) */ MI_CLASS_INT = 1, /**< Integer */ MI_CLASS_LABEL = 2, /**< Enumerated (named data values) */ MI_CLASS_COMPLEX = 3, /**< Complex (real/imaginary) values */ MI_CLASS_UNIFORM_RECORD = 4, /**< Aggregate datatypes consisting of multiple values of the same underlying type. */ MI_CLASS_NON_UNIFORM_RECORD = 5 /**< Aggregate datatypes consisting of multiple values of potentially differing types (not yet implemented). */ } miclass_t; /** \typedef midimclass_t * Dimensions be members of one of several classes. The "MI_DIMCLASS_ANY" * value is never actually assigned to a dimension. It is used in the * programming interface to specify that an operation should apply to * all dimensions regardless of class. */ typedef enum { MI_DIMCLASS_ANY = 0, /**< Don't care (or unknown) */ MI_DIMCLASS_SPATIAL = 1, /**< Spatial dimensions (x, y, z) */ MI_DIMCLASS_TIME = 2, /**< Time dimension */ MI_DIMCLASS_SFREQUENCY = 3, /**< Spatial frequency dimensions */ MI_DIMCLASS_TFREQUENCY = 4, /**< Temporal frequency dimensions */ MI_DIMCLASS_USER = 5, /**< Arbitrary user-defined dimension */ MI_DIMCLASS_RECORD = 6 /**< Record as dimension */ } midimclass_t; /** \typedef miorder_t * Dimension order refers to the idea that data can be structured in * a variety of ways with respect to the dimensions. For example, a typical * 3D scan could be structured as a transverse (ZYX) or sagittal (XZY) image. * Since it may be convenient to write code which expects a particular * dimension order, a user can specify an alternative ordering by using * miset_apparent_dimension_order(). This will cause most functions * to return data as if the file was in the apparent, rather than the * file (native) order. */ typedef enum { MI_DIMORDER_FILE = 0, MI_DIMORDER_APPARENT = 1 } miorder_t; /** \typedef mivoxel_order_t * Voxel order can be either file (native), or apparent, as set by * the function miset_dimension_apparent_voxel_order(). */ typedef enum { MI_ORDER_FILE = 0, /**< File order */ MI_ORDER_APPARENT = 1 /**< Apparent (user) order */ } mivoxel_order_t; /** \typedef miflipping_t * Voxel flipping can be specified to either follow the file's native * order, the opposite of the file's order, or it can be tied to the * value of the dimension's step attribute. A value of MI_NEGATIVE * implies that the voxel order should be rearranged such that the step * attribute is negative, a value of MI_POSITIVE implies the opposite. */ typedef enum { MI_FILE_ORDER = 0, /**< no flip */ MI_COUNTER_FILE_ORDER = 1, /**< flip */ MI_POSITIVE = 2, /**< force step value to be positive */ MI_NEGATIVE = 3 /**< force step value to be negative */ } miflipping_t; /** \typedef micompression_t * Compression type */ typedef enum { MI_COMPRESS_NONE = 0, /**< No compression */ MI_COMPRESS_ZLIB = 1 /**< GZIP compression */ } micompression_t; /** \typedef miboolean_t * Boolean value */ typedef int miboolean_t; /** \typedef midimattr_t * Something about dimension attributes */ typedef unsigned int midimattr_t; /** \typedef misize_t * size of things */ typedef hsize_t misize_t; /*based on HDF5 size*/ /** \typedef miscomplex_t * 16-bit integer complex voxel. */ typedef struct { short real; /**< Real part */ short imag; /**< Imaginary part */ } miscomplex_t; /** \typedef miicomplex_t * 32-bit integer complex voxel. */ typedef struct { int real; /**< Real part */ int imag; /**< Imaginary part */ } miicomplex_t; /** \typedef mifcomplex_t * 32-bit floating point complex voxel. */ typedef struct { float real; /**< Real part */ float imag; /**< Imaginary part */ } mifcomplex_t; /** \typedef midcomplex_t * 64-bit floating point complex voxel. */ typedef struct { double real; /**< Real part */ double imag; /**< Imaginary part */ } midcomplex_t; #endif //MINC2_STRUCTS_H libminc-libminc-2-3-00/libsrc2/minc_compat2.h000066400000000000000000000056341257462267400207350ustar00rootroot00000000000000#if MINC2 /* Functions for enabling/disabling error messages from the library. */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ MNCAPI int MI2typelen(int); MNCAPI int MI2varname(int fd, int varid, char *varnm); MNCAPI int MI2varid(int fd, const char *varnm); MNCAPI int MI2attinq(int fd, int varid, const char *attnm, nc_type *type_ptr, int *length_ptr); MNCAPI int MI2attname(int fd, int varid, int attid, char *name); MNCAPI int MI2inquire(int fd, int *ndims_ptr, int *nvars_ptr, int *natts_ptr, int *unlimdim_ptr); MNCAPI int MI2varinq(int fd, int varid, char *varnm_ptr, nc_type *type_ptr, int *ndims_ptr, int *dims_ptr, int *natts_ptr); MNCAPI int MI2dimid(int fd, const char *dimnm); MNCAPI int MI2diminq(int fd, int dimid, char *dimnm_ptr, long *len_ptr); MNCAPI int MI2dimdef(int fd, const char *dimnm, long length); MNCAPI int MI2attget(int fd, int varid, const char *attnm, void *value); MNCAPI int MI2attput(int fd, int varid, const char *attnm, nc_type val_typ, int val_len, void *val_ptr); MNCAPI int MI2endef(int fd); MNCAPI int MI2vardef(int fd, const char *varnm, nc_type vartype, int ndims, const int *dimids); MNCAPI int MI2varget(int fd, int varid, const long *start_ptr, const long *count_ptr, void *val_ptr); MNCAPI int MI2varput(int fd, int varid, const long *start_ptr, const long *count_ptr, const void *val_ptr); MNCAPI int MI2varput1(int fd, int varid, const long *mindex_ptr, const void *val_ptr); MNCAPI int MI2attdel(int fd, int varid, const char *attnm); MNCAPI int MI2dimrename(int fd, int dimid, const char *new_name); MNCAPI int MI2varputg(int fd, int varid, const long *startp, const long *countp, const long *stridep, const long *imapp, const void *valp); MNCAPI int MI2attcopy(int infd, int invarid, const char *name, int outfd, int outvarid); MNCAPI int MI2redef(int fd); MNCAPI int MI2sync(int fd); MNCAPI int MI2setfill(int fd, int fillmode); #ifndef _MI2_FORCE_NETCDF_ #define nctypelen MI2typelen #define ncvarname MI2varname #define ncvarid MI2varid #define ncdimid MI2dimid #define ncvarinq MI2varinq #define ncdiminq MI2diminq #define ncdimdef MI2dimdef #define ncattdel MI2attdel #define ncvardef MI2vardef #define ncvarput1 MI2varput1 #define ncvarput MI2varput #define ncvarget MI2varget #define ncattinq MI2attinq #define ncvarputg MI2varputg #define nccreate micreate #define ncopen miopen #define ncclose miclose #define ncattput MI2attput #define ncinquire MI2inquire #define ncattname MI2attname #define ncdimrename MI2dimrename #define ncattcopy MI2attcopy #define ncendef MI2endef #define ncattget MI2attget #define ncredef MI2redef #define ncsync MI2sync #define ncsetfill MI2setfill #ifndef NC_NOFILL #define NC_NOFILL 0x100 #endif #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* _MI2_FORCE_NETCDF_ not defined */ #endif /* MINC2 */ libminc-libminc-2-3-00/libsrc2/record.c000066400000000000000000000066161257462267400176340ustar00rootroot00000000000000/**\file record.c * \brief MINC 2.0 Record Functions * \author Bert Vincent */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include "minc2.h" #include "minc2_private.h" /** This method gets the name of the record dimension * TODO: set record name?? */ int miget_record_name(mihandle_t volume, char **name) { return (MI_NOERROR); } /** This method gets the length (i.e., number of fields in the case of * uniform records and number of bytes for non_uniform ones) of the * record. */ int miget_record_length(mihandle_t volume, int *length) { if (volume == NULL || length == NULL) { return (MI_ERROR); } if (volume->volume_class == MI_CLASS_UNIFORM_RECORD || volume->volume_class == MI_CLASS_NON_UNIFORM_RECORD) { *length = H5Tget_nmembers(volume->ftype_id); return (MI_NOERROR); } return (MI_ERROR); } /** This method returns the field name for the given field index. Memory * for returned string is allocated on the heap and should be released using * mifree_name(). */ int miget_record_field_name(mihandle_t volume, int index, char **name) { if (volume == NULL || name == NULL) { return (MI_ERROR); } /* Get the field name. The H5Tget_member_name() function allocates * the memory for the string using malloc(), so we can return the * pointer directly without any further manipulations. */ *name = H5Tget_member_name(volume->ftype_id, index); if (*name == NULL) { return (MI_ERROR); } return (MI_NOERROR); } /** This method sets a field name for the volume record. The volume * must be of class "MI_CLASS_UNIFORM_RECORD". The size of record * type will be increased if necessary to accomodate the new field. */ int miset_record_field_name(mihandle_t volume, int index, const char *name) { hid_t mtype_id; hid_t ftype_id; size_t offset; if (volume == NULL || name == NULL) { return (MI_ERROR); } if (volume->volume_class != MI_CLASS_UNIFORM_RECORD && volume->volume_class != MI_CLASS_NON_UNIFORM_RECORD) { return (MI_ERROR); } /* Get the type of the record's fields. This is recorded as the * type of the volume. */ ftype_id = mitype_to_hdftype(volume->volume_type, FALSE); mtype_id = mitype_to_hdftype(volume->volume_type, TRUE); /* Calculate the offset of the new member. */ offset = index * H5Tget_size(ftype_id); /* If the offset plus the size of the member is larger than the * current size of the structure, increase the size of the structure. */ if (offset + H5Tget_size(ftype_id) > H5Tget_size(volume->ftype_id)) { H5Tset_size(volume->ftype_id, offset + H5Tget_size(ftype_id)); } if (offset + H5Tget_size(mtype_id) > H5Tget_size(volume->mtype_id)) { H5Tset_size(volume->mtype_id, offset + H5Tget_size(mtype_id)); } /* Actually define the field within the structure. */ H5Tinsert(volume->ftype_id, name, offset, ftype_id); H5Tinsert(volume->mtype_id, name, offset, mtype_id); /* Delete the HDF5 type object returned by mitype_to_hdftype(). */ H5Tclose(ftype_id); H5Tclose(mtype_id); return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/slice.c000066400000000000000000000304051257462267400174460ustar00rootroot00000000000000/** * \file slice.c * \brief MINC 2.0 Slice/volume scale functions * \author Bert Vincent * * These functions get and set the real minimum and maximum values for * either a particular slice or an entire volume. * * Each of the slice scale functions take an array of long integer * coordinates to specify the slice to consider. The order of these * coordinates is always raw file (voxel) order, rather than the * apparent order. * * If slice scaling is not enabled, the slice scaling functions will * use the appropriate global volume scale value. ************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "minc2.h" #include "minc2_private.h" #define MIRW_SCALE_SET 0x0001 #define MIRW_SCALE_GET 0x0000 #define MIRW_SCALE_MIN 0x0002 #define MIRW_SCALE_MAX 0x0000 /* Forward declaration */ static int mirw_volume_minmax ( int opcode, mihandle_t volume, double *value ); /** Get the minimum or maximum value for the slice containing the given point. */ static int mirw_slice_minmax ( int opcode, mihandle_t volume, const misize_t start_positions[], misize_t array_length, double *value ) { hid_t dset_id; hid_t fspc_id; hid_t mspc_id; hsize_t hdf_start[MI2_MAX_VAR_DIMS];//VF: should it be hssize_t ? hsize_t hdf_count[MI2_MAX_VAR_DIMS]; misize_t count[MI2_MAX_VAR_DIMS]; int dir[MI2_MAX_VAR_DIMS]; misize_t ndims; misize_t i; int result; if ( volume == NULL || value == NULL ) { return ( MI_ERROR ); /* Bad parameters */ } if ( !volume->has_slice_scaling ) { return mirw_volume_minmax ( opcode, volume, value ); } if ( opcode & MIRW_SCALE_MIN ) { dset_id = volume->imin_id; } else { dset_id = volume->imax_id; } fspc_id = H5Dget_space ( dset_id ); if ( fspc_id < 0 ) { return ( MI_ERROR ); } ndims = H5Sget_simple_extent_ndims ( fspc_id ); if ( ndims > array_length ) { ndims = array_length; } for ( i = 0; i < ndims; i++ ) { count[i] = 1; } mitranslate_hyperslab_origin ( volume, start_positions, count, hdf_start, hdf_count, dir ); result = H5Sselect_elements ( fspc_id, H5S_SELECT_SET, 1, hdf_start ); if ( result < 0 ) { return ( MI_ERROR ); } /* Create a trivial scalar space to read the single value. */ mspc_id = H5Screate ( H5S_SCALAR ); if ( opcode & MIRW_SCALE_SET ) { result = H5Dwrite ( dset_id, H5T_NATIVE_DOUBLE, mspc_id, fspc_id, H5P_DEFAULT, value ); } else { result = H5Dread ( dset_id, H5T_NATIVE_DOUBLE, mspc_id, fspc_id, H5P_DEFAULT, value ); } if ( result < 0 ) { return ( MI_ERROR ); } H5Sclose ( fspc_id ); H5Sclose ( mspc_id ); return ( MI_NOERROR ); } /** * This function sets \a slice_min to the minimum real value of * voxels in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. */ int miget_slice_min ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_min ) { return ( mirw_slice_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_GET, volume, start_positions, array_length, slice_min ) ); } /** * This function sets \a slice_max to the maximum real value of * voxels in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. */ int miget_slice_max ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_max ) { return ( mirw_slice_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_GET, volume, start_positions, array_length, slice_max ) ); } /** * This function sets minimum real value of * values in the slice containing the coordinates \a start_positions. * The \a array_length may be less than or equal to the number of dimensions * in the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. * Coordinates must always be specified in raw file order. */ int miset_slice_min ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_min ) { return ( mirw_slice_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_SET, volume, start_positions, array_length, &slice_min ) ); } /** This function sets maximum real value of values in the slice containing the coordinates \a start_positions. The \a array_length may be less than or equal to the number of dimensions in the volume, extra coordinates will be ignored. Specifying too few coordinates will trigger an error. Coordinates must always be specified in raw file order. */ int miset_slice_max ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_max ) { return ( mirw_slice_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_SET, volume, start_positions, array_length, &slice_max ) ); } /** * This function gets both the minimum and * maximum real value of voxels in the slice containing the coordinates * \a start_positions. The \a array_length may be less than or equal to * the number of dimensions in the volume, extra coordinates will be * ignored. Specifying too few coordinates will trigger an error. * Coordinates must always be specified in raw file order. */ int miget_slice_range ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double *slice_max, double *slice_min ) { int r; r = mirw_slice_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_GET, volume, start_positions, array_length, slice_max ); if ( r < 0 ) { *slice_max = 1.0; } r = mirw_slice_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_GET, volume, start_positions, array_length, slice_min ); if ( r < 0 ) { *slice_min = 0.0; } return ( MI_NOERROR ); } /** * This function the minimum and maximum real value of voxels in the * slice containing the coordinates \a start_positions. The \a * array_length may be less than or equal to the number of dimensions in * the volume, extra coordinates will be ignored. Specifying too few * coordinates will trigger an error. Coordinates must always be * specified in raw file order. */ int miset_slice_range ( mihandle_t volume, const misize_t start_positions[], size_t array_length, double slice_max, double slice_min ) { int r; r = mirw_slice_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_SET, volume, start_positions, array_length, &slice_max ); if ( r < 0 ) { return ( MI_ERROR ); } r = mirw_slice_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_SET, volume, start_positions, array_length, &slice_min ); if ( r < 0 ) { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** Internal function to read/write the volume global minimum or * maximum real range. */ static int mirw_volume_minmax ( int opcode, mihandle_t volume, double *value ) { hid_t dset_id; hid_t fspc_id; hid_t mspc_id; int result; if ( volume == NULL || value == NULL ) { return ( MI_ERROR ); } if ( volume->has_slice_scaling ) { return ( MI_ERROR ); } if ( ( opcode & MIRW_SCALE_SET ) == 0 ) { if ( opcode & MIRW_SCALE_MIN ) { *value = volume->scale_min; return ( MI_NOERROR ); } else { *value = volume->scale_max; return ( MI_NOERROR ); } } if ( opcode & MIRW_SCALE_MIN ) { dset_id = volume->imin_id; } else { dset_id = volume->imax_id; } fspc_id = H5Dget_space ( dset_id ); if ( fspc_id < 0 ) { return ( MI_ERROR ); } /* Make certain the value is a scalar. */ if ( H5Sget_simple_extent_ndims ( fspc_id ) != 0 ) { return ( MI_ERROR ); } /* Create a trivial scalar space to read the single value. */ mspc_id = H5Screate ( H5S_SCALAR ); if ( opcode & MIRW_SCALE_SET ) { result = H5Dwrite ( dset_id, H5T_NATIVE_DOUBLE, mspc_id, fspc_id, H5P_DEFAULT, value ); } else { result = H5Dread ( dset_id, H5T_NATIVE_DOUBLE, mspc_id, fspc_id, H5P_DEFAULT, value ); } if ( result < 0 ) { return ( MI_ERROR ); } /* Update the "cached" values. */ if ( opcode & MIRW_SCALE_MIN ) { volume->scale_min = *value; } else { volume->scale_max = *value; } H5Sclose ( fspc_id ); H5Sclose ( mspc_id ); return ( MI_NOERROR ); } /** * This function returns the minimum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miget_volume_min ( mihandle_t volume, double *vol_min ) { return ( mirw_volume_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_GET, volume, vol_min ) ); } /** * This function returns the maximum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miget_volume_max ( mihandle_t volume, double *vol_max ) { return ( mirw_volume_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_GET, volume, vol_max ) ); } /** * This function sets the minimum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miset_volume_min ( mihandle_t volume, double vol_min ) { return ( mirw_volume_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_SET, volume, &vol_min ) ); } /** * This function sets the maximum real value of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miset_volume_max ( mihandle_t volume, double vol_max ) { return ( mirw_volume_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_SET, volume, &vol_max ) ); } /** * This function retrieves the maximum and minimum real values of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miget_volume_range ( mihandle_t volume, double *vol_max, double *vol_min ) { int r; r = mirw_volume_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_GET, volume, vol_max ); if ( r < 0 ) { return ( MI_ERROR ); } r = mirw_volume_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_GET, volume, vol_min ); if ( r < 0 ) { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** * This function sets the maximum and minimum real values of * voxels in the entire \a volume. If per-slice scaling is enabled, this * function will return an error. */ int miset_volume_range ( mihandle_t volume, double vol_max, double vol_min ) { int r; r = mirw_volume_minmax ( MIRW_SCALE_MAX + MIRW_SCALE_SET, volume, &vol_max ); if ( r < 0 ) { return ( MI_ERROR ); } r = mirw_volume_minmax ( MIRW_SCALE_MIN + MIRW_SCALE_SET, volume, &vol_min ); if ( r < 0 ) { return ( MI_ERROR ); } return ( MI_NOERROR ); } /** Function to get the volume's slice-scaling flag. */ int miget_slice_scaling_flag ( mihandle_t volume, miboolean_t *slice_scaling_flag ) { if ( volume == NULL || slice_scaling_flag == NULL ) { return ( MI_ERROR ); } *slice_scaling_flag = volume->has_slice_scaling; return ( MI_NOERROR ); } /** Function to set the volume's slice-scaling flag. */ int miset_slice_scaling_flag ( mihandle_t volume, miboolean_t slice_scaling_flag ) { if ( volume == NULL ) { return ( MI_ERROR ); } volume->has_slice_scaling = slice_scaling_flag; return ( MI_NOERROR ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/valid.c000066400000000000000000000113201257462267400174410ustar00rootroot00000000000000/** \file valid.c * \brief MINC 2.0 Valid Min/Max and Range Functions. * \author Bert Vincent * * These functions set and get the valid range of the datatype of a volume. * This range refers to the maximum values of the datatype as stored in the * file, not to the range of the "real" values. For example, many scanners * store voxel data as unsigned, 12-bit integers. Since there is no explicit * 12-bit data type in MINC, we may use these functions to set the valid range * in the image to 0..4095. ************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include "minc2.h" #include "minc2_private.h" /** This function gets the maximum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_max(mihandle_t volume, /**< MINC 2.0 volume handle */ double *valid_max) /**< the output value */ { if (volume == NULL || valid_max == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get valid range min with null volume or variable"); /* Invalid arguments */ } *valid_max = volume->valid_max; return (MI_NOERROR); } /** This function sets the maximum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_max(mihandle_t volume, /**< MINC 2.0 volume handle */ double valid_max) /**< the new maximum value */ { if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to set valid range max with null volume "); /* Invalid arguments */ } /* TODO?: Should we require valid max to have some specific relationship * to valid_min? */ volume->valid_max = valid_max; misave_valid_range(volume); return (MI_NOERROR); } /** This function gets the minimum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_min(mihandle_t volume, /**< MINC 2.0 volume handle */ double *valid_min) /**< the output value */ { if (volume == NULL || valid_min == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get valid range min with null volume or variable"); /* Invalid arguments. */ } *valid_min = volume->valid_min; return (MI_NOERROR); } /** This function sets the minimum valid value specific to the data * type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_min(mihandle_t volume, /**< MINC 2.0 volume handle */ double valid_min) /**< the new minimum value */ { if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to set valid range min with null volume "); /* Invalid arguments */ } volume->valid_min = valid_min; misave_valid_range(volume); return (MI_NOERROR); } /** This function gets the minimum and maximum valid value specific to the * data type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miget_volume_valid_range(mihandle_t volume, /**< MINC 2.0 volume handle */ double *valid_max, /**< the output maximum value */ double *valid_min) /**< the output minimum value */ { if (volume == NULL || valid_min == NULL || valid_max == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get valid range with null volume or null variables"); } *valid_min = volume->valid_min; *valid_max = volume->valid_max; return (MI_NOERROR); } /** This function sets the minimum and maximum valid value specific to the * data type of the \a volume parameter. * \retval MI_ERROR on failure * \retval MI_NOERROR on success */ int miset_volume_valid_range(mihandle_t volume, /**< MINC 2.0 volume handle */ double valid_max, /**< the new maximum value */ double valid_min) /**< the output minimum value */ { if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to set valid range with null volume "); } /* TODO?: Again, should we require minvalid_min = valid_min; volume->valid_max = valid_max; misave_valid_range(volume); return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/volprops.c000066400000000000000000000326171257462267400202420ustar00rootroot00000000000000/** \file volprops.c * \brief MINC 2.0 Volume properties functions * \author Leila Baghdadi * * These functions manipulate "volume properties" objects, which are * used to control several options related to MINC 2.0 volume structure. * These include compression, blocking, multi-resolution, and record s * structure. * * This approach was adopted with the intent that it would make the * default volume creation as simple as possible, while allowing a * lot of control for more advanced applications. This approach to * managing properties is also believed to be more readily extensible than * any obvious alternative. ************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #define _GNU_SOURCE 1 #include #include #include "minc2.h" #include "minc2_private.h" /** * \defgroup mi2VPrp MINC 2.0 Volume Properties Functions */ /** Maximum number of elements in a filter parameter list. */ #define MI2_MAX_CD_ELEMENTS 100 /** Create a volume property list. The new list will be returned in the * \a props parameter. When the program is finished * using the property list it should call mifree_volume_props() to free the * memory associated with the list. * \param props A pointer to the returned volume properties handle. * \ingroup mi2VPrp */ int minew_volume_props(mivolumeprops_t *props) { mivolumeprops_t handle; handle = (mivolumeprops_t)malloc(sizeof(struct mivolprops)); if (handle == NULL) { return (MI_ERROR); } /* Initialize all the fields. */ handle->enable_flag = FALSE; handle->depth = 0; handle->compression_type = MI_COMPRESS_NONE; handle->zlib_level = 0; handle->edge_count = 0; handle->edge_lengths = NULL; handle->max_lengths = 0; handle->record_length = 0; handle->record_name = NULL; handle->template_flag = 0; *props = handle; return (MI_NOERROR); } /** Destroy a volume property list. * \param props The volume property list to delete. * \ingroup mi2VPrp */ int mifree_volume_props(mivolumeprops_t props) { if (props == NULL) { return (MI_ERROR); } if (props->edge_lengths != NULL) { free(props->edge_lengths); } if (props->record_name != NULL) { free(props->record_name); } free(props); return (MI_NOERROR); } /*! Get a copy of the volume property list. When the program is finished * using the property list it should call mifree_volume_props() to free the * memory associated with the list. * \param volume A volume handle * \param props A pointer to the returned volume properties handle. * \ingroup mi2VPrp */ int miget_volume_props(mihandle_t volume, mivolumeprops_t *props) { mivolumeprops_t handle; hid_t hdf_vol_dataset; hid_t hdf_plist; int nfilters; unsigned int flags; size_t cd_nelmts; unsigned int cd_values[MI2_MAX_CD_ELEMENTS]; char fname[MI2_CHAR_LENGTH]; int fcode; if (volume->hdf_id < 0) { return (MI_ERROR); } hdf_vol_dataset = midescend_path(volume->hdf_id, "/minc-2.0/image/0/image"); if (hdf_vol_dataset < 0) { return (MI_ERROR); } hdf_plist = H5Dget_create_plist(hdf_vol_dataset); if (hdf_plist < 0) { return (MI_ERROR); } handle = (mivolumeprops_t)malloc(sizeof(struct mivolprops)); if (handle == NULL) { return (MI_ERROR); } /* Get the layout of the raw data for a dataset. */ if (H5Pget_layout(hdf_plist) == H5D_CHUNKED) { hsize_t dims[MI2_MAX_VAR_DIMS]; int i; /* Returns chunk dimensionality */ handle->edge_count = H5Pget_chunk(hdf_plist, MI2_MAX_VAR_DIMS, dims); if (handle->edge_count < 0) { free(handle); return (MI_ERROR); } handle->edge_lengths = (int *)malloc(handle->edge_count*sizeof(int)); if (handle->edge_lengths == NULL) { free(handle); return (MI_ERROR); } for (i = 0; i < handle->edge_count; i++) { handle->edge_lengths[i] = dims[i]; } /* Get the number of filters in the pipeline */ nfilters = H5Pget_nfilters(hdf_plist); if (nfilters == 0) { handle->zlib_level = 0; handle->compression_type = MI_COMPRESS_NONE; } else { for (i = 0; i < nfilters; i++) { cd_nelmts = MI2_MAX_CD_ELEMENTS; fcode = H5Pget_filter1(hdf_plist, i, &flags, &cd_nelmts, cd_values, sizeof(fname), fname); switch (fcode) { case H5Z_FILTER_DEFLATE: handle->compression_type = MI_COMPRESS_ZLIB; handle->zlib_level = cd_values[0]; break; case H5Z_FILTER_SHUFFLE: break; case H5Z_FILTER_FLETCHER32: break; case H5Z_FILTER_SZIP: break; default: break; } } } } else { handle->edge_count = 0; handle->edge_lengths = NULL; handle->zlib_level = 0; handle->compression_type = MI_COMPRESS_NONE; } *props = handle; H5Pclose(hdf_plist); H5Dclose(hdf_vol_dataset); return (MI_NOERROR); } /** Set multi-resolution properties. The \a enable_flag determines * whether or not thumbnail images will be calculated at all. The \a * depth parameter determines the lowest-resolution image that will be * available. The full resolution image is considered to be image #0, * the half resolution image is image #1, the quarter-resolution image * is #2, etc. Therefore a \a depth value of 2 implies both the half * and quarter resolution thumbnails will be calculated and stored in * the file. * \param props A volume property list handle * \param enable_flag TRUE if multiresolution support should be enabled in * this file. * \param depth The maximum depth of multiresolution data * to support. * \ingroup mi2VPrp */ int miset_props_multi_resolution(mivolumeprops_t props, miboolean_t enable_flag, int depth) { if (props == NULL || depth > MI2_MAX_RESOLUTION_GROUP || depth <= 0) { return (MI_ERROR); } props->enable_flag = enable_flag; props->depth = depth; return (MI_NOERROR); } /** Get multi-resolution properties. Returns the value of the \a enable_flag * and \a depth parameters. * \param props A volume property list handle * \param enable_flag Pointer to a boolean which will be set to TRUE if * multiresolution has been enabled. * \param depth Pointer to a integer which will contain the maximum resolution * depth enabled if multiresolution is enabled. * \ingroup mi2VPrp */ int miget_props_multi_resolution(mivolumeprops_t props, miboolean_t *enable_flag, int *depth) { if (props == NULL || enable_flag == NULL || depth == NULL) { return (MI_ERROR); } *enable_flag = props->enable_flag; *depth = props->depth; return (MI_NOERROR); } /** Select a different resolution from a multi-resolution image. * \ingroup mi2VPrp */ int miselect_resolution(mihandle_t volume, int depth) { hid_t grp_id; char path[MI2_MAX_PATH]; if ( volume->hdf_id < 0 || depth > MI2_MAX_RESOLUTION_GROUP || depth < 0) { return (MI_ERROR); } grp_id = H5Gopen1(volume->hdf_id, MI_ROOT_PATH "/image"); if (grp_id < 0) { return (MI_ERROR); } /* Check given depth with the available depth in file. Make sure the selected resolution does exist. */ if (depth > volume->create_props->depth) { return (MI_ERROR); } else if (depth != 0) { if (minc_update_thumbnail(volume, grp_id, 0, depth) < 0) { return (MI_ERROR); } } volume->selected_resolution = depth; if (volume->image_id >= 0) { H5Dclose(volume->image_id); } sprintf(path, "%d/image", depth); volume->image_id = H5Dopen1(grp_id, path); if (volume->volume_class == MI_CLASS_REAL) { if (volume->imax_id >= 0) { H5Dclose(volume->imax_id); } sprintf(path, "%d/image-max", depth); volume->imax_id = H5Dopen1(grp_id, path); if (volume->imin_id >= 0) { H5Dclose(volume->imin_id); } sprintf(path, "%d/image-min", depth); volume->imin_id = H5Dopen1(grp_id, path); } return (MI_NOERROR); } /** Compute or recompute all resolution groups. * * \ingroup mi2VPrp */ int miflush_from_resolution(mihandle_t volume, int depth) { if ( volume->hdf_id < 0 || depth > MI2_MAX_RESOLUTION_GROUP || depth <= 0) { return (MI_ERROR); } if (depth > volume->create_props->depth) { return (MI_ERROR); } else { if (minc_update_thumbnails(volume) < 0) { return (MI_ERROR); } volume->is_dirty = FALSE; } return (MI_NOERROR); } /** Set compression type for a volume property list * Note that enabling compression will automatically * enable blocking with default parameters. * \param props A volume properties list * \param compression_type The type of compression to use (MI_COMPRESS_NONE * or MI_COMPRESS_ZLIB) * \ingroup mi2VPrp */ int miset_props_compression_type(mivolumeprops_t props, micompression_t compression_type) { int i; int edge_lengths[MI2_MAX_VAR_DIMS]; if (props == NULL) { return (MI_ERROR); } switch (compression_type) { case MI_COMPRESS_NONE: props->compression_type = MI_COMPRESS_NONE; break; case MI_COMPRESS_ZLIB: props->compression_type = MI_COMPRESS_ZLIB; props->zlib_level = MI2_DEFAULT_ZLIB_LEVEL; for (i = 0; i < MI2_MAX_VAR_DIMS; i++) { edge_lengths[i] = MI2_CHUNK_SIZE; } miset_props_blocking(props, MI2_MAX_VAR_DIMS, edge_lengths); break; default: return (MI_ERROR); } return (MI_NOERROR); } /** Get compression type for a volume property list * \param props A volume property list handle * \param compression_type A pointer to a variable to which the current * compression type will be assigned. * \ingroup mi2VPrp */ int miget_props_compression_type(mivolumeprops_t props, micompression_t *compression_type) { if (props == NULL) { return (MI_ERROR); } *compression_type = props->compression_type; return (MI_NOERROR); } /** Set zlib compression properties for a volume list. The \a zlib_level * parameter may range from 1 to 9, where higher numbers request that the * library attempt to use more memory (and possibly processing power) to * achieve the highest possible compression ratio. * * \param props A volume property list handle * \param zlib_level An integer specifying the desired compression level. * \ingroup mi2VPrp */ int miset_props_zlib_compression(mivolumeprops_t props, int zlib_level) { if (props == NULL || zlib_level > MI2_MAX_ZLIB_LEVEL) { return (MI_ERROR); } props->zlib_level = zlib_level; return (MI_NOERROR); } /** Get zlib compression properties from a volume property list. * \param props A volume property list handle * \param zlib_level Pointer to an integer variable that will receive the * current compression level. * \ingroup mi2VPrp */ int miget_props_zlib_compression(mivolumeprops_t props, int *zlib_level) { if (props == NULL) { return (MI_ERROR); } *zlib_level = props->zlib_level; return (MI_NOERROR); } /** Set blocking structure properties for the volume * \param props A volume property list handle * \param edge_count * \param edge_lengths * \ingroup mi2VPrp */ int miset_props_blocking(mivolumeprops_t props, int edge_count, const int *edge_lengths) { int i; if (props == NULL || edge_count > MI2_MAX_VAR_DIMS) { return (MI_ERROR); } if (props->edge_lengths != NULL) { free(props->edge_lengths); props->edge_lengths = NULL; } props->edge_count = edge_count; if (edge_count != 0) { props->edge_lengths = (int *) malloc(edge_count*sizeof(int)); if (props->edge_lengths == NULL) { return (MI_ERROR); } for (i=0; i< edge_count; i++){ props->edge_lengths[i] = edge_lengths[i]; } } return (MI_NOERROR); } /** Get blocking structure properties for the volume * \param props The properties structure from which to get the information * \param edge_count Returns the number of edges (dimensions) in a block * \param edge_lengths The lengths of the edges * \param max_lengths The number of elements of the edge_lengths array * \ingroup mi2VPrp */ int miget_props_blocking(mivolumeprops_t props, int *edge_count, int *edge_lengths, int max_lengths) { int i; if (props == NULL) { return (MI_ERROR); } *edge_count = props->edge_count; /* If max_lengths is greater than the actual edge count, reduce max_lengths * to the edge_count */ if (max_lengths > props->edge_count) { max_lengths = props->edge_count; } /*edge_lengths = (int *) malloc(max_lengths *sizeof(int));*/ for (i=0; i< max_lengths; i++){ edge_lengths[i] = props->edge_lengths[i]; } return (MI_NOERROR); } /** Set properties for uniform/nonuniform record dimension * \ingroup mi2VPrp */ int miset_props_record(mivolumeprops_t props, misize_t record_length, char *record_name) { if (props == NULL) { return (MI_ERROR); } if (record_length > 0) { props->record_length = record_length; } if (props->record_name != NULL) { free(props->record_name); props->record_name = NULL; } props->record_name = strdup(record_name); return (MI_NOERROR); } /** Set the template volume flag * \ingroup mi2VPrp */ int miset_props_template(mivolumeprops_t props, int template_flag) { if (props == NULL) { return (MI_ERROR); } props->template_flag = template_flag; return (MI_NOERROR); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/libsrc2/volume.c000066400000000000000000001422541257462267400176640ustar00rootroot00000000000000/** \file volume.c * \brief MINC 2.0 Volume Functions * \author Leila Baghdadi, Bert Vincent * * Functions to create, open, and close MINC volume objects. ************************************************************************/ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #ifdef HAVE_SYS_TYPES_H #include #endif //HAVE_SYS_TYPES_H #ifdef HAVE_UNISTD_H #include #endif //HAVE_UNISTD_H #ifdef HAVE_MINC1 #include "minc.h" #endif //HAVE_MINC1 #include #include #include #include #include "minc2.h" #include "minc2_private.h" /* So we build with 1.8.4 */ #ifndef H5F_LIBVER_18 #define H5F_LIBVER_18 H5F_LIBVER_LATEST #endif /** * \defgroup mi2Vol MINC 2.0 Volume Functions */ /* Forward declarations */ static void miread_valid_range(mihandle_t volume, double *valid_max, double *valid_min); static int _miset_volume_class(mihandle_t volume, miclass_t volclass); static int _miget_volume_class(mihandle_t volume, miclass_t *volclass); /** * Creates a (hopefully) unique identifier to associate with a * MINC file, by concatenating various information about the * system, process, etc. * returns the length of identifier */ static int _generate_ident( char * id_str, size_t length ) { static int identx = 1; /* Static ID counter */ time_t now; struct tm tm_buf; char host_str[128]; char user_str[128]; char *temp_ptr; char time_str[26]; int result; // Linking in ws2_32 for gethostname is problematic with static libraries. #ifdef _WIN32 strcpy(host_str, "unknown"); #else if (gethostname(host_str, sizeof(host_str)) != 0) { strcpy(host_str, "unknown"); } #endif temp_ptr = getenv("LOGNAME"); if (temp_ptr != NULL) { strncpy(user_str, temp_ptr, sizeof(user_str) - 1); } else { strcpy(user_str, "nobody"); } time(&now); #ifdef _WIN32 memcpy(&tm_buf, localtime(&now), sizeof(tm_buf)); #else localtime_r(&now, &tm_buf); #endif strftime(time_str, sizeof(time_str), "%Y.%m.%d.%H.%M.%S", &tm_buf); result = snprintf(id_str, length, "%s:%s:%s:%u:%u", user_str, host_str, time_str, getpid(), identx++); return result; } /** * open HDF5 file */ static hid_t _hdf_open(const char *path, int mode) { hid_t fd; /* hid_t grp_id; hid_t dset_id; int ndims;*/ H5E_BEGIN_TRY { #if HDF5_MMAP_TEST if (mode & 0x8000) { hid_t prp_id; prp_id = H5Pcreate(H5P_FILE_ACCESS); H5Pset_fapl_mmap(prp_id, 8192, 1); fd = H5Fopen(path, mode & 0x7FFF, prp_id); H5Pclose(prp_id); } else { fd = H5Fopen(path, mode, H5P_DEFAULT); } #else fd = H5Fopen(path, mode, H5P_DEFAULT); #endif } H5E_END_TRY; /* Open the image variables. */ //TODO: convert // H5E_BEGIN_TRY // { // dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image"); // if (dset_id >= 0) { // hid_t type_id; // int is_compound = 0; // // hdf_get_diminfo(dset_id, &ndims, dims); // // #ifndef NO_EMULATE_VECTOR_DIMENSION // /* See if a vector_dimension needs to be emulated. // */ // type_id = H5Dget_type(dset_id); // if (type_id >= 0) { // if (H5Tget_class(type_id) == H5T_COMPOUND) { // /* OK, it's compound type. */ // struct m2_dim *dim = hdf_dim_add(file, MIvector_dimension, // H5Tget_nmembers(type_id)); // dim->is_fake = 1; // dims[ndims++] = H5Tget_nmembers(type_id); // is_compound = 1; // } // H5Tclose(type_id); // } // #endif /* NO_EMULATE_VECTOR_DIMENSION */ // // var = hdf_var_add(file, MIimage, "/minc-2.0/image/0/image", // ndims, dims); // var->is_cmpd = is_compound; // // H5Dclose(dset_id); // } // // dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-min"); // if (dset_id >= 0) { // hdf_get_diminfo(dset_id, &ndims, dims); // hdf_var_add(file, MIimagemin, "/minc-2.0/image/0/image-min", // ndims, dims); // H5Dclose(dset_id); // } // // dset_id = H5Dopen1(fd, "/minc-2.0/image/0/image-max"); // if (dset_id >= 0) { // hdf_get_diminfo(dset_id, &ndims, dims); // hdf_var_add(file, MIimagemax, "/minc-2.0/image/0/image-max", // ndims, dims); // H5Dclose(dset_id); // } // } H5E_END_TRY; // // /* Open all of the datasets in the "dimensions" category. // */ // grp_id = H5Gopen2(fd, "/minc-2.0/dimensions", H5P_DEFAULT); // hdf_open_dsets(file, grp_id, "/minc-2.0/dimensions/", 1); // H5Gclose(grp_id); // // /* Open all of the datasets in the "info" category. // */ // grp_id = H5Gopen2(fd, "/minc-2.0/info", H5P_DEFAULT); // hdf_open_dsets(file, grp_id, "/minc-2.0/info/", 0); // H5Gclose(grp_id); return fd; } /** * Create an HDF5 file. */ static hid_t _hdf_create(const char *path, int cmode) { hid_t grp_id; hid_t fd; hid_t tmp_id; hid_t hdf_gpid; hid_t fpid; fpid = H5Pcreate (H5P_FILE_ACCESS); /*VF use all the features of new HDF5 1.8*/ H5Pset_libver_bounds (fpid, H5F_LIBVER_18, H5F_LIBVER_18); H5E_BEGIN_TRY { fd = H5Fcreate(path, cmode, H5P_DEFAULT, fpid); } H5E_END_TRY; if (fd < 0) { /*TODO: report error properly*/ return MI_LOG_ERROR(MI2_MSG_CREATEFILE,path); } /* Create the default groups. * Should we use a non-zero value for size_hint (parameter 3)??? */ hdf_gpid = H5Pcreate (H5P_GROUP_CREATE); H5Pset_attr_phase_change (hdf_gpid, 0, 0); MI_CHECK_HDF_CALL_RET(grp_id = H5Gcreate2(fd, MI_ROOT_PATH , H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2") MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "dimensions", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2") H5Gclose(tmp_id); MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "info", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2") H5Gclose(tmp_id); MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "image", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2") H5Gclose(tmp_id); MI_CHECK_HDF_CALL_RET(tmp_id = H5Gcreate2(grp_id, "image/0", H5P_DEFAULT, hdf_gpid, H5P_DEFAULT),"H5Gcreate2") H5Pclose ( hdf_gpid ); H5Gclose(tmp_id); H5Gclose(grp_id); return fd; } static int _hdf_close(hid_t fd) { //TODO: make sure we save all that is needed MI_CHECK_HDF_CALL_RET(H5Fclose(fd),"H5Fclose") return MI_NOERROR; } /** Create the actual image for the volume. * Note that the image dataset muct be created in the hierarchy * before the image data can be added. * \ingroup mi2Vol */ int micreate_volume_image(mihandle_t volume) { char dimorder[MI2_CHAR_LENGTH]; int i; hid_t dataspace_id; hid_t dset_id; hsize_t hdf_size[MI2_MAX_VAR_DIMS]; /* Try creating IMAGE dataset i.e. /minc-2.0/image/0/image */ dimorder[0] = '\0'; /* Set string to empty */ for (i = 0; i < volume->number_of_dims; i++) { hdf_size[i] = volume->dim_handles[i]->length; /* Create the dimorder string, ordered comma-separated list of dimension names. */ strncat(dimorder, volume->dim_handles[i]->name, MI2_CHAR_LENGTH - 1); if (i != volume->number_of_dims - 1) { strncat(dimorder, ",", MI2_CHAR_LENGTH - 1); } } /* Create a SIMPLE dataspace */ dataspace_id = H5Screate_simple(volume->number_of_dims, hdf_size, NULL); if (dataspace_id < 0) { return MI_ERROR; } MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate1(volume->hdf_id, MI_ROOT_PATH "/image/0/image", volume->ftype_id, dataspace_id, volume->plist_id),"H5Dcreate1") volume->image_id = dset_id; add_standard_minc_attributes(volume->hdf_id,volume->image_id); /* Create the dimorder attribute, ordered comma-separated list of dimension names. */ miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING, strlen(dimorder), dimorder); H5Sclose(dataspace_id); if (volume->volume_class == MI_CLASS_REAL) { int ndims; hid_t dcpl_id; double dtmp; MI_CHECK_HDF_CALL_RET(dcpl_id = H5Pcreate(H5P_DATASET_CREATE),"H5Pcreate") if (volume->has_slice_scaling) { /* TODO: Find the slowest-varying spatial dimension; that forms * the basis for the image-min and image-max variables. Right * now this is an oversimplification! */ ndims = volume->number_of_dims - 2; MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate_simple(ndims, hdf_size, NULL),"H5Screate_simple") } else { ndims = 0; MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate(H5S_SCALAR),"H5Screate") } if (ndims != 0) { dimorder[0] = '\0'; /* Set string to empty */ for (i = 0; i < ndims; i++) { /* Create the dimorder string, ordered comma-separated list of dimension names. */ strncat(dimorder, volume->dim_handles[i]->name, MI2_CHAR_LENGTH - 1); if (i != volume->number_of_dims - 1) { strncat(dimorder, ",", MI2_CHAR_LENGTH - 1); } } } /* Create the image minimum dataset for FULL-RESOLUTION storage of data */ dtmp = 0.0; H5Pset_fill_value(dcpl_id, H5T_NATIVE_DOUBLE, &dtmp); MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate1(volume->hdf_id, MI_ROOT_PATH "/image/0/image-min", H5T_IEEE_F64LE, dataspace_id, dcpl_id),H5Dcreate1) if (ndims != 0) { miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING, strlen(dimorder), dimorder); } volume->imin_id = dset_id; add_standard_minc_attributes(volume->hdf_id,volume->imin_id); /* Create the image maximum dataset for FULL-RESOLUTION storage of data */ dtmp = 1.0; H5Pset_fill_value(dcpl_id, H5T_NATIVE_DOUBLE, &dtmp); MI_CHECK_HDF_CALL_RET(dset_id = H5Dcreate1(volume->hdf_id, MI_ROOT_PATH "/image/0/image-max", H5T_IEEE_F64LE, dataspace_id, dcpl_id),"H5Dcreate1") if (ndims != 0) { miset_attr_at_loc(dset_id, "dimorder", MI_TYPE_STRING, strlen(dimorder), dimorder); } volume->imax_id = dset_id; add_standard_minc_attributes(volume->hdf_id,volume->imax_id); H5Sclose(dataspace_id); H5Pclose(dcpl_id); } return (MI_NOERROR); } /** Set up the array of conversions from voxel to world coordinate order. */ static int miset_volume_world_indices(mihandle_t hvol) { int i; for (i = 0; i < hvol->number_of_dims; i++) { midimhandle_t hdim = hvol->dim_handles[i]; hdim->world_index = -1; if (hdim->dim_class == MI_DIMCLASS_SPATIAL) { if (!strcmp(hdim->name, MIxspace)) { hdim->world_index = MI2_X; } else if (!strcmp(hdim->name, MIyspace)) { hdim->world_index = MI2_Y; } else if (!strcmp(hdim->name, MIzspace)) { hdim->world_index = MI2_Z; } } else if (hdim->dim_class == MI_DIMCLASS_SFREQUENCY) { if (!strcmp(hdim->name, MIxfrequency)) { hdim->world_index = MI2_X; } else if (!strcmp(hdim->name, MIyfrequency)) { hdim->world_index = MI2_Y; } else if (!strcmp(hdim->name, MIzfrequency)) { hdim->world_index = MI2_Z; } } } return (MI_NOERROR); } /** Create and initialize a MINC 2.0 volume structure. */ static mihandle_t mialloc_volume_handle(void) { mihandle_t handle = (mihandle_t) malloc(sizeof(struct mivolume)); if (handle != NULL) { /* Clear the memory by default. */ memset(handle, 0, sizeof(struct mivolume)); /* Set the defaults for the data structure */ handle->scale_min = 0.0; handle->scale_max = 1.0; handle->image_id = -1; handle->imax_id = -1; handle->imin_id = -1; handle->plist_id = -1; handle->has_slice_scaling = FALSE; handle->is_dirty = FALSE; handle->dim_indices = NULL; handle->selected_resolution = 0; } return (handle); } /** Create a volume with the specified name, dimensions, type, class, volume properties and retrieve the volume handle. \ingroup mi2Vol */ int micreate_volume(const char *filename, int number_of_dimensions, midimhandle_t dimensions[], mitype_t volume_type, miclass_t volume_class, mivolumeprops_t create_props, mihandle_t *volume) { int i; int stat; hid_t file_id; hid_t hdf_type; hid_t hdf_plist; hid_t fspc_id; hsize_t dim[1]; hid_t grp_id; herr_t status; hid_t dataset_id = -1; hid_t dataset_width = -1; hid_t dataspace_id = -1; char *name; size_t size; hsize_t hdf_size[MI2_MAX_VAR_DIMS]; mihandle_t handle; mivolumeprops_t props_handle; char ident_str[128]; hid_t tmp_type; int dimension_is_vector = 0; /* Initialization. For the actual body of this function look at m2utils.c */ miinit(); /* Validate the parameters. */ if (filename == NULL) { return MI_LOG_ERROR(MI2_MSG_CREATEFILE," (NULL) "); } if (dimensions == NULL && number_of_dimensions != 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC," Can't create volume with undefined dimensions"); } /* Allocate space for the volume handle */ handle = mialloc_volume_handle(); if (handle == NULL) { return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,sizeof(struct mivolume)); } /* Initialize some of the variables associated with the volume handle. */ handle->mode = MI2_OPEN_RDWR; handle->number_of_dims = number_of_dimensions; /* convert minc type to hdf type */ hdf_type = mitype_to_hdftype(volume_type, FALSE); /* Setting up volume type_id */ switch (volume_class) { case MI_CLASS_REAL: case MI_CLASS_INT: handle->ftype_id = hdf_type; handle->mtype_id = H5Tget_native_type(handle->ftype_id, H5T_DIR_ASCEND); break; case MI_CLASS_LABEL: /* A volume of class LABEL must have an integer type (positive). */ switch (volume_type) { case MI_TYPE_UBYTE: case MI_TYPE_USHORT: case MI_TYPE_UINT: case MI_TYPE_BYTE: case MI_TYPE_SHORT: case MI_TYPE_INT: MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Tenum_create(hdf_type),"H5Tenum_create") tmp_type = H5Tget_native_type(hdf_type, H5T_DIR_ASCEND); H5Tclose(hdf_type); hdf_type = tmp_type; /* Create an enumerated type with the native type as it's base. */ MI_CHECK_HDF_CALL_RET(handle->mtype_id = H5Tenum_create(hdf_type),"H5Tenum_create") H5Tclose(hdf_type); miinit_enum(handle->ftype_id); miinit_enum(handle->mtype_id); break; default: free(handle); return (MI_ERROR); } break; case MI_CLASS_COMPLEX: switch (volume_type) { case MI_TYPE_SCOMPLEX: case MI_TYPE_ICOMPLEX: case MI_TYPE_FCOMPLEX: case MI_TYPE_DCOMPLEX: handle->ftype_id = hdf_type; handle->mtype_id = mitype_to_hdftype(volume_type, TRUE); break; default: free(handle); return MI_LOG_ERROR(MI2_MSG_BADTYPE,volume_type); } break; case MI_CLASS_UNIFORM_RECORD: MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Tcreate(H5T_COMPOUND, H5Tget_size(hdf_type)),"H5Tcreate") MI_CHECK_HDF_CALL_RET(handle->mtype_id = H5Tcreate(H5T_COMPOUND, H5Tget_size(hdf_type)),"H5Tcreate") H5Tclose(hdf_type); break; default: free(handle); return (MI_ERROR); } handle->volume_class = volume_class; /* Create file in HDF5 with the given filename and H5F_ACC_TRUNC: Truncate file, if it already exists, erasing all data previously stored in the file. and create ID and ID access as default. */ file_id = _hdf_create(filename, H5F_ACC_TRUNC); if (file_id < 0) { free(handle); return (MI_ERROR); } handle->hdf_id = file_id; _generate_ident(ident_str, sizeof(ident_str)); miset_attribute(handle, MI_ROOT_PATH, "ident", MI_TYPE_STRING, strlen(ident_str), ident_str); miset_attribute(handle, MI_ROOT_PATH, "minc_version", MI_TYPE_STRING, strlen(MINC_VERSION), MINC_VERSION); _miset_volume_class(handle, handle->volume_class); /* Create a new property list for the volume */ MI_CHECK_HDF_CALL_RET(hdf_plist = H5Pcreate(H5P_DATASET_CREATE),"H5Pcreate") handle->plist_id = hdf_plist; /* Set fill value to guarantee valid data on incomplete datasets. */ if (volume_class != MI_CLASS_LABEL && volume_class != MI_CLASS_UNIFORM_RECORD) { size_t siz = H5Tget_size(handle->ftype_id); char *tmp = calloc(1, siz); H5Pset_fill_value(hdf_plist, handle->ftype_id, tmp); free(tmp); } /* See if chunking and/or compression should be enabled and if yes set the type of storage used to store the raw data for a dataset. */ if (create_props != NULL && (create_props->compression_type == MI_COMPRESS_ZLIB || create_props->edge_count != 0)) { /* Set the storage to CHUNKED */ MI_CHECK_HDF_CALL_RET(stat = H5Pset_layout(hdf_plist, H5D_CHUNKED),"H5Pset_layout") /* Create an array, hdf_size, containing the size of each chunk */ for (i=0; i < number_of_dimensions; i++) { hdf_size[i] = create_props->edge_lengths[i]; /* If the size of each chunk is greater than the size of the corresponding dimension, set the chunk size to the dimension size */ if (hdf_size[i] > dimensions[i]->length) { hdf_size[i] = dimensions[i]->length; } } /* Sets the size of the chunks used to store a chunked layout dataset */ MI_CHECK_HDF_CALL_RET(stat = H5Pset_chunk(hdf_plist, number_of_dimensions, hdf_size),"H5Pset_chunk") /* Sets compression method and compression level */ MI_CHECK_HDF_CALL_RET(stat = H5Pset_deflate(hdf_plist, create_props->zlib_level),"H5Pset_deflate") } else { /* No COMPRESSION or CHUNKING is enabled */ MI_CHECK_HDF_CALL_RET(stat = H5Pset_layout(hdf_plist, H5D_CONTIGUOUS),"H5Pset_layout") /* CONTIGUOUS data */ } /* See if Multi-res is set to a level above 0 and if yes create subgroups i.e., /minc-2.0/image/1/.. /minc-2.0/image/2/.. etc */ // must add some code to make sure that the res level is possible if (create_props != NULL && create_props->depth > 0) { for (i=0; i < create_props->depth ; i++) { if (minc_create_thumbnail(handle, i+1) < 0) { free(handle); return (MI_ERROR); } } } /* Try creating DIMENSIONS GROUP i.e. /minc-2.0/dimensions */ MI_CHECK_HDF_CALL_RET(grp_id = H5Gopen1(file_id, MI_FULLDIMENSIONS_PATH),"H5Gopen1") /* Once the DIMENSIONS GROUP is opened, create each dimension. */ for (i=0; i < number_of_dimensions ; i++) { /* First create the dataspace required to create a dimension variable (dataset) */ if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) { dim[0] = dimensions[i]->length; MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate_simple(1, dim, NULL),"H5Screate_simple") } else { MI_CHECK_HDF_CALL_RET(dataspace_id = H5Screate(H5S_SCALAR),"H5Screate") } if (dataspace_id < 0) { free(handle); return (MI_ERROR); } dimension_is_vector= (strcmp ( dimensions[i]->name, MIvector_dimension ) == 0 ); /* Create a dataset(dimension variable name) in DIMENSIONS GROUP */ MI_CHECK_HDF_CALL_RET(dataset_id = H5Dcreate1(grp_id, dimensions[i]->name, H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT),"H5Dcreate1") /* Dimension variable for a regular dimension contains no meaningful data. Whereas, Dimension variable for an irregular dimension contains a vector with the lengths equal to the sampled points along the dimension. Also, create a variable named "-width" and write the dimension->widths. */ if(!dimension_is_vector ) add_standard_minc_attributes(file_id,dataset_id); /*vector dimension is a record*/ /* Check for irregular dimension and make sure offset values are provided for this dimension */ if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) { if (dimensions[i]->offsets == NULL) { free(handle); return (MI_ERROR); } else { /* If dimension is regularly sampled */ MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dataset_id),"H5Dget_space") /* Write the raw data from buffer (dimensions[i]->offsets) to the dataset. */ MI_CHECK_HDF_CALL_RET(status = H5Dwrite(dataset_id, H5T_NATIVE_DOUBLE, dataspace_id, fspc_id, H5P_DEFAULT, dimensions[i]->offsets),"H5Dwrite") /* Write the raw data from buffer (dimensions[i]->offsets) to the dataset. */ size = strlen(dimensions[i]->name) + 6 + 1; name = malloc(size); strcpy(name, dimensions[i]->name); strcat(name, "-width"); /* Create dataset dimension_name-width */ dataset_width = H5Dcreate1(grp_id, name, H5T_IEEE_F64LE, dataspace_id, H5P_DEFAULT); /* Return an Id for the dataspace of the dataset dataset_width */ MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dataset_width),"H5Dget_space") /* Write the raw data from buffer (dimensions[i]->widths) to the dataset. */ MI_CHECK_HDF_CALL_RET(status = H5Dwrite(dataset_width, H5T_NATIVE_DOUBLE, dataspace_id, fspc_id, H5P_DEFAULT, dimensions[i]->widths),"H5Dwrite") /* Create new attribute "length", with appropriate type (to hdf5) conversion. miset_attr_at_loc(..) is implemented at m2utils.c */ miset_attr_at_loc(dataset_width, "length", MI_TYPE_INT, 1, &dimensions[i]->length); /* Close the specified datatset */ H5Dclose(dataset_width); free(name); } } if (dimensions[i]->attr & MI_DIMATTR_NOT_REGULARLY_SAMPLED) { name = "irregular"; } else { name = "regular__"; } /* Create attribute "spacing" and set its value to "regular__" or "irregular" */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "spacing", MI_TYPE_STRING, strlen(name), name); switch (dimensions[i]->dim_class) { case MI_DIMCLASS_SPATIAL: name = "spatial"; break; case MI_DIMCLASS_TIME: name = "time___"; break; case MI_DIMCLASS_SFREQUENCY: name = "sfreq__"; break; case MI_DIMCLASS_TFREQUENCY: name = "tfreq__"; break; case MI_DIMCLASS_USER: name = "user___"; break; case MI_DIMCLASS_RECORD: name = "record_"; break; case MI_DIMCLASS_ANY: default: /* These should not be seen in this context!!! */ return (MI_ERROR); } if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "class", MI_TYPE_STRING, strlen(name), name); /* Create Dimension attribute "direction_cosines" */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "direction_cosines", MI_TYPE_DOUBLE, 3, dimensions[i]->direction_cosines); /* Save dimension length */ miset_attr_at_loc(dataset_id, "length", MI_TYPE_INT, 1, &dimensions[i]->length); /* Save step value. */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "step", MI_TYPE_DOUBLE, 1, &dimensions[i]->step); /* Save start value. */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "start", MI_TYPE_DOUBLE, 1, &dimensions[i]->start); /* Save units. */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "units", MI_TYPE_STRING, strlen(dimensions[i]->units), dimensions[i]->units); /* Save sample width. */ if(!dimension_is_vector) miset_attr_at_loc(dataset_id, "width", MI_TYPE_DOUBLE, 1, &dimensions[i]->width); /* Save comments. If user has not specified any comments, do not add this attribute */ if (dimensions[i]->comments != NULL) { miset_attr_at_loc(dataset_id, "comments", MI_TYPE_STRING, strlen(dimensions[i]->comments), dimensions[i]->comments); } /* Close the dataset with the specified Id */ H5Dclose(dataset_id); } //for (i=0; i < number_of_dimensions ; i++) /* Close the group with the specified Id */ H5Gclose(grp_id); /* Allocate space for all the dimension handles Note, each volume handle is associated with an array of dimension handles in the order that they were create (i.e, file order) */ handle->dim_handles = (midimhandle_t *)malloc(number_of_dimensions * sizeof(midimhandle_t)); if (handle->dim_handles == NULL) { return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,number_of_dimensions * sizeof(midimhandle_t)); } /* Once the space for all dimension handles is created fill the dimension handle array with the appropriate dimension handle. */ for (i = 0; i < number_of_dimensions; i++) { handle->dim_handles[i] = dimensions[i]; dimensions[i]->volume_handle = handle; } miset_volume_world_indices(handle); /* Verify the volume type. */ switch (volume_type) { case MI_TYPE_BYTE: case MI_TYPE_SHORT: case MI_TYPE_INT: case MI_TYPE_FLOAT: case MI_TYPE_DOUBLE: case MI_TYPE_STRING: case MI_TYPE_UBYTE: case MI_TYPE_USHORT: case MI_TYPE_UINT: case MI_TYPE_SCOMPLEX: case MI_TYPE_ICOMPLEX: case MI_TYPE_FCOMPLEX: case MI_TYPE_DCOMPLEX: case MI_TYPE_UNKNOWN: break; default: return MI_LOG_ERROR(MI2_MSG_BADTYPE,volume_type); } handle->volume_type = volume_type; /* Set the initial value of the valid-range */ miinit_default_range(handle->volume_type, &handle->valid_max, &handle->valid_min); /* Get the voxel to world transform for the volume */ miget_voxel_to_world(handle, handle->v2w_transform); /* Calculate the inverse transform */ miinvert_transform(handle->v2w_transform, handle->w2v_transform); /* Allocated space for the volume properties */ props_handle = (mivolumeprops_t)malloc(sizeof(struct mivolprops)); /* Initialize volume properties with zero */ memset(props_handle, 0, sizeof (struct mivolprops)); /* If volume properties is specified by the user set all the properties of the volume handle */ if (create_props != NULL) { /* Set the enable_flag for multi-resolution */ props_handle->enable_flag = create_props->enable_flag; /* Set the depth of multi-resolution, i.e., how many levels of resolution is specified maximum is 16. */ props_handle->depth = create_props->depth; /* Set compression type, currently two values either no compression or zlib is applicable. */ switch (create_props->compression_type) { case MI_COMPRESS_NONE: props_handle->compression_type = MI_COMPRESS_NONE; break; case MI_COMPRESS_ZLIB: props_handle->compression_type = MI_COMPRESS_ZLIB; break; default: free(props_handle); return MI_LOG_ERROR(MI2_MSG_BADTYPE,create_props->compression_type); } /* Note that setting compression on (i.e., MI_COMPRESS_ZLIB) turns chunking on by default. Need to set the number of chunks (edge_count) */ props_handle->zlib_level = create_props->zlib_level; props_handle->edge_count = create_props->edge_count; /* Allocate space for an array which holds the size of each chunk and fill the array with the appropriiate chunk sizes. */ props_handle->edge_lengths = (int *)malloc(create_props->max_lengths*sizeof(int)); for (i=0; imax_lengths; i++) { props_handle->edge_lengths[i] = create_props->edge_lengths[i]; } props_handle->max_lengths = create_props->max_lengths; props_handle->record_length = create_props->record_length; /* Explicitly allocate storage for name */ if (create_props->record_name != NULL) { props_handle->record_name =malloc(strlen(create_props->record_name) + 1 ); strcpy(props_handle->record_name, create_props->record_name); } props_handle->template_flag = create_props->template_flag; } /* Set the handle to volume properties */ handle->create_props = props_handle; /* Return volume handle */ *volume = handle; return (MI_NOERROR); } /** Return the number of dimensions associated with this volume. * \ingroup mi2Vol */ int miget_volume_dimension_count(mihandle_t volume, midimclass_t cls, midimattr_t attr, int *number_of_dimensions) { int i, count=0; /* Validate the parameters */ if (volume == NULL || number_of_dimensions == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get dimension count with null volume or null variable"); } /* For each dimension check to make sure that dimension class and attribute match with the specified parameters and if yes increment the dimension count */ for (i=0; i< volume->number_of_dims; i++) { if ((cls == MI_DIMCLASS_ANY || volume->dim_handles[i]->dim_class == cls) && (attr == MI_DIMATTR_ALL || volume->dim_handles[i]->attr == attr)) { count++; } } *number_of_dimensions = count; return (MI_NOERROR); } /** Returns the number of voxels in the volume. * \ingroup mi2Vol */ int miget_volume_voxel_count(mihandle_t volume, misize_t *number_of_voxels) { char path[MI2_MAX_PATH]; hid_t dset_id; hid_t fspc_id; /* Validate parameters */ if (volume == NULL || number_of_voxels == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to get voxel count with null volume or null variable"); } /* Quickest way to do this is with the dataspace identifier of the * volume. Use the volume's current resolution. */ sprintf(path, MI_ROOT_PATH "/image/%d/image", volume->selected_resolution); /* Open the dataset with the specified path */ MI_CHECK_HDF_CALL_RET(dset_id = H5Dopen1(volume->hdf_id, path),"H5Dopen1"); /* Get an Id to the copy of the dataspace */ MI_CHECK_HDF_CALL_RET(fspc_id = H5Dget_space(dset_id),"H5Dget_space"); /* Determines the number of elements in the dataspace and cast the result to an integer. */ *number_of_voxels = (misize_t) H5Sget_simple_extent_npoints(fspc_id); /* Close the dataspace */ H5Sclose(fspc_id); /* Close the dataset */ H5Dclose(dset_id); return (MI_NOERROR); } /* Get the number of dimensions in the file */ static int _miget_file_dimension_count(hid_t file_id) { hid_t dset_id; hid_t space_id; int result = -1; /* hdf5 macro can temporarily disable the automatic error printing */ H5E_BEGIN_TRY { dset_id = midescend_path(file_id, MI_ROOT_PATH "/image/0/image"); } H5E_END_TRY; if (dset_id >= 0) { /* Get an Id to the copy of the dataspace */ MI_CHECK_HDF_CALL(space_id = H5Dget_space(dset_id),"H5Dget_space"); if (space_id > 0) { /* Determine the dimensionality of the dataspace */ MI_CHECK_HDF_CALL(result = H5Sget_simple_extent_ndims(space_id),"H5Sget_simple_extent_ndims"); /* Close the dataspace */ H5Sclose(space_id); } /* Close the dataset */ H5Dclose(dset_id); } return (result); } /* Get dimension variable attributes for the given dimension name */ static int _miset_volume_class(mihandle_t volume, miclass_t volume_class) { const char *class_ptr; switch (volume_class) { case MI_CLASS_REAL: class_ptr = "real___"; break; case MI_CLASS_INT: class_ptr = "integer"; break; case MI_CLASS_LABEL: class_ptr = "label__"; break; case MI_CLASS_COMPLEX: class_ptr = "complex"; break; case MI_CLASS_UNIFORM_RECORD: class_ptr = "array__"; break; default: return MI_LOG_ERROR(MI2_MSG_GENERIC,"Unknown volume class"); } miset_attribute(volume, MI_ROOT_PATH, "class", MI_TYPE_STRING, strlen(class_ptr), class_ptr); return (MI_NOERROR); } static int _miget_volume_class(mihandle_t volume, miclass_t *volume_class) { char class_buf[MI2_CHAR_LENGTH]; if( miget_attribute(volume, MI_ROOT_PATH, "class", MI_TYPE_STRING, MI2_CHAR_LENGTH, class_buf) == MI_NOERROR ) { if (!strcmp(class_buf, "label__")) { *volume_class = MI_CLASS_LABEL; } else if (!strcmp(class_buf, "integer")) { *volume_class = MI_CLASS_INT; } else if (!strcmp(class_buf, "complex")) { *volume_class = MI_CLASS_COMPLEX; } else if (!strcmp(class_buf, "array__")) { *volume_class = MI_CLASS_UNIFORM_RECORD; } else { *volume_class = MI_CLASS_REAL; } } else { /*probably volume doesn't have this attribute*/ *volume_class = MI_CLASS_REAL; } return (MI_NOERROR); } /* Get dimension variable attributes for the given dimension name */ static int _miget_file_dimension(mihandle_t volume, const char *dimname, midimhandle_t *hdim_ptr) { char path[MI2_CHAR_LENGTH]; char temp[MI2_CHAR_LENGTH]; midimhandle_t hdim; unsigned int len; /* Create a path with the dimension name */ sprintf(path, MI_ROOT_PATH "/dimensions/%s", dimname); /* Allocate space for the dimension handle */ hdim = (midimhandle_t) malloc(sizeof (*hdim)); /* Initialize everything to zero */ memset(hdim, 0, sizeof (*hdim)); hdim->name = strdup(dimname); /* hdf5 macro can temporarily disable the automatic error printing */ H5E_BEGIN_TRY { int r; /* Get the attribute (spacing) from a minc file */ r = miget_attribute(volume, path, "spacing", MI_TYPE_STRING, MI2_CHAR_LENGTH, temp); if (r==MI_NOERROR && !strcmp(temp, "irregular")) { hdim->attr |= MI_DIMATTR_NOT_REGULARLY_SAMPLED; } else { hdim->attr |= MI_DIMATTR_REGULARLY_SAMPLED; } /* Get the attribute (class) from a minc file */ r = miget_attribute(volume, path, "class", MI_TYPE_STRING, MI2_CHAR_LENGTH, temp); if (r < 0) { /* Get the default class. */ if (!strcmp(dimname, MItime)) { hdim->dim_class = MI_DIMCLASS_TIME; } else if (!strcmp(dimname, MIvector_dimension)) { hdim->dim_class = MI_DIMCLASS_RECORD; hdim->step = 0.0; } else { hdim->dim_class = MI_DIMCLASS_SPATIAL; } } else { if (!strcmp(temp, "spatial")) { hdim->dim_class = MI_DIMCLASS_SPATIAL; } else if (!strcmp(temp, "time___")) { hdim->dim_class = MI_DIMCLASS_TIME; } else if (!strcmp(temp, "sfreq__")) { hdim->dim_class = MI_DIMCLASS_SFREQUENCY; } else if (!strcmp(temp, "tfreq__")) { hdim->dim_class = MI_DIMCLASS_TFREQUENCY; } else if (!strcmp(temp, "user___")) { hdim->dim_class = MI_DIMCLASS_USER; } else if (!strcmp(temp, "record_")) { hdim->dim_class = MI_DIMCLASS_RECORD; } else { MI_LOG_ERROR(MI2_MSG_GENERIC,"Unknown dimension type"); } } /* Get the attribute (length) from a minc file. We have to do this in * two steps, as MI_TYPE_UINT is not necessarily the same size as * hsize_t/misize_t, so we have to read the value into a variable of * the right type, then assign it to the structure member, to guarantee * proper promotion. */ r = miget_attribute(volume, path, "length", MI_TYPE_UINT, 1, &len); if (r < 0) { MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine dimension length"); } hdim->length = len; /* Will promote unsigned int to misize_t. */ /* Get the attribute (start) from a minc file for NON vector_dimension only */ if (strcmp(dimname, "vector_dimension")) { r = miget_attribute(volume, path, MIstart, MI_TYPE_DOUBLE, 1, &hdim->start); if (r < 0) { hdim->start = 0.0; } /* Get the attribute (step) from a minc file */ r = miget_attribute(volume, path, MIstep, MI_TYPE_DOUBLE, 1, &hdim->step); if (r < 0) { hdim->step = 1.0; } } /* Get the attribute (direction_cosines) from a minc file */ r = miget_attribute(volume, path, MIdirection_cosines, MI_TYPE_DOUBLE, 3, hdim->direction_cosines); if (r < 0) { hdim->direction_cosines[MI2_X] = 0.0; hdim->direction_cosines[MI2_Y] = 0.0; hdim->direction_cosines[MI2_Z] = 0.0; if (!strcmp(dimname, MIxspace)) { hdim->direction_cosines[MI2_X] = 1.0; } else if (!strcmp(dimname, MIyspace)) { hdim->direction_cosines[MI2_Y] = 1.0; } else if (!strcmp(dimname, MIzspace)) { hdim->direction_cosines[MI2_Z] = 1.0; } } r = miget_attribute(volume, path, "units", MI_TYPE_STRING, MI2_CHAR_LENGTH, temp); if (r < 0) { hdim->units = strdup(""); } else { hdim->units = strdup(temp); } } H5E_END_TRY; /* Return the dimension handle */ *hdim_ptr = hdim; hdim->volume_handle = volume; return (MI_NOERROR); } /** Opens an existing MINC volume for read-only access if mode argument is * MI2_OPEN_READ, or read-write access if mode argument is MI2_OPEN_RDWR. * \ingroup mi2Vol */ int miopen_volume(const char *filename, int mode, mihandle_t *volume) { hid_t file_id; hid_t dset_id; hid_t space_id; mihandle_t handle; int hdf_mode; char dimorder[MI2_CHAR_LENGTH]; int i,r; char *p1, *p2; H5T_class_t hdf_class; size_t nbytes; int is_signed; int n_dimensions; /* Initialization. For the actual body of this function look at m2utils.c */ miinit(); /* Convert the specified mode to hdf mode */ if (mode == MI2_OPEN_READ) { hdf_mode = H5F_ACC_RDONLY; } else if (mode == MI2_OPEN_RDWR) { hdf_mode = H5F_ACC_RDWR; } else { return (MI_ERROR); } /* Allocate space for the volume handle */ handle = mialloc_volume_handle(); if (handle == NULL) { return MI_LOG_ERROR(MI2_MSG_OUTOFMEM,sizeof(struct mivolume)); } /* Open the hdf file using the given filename and mode */ file_id = _hdf_open(filename, hdf_mode); if (file_id < 0) { /*try to convert MINC1 file*/ #ifdef HAVE_MINC1 char * temp_file=NULL; if ( mode == MI2_OPEN_READ ) { if( (temp_file=micreate_tempfile())) { if( minc_format_convert(filename,temp_file) == MI_NOERROR ) { if( (file_id = _hdf_open(temp_file, hdf_mode) ) >0) { unlink( temp_file ); /*file will be deleted immedeately after closing...*/ free( temp_file ); } else { unlink( temp_file ); free( temp_file ); free( handle ); return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename); } } else { free( temp_file ); free( handle ); return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename); } } else { free( temp_file ); free( handle ); return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename); } } else { free( handle ); return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename); } #else free( handle ); return MI_LOG_ERROR(MI2_MSG_OPENFILE,filename); #endif } /* Set some varibales associated with the volume handle */ handle->hdf_id = file_id; handle->mode = mode; /* Get the volume class. */ _miget_volume_class(handle, &handle->volume_class); /* GET THE DIMENSION COUNT */ n_dimensions = handle->number_of_dims = _miget_file_dimension_count(file_id); if( n_dimensions <= 0 ) { free(handle); return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to open minc file without image variable"); } /* READ EACH OF THE DIMENSIONS */ handle->dim_handles = (midimhandle_t *)malloc(n_dimensions * sizeof(midimhandle_t)); if(handle->dim_handles == NULL) { free(handle); return MI_LOG_ERROR(MI2_MSG_OUTOFMEM, n_dimensions * sizeof(midimhandle_t)); } /* Get the attribute (dimorder) from the image dataset */ r = miget_attribute(handle, MI_ROOT_PATH "/image/0/image", "dimorder", MI_TYPE_STRING, sizeof(dimorder), dimorder); if ( r < 0) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine dimension order"); } p1 = dimorder; /* Break the ordered, comma-separated list of dimension names to get each individual dimension name */ for (i = 0; i < handle->number_of_dims; i++) { p2 = strchr(p1, ','); if (p2 != NULL) { *p2 = '\0'; } /* Get dimension variable attributes for each dimension */ _miget_file_dimension(handle, p1, &handle->dim_handles[i]); p1 = p2 + 1; } if( miset_volume_world_indices(handle) < 0 ) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Can't determine world indices"); } /* SEE IF SLICE SCALING IS ENABLED */ handle->has_slice_scaling = FALSE; /* hdf5 macro can temporarily disable the automatic error printing */ H5E_BEGIN_TRY { /* Open the dataset image-max at the specified path*/ dset_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-max"); } H5E_END_TRY; if (dset_id >= 0) { /* Get the Id of the copy of the dataspace of the dataset */ space_id = H5Dget_space(dset_id); if (space_id >= 0) { /* If the dimensionality of the image-max variable is one or * greater, we consider this volume to have slice-scaling enabled. */ if (H5Sget_simple_extent_ndims(space_id) >= 1) { handle->has_slice_scaling = TRUE; } H5Sclose(space_id); /* Close the dataspace handle */ } H5Dclose(dset_id); /* Close the dataset handle */ } if (!handle->has_slice_scaling) { /* Read the minimum scalar of the given type at the specified path */ miget_scalar(handle->hdf_id, H5T_NATIVE_DOUBLE, MI_ROOT_PATH "/image/0/image-min", &handle->scale_min); /* Read the maximum scalar of the given type at the specified path */ miget_scalar(handle->hdf_id, H5T_NATIVE_DOUBLE, MI_ROOT_PATH "/image/0/image-max", &handle->scale_max); } /* Read the current voxel-to-world transform */ miget_voxel_to_world(handle, handle->v2w_transform); /* Calculate the inverse transform */ miinvert_transform(handle->v2w_transform, handle->w2v_transform); /* Open the image dataset */ MI_CHECK_HDF_CALL_RET(handle->image_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image"),"H5Dopen1"); /* Get the Id for the copy of the datatype for the dataset */ MI_CHECK_HDF_CALL_RET(handle->ftype_id = H5Dget_type(handle->image_id),"H5Dget_type"); switch (H5Tget_class(handle->ftype_id)) { case H5T_INTEGER: case H5T_FLOAT: handle->mtype_id = H5Tget_native_type(handle->ftype_id, H5T_DIR_ASCEND); break; case H5T_COMPOUND: handle->mtype_id = H5Tcreate(H5T_COMPOUND, H5Tget_size(handle->ftype_id)); for (i = 0; i < H5Tget_nmembers(handle->ftype_id); i++) { hid_t tmp_id = H5Tget_member_type(handle->ftype_id, i); size_t tmp_off = H5Tget_member_offset(handle->ftype_id, i); char *tmp_nm = H5Tget_member_name(handle->ftype_id, i); hid_t tmp2_id = H5Tget_native_type(tmp_id, H5T_DIR_ASCEND); H5Tinsert(handle->mtype_id, tmp_nm, tmp_off, tmp2_id); free(tmp_nm); H5Tclose(tmp_id); H5Tclose(tmp2_id); } break; case H5T_ENUM: handle->mtype_id = H5Tcopy(handle->ftype_id); miinit_enum(handle->ftype_id); miinit_enum(handle->mtype_id); /* Set native order ---> is not allowed after order is set */ //H5Tset_order(handle->mtype_id, H5Tget_order(H5T_NATIVE_INT)); break; default: return (MI_ERROR); } /* hdf5 macro can temporarily disable the automatic error printing */ H5E_BEGIN_TRY { /* Open both image-min and image-max datasets */ handle->imax_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-max"); handle->imin_id = H5Dopen1(file_id, MI_ROOT_PATH "/image/0/image-min"); } H5E_END_TRY; /* Convert the type to a MINC type. */ /* Get the class Id for the datatype */ hdf_class = H5Tget_class(handle->ftype_id); /* Get the size of the datatype */ nbytes = H5Tget_size(handle->ftype_id); switch (hdf_class) { case H5T_INTEGER: case H5T_ENUM: /* label images */ is_signed = (H5Tget_sign(handle->ftype_id) == H5T_SGN_2); switch (nbytes) { case 1: handle->volume_type = (is_signed ? MI_TYPE_BYTE : MI_TYPE_UBYTE); break; case 2: handle->volume_type = (is_signed ? MI_TYPE_SHORT : MI_TYPE_USHORT); break; case 4: handle->volume_type = (is_signed ? MI_TYPE_INT : MI_TYPE_UINT); break; default: return MI_LOG_ERROR(MI2_MSG_BADTYPE,hdf_class); } break; case H5T_FLOAT: handle->volume_type = (nbytes == 4) ? MI_TYPE_FLOAT : MI_TYPE_DOUBLE; break; case H5T_STRING: handle->volume_type = MI_TYPE_STRING; break; case H5T_ARRAY: /* TODO: handle this case for uniform records (arrays)? */ break; case H5T_COMPOUND: /* TODO: handle this case for non-uniform records? */ break; default: return MI_LOG_ERROR(MI2_MSG_BADTYPE,hdf_class); } /* Read the current settings for valid-range */ miread_valid_range(handle, &handle->valid_max, &handle->valid_min); *volume = handle; return (MI_NOERROR); } /** Writes any changes associated with the volume to disk. \ingroup mi2Vol */ static int miflush_volume(mihandle_t volume) { if ((volume->mode & MI2_OPEN_RDWR) != 0) { H5Fflush(volume->hdf_id, H5F_SCOPE_GLOBAL); misave_valid_range(volume); } return (MI_NOERROR); } /** Close an existing MINC volume. If the volume was newly created, * all changes will be written to disk. In all cases this function closes * the open volume and frees memory associated with the volume handle. * \ingroup mi2Vol */ int miclose_volume(mihandle_t volume) { int i; if (volume == NULL) { return MI_LOG_ERROR(MI2_MSG_GENERIC,"Trying to close null volume"); } if (volume->is_dirty) { minc_update_thumbnails(volume); volume->is_dirty = FALSE; } miflush_volume(volume); if (volume->image_id > 0) { H5Dclose(volume->image_id); } if (volume->imax_id > 0) { H5Dclose(volume->imax_id); } if (volume->imin_id > 0) { H5Dclose(volume->imin_id); } if (volume->ftype_id > 0) { H5Tclose(volume->ftype_id); } if (volume->mtype_id > 0) { H5Tclose(volume->mtype_id); } if (volume->plist_id > 0) { H5Pclose(volume->plist_id); } if (_hdf_close(volume->hdf_id) < 0) { return (MI_ERROR); } if (volume->dim_handles != NULL) { for(i=0;inumber_of_dims;i++) { mifree_dimension_handle(volume->dim_handles[i]); } free(volume->dim_handles); } if (volume->dim_indices != NULL) { free(volume->dim_indices); } if (volume->create_props != NULL) { mifree_volume_props(volume->create_props); } free(volume); return (MI_NOERROR); } /** \internal */ void miinit_default_range(mitype_t mitype, double *valid_max, double *valid_min) { switch (mitype) { case MI_TYPE_BYTE: *valid_min = CHAR_MIN; *valid_max = CHAR_MAX; break; case MI_TYPE_SHORT: *valid_min = SHRT_MIN; *valid_max = SHRT_MAX; break; case MI_TYPE_INT: *valid_min = INT_MIN; *valid_max = INT_MAX; break; case MI_TYPE_UBYTE: *valid_min = 0; *valid_max = UCHAR_MAX; break; case MI_TYPE_USHORT: *valid_min = 0; *valid_max = USHRT_MAX; break; case MI_TYPE_UINT: *valid_min = 0; *valid_max = UINT_MAX; break; case MI_TYPE_FLOAT: *valid_min = -FLT_MAX; *valid_max = FLT_MAX; break; case MI_TYPE_DOUBLE: *valid_min = -DBL_MAX; *valid_max = DBL_MAX; break; case MI_TYPE_DCOMPLEX: *valid_min = -DBL_MAX; *valid_max = DBL_MAX; break; case MI_TYPE_FCOMPLEX: *valid_min = -FLT_MAX; *valid_max = FLT_MAX; break; default: *valid_min = 0; *valid_max = 1; MI_LOG_ERROR(MI2_MSG_BADTYPE,mitype); break; } } /** \internal */ static void miread_valid_range(mihandle_t volume, double *valid_max, double *valid_min) { int r; double range[2]; H5E_BEGIN_TRY { r = miget_attribute(volume, MI_ROOT_PATH "/image/0/image", "valid_range", MI_TYPE_DOUBLE, 2, range); } H5E_END_TRY; if (r == MI_NOERROR) { if (range[0] < range[1]) { *valid_min = range[0]; *valid_max = range[1]; } else { *valid_min = range[1]; *valid_max = range[0]; } } else { /* Didn't find the attribute, so assign default values. */ miinit_default_range(volume->volume_type, valid_max, valid_min); } } /** \internal * This function saves the current valid range set for a MINC file. */ void misave_valid_range(mihandle_t volume) { double range[2]; range[0] = volume->valid_min; range[1] = volume->valid_max; miset_attribute(volume, MI_ROOT_PATH "/image/0/image", "valid_range", MI_TYPE_DOUBLE, 2, range); } int miget_slice_dimension_count(mihandle_t volume, midimclass_t dimclass, midimattr_t attr, int *number_of_dimensions) { int number_of_volume_dimensions=-1; hid_t image_max_fspc_id; int slice_ndims; int result=-1; if( miget_volume_dimension_count(volume,dimclass,attr, &number_of_volume_dimensions) <0 ) { return -1; } if(!volume->has_slice_scaling) { *number_of_dimensions=number_of_volume_dimensions; return MI_NOERROR; } image_max_fspc_id=H5Dget_space(volume->imax_id); slice_ndims = H5Sget_simple_extent_ndims ( image_max_fspc_id ); if(slice_ndims>=0) { result=MI_NOERROR; *number_of_dimensions=number_of_volume_dimensions-slice_ndims; } H5Sclose(image_max_fspc_id); return result; } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/nifti/000077500000000000000000000000001257462267400157525ustar00rootroot00000000000000libminc-libminc-2-3-00/nifti/nifti1.h000066400000000000000000002071641257462267400173270ustar00rootroot00000000000000/** \file nifti1.h \brief Official definition of the nifti1 header. Written by Bob Cox, SSCC, NIMH. HISTORY: 29 Nov 2007 [rickr] - added DT_RGBA32 and NIFTI_TYPE_RGBA32 - added NIFTI_INTENT codes: TIME_SERIES, NODE_INDEX, RGB_VECTOR, RGBA_VECTOR, SHAPE */ #ifndef _NIFTI_HEADER_ #define _NIFTI_HEADER_ /***************************************************************************** ** This file defines the "NIFTI-1" header format. ** ** It is derived from 2 meetings at the NIH (31 Mar 2003 and ** ** 02 Sep 2003) of the Data Format Working Group (DFWG), ** ** chartered by the NIfTI (Neuroimaging Informatics Technology ** ** Initiative) at the National Institutes of Health (NIH). ** **--------------------------------------------------------------** ** Neither the National Institutes of Health (NIH), the DFWG, ** ** nor any of the members or employees of these institutions ** ** imply any warranty of usefulness of this material for any ** ** purpose, and do not assume any liability for damages, ** ** incidental or otherwise, caused by any use of this document. ** ** If these conditions are not acceptable, do not use this! ** **--------------------------------------------------------------** ** Author: Robert W Cox (NIMH, Bethesda) ** ** Advisors: John Ashburner (FIL, London), ** ** Stephen Smith (FMRIB, Oxford), ** ** Mark Jenkinson (FMRIB, Oxford) ** ******************************************************************************/ /*---------------------------------------------------------------------------*/ /* Note that the ANALYZE 7.5 file header (dbh.h) is (c) Copyright 1986-1995 Biomedical Imaging Resource Mayo Foundation Incorporation of components of dbh.h are by permission of the Mayo Foundation. Changes from the ANALYZE 7.5 file header in this file are released to the public domain, including the functional comments and any amusing asides. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*! INTRODUCTION TO NIFTI-1: ------------------------ The twin (and somewhat conflicting) goals of this modified ANALYZE 7.5 format are: (a) To add information to the header that will be useful for functional neuroimaging data analysis and display. These additions include: - More basic data types. - Two affine transformations to specify voxel coordinates. - "Intent" codes and parameters to describe the meaning of the data. - Affine scaling of the stored data values to their "true" values. - Optional storage of the header and image data in one file (.nii). (b) To maintain compatibility with non-NIFTI-aware ANALYZE 7.5 compatible software (i.e., such a program should be able to do something useful with a NIFTI-1 dataset -- at least, with one stored in a traditional .img/.hdr file pair). Most of the unused fields in the ANALYZE 7.5 header have been taken, and some of the lesser-used fields have been co-opted for other purposes. Notably, most of the data_history substructure has been co-opted for other purposes, since the ANALYZE 7.5 format describes this substructure as "not required". NIFTI-1 FLAG (MAGIC STRINGS): ---------------------------- To flag such a struct as being conformant to the NIFTI-1 spec, the last 4 bytes of the header must be either the C String "ni1" or "n+1"; in hexadecimal, the 4 bytes 6E 69 31 00 or 6E 2B 31 00 (in any future version of this format, the '1' will be upgraded to '2', etc.). Normally, such a "magic number" or flag goes at the start of the file, but trying to avoid clobbering widely-used ANALYZE 7.5 fields led to putting this marker last. However, recall that "the last shall be first" (Matthew 20:16). If a NIFTI-aware program reads a header file that is NOT marked with a NIFTI magic string, then it should treat the header as an ANALYZE 7.5 structure. NIFTI-1 FILE STORAGE: -------------------- "ni1" means that the image data is stored in the ".img" file corresponding to the header file (starting at file offset 0). "n+1" means that the image data is stored in the same file as the header information. We recommend that the combined header+data filename suffix be ".nii". When the dataset is stored in one file, the first byte of image data is stored at byte location (int)vox_offset in this combined file. The minimum allowed value of vox_offset is 352; for compatibility with some software, vox_offset should be an integral multiple of 16. GRACE UNDER FIRE: ---------------- Most NIFTI-aware programs will only be able to handle a subset of the full range of datasets possible with this format. All NIFTI-aware programs should take care to check if an input dataset conforms to the program's needs and expectations (e.g., check datatype, intent_code, etc.). If the input dataset can't be handled by the program, the program should fail gracefully (e.g., print a useful warning; not crash). SAMPLE CODES: ------------ The associated files nifti1_io.h and nifti1_io.c provide a sample implementation in C of a set of functions to read, write, and manipulate NIFTI-1 files. The file nifti1_test.c is a sample program that uses the nifti1_io.c functions. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* HEADER STRUCT DECLARATION: ------------------------- In the comments below for each field, only NIFTI-1 specific requirements or changes from the ANALYZE 7.5 format are described. For convenience, the 348 byte header is described as a single struct, rather than as the ANALYZE 7.5 group of 3 substructs. Further comments about the interpretation of various elements of this header are after the data type definition itself. Fields that are marked as ++UNUSED++ have no particular interpretation in this standard. (Also see the UNUSED FIELDS comment section, far below.) The presumption below is that the various C types have particular sizes: sizeof(int) = sizeof(float) = 4 ; sizeof(short) = 2 -----------------------------------------------------------------------------*/ /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ /*! \struct nifti_1_header \brief Data structure defining the fields in the nifti1 header. This binary header should be found at the beginning of a valid NIFTI-1 header file. */ /*************************/ /************************/ struct nifti_1_header { /* NIFTI-1 usage */ /* ANALYZE 7.5 field(s) */ /*************************/ /************************/ /*--- was header_key substruct ---*/ int sizeof_hdr; /*!< MUST be 348 */ /* int sizeof_hdr; */ char data_type[10]; /*!< ++UNUSED++ */ /* char data_type[10]; */ char db_name[18]; /*!< ++UNUSED++ */ /* char db_name[18]; */ int extents; /*!< ++UNUSED++ */ /* int extents; */ short session_error; /*!< ++UNUSED++ */ /* short session_error; */ char regular; /*!< ++UNUSED++ */ /* char regular; */ char dim_info; /*!< MRI slice ordering. */ /* char hkey_un0; */ /*--- was image_dimension substruct ---*/ short dim[8]; /*!< Data array dimensions.*/ /* short dim[8]; */ float intent_p1 ; /*!< 1st intent parameter. */ /* short unused8; */ /* short unused9; */ float intent_p2 ; /*!< 2nd intent parameter. */ /* short unused10; */ /* short unused11; */ float intent_p3 ; /*!< 3rd intent parameter. */ /* short unused12; */ /* short unused13; */ short intent_code ; /*!< NIFTI_INTENT_* code. */ /* short unused14; */ short datatype; /*!< Defines data type! */ /* short datatype; */ short bitpix; /*!< Number bits/voxel. */ /* short bitpix; */ short slice_start; /*!< First slice index. */ /* short dim_un0; */ float pixdim[8]; /*!< Grid spacings. */ /* float pixdim[8]; */ float vox_offset; /*!< Offset into .nii file */ /* float vox_offset; */ float scl_slope ; /*!< Data scaling: slope. */ /* float funused1; */ float scl_inter ; /*!< Data scaling: offset. */ /* float funused2; */ short slice_end; /*!< Last slice index. */ /* float funused3; */ char slice_code ; /*!< Slice timing order. */ char xyzt_units ; /*!< Units of pixdim[1..4] */ float cal_max; /*!< Max display intensity */ /* float cal_max; */ float cal_min; /*!< Min display intensity */ /* float cal_min; */ float slice_duration;/*!< Time for 1 slice. */ /* float compressed; */ float toffset; /*!< Time axis shift. */ /* float verified; */ int glmax; /*!< ++UNUSED++ */ /* int glmax; */ int glmin; /*!< ++UNUSED++ */ /* int glmin; */ /*--- was data_history substruct ---*/ char descrip[80]; /*!< any text you like. */ /* char descrip[80]; */ char aux_file[24]; /*!< auxiliary filename. */ /* char aux_file[24]; */ short qform_code ; /*!< NIFTI_XFORM_* code. */ /*-- all ANALYZE 7.5 ---*/ short sform_code ; /*!< NIFTI_XFORM_* code. */ /* fields below here */ /* are replaced */ float quatern_b ; /*!< Quaternion b param. */ float quatern_c ; /*!< Quaternion c param. */ float quatern_d ; /*!< Quaternion d param. */ float qoffset_x ; /*!< Quaternion x shift. */ float qoffset_y ; /*!< Quaternion y shift. */ float qoffset_z ; /*!< Quaternion z shift. */ float srow_x[4] ; /*!< 1st row affine transform. */ float srow_y[4] ; /*!< 2nd row affine transform. */ float srow_z[4] ; /*!< 3rd row affine transform. */ char intent_name[16];/*!< 'name' or meaning of data. */ char magic[4] ; /*!< MUST be "ni1\0" or "n+1\0". */ } ; /**** 348 bytes total ****/ typedef struct nifti_1_header nifti_1_header ; /*---------------------------------------------------------------------------*/ /* HEADER EXTENSIONS: ----------------- After the end of the 348 byte header (e.g., after the magic field), the next 4 bytes are a char array field named "extension". By default, all 4 bytes of this array should be set to zero. In a .nii file, these 4 bytes will always be present, since the earliest start point for the image data is byte #352. In a separate .hdr file, these bytes may or may not be present. If not present (i.e., if the length of the .hdr file is 348 bytes), then a NIfTI-1 compliant program should use the default value of extension={0,0,0,0}. The first byte (extension[0]) is the only value of this array that is specified at present. The other 3 bytes are reserved for future use. If extension[0] is nonzero, it indicates that extended header information is present in the bytes following the extension array. In a .nii file, this extended header data is before the image data (and vox_offset must be set correctly to allow for this). In a .hdr file, this extended data follows extension and proceeds (potentially) to the end of the file. The format of extended header data is weakly specified. Each extension must be an integer multiple of 16 bytes long. The first 8 bytes of each extension comprise 2 integers: int esize , ecode ; These values may need to be byte-swapped, as indicated by dim[0] for the rest of the header. * esize is the number of bytes that form the extended header data + esize must be a positive integral multiple of 16 + this length includes the 8 bytes of esize and ecode themselves * ecode is a non-negative integer that indicates the format of the extended header data that follows + different ecode values are assigned to different developer groups + at present, the "registered" values for code are = 0 = unknown private format (not recommended!) = 2 = DICOM format (i.e., attribute tags and values) = 4 = AFNI group (i.e., ASCII XML-ish elements) In the interests of interoperability (a primary rationale for NIfTI), groups developing software that uses this extension mechanism are encouraged to document and publicize the format of their extensions. To this end, the NIfTI DFWG will assign even numbered codes upon request to groups submitting at least rudimentary documentation for the format of their extension; at present, the contact is mailto:rwcox@nih.gov. The assigned codes and documentation will be posted on the NIfTI website. All odd values of ecode (and 0) will remain unassigned; at least, until the even ones are used up, when we get to 2,147,483,646. Note that the other contents of the extended header data section are totally unspecified by the NIfTI-1 standard. In particular, if binary data is stored in such a section, its byte order is not necessarily the same as that given by examining dim[0]; it is incumbent on the programs dealing with such data to determine the byte order of binary extended header data. Multiple extended header sections are allowed, each starting with an esize,ecode value pair. The first esize value, as described above, is at bytes #352-355 in the .hdr or .nii file (files start at byte #0). If this value is positive, then the second (esize2) will be found starting at byte #352+esize1 , the third (esize3) at byte #352+esize1+esize2, et cetera. Of course, in a .nii file, the value of vox_offset must be compatible with these extensions. If a malformed file indicates that an extended header data section would run past vox_offset, then the entire extended header section should be ignored. In a .hdr file, if an extended header data section would run past the end-of-file, that extended header data should also be ignored. With the above scheme, a program can successively examine the esize and ecode values, and skip over each extended header section if the program doesn't know how to interpret the data within. Of course, any program can simply ignore all extended header sections simply by jumping straight to the image data using vox_offset. -----------------------------------------------------------------------------*/ /*! \struct nifti1_extender \brief This structure represents a 4-byte string that should follow the binary nifti_1_header data in a NIFTI-1 header file. If the char values are {1,0,0,0}, the file is expected to contain extensions, values of {0,0,0,0} imply the file does not contain extensions. Other sequences of values are not currently defined. */ struct nifti1_extender { char extension[4] ; } ; typedef struct nifti1_extender nifti1_extender ; /*! \struct nifti1_extension \brief Data structure defining the fields of a header extension. */ struct nifti1_extension { int esize ; /*!< size of extension, in bytes (must be multiple of 16) */ int ecode ; /*!< extension code, one of the NIFTI_ECODE_ values */ char * edata ; /*!< raw data, with no byte swapping (length is esize-8) */ } ; typedef struct nifti1_extension nifti1_extension ; /*---------------------------------------------------------------------------*/ /* DATA DIMENSIONALITY (as in ANALYZE 7.5): --------------------------------------- dim[0] = number of dimensions; - if dim[0] is outside range 1..7, then the header information needs to be byte swapped appropriately - ANALYZE supports dim[0] up to 7, but NIFTI-1 reserves dimensions 1,2,3 for space (x,y,z), 4 for time (t), and 5,6,7 for anything else needed. dim[i] = length of dimension #i, for i=1..dim[0] (must be positive) - also see the discussion of intent_code, far below pixdim[i] = voxel width along dimension #i, i=1..dim[0] (positive) - cf. ORIENTATION section below for use of pixdim[0] - the units of pixdim can be specified with the xyzt_units field (also described far below). Number of bits per voxel value is in bitpix, which MUST correspond with the datatype field. The total number of bytes in the image data is dim[1] * ... * dim[dim[0]] * bitpix / 8 In NIFTI-1 files, dimensions 1,2,3 are for space, dimension 4 is for time, and dimension 5 is for storing multiple values at each spatiotemporal voxel. Some examples: - A typical whole-brain FMRI experiment's time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 20 pixdim[3] = 5.0 - dim[4] = 120 pixdim[4] = 2.0 - A typical T1-weighted anatomical volume: - dim[0] = 3 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - A single slice EPI time series: - dim[0] = 4 - dim[1] = 64 pixdim[1] = 3.75 xyzt_units = NIFTI_UNITS_MM - dim[2] = 64 pixdim[2] = 3.75 | NIFTI_UNITS_SEC - dim[3] = 1 pixdim[3] = 5.0 - dim[4] = 1200 pixdim[4] = 0.2 - A 3-vector stored at each point in a 3D volume: - dim[0] = 5 - dim[1] = 256 pixdim[1] = 1.0 xyzt_units = NIFTI_UNITS_MM - dim[2] = 256 pixdim[2] = 1.0 - dim[3] = 128 pixdim[3] = 1.1 - dim[4] = 1 pixdim[4] = 0.0 - dim[5] = 3 intent_code = NIFTI_INTENT_VECTOR - A single time series with a 3x3 matrix at each point: - dim[0] = 5 - dim[1] = 1 xyzt_units = NIFTI_UNITS_SEC - dim[2] = 1 - dim[3] = 1 - dim[4] = 1200 pixdim[4] = 0.2 - dim[5] = 9 intent_code = NIFTI_INTENT_GENMATRIX - intent_p1 = intent_p2 = 3.0 (indicates matrix dimensions) -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA STORAGE: ------------ If the magic field is "n+1", then the voxel data is stored in the same file as the header. In this case, the voxel data starts at offset (int)vox_offset into the header file. Thus, vox_offset=352.0 means that the data starts immediately after the NIFTI-1 header. If vox_offset is greater than 352, the NIFTI-1 format does not say much about the contents of the dataset file between the end of the header and the start of the data. FILES: ----- If the magic field is "ni1", then the voxel data is stored in the associated ".img" file, starting at offset 0 (i.e., vox_offset is not used in this case, and should be set to 0.0). When storing NIFTI-1 datasets in pairs of files, it is customary to name the files in the pattern "name.hdr" and "name.img", as in ANALYZE 7.5. When storing in a single file ("n+1"), the file name should be in the form "name.nii" (the ".nft" and ".nif" suffixes are already taken; cf. http://www.icdatamaster.com/n.html ). BYTE ORDERING: ------------- The byte order of the data arrays is presumed to be the same as the byte order of the header (which is determined by examining dim[0]). Floating point types are presumed to be stored in IEEE-754 format. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DETAILS ABOUT vox_offset: ------------------------ In a .nii file, the vox_offset field value is interpreted as the start location of the image data bytes in that file. In a .hdr/.img file pair, the vox_offset field value is the start location of the image data bytes in the .img file. * If vox_offset is less than 352 in a .nii file, it is equivalent to 352 (i.e., image data never starts before byte #352 in a .nii file). * The default value for vox_offset in a .nii file is 352. * In a .hdr file, the default value for vox_offset is 0. * vox_offset should be an integer multiple of 16; otherwise, some programs may not work properly (e.g., SPM). This is to allow memory-mapped input to be properly byte-aligned. Note that since vox_offset is an IEEE-754 32 bit float (for compatibility with the ANALYZE-7.5 format), it effectively has a 24 bit mantissa. All integers from 0 to 2^24 can be represented exactly in this format, but not all larger integers are exactly storable as IEEE-754 32 bit floats. However, unless you plan to have vox_offset be potentially larger than 16 MB, this should not be an issue. (Actually, any integral multiple of 16 up to 2^27 can be represented exactly in this format, which allows for up to 128 MB of random information before the image data. If that isn't enough, then perhaps this format isn't right for you.) In a .img file (i.e., image data stored separately from the NIfTI-1 header), data bytes between #0 and #vox_offset-1 (inclusive) are completely undefined and unregulated by the NIfTI-1 standard. One potential use of having vox_offset > 0 in the .hdr/.img file pair storage method is to make the .img file be a copy of (or link to) a pre-existing image file in some other format, such as DICOM; then vox_offset would be set to the offset of the image data in this file. (It may not be possible to follow the "multiple-of-16 rule" with an arbitrary external file; using the NIfTI-1 format in such a case may lead to a file that is incompatible with software that relies on vox_offset being a multiple of 16.) In a .nii file, data bytes between #348 and #vox_offset-1 (inclusive) may be used to store user-defined extra information; similarly, in a .hdr file, any data bytes after byte #347 are available for user-defined extra information. The (very weak) regulation of this extra header data is described elsewhere. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* DATA SCALING: ------------ If the scl_slope field is nonzero, then each voxel value in the dataset should be scaled as y = scl_slope * x + scl_inter where x = voxel value stored y = "true" voxel value Normally, we would expect this scaling to be used to store "true" floating values in a smaller integer datatype, but that is not required. That is, it is legal to use scaling even if the datatype is a float type (crazy, perhaps, but legal). - 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. The cal_min and cal_max fields (if nonzero) are used for mapping (possibly scaled) dataset values to display colors: - Minimum display intensity (black) corresponds to dataset value cal_min. - Maximum display intensity (white) corresponds to dataset value cal_max. - Dataset values below cal_min should display as black also, and values above cal_max as white. - Colors "black" and "white", of course, may refer to any scalar display scheme (e.g., a color lookup table specified via aux_file). - cal_min and cal_max only make sense when applied to scalar-valued datasets (i.e., dim[0] < 5 or dim[5] = 1). -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* TYPE OF DATA (acceptable values for datatype field): --------------------------------------------------- Values of datatype smaller than 256 are ANALYZE 7.5 compatible. Larger values are NIFTI-1 additions. These are all multiples of 256, so that no bits below position 8 are set in datatype. But there is no need to use only powers-of-2, as the original ANALYZE 7.5 datatype codes do. The additional codes are intended to include a complete list of basic scalar types, including signed and unsigned integers from 8 to 64 bits, floats from 32 to 128 bits, and complex (float pairs) from 64 to 256 bits. Note that most programs will support only a few of these datatypes! A NIFTI-1 program should fail gracefully (e.g., print a warning message) when it encounters a dataset with a type it doesn't like. -----------------------------------------------------------------------------*/ #undef DT_UNKNOWN /* defined in dirent.h on some Unix systems */ /*! \defgroup NIFTI1_DATATYPES \brief nifti1 datatype codes @{ */ /*--- the original ANALYZE 7.5 type codes ---*/ #define DT_NONE 0 #define DT_UNKNOWN 0 /* what it says, dude */ #define DT_BINARY 1 /* binary (1 bit/voxel) */ #define DT_UNSIGNED_CHAR 2 /* unsigned char (8 bits/voxel) */ #define DT_SIGNED_SHORT 4 /* signed short (16 bits/voxel) */ #define DT_SIGNED_INT 8 /* signed int (32 bits/voxel) */ #define DT_FLOAT 16 /* float (32 bits/voxel) */ #define DT_COMPLEX 32 /* complex (64 bits/voxel) */ #define DT_DOUBLE 64 /* double (64 bits/voxel) */ #define DT_RGB 128 /* RGB triple (24 bits/voxel) */ #define DT_ALL 255 /* not very useful (?) */ /*----- another set of names for the same ---*/ #define DT_UINT8 2 #define DT_INT16 4 #define DT_INT32 8 #define DT_FLOAT32 16 #define DT_COMPLEX64 32 #define DT_FLOAT64 64 #define DT_RGB24 128 /*------------------- new codes for NIFTI ---*/ #define DT_INT8 256 /* signed char (8 bits) */ #define DT_UINT16 512 /* unsigned short (16 bits) */ #define DT_UINT32 768 /* unsigned int (32 bits) */ #define DT_INT64 1024 /* long long (64 bits) */ #define DT_UINT64 1280 /* unsigned long long (64 bits) */ #define DT_FLOAT128 1536 /* long double (128 bits) */ #define DT_COMPLEX128 1792 /* double pair (128 bits) */ #define DT_COMPLEX256 2048 /* long double pair (256 bits) */ #define DT_RGBA32 2304 /* 4 byte RGBA (32 bits/voxel) */ /* @} */ /*------- aliases for all the above codes ---*/ /*! \defgroup NIFTI1_DATATYPE_ALIASES \brief aliases for the nifti1 datatype codes @{ */ /*! unsigned char. */ #define NIFTI_TYPE_UINT8 2 /*! signed short. */ #define NIFTI_TYPE_INT16 4 /*! signed int. */ #define NIFTI_TYPE_INT32 8 /*! 32 bit float. */ #define NIFTI_TYPE_FLOAT32 16 /*! 64 bit complex = 2 32 bit floats. */ #define NIFTI_TYPE_COMPLEX64 32 /*! 64 bit float = double. */ #define NIFTI_TYPE_FLOAT64 64 /*! 3 8 bit bytes. */ #define NIFTI_TYPE_RGB24 128 /*! signed char. */ #define NIFTI_TYPE_INT8 256 /*! unsigned short. */ #define NIFTI_TYPE_UINT16 512 /*! unsigned int. */ #define NIFTI_TYPE_UINT32 768 /*! signed long long. */ #define NIFTI_TYPE_INT64 1024 /*! unsigned long long. */ #define NIFTI_TYPE_UINT64 1280 /*! 128 bit float = long double. */ #define NIFTI_TYPE_FLOAT128 1536 /*! 128 bit complex = 2 64 bit floats. */ #define NIFTI_TYPE_COMPLEX128 1792 /*! 256 bit complex = 2 128 bit floats */ #define NIFTI_TYPE_COMPLEX256 2048 /*! 4 8 bit bytes. */ #define NIFTI_TYPE_RGBA32 2304 /* @} */ /*-------- sample typedefs for complicated types ---*/ #if 0 typedef struct { float r,i; } complex_float ; typedef struct { double r,i; } complex_double ; typedef struct { long double r,i; } complex_longdouble ; typedef struct { unsigned char r,g,b; } rgb_byte ; #endif /*---------------------------------------------------------------------------*/ /* INTERPRETATION OF VOXEL DATA: ---------------------------- The intent_code field can be used to indicate that the voxel data has some particular meaning. In particular, a large number of codes is given to indicate that the the voxel data should be interpreted as being drawn from a given probability distribution. VECTOR-VALUED DATASETS: ---------------------- The 5th dimension of the dataset, if present (i.e., dim[0]=5 and dim[5] > 1), contains multiple values (e.g., a vector) to be stored at each spatiotemporal location. For example, the header values - dim[0] = 5 - dim[1] = 64 - dim[2] = 64 - dim[3] = 20 - dim[4] = 1 (indicates no time axis) - dim[5] = 3 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_VECTOR mean that this dataset should be interpreted as a 3D volume (64x64x20), with a 3-vector of floats defined at each point in the 3D grid. A program reading a dataset with a 5th dimension may want to reformat the image data to store each voxels' set of values together in a struct or array. This programming detail, however, is beyond the scope of the NIFTI-1 file specification! Uses of dimensions 6 and 7 are also not specified here. STATISTICAL PARAMETRIC DATASETS (i.e., SPMs): -------------------------------------------- Values of intent_code from NIFTI_FIRST_STATCODE to NIFTI_LAST_STATCODE (inclusive) indicate that the numbers in the dataset should be interpreted as being drawn from a given distribution. Most such distributions have auxiliary parameters (e.g., NIFTI_INTENT_TTEST has 1 DOF parameter). If the dataset DOES NOT have a 5th dimension, then the auxiliary parameters are the same for each voxel, and are given in header fields intent_p1, intent_p2, and intent_p3. If the dataset DOES have a 5th dimension, then the auxiliary parameters are different for each voxel. For example, the header values - dim[0] = 5 - dim[1] = 128 - dim[2] = 128 - dim[3] = 1 (indicates a single slice) - dim[4] = 1 (indicates no time axis) - dim[5] = 2 - datatype = DT_FLOAT - intent_code = NIFTI_INTENT_TTEST mean that this is a 2D dataset (128x128) of t-statistics, with the t-statistic being in the first "plane" of data and the degrees-of-freedom parameter being in the second "plane" of data. If the dataset 5th dimension is used to store the voxel-wise statistical parameters, then dim[5] must be 1 plus the number of parameters required by that distribution (e.g., intent_code=NIFTI_INTENT_TTEST implies dim[5] must be 2, as in the example just above). Note: intent_code values 2..10 are compatible with AFNI 1.5x (which is why there is no code with value=1, which is obsolescent in AFNI). OTHER INTENTIONS: ---------------- The purpose of the intent_* fields is to help interpret the values stored in the dataset. Some non-statistical values for intent_code and conventions are provided for storing other complex data types. The intent_name field provides space for a 15 character (plus 0 byte) 'name' string for the type of data stored. Examples: - intent_code = NIFTI_INTENT_ESTIMATE; intent_name = "T1"; could be used to signify that the voxel values are estimates of the NMR parameter T1. - intent_code = NIFTI_INTENT_TTEST; intent_name = "House"; could be used to signify that the voxel values are t-statistics for the significance of 'activation' response to a House stimulus. - intent_code = NIFTI_INTENT_DISPVECT; intent_name = "ToMNI152"; could be used to signify that the voxel values are a displacement vector that transforms each voxel (x,y,z) location to the corresponding location in the MNI152 standard brain. - intent_code = NIFTI_INTENT_SYMMATRIX; intent_name = "DTI"; could be used to signify that the voxel values comprise a diffusion tensor image. If no data name is implied or needed, intent_name[0] should be set to 0. -----------------------------------------------------------------------------*/ /*! default: no intention is indicated in the header. */ #define NIFTI_INTENT_NONE 0 /*-------- These codes are for probability distributions ---------------*/ /* Most distributions have a number of parameters, below denoted by p1, p2, and p3, and stored in - intent_p1, intent_p2, intent_p3 if dataset doesn't have 5th dimension - image data array if dataset does have 5th dimension Functions to compute with many of the distributions below can be found in the CDF library from U Texas. Formulas for and discussions of these distributions can be found in the following books: [U] Univariate Discrete Distributions, NL Johnson, S Kotz, AW Kemp. [C1] Continuous Univariate Distributions, vol. 1, NL Johnson, S Kotz, N Balakrishnan. [C2] Continuous Univariate Distributions, vol. 2, NL Johnson, S Kotz, N Balakrishnan. */ /*----------------------------------------------------------------------*/ /*! [C2, chap 32] Correlation coefficient R (1 param): p1 = degrees of freedom R/sqrt(1-R*R) is t-distributed with p1 DOF. */ /*! \defgroup NIFTI1_INTENT_CODES \brief nifti1 intent codes, to describe intended meaning of dataset contents @{ */ #define NIFTI_INTENT_CORREL 2 /*! [C2, chap 28] Student t statistic (1 param): p1 = DOF. */ #define NIFTI_INTENT_TTEST 3 /*! [C2, chap 27] Fisher F statistic (2 params): p1 = numerator DOF, p2 = denominator DOF. */ #define NIFTI_INTENT_FTEST 4 /*! [C1, chap 13] Standard normal (0 params): Density = N(0,1). */ #define NIFTI_INTENT_ZSCORE 5 /*! [C1, chap 18] Chi-squared (1 param): p1 = DOF. Density(x) proportional to exp(-x/2) * x^(p1/2-1). */ #define NIFTI_INTENT_CHISQ 6 /*! [C2, chap 25] Beta distribution (2 params): p1=a, p2=b. Density(x) proportional to x^(a-1) * (1-x)^(b-1). */ #define NIFTI_INTENT_BETA 7 /*! [U, chap 3] Binomial distribution (2 params): p1 = number of trials, p2 = probability per trial. Prob(x) = (p1 choose x) * p2^x * (1-p2)^(p1-x), for x=0,1,...,p1. */ #define NIFTI_INTENT_BINOM 8 /*! [C1, chap 17] Gamma distribution (2 params): p1 = shape, p2 = scale. Density(x) proportional to x^(p1-1) * exp(-p2*x). */ #define NIFTI_INTENT_GAMMA 9 /*! [U, chap 4] Poisson distribution (1 param): p1 = mean. Prob(x) = exp(-p1) * p1^x / x! , for x=0,1,2,.... */ #define NIFTI_INTENT_POISSON 10 /*! [C1, chap 13] Normal distribution (2 params): p1 = mean, p2 = standard deviation. */ #define NIFTI_INTENT_NORMAL 11 /*! [C2, chap 30] Noncentral F statistic (3 params): p1 = numerator DOF, p2 = denominator DOF, p3 = numerator noncentrality parameter. */ #define NIFTI_INTENT_FTEST_NONC 12 /*! [C2, chap 29] Noncentral chi-squared statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_CHISQ_NONC 13 /*! [C2, chap 23] Logistic distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to sech^2((x-p1)/(2*p2)). */ #define NIFTI_INTENT_LOGISTIC 14 /*! [C2, chap 24] Laplace distribution (2 params): p1 = location, p2 = scale. Density(x) proportional to exp(-abs(x-p1)/p2). */ #define NIFTI_INTENT_LAPLACE 15 /*! [C2, chap 26] Uniform distribution: p1 = lower end, p2 = upper end. */ #define NIFTI_INTENT_UNIFORM 16 /*! [C2, chap 31] Noncentral t statistic (2 params): p1 = DOF, p2 = noncentrality parameter. */ #define NIFTI_INTENT_TTEST_NONC 17 /*! [C1, chap 21] Weibull distribution (3 params): p1 = location, p2 = scale, p3 = power. Density(x) proportional to ((x-p1)/p2)^(p3-1) * exp(-((x-p1)/p2)^p3) for x > p1. */ #define NIFTI_INTENT_WEIBULL 18 /*! [C1, chap 18] Chi distribution (1 param): p1 = DOF. Density(x) proportional to x^(p1-1) * exp(-x^2/2) for x > 0. p1 = 1 = 'half normal' distribution p1 = 2 = Rayleigh distribution p1 = 3 = Maxwell-Boltzmann distribution. */ #define NIFTI_INTENT_CHI 19 /*! [C1, chap 15] Inverse Gaussian (2 params): p1 = mu, p2 = lambda Density(x) proportional to exp(-p2*(x-p1)^2/(2*p1^2*x)) / x^3 for x > 0. */ #define NIFTI_INTENT_INVGAUSS 20 /*! [C2, chap 22] Extreme value type I (2 params): p1 = location, p2 = scale cdf(x) = exp(-exp(-(x-p1)/p2)). */ #define NIFTI_INTENT_EXTVAL 21 /*! Data is a 'p-value' (no params). */ #define NIFTI_INTENT_PVAL 22 /*! Data is ln(p-value) (no params). To be safe, a program should compute p = exp(-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log(p). */ #define NIFTI_INTENT_LOGPVAL 23 /*! Data is log10(p-value) (no params). To be safe, a program should compute p = pow(10.,-abs(this_value)). The nifti_stats.c library returns this_value as positive, so that this_value = -log10(p). */ #define NIFTI_INTENT_LOG10PVAL 24 /*! Smallest intent_code that indicates a statistic. */ #define NIFTI_FIRST_STATCODE 2 /*! Largest intent_code that indicates a statistic. */ #define NIFTI_LAST_STATCODE 24 /*---------- these values for intent_code aren't for statistics ----------*/ /*! To signify that the value at each voxel is an estimate of some parameter, set intent_code = NIFTI_INTENT_ESTIMATE. The name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_ESTIMATE 1001 /*! To signify that the value at each voxel is an index into some set of labels, set intent_code = NIFTI_INTENT_LABEL. The filename with the labels may stored in aux_file. */ #define NIFTI_INTENT_LABEL 1002 /*! To signify that the value at each voxel is an index into the NeuroNames labels set, set intent_code = NIFTI_INTENT_NEURONAME. */ #define NIFTI_INTENT_NEURONAME 1003 /*! To store an M x N matrix at each voxel: - dataset must have a 5th dimension (dim[0]=5 and dim[5]>1) - intent_code must be NIFTI_INTENT_GENMATRIX - dim[5] must be M*N - intent_p1 must be M (in float format) - intent_p2 must be N (ditto) - the matrix values A[i][[j] are stored in row-order: - A[0][0] A[0][1] ... A[0][N-1] - A[1][0] A[1][1] ... A[1][N-1] - etc., until - A[M-1][0] A[M-1][1] ... A[M-1][N-1] */ #define NIFTI_INTENT_GENMATRIX 1004 /*! To store an NxN symmetric matrix at each voxel: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_SYMMATRIX - dim[5] must be N*(N+1)/2 - intent_p1 must be N (in float format) - the matrix values A[i][[j] are stored in row-order: - A[0][0] - A[1][0] A[1][1] - A[2][0] A[2][1] A[2][2] - etc.: row-by-row */ #define NIFTI_INTENT_SYMMATRIX 1005 /*! To signify that the vector value at each voxel is to be taken as a displacement field or vector: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_DISPVECT - dim[5] must be the dimensionality of the displacment vector (e.g., 3 for spatial displacement, 2 for in-plane) */ #define NIFTI_INTENT_DISPVECT 1006 /* specifically for displacements */ #define NIFTI_INTENT_VECTOR 1007 /* for any other type of vector */ /*! To signify that the vector value at each voxel is really a spatial coordinate (e.g., the vertices or nodes of a surface mesh): - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_POINTSET - dim[0] = 5 - dim[1] = number of points - dim[2] = dim[3] = dim[4] = 1 - dim[5] must be the dimensionality of space (e.g., 3 => 3D space). - intent_name may describe the object these points come from (e.g., "pial", "gray/white" , "EEG", "MEG"). */ #define NIFTI_INTENT_POINTSET 1008 /*! To signify that the vector value at each voxel is really a triple of indexes (e.g., forming a triangle) from a pointset dataset: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_TRIANGLE - dim[0] = 5 - dim[1] = number of triangles - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 - datatype should be an integer type (preferably DT_INT32) - the data values are indexes (0,1,...) into a pointset dataset. */ #define NIFTI_INTENT_TRIANGLE 1009 /*! To signify that the vector value at each voxel is a quaternion: - dataset must have a 5th dimension - intent_code must be NIFTI_INTENT_QUATERNION - dim[0] = 5 - dim[5] = 4 - datatype should be a floating point type */ #define NIFTI_INTENT_QUATERNION 1010 /*! Dimensionless value - no params - although, as in _ESTIMATE the name of the parameter may be stored in intent_name. */ #define NIFTI_INTENT_DIMLESS 1011 /*---------- these values apply to GIFTI datasets ----------*/ /*! To signify that the value at each location is from a time series. */ #define NIFTI_INTENT_TIME_SERIES 2001 /*! To signify that the value at each location is a node index, from a complete surface dataset. */ #define NIFTI_INTENT_NODE_INDEX 2002 /*! To signify that the vector value at each location is an RGB triplet, of whatever type. - dataset must have a 5th dimension - dim[0] = 5 - dim[1] = number of nodes - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 3 */ #define NIFTI_INTENT_RGB_VECTOR 2003 /*! To signify that the vector value at each location is a 4 valued RGBA vector, of whatever type. - dataset must have a 5th dimension - dim[0] = 5 - dim[1] = number of nodes - dim[2] = dim[3] = dim[4] = 1 - dim[5] = 4 */ #define NIFTI_INTENT_RGBA_VECTOR 2004 /*! To signify that the value at each location is a shape value, such as the curvature. */ #define NIFTI_INTENT_SHAPE 2005 /* @} */ /*---------------------------------------------------------------------------*/ /* 3D IMAGE (VOLUME) ORIENTATION AND LOCATION IN SPACE: --------------------------------------------------- There are 3 different methods by which continuous coordinates can attached to voxels. The discussion below emphasizes 3D volumes, and the continuous coordinates are referred to as (x,y,z). The voxel index coordinates (i.e., the array indexes) are referred to as (i,j,k), with valid ranges: i = 0 .. dim[1]-1 j = 0 .. dim[2]-1 (if dim[0] >= 2) k = 0 .. dim[3]-1 (if dim[0] >= 3) The (x,y,z) coordinates refer to the CENTER of a voxel. In methods 2 and 3, the (x,y,z) axes refer to a subject-based coordinate system, with +x = Right +y = Anterior +z = Superior. This is a right-handed coordinate system. However, the exact direction these axes point with respect to the subject depends on qform_code (Method 2) and sform_code (Method 3). N.B.: The i index varies most rapidly, j index next, k index slowest. Thus, voxel (i,j,k) is stored starting at location (i + j*dim[1] + k*dim[1]*dim[2]) * (bitpix/8) into the dataset array. N.B.: The ANALYZE 7.5 coordinate system is +x = Left +y = Anterior +z = Superior which is a left-handed coordinate system. This backwardness is too difficult to tolerate, so this NIFTI-1 standard specifies the coordinate order which is most common in functional neuroimaging. N.B.: The 3 methods below all give the locations of the voxel centers in the (x,y,z) coordinate system. In many cases, programs will wish to display image data on some other grid. In such a case, the program will need to convert its desired (x,y,z) values into (i,j,k) values in order to extract (or interpolate) the image data. This operation would be done with the inverse transformation to those described below. N.B.: Method 2 uses a factor 'qfac' which is either -1 or 1; qfac is stored in the otherwise unused pixdim[0]. If pixdim[0]=0.0 (which should not occur), we take qfac=1. Of course, pixdim[0] is only used when reading a NIFTI-1 header, not when reading an ANALYZE 7.5 header. N.B.: The units of (x,y,z) can be specified using the xyzt_units field. METHOD 1 (the "old" way, used only when qform_code = 0): ------------------------------------------------------- The coordinate mapping from (i,j,k) to (x,y,z) is the ANALYZE 7.5 way. This is a simple scaling relationship: x = pixdim[1] * i y = pixdim[2] * j z = pixdim[3] * k No particular spatial orientation is attached to these (x,y,z) coordinates. (NIFTI-1 does not have the ANALYZE 7.5 orient field, which is not general and is often not set properly.) This method is not recommended, and is present mainly for compatibility with ANALYZE 7.5 files. METHOD 2 (used when qform_code > 0, which should be the "normal" case): --------------------------------------------------------------------- The (x,y,z) coordinates are given by the pixdim[] scales, a rotation matrix, and a shift. This method is intended to represent "scanner-anatomical" coordinates, which are often embedded in the image header (e.g., DICOM fields (0020,0032), (0020,0037), (0028,0030), and (0018,0050)), and represent the nominal orientation and location of the data. This method can also be used to represent "aligned" coordinates, which would typically result from some post-acquisition alignment of the volume to a standard orientation (e.g., the same subject on another day, or a rigid rotation to true anatomical orientation from the tilted position of the subject in the scanner). The formula for (x,y,z) in terms of header parameters and (i,j,k) is: [ x ] [ R11 R12 R13 ] [ pixdim[1] * i ] [ qoffset_x ] [ y ] = [ R21 R22 R23 ] [ pixdim[2] * j ] + [ qoffset_y ] [ z ] [ R31 R32 R33 ] [ qfac * pixdim[3] * k ] [ qoffset_z ] The qoffset_* shifts are in the NIFTI-1 header. Note that the center of the (i,j,k)=(0,0,0) voxel (first value in the dataset array) is just (x,y,z)=(qoffset_x,qoffset_y,qoffset_z). The rotation matrix R is calculated from the quatern_* parameters. This calculation is described below. The scaling factor qfac is either 1 or -1. The rotation matrix R defined by the quaternion parameters is "proper" (has determinant 1). This may not fit the needs of the data; for example, if the image grid is i increases from Left-to-Right j increases from Anterior-to-Posterior k increases from Inferior-to-Superior Then (i,j,k) is a left-handed triple. In this example, if qfac=1, the R matrix would have to be [ 1 0 0 ] [ 0 -1 0 ] which is "improper" (determinant = -1). [ 0 0 1 ] If we set qfac=-1, then the R matrix would be [ 1 0 0 ] [ 0 -1 0 ] which is proper. [ 0 0 -1 ] This R matrix is represented by quaternion [a,b,c,d] = [0,1,0,0] (which encodes a 180 degree rotation about the x-axis). METHOD 3 (used when sform_code > 0): ----------------------------------- The (x,y,z) coordinates are given by a general affine transformation of the (i,j,k) indexes: x = srow_x[0] * i + srow_x[1] * j + srow_x[2] * k + srow_x[3] y = srow_y[0] * i + srow_y[1] * j + srow_y[2] * k + srow_y[3] z = srow_z[0] * i + srow_z[1] * j + srow_z[2] * k + srow_z[3] The srow_* vectors are in the NIFTI_1 header. Note that no use is made of pixdim[] in this method. WHY 3 METHODS? -------------- Method 1 is provided only for backwards compatibility. The intention is that Method 2 (qform_code > 0) represents the nominal voxel locations as reported by the scanner, or as rotated to some fiducial orientation and location. Method 3, if present (sform_code > 0), is to be used to give the location of the voxels in some standard space. The sform_code indicates which standard space is present. Both methods 2 and 3 can be present, and be useful in different contexts (method 2 for displaying the data on its original grid; method 3 for displaying it on a standard grid). In this scheme, a dataset would originally be set up so that the Method 2 coordinates represent what the scanner reported. Later, a registration to some standard space can be computed and inserted in the header. Image display software can use either transform, depending on its purposes and needs. In Method 2, the origin of coordinates would generally be whatever the scanner origin is; for example, in MRI, (0,0,0) is the center of the gradient coil. In Method 3, the origin of coordinates would depend on the value of sform_code; for example, for the Talairach coordinate system, (0,0,0) corresponds to the Anterior Commissure. QUATERNION REPRESENTATION OF ROTATION MATRIX (METHOD 2) ------------------------------------------------------- The orientation of the (x,y,z) axes relative to the (i,j,k) axes in 3D space is specified using a unit quaternion [a,b,c,d], where a*a+b*b+c*c+d*d=1. The (b,c,d) values are all that is needed, since we require that a = sqrt(1.0-(b*b+c*c+d*d)) be nonnegative. The (b,c,d) values are stored in the (quatern_b,quatern_c,quatern_d) fields. The quaternion representation is chosen for its compactness in representing rotations. The (proper) 3x3 rotation matrix that corresponds to [a,b,c,d] is [ a*a+b*b-c*c-d*d 2*b*c-2*a*d 2*b*d+2*a*c ] R = [ 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 ] [ R11 R12 R13 ] = [ R21 R22 R23 ] [ R31 R32 R33 ] If (p,q,r) is a unit 3-vector, then rotation of angle h about that direction is represented by the quaternion [a,b,c,d] = [cos(h/2), p*sin(h/2), q*sin(h/2), r*sin(h/2)]. Requiring a >= 0 is equivalent to requiring -Pi <= h <= Pi. (Note that [-a,-b,-c,-d] represents the same rotation as [a,b,c,d]; there are 2 quaternions that can be used to represent a given rotation matrix R.) To rotate a 3-vector (x,y,z) using quaternions, we compute the quaternion product [0,x',y',z'] = [a,b,c,d] * [0,x,y,z] * [a,-b,-c,-d] which is equivalent to the matrix-vector multiply [ x' ] [ x ] [ y' ] = R [ y ] (equivalence depends on a*a+b*b+c*c+d*d=1) [ z' ] [ z ] Multiplication of 2 quaternions is defined by the following: [a,b,c,d] = a*1 + b*I + c*J + d*K where I*I = J*J = K*K = -1 (I,J,K are square roots of -1) I*J = K J*K = I K*I = J J*I = -K K*J = -I I*K = -J (not commutative!) For example [a,b,0,0] * [0,0,0,1] = [0,0,-b,a] since this expands to (a+b*I)*(K) = (a*K+b*I*K) = (a*K-b*J). The above formula shows how to go from quaternion (b,c,d) to rotation matrix and direction cosines. Conversely, given R, we can compute the fields for the NIFTI-1 header by a = 0.5 * sqrt(1+R11+R22+R33) (not stored) b = 0.25 * (R32-R23) / a => quatern_b c = 0.25 * (R13-R31) / a => quatern_c d = 0.25 * (R21-R12) / a => quatern_d If a=0 (a 180 degree rotation), alternative formulas are needed. See the nifti1_io.c function mat44_to_quatern() for an implementation of the various cases in converting R to [a,b,c,d]. Note that R-transpose (= R-inverse) would lead to the quaternion [a,-b,-c,-d]. The choice to specify the qoffset_x (etc.) values in the final coordinate system is partly to make it easy to convert DICOM images to this format. The DICOM attribute "Image Position (Patient)" (0020,0032) stores the (Xd,Yd,Zd) coordinates of the center of the first voxel. Here, (Xd,Yd,Zd) refer to DICOM coordinates, and Xd=-x, Yd=-y, Zd=z, where (x,y,z) refers to the NIFTI coordinate system discussed above. (i.e., DICOM +Xd is Left, +Yd is Posterior, +Zd is Superior, whereas +x is Right, +y is Anterior , +z is Superior. ) Thus, if the (0020,0032) DICOM attribute is extracted into (px,py,pz), then qoffset_x = -px qoffset_y = -py qoffset_z = pz is a reasonable setting when qform_code=NIFTI_XFORM_SCANNER_ANAT. That is, DICOM's coordinate system is 180 degrees rotated about the z-axis from the neuroscience/NIFTI coordinate system. To transform between DICOM and NIFTI, you just have to negate the x- and y-coordinates. The DICOM attribute (0020,0037) "Image Orientation (Patient)" gives the orientation of the x- and y-axes of the image data in terms of 2 3-vectors. The first vector is a unit vector along the x-axis, and the second is along the y-axis. If the (0020,0037) attribute is extracted into the value (xa,xb,xc,ya,yb,yc), then the first two columns of the R matrix would be [ -xa -ya ] [ -xb -yb ] [ xc yc ] The negations are because DICOM's x- and y-axes are reversed relative to NIFTI's. The third column of the R matrix gives the direction of displacement (relative to the subject) along the slice-wise direction. This orientation is not encoded in the DICOM standard in a simple way; DICOM is mostly concerned with 2D images. The third column of R will be either the cross-product of the first 2 columns or its negative. It is possible to infer the sign of the 3rd column by examining the coordinates in DICOM attribute (0020,0032) "Image Position (Patient)" for successive slices. However, this method occasionally fails for reasons that I (RW Cox) do not understand. -----------------------------------------------------------------------------*/ /* [qs]form_code value: */ /* x,y,z coordinate system refers to: */ /*-----------------------*/ /*---------------------------------------*/ /*! \defgroup NIFTI1_XFORM_CODES \brief nifti1 xform codes to describe the "standard" coordinate system @{ */ /*! Arbitrary coordinates (Method 1). */ #define NIFTI_XFORM_UNKNOWN 0 /*! Scanner-based anatomical coordinates */ #define NIFTI_XFORM_SCANNER_ANAT 1 /*! Coordinates aligned to another file's, or to anatomical "truth". */ #define NIFTI_XFORM_ALIGNED_ANAT 2 /*! Coordinates aligned to Talairach- Tournoux Atlas; (0,0,0)=AC, etc. */ #define NIFTI_XFORM_TALAIRACH 3 /*! MNI 152 normalized coordinates. */ #define NIFTI_XFORM_MNI_152 4 /* @} */ /*---------------------------------------------------------------------------*/ /* UNITS OF SPATIAL AND TEMPORAL DIMENSIONS: ---------------------------------------- The codes below can be used in xyzt_units to indicate the units of pixdim. As noted earlier, dimensions 1,2,3 are for x,y,z; dimension 4 is for time (t). - If dim[4]=1 or dim[0] < 4, there is no time axis. - A single time series (no space) would be specified with - dim[0] = 4 (for scalar data) or dim[0] = 5 (for vector data) - dim[1] = dim[2] = dim[3] = 1 - dim[4] = number of time points - pixdim[4] = time step - xyzt_units indicates units of pixdim[4] - dim[5] = number of values stored at each time point Bits 0..2 of xyzt_units specify the units of pixdim[1..3] (e.g., spatial units are values 1..7). Bits 3..5 of xyzt_units specify the units of pixdim[4] (e.g., temporal units are multiples of 8). This compression of 2 distinct concepts into 1 byte is due to the limited space available in the 348 byte ANALYZE 7.5 header. The macros XYZT_TO_SPACE and XYZT_TO_TIME can be used to mask off the undesired bits from the xyzt_units fields, leaving "pure" space and time codes. Inversely, the macro SPACE_TIME_TO_XYZT can be used to assemble a space code (0,1,2,...,7) with a time code (0,8,16,32,...,56) into the combined value for xyzt_units. Note that codes are provided to indicate the "time" axis units are actually frequency in Hertz (_HZ), in part-per-million (_PPM) or in radians-per-second (_RADS). The toffset field can be used to indicate a nonzero start point for the time axis. That is, time point #m is at t=toffset+m*pixdim[4] for m=0..dim[4]-1. -----------------------------------------------------------------------------*/ /*! \defgroup NIFTI1_UNITS \brief nifti1 units codes to describe the unit of measurement for each dimension of the dataset @{ */ /*! NIFTI code for unspecified units. */ #define NIFTI_UNITS_UNKNOWN 0 /** Space codes are multiples of 1. **/ /*! NIFTI code for meters. */ #define NIFTI_UNITS_METER 1 /*! NIFTI code for millimeters. */ #define NIFTI_UNITS_MM 2 /*! NIFTI code for micrometers. */ #define NIFTI_UNITS_MICRON 3 /** Time codes are multiples of 8. **/ /*! NIFTI code for seconds. */ #define NIFTI_UNITS_SEC 8 /*! NIFTI code for milliseconds. */ #define NIFTI_UNITS_MSEC 16 /*! NIFTI code for microseconds. */ #define NIFTI_UNITS_USEC 24 /*** These units are for spectral data: ***/ /*! NIFTI code for Hertz. */ #define NIFTI_UNITS_HZ 32 /*! NIFTI code for ppm. */ #define NIFTI_UNITS_PPM 40 /*! NIFTI code for radians per second. */ #define NIFTI_UNITS_RADS 48 /* @} */ #undef XYZT_TO_SPACE #undef XYZT_TO_TIME #define XYZT_TO_SPACE(xyzt) ( (xyzt) & 0x07 ) #define XYZT_TO_TIME(xyzt) ( (xyzt) & 0x38 ) #undef SPACE_TIME_TO_XYZT #define SPACE_TIME_TO_XYZT(ss,tt) ( (((char)(ss)) & 0x07) \ | (((char)(tt)) & 0x38) ) /*---------------------------------------------------------------------------*/ /* MRI-SPECIFIC SPATIAL AND TEMPORAL INFORMATION: --------------------------------------------- A few fields are provided to store some extra information that is sometimes important when storing the image data from an FMRI time series experiment. (After processing such data into statistical images, these fields are not likely to be useful.) { freq_dim } = These fields encode which spatial dimension (1,2, or 3) { phase_dim } = corresponds to which acquisition dimension for MRI data. { slice_dim } = Examples: Rectangular scan multi-slice EPI: freq_dim = 1 phase_dim = 2 slice_dim = 3 (or some permutation) Spiral scan multi-slice EPI: freq_dim = phase_dim = 0 slice_dim = 3 since the concepts of frequency- and phase-encoding directions don't apply to spiral scan slice_duration = If this is positive, AND if slice_dim is nonzero, indicates the amount of time used to acquire 1 slice. slice_duration*dim[slice_dim] can be less than pixdim[4] with a clustered acquisition method, for example. slice_code = If this is nonzero, AND if slice_dim is nonzero, AND if slice_duration is positive, indicates the timing pattern of the slice acquisition. The following codes are defined: NIFTI_SLICE_SEQ_INC == sequential increasing NIFTI_SLICE_SEQ_DEC == sequential decreasing NIFTI_SLICE_ALT_INC == alternating increasing NIFTI_SLICE_ALT_DEC == alternating decreasing NIFTI_SLICE_ALT_INC2 == alternating increasing #2 NIFTI_SLICE_ALT_DEC2 == alternating decreasing #2 { slice_start } = Indicates the start and end of the slice acquisition { slice_end } = pattern, when slice_code is nonzero. These values are present to allow for the possible addition of "padded" slices at either end of the volume, which don't fit into the slice timing pattern. If there are no padding slices, then slice_start=0 and slice_end=dim[slice_dim]-1 are the correct values. For these values to be meaningful, slice_start must be non-negative and slice_end must be greater than slice_start. Otherwise, they should be ignored. The following table indicates the slice timing pattern, relative to time=0 for the first slice acquired, for some sample cases. Here, dim[slice_dim]=7 (there are 7 slices, labeled 0..6), slice_duration=0.1, and slice_start=1, slice_end=5 (1 padded slice on each end). slice index SEQ_INC SEQ_DEC ALT_INC ALT_DEC ALT_INC2 ALT_DEC2 6 : n/a n/a n/a n/a n/a n/a n/a = not applicable 5 : 0.4 0.0 0.2 0.0 0.4 0.2 (slice time offset 4 : 0.3 0.1 0.4 0.3 0.1 0.0 doesn't apply to 3 : 0.2 0.2 0.1 0.1 0.3 0.3 slices outside 2 : 0.1 0.3 0.3 0.4 0.0 0.1 the range 1 : 0.0 0.4 0.0 0.2 0.2 0.4 slice_start .. 0 : n/a n/a n/a n/a n/a n/a slice_end) The SEQ slice_codes are sequential ordering (uncommon but not unknown), either increasing in slice number or decreasing (INC or DEC), as illustrated above. The ALT slice codes are alternating ordering. The 'standard' way for these to operate (without the '2' on the end) is for the slice timing to start at the edge of the slice_start .. slice_end group (at slice_start for INC and at slice_end for DEC). For the 'ALT_*2' slice_codes, the slice timing instead starts at the first slice in from the edge (at slice_start+1 for INC2 and at slice_end-1 for DEC2). This latter acquisition scheme is found on some Siemens scanners. The fields freq_dim, phase_dim, slice_dim are all squished into the single byte field dim_info (2 bits each, since the values for each field are limited to the range 0..3). This unpleasantness is due to lack of space in the 348 byte allowance. The macros DIM_INFO_TO_FREQ_DIM, DIM_INFO_TO_PHASE_DIM, and DIM_INFO_TO_SLICE_DIM can be used to extract these values from the dim_info byte. The macro FPS_INTO_DIM_INFO can be used to put these 3 values into the dim_info byte. -----------------------------------------------------------------------------*/ #undef DIM_INFO_TO_FREQ_DIM #undef DIM_INFO_TO_PHASE_DIM #undef DIM_INFO_TO_SLICE_DIM #define DIM_INFO_TO_FREQ_DIM(di) ( ((di) ) & 0x03 ) #define DIM_INFO_TO_PHASE_DIM(di) ( ((di) >> 2) & 0x03 ) #define DIM_INFO_TO_SLICE_DIM(di) ( ((di) >> 4) & 0x03 ) #undef FPS_INTO_DIM_INFO #define FPS_INTO_DIM_INFO(fd,pd,sd) ( ( ( ((char)(fd)) & 0x03) ) | \ ( ( ((char)(pd)) & 0x03) << 2 ) | \ ( ( ((char)(sd)) & 0x03) << 4 ) ) /*! \defgroup NIFTI1_SLICE_ORDER \brief nifti1 slice order codes, describing the acquisition order of the slices @{ */ #define NIFTI_SLICE_UNKNOWN 0 #define NIFTI_SLICE_SEQ_INC 1 #define NIFTI_SLICE_SEQ_DEC 2 #define NIFTI_SLICE_ALT_INC 3 #define NIFTI_SLICE_ALT_DEC 4 #define NIFTI_SLICE_ALT_INC2 5 /* 05 May 2005: RWCox */ #define NIFTI_SLICE_ALT_DEC2 6 /* 05 May 2005: RWCox */ /* @} */ /*---------------------------------------------------------------------------*/ /* UNUSED FIELDS: ------------- Some of the ANALYZE 7.5 fields marked as ++UNUSED++ may need to be set to particular values for compatibility with other programs. The issue of interoperability of ANALYZE 7.5 files is a murky one -- not all programs require exactly the same set of fields. (Unobscuring this murkiness is a principal motivation behind NIFTI-1.) Some of the fields that may need to be set for other (non-NIFTI aware) software to be happy are: extents dbh.h says this should be 16384 regular dbh.h says this should be the character 'r' glmin, } dbh.h says these values should be the min and max voxel glmax } values for the entire dataset It is best to initialize ALL fields in the NIFTI-1 header to 0 (e.g., with calloc()), then fill in what is needed. -----------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* MISCELLANEOUS C MACROS -----------------------------------------------------------------------------*/ /*.................*/ /*! Given a nifti_1_header struct, check if it has a good magic number. Returns NIFTI version number (1..9) if magic is good, 0 if it is not. */ #define NIFTI_VERSION(h) \ ( ( (h).magic[0]=='n' && (h).magic[3]=='\0' && \ ( (h).magic[1]=='i' || (h).magic[1]=='+' ) && \ ( (h).magic[2]>='1' && (h).magic[2]<='9' ) ) \ ? (h).magic[2]-'0' : 0 ) /*.................*/ /*! Check if a nifti_1_header struct says if the data is stored in the same file or in a separate file. Returns 1 if the data is in the same file as the header, 0 if it is not. */ #define NIFTI_ONEFILE(h) ( (h).magic[1] == '+' ) /*.................*/ /*! Check if a nifti_1_header struct needs to be byte swapped. Returns 1 if it needs to be swapped, 0 if it does not. */ #define NIFTI_NEEDS_SWAP(h) ( (h).dim[0] < 0 || (h).dim[0] > 7 ) /*.................*/ /*! Check if a nifti_1_header struct contains a 5th (vector) dimension. Returns size of 5th dimension if > 1, returns 0 otherwise. */ #define NIFTI_5TH_DIM(h) ( ((h).dim[0]>4 && (h).dim[5]>1) ? (h).dim[5] : 0 ) /*****************************************************************************/ /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_HEADER_ */ libminc-libminc-2-3-00/nifti/nifti1_io.c000066400000000000000000010227421257462267400200070ustar00rootroot00000000000000#define _NIFTI1_IO_C_ #include "nifti1_io.h" /* typedefs, prototypes, macros, etc. */ /*****===================================================================*****/ /***** Sample functions to deal with NIFTI-1 and ANALYZE files *****/ /*****...................................................................*****/ /***** This code is released to the public domain. *****/ /*****...................................................................*****/ /***** Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH *****/ /***** Date: August 2003 *****/ /*****...................................................................*****/ /***** Neither the National Institutes of Health (NIH), nor any of its *****/ /***** employees imply any warranty of usefulness of this software for *****/ /***** any purpose, and do not assume any liability for damages, *****/ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ #define MIN_EXT_LEN 4 /* All legal extensions have 4 characters */ #define MAX_EXT_LEN (MIN_EXT_LEN + 3) /* MIN + ".gz" */ #define NUM_EXT 4 /* 4 different possible extensions */ /** \file nifti1_io.c \brief main collection of nifti1 i/o routines - written by Bob Cox, SSCC NIMH - revised by Mark Jenkinson, FMRIB - revised by Rick Reynolds, SSCC, NIMH - revised by Kate Fissell, University of Pittsburgh The library history can be viewed via "nifti_tool -nifti_hist".
The library version can be viewed via "nifti_tool -nifti_ver". */ /*! global history and version strings, for printing */ static char * gni_history[] = { "----------------------------------------------------------------------\n" "history (of nifti library changes):\n" "\n", "0.0 August, 2003 [rwcox]\n" " (Robert W Cox of the National Institutes of Health, SSCC/DIRP/NIMH)\n" " - initial version\n" "\n", "0.1 July/August, 2004 [Mark Jenkinson]\n" " (FMRIB Centre, University of Oxford, UK)\n" " - Mainly adding low-level IO and changing things to allow gzipped\n" " files to be read and written\n" " - Full backwards compatability should have been maintained\n" "\n", "0.2 16 Nov 2004 [rickr]\n" " (Rick Reynolds of the National Institutes of Health, SSCC/DIRP/NIMH)\n" " - included Mark's changes in the AFNI distribution (including znzlib/)\n" " (HAVE_ZLIB is commented out for the standard distribution)\n" " - modified nifti_validfilename() and nifti_makebasename()\n" " - added nifti_find_file_extension()\n" "\n", "0.3 3 Dec 2004 [rickr]\n" " - note: header extensions are not yet checked for\n" " - added formatted history as global string, for printing\n" " - added nifti_disp_lib_hist(), to display the nifti library history\n" " - added nifti_disp_lib_version(), to display the nifti library history\n", " - re-wrote nifti_findhdrname()\n" " o used nifti_find_file_extension()\n" " o changed order of file tests (default is .nii, depends on input)\n" " o free hdrname on failure\n" " - made similar changes to nifti_findimgname()\n" " - check for NULL return from nifti_findhdrname() calls\n", " - removed most of ERREX() macros\n" " - modified nifti_image_read()\n" " o added debug info and error checking (on gni_debug > 0, only)\n" " o fail if workingname is NULL\n" " o check for failure to open header file\n" " o free workingname on failure\n" " o check for failure of nifti_image_load()\n" " o check for failure of nifti_convert_nhdr2nim()\n", " - changed nifti_image_load() to int, and check nifti_read_buffer return\n" " - changed nifti_read_buffer() to fail on short read, and to count float\n" " fixes (to print on debug)\n" " - changed nifti_image_infodump to print to stderr\n" " - updated function header comments, or moved comments above header\n" " - removed const keyword\n" " - added LNI_FERR() macro for error reporting on input files\n" "\n", "0.4 10 Dec 2004 [rickr] - added header extensions\n" " - in nifti1_io.h:\n" " o added num_ext and ext_list to the definition of nifti_image\n" " o made many functions static (more to follow)\n" " o added LNI_MAX_NIA_EXT_LEN, for max nifti_type 3 extension length\n", " - added __DATE__ to version output in nifti_disp_lib_version()\n" " - added nifti_disp_matrix_orient() to print orientation information\n" " - added '.nia' as a valid file extension in nifti_find_file_extension()\n" " - added much more debug output\n" " - in nifti_image_read(), in the case of an ASCII header, check for\n" " extensions after the end of the header\n", " - added nifti_read_extensions() function\n" " - added nifti_read_next_extension() function\n" " - added nifti_add_exten_to_list() function\n" " - added nifti_check_extension() function\n" " - added nifti_write_extensions() function\n" " - added nifti_extension_size() function\n" " - in nifti_set_iname_offest():\n" " o adjust offset by the extension size and the extender size\n", " o fixed the 'ceiling modulo 16' computation\n" " - in nifti_image_write_hdr_img2(): \n" " o added extension writing\n" " o check for NULL return from nifti_findimgname()\n" " - include number of extensions in nifti_image_to_ascii() output\n" " - in nifti_image_from_ascii():\n" " o return bytes_read as a parameter, computed from the final spos\n" " o extract num_ext from ASCII header\n" "\n", "0.5 14 Dec 2004 [rickr] - added sub-brick reading functions\n" " - added nifti_brick_list type to nifti1_io.h, along with new prototypes\n" " - added main nifti_image_read_bricks() function, with description\n" " - added nifti_image_load_bricks() - library function (requires nim)\n" " - added valid_nifti_brick_list() - library function\n" " - added free_NBL() - library function\n", " - added update_nifti_image_for_brick_list() for dimension update\n" " - added nifti_load_NBL_bricks(), nifti_alloc_NBL_mem(),\n" " nifti_copynsort() and force_positive() (static functions)\n" " - in nifti_image_read(), check for failed load only if read_data is set\n" " - broke most of nifti_image_load() into nifti_image_load_prep()\n" "\n", "0.6 15 Dec 2004 [rickr] - added sub-brick writing functionality\n" " - in nifti1_io.h, removed znzlib directory from include - all nifti\n" " library files are now under the nifti directory\n" " - nifti_read_extensions(): print no offset warning for nifti_type 3\n" " - nifti_write_all_data():\n" " o pass nifti_brick_list * NBL, for optional writing\n" " o if NBL, write each sub-brick, sequentially\n", " - nifti_set_iname_offset(): case 1 must have sizeof() cast to int\n" " - pass NBL to nifti_image_write_hdr_img2(), and allow NBL or data\n" " - added nifti_image_write_bricks() wrapper for ...write_hdr_img2()\n" " - included compression abilities\n" "\n", "0.7 16 Dec 2004 [rickr] - minor changes to extension reading\n" "\n", "0.8 21 Dec 2004 [rickr] - restrict extension reading, and minor changes\n" " - in nifti_image_read(), compute bytes for extensions (see remaining)\n" " - in nifti_read_extensions(), pass 'remain' as space for extensions,\n" " pass it to nifti_read_next_ext(), and update for each one read \n" " - in nifti_check_extension(), require (size <= remain)\n", " - in update_nifti_image_brick_list(), update nvox\n" " - in nifti_image_load_bricks(), make explicit check for nbricks <= 0\n" " - in int_force_positive(), check for (!list)\n" " - in swap_nifti_header(), swap sizeof_hdr, and reorder to struct order\n" " - change get_filesize functions to signed ( < 0 is no file or error )\n", " - in nifti_validfilename(), lose redundant (len < 0) check\n" " - make print_hex_vals() static\n" " - in disp_nifti_1_header, restrict string field widths\n" "\n", "0.9 23 Dec 2004 [rickr] - minor changes\n" " - broke ASCII header reading out of nifti_image_read(), into new\n" " functions has_ascii_header() and read_ascii_image()\n", " - check image_read failure and znzseek failure\n" " - altered some debug output\n" " - nifti_write_all_data() now returns an int\n" "\n", "0.10 29 Dec 2004 [rickr]\n" " - renamed nifti_valid_extension() to nifti_check_extension()\n" " - added functions nifti_makehdrname() and nifti_makeimgname()\n" " - added function valid_nifti_extensions()\n" " - in nifti_write_extensions(), check for validity before writing\n", " - rewrote nifti_image_write_hdr_img2():\n" " o set write_data and leave_open flags from write_opts\n" " o add debug print statements\n" " o use nifti_write_ascii_image() for the ascii case\n" " o rewrote the logic of all cases to be easier to follow\n", " - broke out code as nifti_write_ascii_image() function\n" " - added debug to top-level write functions, and free the znzFile\n" " - removed unused internal function nifti_image_open()\n" "\n", "0.11 30 Dec 2004 [rickr] - small mods\n" " - moved static function prototypes from header to C file\n" " - free extensions in nifti_image_free()\n" "\n", "1.0 07 Jan 2005 [rickr] - INITIAL RELEASE VERSION\n" " - added function nifti_set_filenames()\n" " - added function nifti_read_header()\n" " - added static function nhdr_looks_good()\n" " - added static function need_nhdr_swap()\n" " - exported nifti_add_exten_to_list symbol\n", " - fixed #bytes written in nifti_write_extensions()\n" " - only modify offset if it is too small (nifti_set_iname_offset)\n" " - added nifti_type 3 to nifti_makehdrname and nifti_makeimgname\n" " - added function nifti_set_filenames()\n" "\n", "1.1 07 Jan 2005 [rickr]\n" " - in nifti_read_header(), swap if needed\n" "\n", "1.2 07 Feb 2005 [kate fissell c/o rickr] \n" " - nifti1.h: added doxygen comments for main struct and #define groups\n" " - nifti1_io.h: added doxygen comments for file and nifti_image struct\n" " - nifti1_io.h: added doxygen comments for file and some functions\n" " - nifti1_io.c: changed nifti_copy_nim_info to use memcpy\n" "\n", "1.3 09 Feb 2005 [rickr]\n" " - nifti1.h: added doxygen comments for extension structs\n" " - nifti1_io.h: put most #defines in #ifdef _NIFTI1_IO_C_ block\n" " - added a doxygen-style description to every exported function\n" " - added doxygen-style comments within some functions\n" " - re-exported many znzFile functions that I had made static\n" " - re-added nifti_image_open (sorry, Mark)\n" " - every exported function now has 'nifti' in the name (19 functions)\n", " - made sure every alloc() has a failure test\n" " - added nifti_copy_extensions function, for use in nifti_copy_nim_info\n" " - nifti_is_gzfile: added initial strlen test\n" " - nifti_set_filenames: added set_byte_order parameter option\n" " (it seems appropriate to set the BO when new files are associated)\n" " - disp_nifti_1_header: prints to stdout (a.o.t. stderr), with fflush\n" "\n", "1.4 23 Feb 2005 [rickr] - sourceforge merge\n" " - merged into the nifti_io CVS directory structure at sourceforge.net\n" " - merged in 4 changes by Mark, and re-added his const keywords\n" " - cast some pointers to (void *) for -pedantic compile option\n" " - added nifti_free_extensions()\n" "\n", "1.5 02 Mar 2005 [rickr] - started nifti global options\n" " - gni_debug is now g_opts.debug\n" " - added validity check parameter to nifti_read_header\n" " - need_nhdr_swap no longer does test swaps on the stack\n" "\n", "1.6 05 April 2005 [rickr] - validation and collapsed_image_read\n" " - added nifti_read_collapsed_image(), an interface for reading partial\n" " datasets, specifying a subset of array indices\n" " - for read_collapsed_image, added static functions: rci_read_data(),\n" " rci_alloc_mem(), and make_pivot_list()\n", " - added nifti_nim_is_valid() to check for consistency (more to do)\n" " - added nifti_nim_has_valid_dims() to do many dimensions tests\n" "\n", "1.7 08 April 2005 [rickr]\n" " - added nifti_update_dims_from_array() - to update dimensions\n" " - modified nifti_makehdrname() and nifti_makeimgname():\n" " if prefix has a valid extension, use it (else make one up)\n" " - added nifti_get_intlist - for making an array of ints\n" " - fixed init of NBL->bsize in nifti_alloc_NBL_mem() {thanks, Bob}\n" "\n", "1.8 14 April 2005 [rickr]\n" " - added nifti_set_type_from_names(), for nifti_set_filenames()\n" " (only updates type if number of files does not match it)\n" " - added is_valid_nifti_type(), just to be sure\n" " - updated description of nifti_read_collapsed_image() for *data change\n" " (if *data is already set, assume memory exists for results)\n" " - modified rci_alloc_mem() to allocate only if *data is NULL\n" "\n", "1.9 19 April 2005 [rickr]\n" " - added extension codes NIFTI_ECODE_COMMENT and NIFTI_ECODE_XCEDE\n" " - added nifti_type codes NIFTI_MAX_ECODE and NIFTI_MAX_FTYPE\n" " - added nifti_add_extension() {exported}\n" " - added nifti_fill_extension() as a static function\n" " - added nifti_is_valid_ecode() {exported}\n", " - nifti_type values are now NIFTI_FTYPE_* file codes\n" " - in nifti_read_extensions(), decrement 'remain' by extender size, 4\n" " - in nifti_set_iname_offset(), case 1, update if offset differs\n" " - only output '-d writing nifti file' if debug > 1\n" "\n", "1.10 10 May 2005 [rickr]\n" " - files are read using ZLIB only if they end in '.gz'\n" "\n", "1.11 12 August 2005 [kate fissell]\n" " - Kate's 0.2 release packaging, for sourceforge\n" "\n", "1.12 17 August 2005 [rickr] - comment (doxygen) updates\n" " - updated comments for most functions (2 updates from Cinly Ooi)\n" " - added nifti_type_and_names_match()\n" "\n", "1.12a 24 August 2005 [rickr] - remove all tabs from Clibs/*/*.[ch]\n", "1.12b 25 August 2005 [rickr] - changes by Hans Johnson\n", "1.13 25 August 2005 [rickr]\n", " - finished changes by Hans for Insight\n" " - added const in all appropraite parameter locations (30-40)\n" " (any pointer referencing data that will not change)\n" " - shortened all string constants below 509 character limit\n" "1.14 28 October 2005 [HJohnson]\n", " - use nifti_set_filenames() in nifti_convert_nhdr2nim()\n" "1.15 02 November 2005 [rickr]\n", " - added skip_blank_ext to nifti_global_options\n" " - added nifti_set_skip_blank_ext(), to set option\n" " - if skip_blank_ext and no extensions, do not read/write extender\n" "1.16 18 November 2005 [rickr]\n", " - removed any test or access of dim[i], i>dim[0]\n" " - do not set pixdim for collapsed dims to 1.0, leave them as they are\n" " - added magic and dim[i] tests in nifti_hdr_looks_good()\n" " - added 2 size_t casts\n" "1.17 22 November 2005 [rickr]\n", " - in hdr->nim, for i > dim[0], pass 0 or 1, else set to 1\n" "1.18 02 March 2006 [rickr]\n", " - in nifti_alloc_NBL_mem(), fixed nt=0 case from 1.17 change\n" "1.19 23 May 2006 [HJohnson,rickr]\n", " - nifti_write_ascii_image(): free(hstr)\n" " - nifti_copy_extensions(): clear num_ext and ext_list\n" "1.20 27 Jun 2006 [rickr]\n", " - nifti_findhdrname(): fixed assign of efirst to match stated logic\n" " (problem found by Atle BjĆørnerud)\n" "1.21 05 Sep 2006 [rickr] update for nifticlib-0.4 release\n", " - was reminded to actually add nifti_set_skip_blank_ext()\n" " - init g_opts.skip_blank_ext to 0\n" "1.22 01 Jun 2007 nifticlib-0.5 release\n", "1.23 05 Jun 2007 nifti_add_exten_to_list: revert on failure, free old list\n" "1.24 07 Jun 2007 nifti_copy_extensions: use esize-8 for data size\n" "1.25 12 Jun 2007 [rickr] EMPTY_IMAGE creation\n", " - added nifti_make_new_header() - to create from dims/dtype\n" " - added nifti_make_new_nim() - to create from dims/dtype/fill\n" " - added nifti_is_valid_datatype(), and more debug info\n", "1.26 27 Jul 2007 [rickr] handle single volumes > 2^31 bytes (but < 2^32)\n", "1.27 28 Jul 2007 [rickr] nim->nvox, NBL-bsize are now type size_t\n" "1.28 30 Jul 2007 [rickr] size_t updates\n", "1.29 08 Aug 2007 [rickr] for list, valid_nifti_brick_list requires 3 dims\n" "1.30 08 Nov 2007 [Yaroslav/rickr]\n" " - fix ARM struct alignment problem in byte-swapping routines\n", "1.31 29 Nov 2007 [rickr] for nifticlib-1.0.0\n" " - added nifti_datatype_to/from_string routines\n" " - added DT_RGBA32/NIFTI_TYPE_RGBA32 datatype macros (2304)\n" " - added NIFTI_ECODE_FREESURFER (14)\n", "1.32 08 Dec 2007 [rickr]\n" " - nifti_hdr_looks_good() allows ANALYZE headers (req. by V. Luccio)\n" " - added nifti_datatype_is_valid()\n", "1.33 05 Feb 2008 [hansj,rickr] - block nia.gz use\n" "1.34 13 Jun 2008 [rickr] - added nifti_compiled_with_zlib()\n" "1.35 03 Aug 2008 [rickr]\n", " - deal with swapping, so that CPU type does not affect output\n" " (motivated by C Burns)\n" " - added nifti_analyze75 structure and nifti_swap_as_analyze()\n" " - previous swap_nifti_header is saved as old_swap_nifti_header\n" " - also swap UNUSED fields in nifti_1_header struct\n", "1.36 07 Oct 2008 [rickr]\n", " - added nifti_NBL_matches_nim() check for write_bricks()\n" "1.37 10 Mar 2009 [rickr]\n", " - H Johnson cast updates (06 Feb)\n" " - added NIFTI_ECODE_PYPICKLE for PyNIfTI (06 Feb)\n" " - added NIFTI_ECODEs 18-28 for the LONI MiND group\n" "1.38 28 Apr 2009 [rickr]\n", " - uppercase extensions are now valid (requested by M. Coursolle)\n" " - nifti_set_allow_upper_fext controls this option (req by C. Ooi)\n" "1.39 23 Jun 2009 [rickr]: added 4 checks of alloc() returns\n", "1.40 16 Mar 2010 [rickr]: added NIFTI_ECODE_VOXBO for D. Kimberg\n", "1.41 28 Apr 2010 [rickr]: added NIFTI_ECODE_CARET for J. Harwell\n", "1.42 06 Jul 2010 [rickr]: trouble with large (gz) files\n", " - noted/investigated by M Hanke and Y Halchenko\n" " - fixed znzread/write, noting example by M Adler\n" " - changed nifti_swap_* routines/calls to take size_t (6)\n" "1.43 07 Jul 2010 [rickr]: fixed znzR/W to again return nmembers\n", "1.44 26 Jun 2015 [rdvincent]: Minor cleanup.\n", "----------------------------------------------------------------------\n" }; static char gni_version[] = "nifti library version 1.44 (26 June, 2015)"; /*! global nifti options structure - init with defaults */ static nifti_global_options g_opts = { 1, /* debug level */ 0, /* skip_blank_ext - skip extender if no extensions */ 1 /* allow_upper_fext - allow uppercase file extensions */ }; /*! global nifti types structure list (per type, ordered oldest to newest) */ static nifti_type_ele nifti_type_list[] = { /* type nbyper swapsize name */ { 0, 0, 0, "DT_UNKNOWN" }, { 0, 0, 0, "DT_NONE" }, { 1, 0, 0, "DT_BINARY" }, /* not usable */ { 2, 1, 0, "DT_UNSIGNED_CHAR" }, { 2, 1, 0, "DT_UINT8" }, { 2, 1, 0, "NIFTI_TYPE_UINT8" }, { 4, 2, 2, "DT_SIGNED_SHORT" }, { 4, 2, 2, "DT_INT16" }, { 4, 2, 2, "NIFTI_TYPE_INT16" }, { 8, 4, 4, "DT_SIGNED_INT" }, { 8, 4, 4, "DT_INT32" }, { 8, 4, 4, "NIFTI_TYPE_INT32" }, { 16, 4, 4, "DT_FLOAT" }, { 16, 4, 4, "DT_FLOAT32" }, { 16, 4, 4, "NIFTI_TYPE_FLOAT32" }, { 32, 8, 4, "DT_COMPLEX" }, { 32, 8, 4, "DT_COMPLEX64" }, { 32, 8, 4, "NIFTI_TYPE_COMPLEX64" }, { 64, 8, 8, "DT_DOUBLE" }, { 64, 8, 8, "DT_FLOAT64" }, { 64, 8, 8, "NIFTI_TYPE_FLOAT64" }, { 128, 3, 0, "DT_RGB" }, { 128, 3, 0, "DT_RGB24" }, { 128, 3, 0, "NIFTI_TYPE_RGB24" }, { 255, 0, 0, "DT_ALL" }, { 256, 1, 0, "DT_INT8" }, { 256, 1, 0, "NIFTI_TYPE_INT8" }, { 512, 2, 2, "DT_UINT16" }, { 512, 2, 2, "NIFTI_TYPE_UINT16" }, { 768, 4, 4, "DT_UINT32" }, { 768, 4, 4, "NIFTI_TYPE_UINT32" }, { 1024, 8, 8, "DT_INT64" }, { 1024, 8, 8, "NIFTI_TYPE_INT64" }, { 1280, 8, 8, "DT_UINT64" }, { 1280, 8, 8, "NIFTI_TYPE_UINT64" }, { 1536, 16, 16, "DT_FLOAT128" }, { 1536, 16, 16, "NIFTI_TYPE_FLOAT128" }, { 1792, 16, 8, "DT_COMPLEX128" }, { 1792, 16, 8, "NIFTI_TYPE_COMPLEX128" }, { 2048, 32, 16, "DT_COMPLEX256" }, { 2048, 32, 16, "NIFTI_TYPE_COMPLEX256" }, { 2304, 4, 0, "DT_RGBA32" }, { 2304, 4, 0, "NIFTI_TYPE_RGBA32" }, }; /*---------------------------------------------------------------------------*/ /* prototypes for internal functions - not part of exported library */ /* extension routines */ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int remain ); static int nifti_read_next_extension( nifti1_extension * nex, nifti_image *nim, int remain, znzFile fp ); static int nifti_check_extension(nifti_image *nim, int size,int code, int rem); static void update_nifti_image_for_brick_list(nifti_image * nim , int nbricks); static int nifti_add_exten_to_list(nifti1_extension * new_ext, nifti1_extension ** list, int new_length); static int nifti_fill_extension(nifti1_extension * ext, const char * data, int len, int ecode); /* NBL routines */ static int nifti_load_NBL_bricks(nifti_image * nim , int * slist, int * sindex, nifti_brick_list * NBL, znzFile fp ); static int nifti_alloc_NBL_mem( nifti_image * nim, int nbricks, nifti_brick_list * nbl); static int nifti_copynsort(int nbricks, const int *blist, int **slist, int **sindex); static int nifti_NBL_matches_nim(const nifti_image *nim, const nifti_brick_list *NBL); /* for nifti_read_collapsed_image: */ static int rci_read_data(nifti_image *nim, int *pivots, int *prods, int nprods, const int dims[], char *data, znzFile fp, size_t base_offset); static int rci_alloc_mem(void ** data, int prods[8], int nprods, int nbyper ); static int make_pivot_list(nifti_image * nim, const int dims[], int pivots[], int prods[], int * nprods ); /* misc */ static int compare_strlist (const char * str, char ** strlist, int len); static int fileext_compare (const char * test_ext, const char * known_ext); static int fileext_n_compare (const char * test_ext, const char * known_ext, int maxlen); static int is_mixedcase (const char * str); static int is_uppercase (const char * str); static int make_lowercase (char * str); static int make_uppercase (char * str); static int need_nhdr_swap (short dim0, int hdrsize); static int print_hex_vals (const char * data, int nbytes, FILE * fp); static int unescape_string (char *str); /* string utility functions */ static char *escapize_string (const char *str); /* internal I/O routines */ static znzFile nifti_image_load_prep( nifti_image *nim ); static int has_ascii_header(znzFile fp); /*---------------------------------------------------------------------------*/ /* for calling from some main program */ /*----------------------------------------------------------------------*/ /*! display the nifti library module history (via stdout) *//*--------------------------------------------------------------------*/ void nifti_disp_lib_hist( void ) { int c, len = sizeof(gni_history)/sizeof(char *); for( c = 0; c < len; c++ ) fputs(gni_history[c], stdout); } /*----------------------------------------------------------------------*/ /*! display the nifti library version (via stdout) *//*--------------------------------------------------------------------*/ void nifti_disp_lib_version( void ) { printf("%s, compiled %s\n", gni_version, __DATE__); } /*----------------------------------------------------------------------*/ /*! nifti_image_read_bricks - read nifti data as array of bricks * * 13 Dec 2004 [rickr] * * \param hname - filename of dataset to read (must be valid) * \param nbricks - number of sub-bricks to read * (if blist is valid, nbricks must be > 0) * \param blist - list of sub-bricks to read * (can be NULL; if NULL, read complete dataset) * \param NBL - pointer to empty nifti_brick_list struct * (must be a valid pointer) * * \return *
nim - same as nifti_image_read, but * nim->nt = NBL->nbricks (or nt*nu*nv*nw) * nim->nu,nv,nw = 1 * nim->data = NULL *
NBL - filled with data volumes * * By default, this function will read the nifti dataset and break the data * into a list of nt*nu*nv*nw sub-bricks, each having size nx*ny*nz elements. * That is to say, instead of reading the entire dataset as a single array, * break it up into sub-bricks (volumes), each of size nx*ny*nz elements. * * Note: in the returned nifti_image, nu, nv and nw will always be 1. The * intention of this function is to collapse the dataset into a single * array of volumes (of length nbricks or nt*nu*nv*nw). * * If 'blist' is valid, it is taken to be a list of sub-bricks, of length * 'nbricks'. The data will still be separated into sub-bricks of size * nx*ny*nz elements, but now 'nbricks' sub-bricks will be returned, of the * caller's choosing via 'blist'. * * E.g. consider a dataset with 12 sub-bricks (numbered 0..11), and the * following code: * *
 * { nifti_brick_list   NB_orig, NB_select;
 *   nifti_image      * nim_orig, * nim_select;
 *   int                blist[5] = { 7, 0, 5, 5, 9 };
 *
 *   nim_orig   = nifti_image_read_bricks("myfile.nii", 0, NULL,  &NB_orig);
 *   nim_select = nifti_image_read_bricks("myfile.nii", 5, blist, &NB_select);
 * }
 * 
* * Here, nim_orig gets the entire dataset, where NB_orig.nbricks = 12. But * nim_select has NB_select.nbricks = 5. * * Note that the first case is not quite the same as just calling the * nifti_image_read function, as here the data is separated into sub-bricks. * * Note that valid blist elements are in [0..nt*nu*nv*nw-1], * or written [ 0 .. (dim[4]*dim[5]*dim[6]*dim[7] - 1) ]. * * Note that, as is the case with all of the reading functions, the * data will be allocated, read in, and properly byte-swapped, if * necessary. * * \sa nifti_image_load_bricks, nifti_free_NBL, valid_nifti_brick_list, nifti_image_read *//*----------------------------------------------------------------------*/ nifti_image *nifti_image_read_bricks(const char * hname, int nbricks, const int * blist, nifti_brick_list * NBL) { nifti_image * nim; if( !hname || !NBL ){ fprintf(stderr,"** nifti_image_read_bricks: bad params (%p,%p)\n", (const void *)hname, (void *)NBL); return NULL; } if( blist && nbricks <= 0 ){ fprintf(stderr,"** nifti_image_read_bricks: bad nbricks, %d\n", nbricks); return NULL; } nim = nifti_image_read(hname, 0); /* read header, but not data */ if( !nim ) return NULL; /* errors were already printed */ /* if we fail, free image and return */ if( nifti_image_load_bricks(nim, nbricks, blist, NBL) <= 0 ){ nifti_image_free(nim); return NULL; } if( blist ) update_nifti_image_for_brick_list(nim, nbricks); return nim; } /*---------------------------------------------------------------------- * update_nifti_image_for_brick_list - update nifti_image * * When loading a specific brick list, the distinction between * nt, nu, nv and nw is lost. So put everything in t, and set * dim[0] = 4. *----------------------------------------------------------------------*/ static void update_nifti_image_for_brick_list( nifti_image * nim , int nbricks ) { int ndim; if( g_opts.debug > 2 ){ fprintf(stderr,"+d updating image dimensions for %d bricks in list\n", nbricks); fprintf(stderr," ndim = %d\n",nim->ndim); fprintf(stderr," nx,ny,nz,nt,nu,nv,nw: (%d,%d,%d,%d,%d,%d,%d)\n", nim->nx, nim->ny, nim->nz, nim->nt, nim->nu, nim->nv, nim->nw); } nim->nt = nbricks; nim->nu = nim->nv = nim->nw = 1; nim->dim[4] = nbricks; nim->dim[5] = nim->dim[6] = nim->dim[7] = 1; /* compute nvox */ /* do not rely on dimensions above dim[0] 16 Nov 2005 [rickr] */ for( nim->nvox = 1, ndim = 1; ndim <= nim->dim[0]; ndim++ ) nim->nvox *= nim->dim[ndim]; /* update the dimensions to 4 or lower */ for( ndim = 4; (ndim > 1) && (nim->dim[ndim] <= 1); ndim-- ) ; if( g_opts.debug > 2 ){ fprintf(stderr,"+d ndim = %d -> %d\n",nim->ndim, ndim); fprintf(stderr," --> (%d,%d,%d,%d,%d,%d,%d)\n", nim->nx, nim->ny, nim->nz, nim->nt, nim->nu, nim->nv, nim->nw); } nim->dim[0] = nim->ndim = ndim; } /*----------------------------------------------------------------------*/ /*! nifti_update_dims_from_array - update nx, ny, ... from nim->dim[] Fix all the dimension information, based on a new nim->dim[]. Note: we assume that dim[0] will not increase. Check for updates to pixdim[], dx,..., nx,..., nvox, ndim, dim[0]. *//*--------------------------------------------------------------------*/ int nifti_update_dims_from_array( nifti_image * nim ) { int c, ndim; if( !nim ){ fprintf(stderr,"** update_dims: missing nim\n"); return 1; } if( g_opts.debug > 2 ){ fprintf(stderr,"+d updating image dimensions given nim->dim:"); for( c = 0; c < 8; c++ ) fprintf(stderr," %d", nim->dim[c]); fputc('\n',stderr); } /* verify dim[0] first */ if(nim->dim[0] < 1 || nim->dim[0] > 7){ fprintf(stderr,"** invalid dim[0], dim[] = "); for( c = 0; c < 8; c++ ) fprintf(stderr," %d", nim->dim[c]); fputc('\n',stderr); return 1; } /* set nx, ny ..., dx, dy, ..., one by one */ /* less than 1, set to 1, else copy */ if(nim->dim[1] < 1) nim->nx = nim->dim[1] = 1; else nim->nx = nim->dim[1]; nim->dx = nim->pixdim[1]; /* if undefined, or less than 1, set to 1 */ if(nim->dim[0] < 2 || (nim->dim[0] >= 2 && nim->dim[2] < 1)) nim->ny = nim->dim[2] = 1; else nim->ny = nim->dim[2]; /* copy delta values, in any case */ nim->dy = nim->pixdim[2]; if(nim->dim[0] < 3 || (nim->dim[0] >= 3 && nim->dim[3] < 1)) nim->nz = nim->dim[3] = 1; else /* just copy vals from arrays */ nim->nz = nim->dim[3]; nim->dz = nim->pixdim[3]; if(nim->dim[0] < 4 || (nim->dim[0] >= 4 && nim->dim[4] < 1)) nim->nt = nim->dim[4] = 1; else /* just copy vals from arrays */ nim->nt = nim->dim[4]; nim->dt = nim->pixdim[4]; if(nim->dim[0] < 5 || (nim->dim[0] >= 5 && nim->dim[5] < 1)) nim->nu = nim->dim[5] = 1; else /* just copy vals from arrays */ nim->nu = nim->dim[5]; nim->du = nim->pixdim[5]; if(nim->dim[0] < 6 || (nim->dim[0] >= 6 && nim->dim[6] < 1)) nim->nv = nim->dim[6] = 1; else /* just copy vals from arrays */ nim->nv = nim->dim[6]; nim->dv = nim->pixdim[6]; if(nim->dim[0] < 7 || (nim->dim[0] >= 7 && nim->dim[7] < 1)) nim->nw = nim->dim[7] = 1; else /* just copy vals from arrays */ nim->nw = nim->dim[7]; nim->dw = nim->pixdim[7]; for( c = 1, nim->nvox = 1; c <= nim->dim[0]; c++ ) nim->nvox *= nim->dim[c]; /* compute ndim, assuming it can be no larger than the old one */ for( ndim = nim->dim[0]; (ndim > 1) && (nim->dim[ndim] <= 1); ndim-- ) ; if( g_opts.debug > 2 ){ fprintf(stderr,"+d ndim = %d -> %d\n",nim->ndim, ndim); fprintf(stderr," --> (%d,%d,%d,%d,%d,%d,%d)\n", nim->nx, nim->ny, nim->nz, nim->nt, nim->nu, nim->nv, nim->nw); } nim->dim[0] = nim->ndim = ndim; return 0; } /*----------------------------------------------------------------------*/ /*! Load the image data from disk into an already-prepared image struct. * * \param nim - initialized nifti_image, without data * \param nbricks - the length of blist (must be 0 if blist is NULL) * \param blist - an array of xyz volume indices to read (can be NULL) * \param NBL - pointer to struct where resulting data will be stored * * If blist is NULL, read all sub-bricks. * * \return the number of loaded bricks (NBL->nbricks), * 0 on failure, < 0 on error * * NOTE: it is likely that another function will copy the data pointers * out of NBL, in which case the only pointer the calling function * will want to free is NBL->bricks (not each NBL->bricks[i]). *//*--------------------------------------------------------------------*/ int nifti_image_load_bricks( nifti_image * nim , int nbricks, const int * blist, nifti_brick_list * NBL ) { int * slist = NULL, * sindex = NULL, rv; znzFile fp; /* we can have blist == NULL */ if( !nim || !NBL ){ fprintf(stderr,"** nifti_image_load_bricks, bad params (%p,%p)\n", (void *)nim, (void *)NBL); return -1; } if( blist && nbricks <= 0 ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d load_bricks: received blist with nbricks = %d," "ignoring blist\n", nbricks); blist = NULL; /* pretend nothing was passed */ } if( blist && ! valid_nifti_brick_list(nim, nbricks, blist, g_opts.debug>0) ) return -1; /* for efficiency, let's read the file in order */ if( blist && nifti_copynsort( nbricks, blist, &slist, &sindex ) != 0 ) return -1; /* open the file and position the FILE pointer */ fp = nifti_image_load_prep( nim ); if( !fp ){ if( g_opts.debug > 0 ) fprintf(stderr,"** nifti_image_load_bricks, failed load_prep\n"); if( blist ){ free(slist); free(sindex); } return -1; } /* this will flag to allocate defaults */ if( !blist ) nbricks = 0; if( nifti_alloc_NBL_mem( nim, nbricks, NBL ) != 0 ){ if( blist ){ free(slist); free(sindex); } znzclose(fp); return -1; } rv = nifti_load_NBL_bricks(nim, slist, sindex, NBL, fp); if( rv != 0 ){ nifti_free_NBL( NBL ); /* failure! */ NBL->nbricks = 0; /* repetative, but clear */ } if( slist ){ free(slist); free(sindex); } znzclose(fp); return NBL->nbricks; } /*----------------------------------------------------------------------*/ /*! nifti_free_NBL - free all pointers and clear structure * * note: this does not presume to free the structure pointer *//*--------------------------------------------------------------------*/ void nifti_free_NBL( nifti_brick_list * NBL ) { int c; if( NBL->bricks ){ for( c = 0; c < NBL->nbricks; c++ ) if( NBL->bricks[c] ) free(NBL->bricks[c]); free(NBL->bricks); NBL->bricks = NULL; } NBL->bsize = NBL->nbricks = 0; } /*---------------------------------------------------------------------- * nifti_load_NBL_bricks - read the file data into the NBL struct * * return 0 on success, -1 on failure *----------------------------------------------------------------------*/ static int nifti_load_NBL_bricks( nifti_image * nim , int * slist, int * sindex, nifti_brick_list * NBL, znzFile fp ) { size_t oposn, fposn; /* orig and current file positions */ size_t rv; long test; int c; int prev, isrc, idest; /* previous and current sub-brick, and new index */ test = znztell(fp); /* store current file position */ if( test < 0 ){ fprintf(stderr,"** load bricks: ztell failed??\n"); return -1; } fposn = oposn = test; /* first, handle the default case, no passed blist */ if( !slist ){ for( c = 0; c < NBL->nbricks; c++ ) { rv = nifti_read_buffer(fp, NBL->bricks[c], NBL->bsize, nim); if( rv != NBL->bsize ){ fprintf(stderr,"** load bricks: cannot read brick %d from '%s'\n", c, nim->iname ? nim->iname : nim->fname); return -1; } } if( g_opts.debug > 1 ) fprintf(stderr,"+d read %d default %u-byte bricks from file %s\n", NBL->nbricks, (unsigned int)NBL->bsize, nim->iname ? nim->iname:nim->fname ); return 0; } if( !sindex ){ fprintf(stderr,"** load_NBL_bricks: missing index list\n"); return -1; } prev = -1; /* use prev for previous sub-brick */ for( c = 0; c < NBL->nbricks; c++ ){ isrc = slist[c]; /* this is original brick index (c is new one) */ idest = sindex[c]; /* this is the destination index for this data */ /* if this sub-brick is not the previous, we must read from disk */ if( isrc != prev ){ /* if we are not looking at the correct sub-brick, scan forward */ if( fposn != (oposn + isrc*NBL->bsize) ){ fposn = oposn + isrc*NBL->bsize; if( znzseek(fp, (long)fposn, SEEK_SET) < 0 ){ fprintf(stderr,"** failed to locate brick %d in file '%s'\n", isrc, nim->iname ? nim->iname : nim->fname); return -1; } } /* only 10,000 lines later and we're actually reading something! */ rv = nifti_read_buffer(fp, NBL->bricks[idest], NBL->bsize, nim); if( rv != NBL->bsize ){ fprintf(stderr,"** failed to read brick %d from file '%s'\n", isrc, nim->iname ? nim->iname : nim->fname); if( g_opts.debug > 1 ) fprintf(stderr," (read %u of %u bytes)\n", (unsigned int)rv, (unsigned int)NBL->bsize); return -1; } fposn += NBL->bsize; } else { /* we have already read this sub-brick, just copy the previous one */ /* note that this works because they are sorted */ memcpy(NBL->bricks[idest], NBL->bricks[sindex[c-1]], NBL->bsize); } prev = isrc; /* in any case, note the now previous sub-brick */ } return 0; } /*---------------------------------------------------------------------- * nifti_alloc_NBL_mem - allocate memory for bricks * * return 0 on success, -1 on failure *----------------------------------------------------------------------*/ static int nifti_alloc_NBL_mem(nifti_image * nim, int nbricks, nifti_brick_list * nbl) { int c; /* if nbricks is not specified, use the default */ if( nbricks > 0 ) nbl->nbricks = nbricks; else { /* I missed this one with the 1.17 change 02 Mar 2006 [rickr] */ nbl->nbricks = 1; for( c = 4; c <= nim->ndim; c++ ) nbl->nbricks *= nim->dim[c]; } nbl->bsize = (size_t)nim->nx * nim->ny * nim->nz * nim->nbyper;/* bytes */ nbl->bricks = (void **)malloc(nbl->nbricks * sizeof(void *)); if( ! nbl->bricks ){ fprintf(stderr,"** NANM: failed to alloc %d void ptrs\n",nbricks); return -1; } for( c = 0; c < nbl->nbricks; c++ ){ nbl->bricks[c] = (void *)malloc(nbl->bsize); if( ! nbl->bricks[c] ){ fprintf(stderr,"** NANM: failed to alloc %u bytes for brick %d\n", (unsigned int)nbl->bsize, c); /* so free and clear everything before returning */ while( c > 0 ){ c--; free(nbl->bricks[c]); } free(nbl->bricks); nbl->bricks = NULL; nbl->bsize = nbl->nbricks = 0; return -1; } } if( g_opts.debug > 2 ) fprintf(stderr,"+d NANM: alloc'd %d bricks of %u bytes for NBL\n", nbl->nbricks, (unsigned int)nbl->bsize); return 0; } /*---------------------------------------------------------------------- * nifti_copynsort - copy int list, and sort with indices * * 1. duplicate the incoming list * 2. create an sindex list, and init with 0..nbricks-1 * 3. do a slow insertion sort on the small slist, along with sindex list * 4. check results, just to be positive * * So slist is sorted, and sindex hold original positions. * * return 0 on success, -1 on failure *----------------------------------------------------------------------*/ static int nifti_copynsort(int nbricks, const int * blist, int ** slist, int ** sindex) { int * stmp, * itmp; /* for ease of typing/reading */ int c1, c2, spos, tmp; *slist = (int *)malloc(nbricks * sizeof(int)); *sindex = (int *)malloc(nbricks * sizeof(int)); if( !*slist || !*sindex ){ fprintf(stderr,"** NCS: failed to alloc %d ints for sorting\n",nbricks); if(*slist) free(*slist); /* maybe one succeeded */ if(*sindex) free(*sindex); return -1; } /* init the lists */ memcpy(*slist, blist, nbricks*sizeof(int)); for( c1 = 0; c1 < nbricks; c1++ ) (*sindex)[c1] = c1; /* now actually sort slist */ stmp = *slist; itmp = *sindex; for( c1 = 0; c1 < nbricks-1; c1++ ) { /* find smallest value, init to current */ spos = c1; for( c2 = c1+1; c2 < nbricks; c2++ ) if( stmp[c2] < stmp[spos] ) spos = c2; if( spos != c1 ) /* swap: fine, don't maintain sub-order, see if I care */ { tmp = stmp[c1]; /* first swap the sorting values */ stmp[c1] = stmp[spos]; stmp[spos] = tmp; tmp = itmp[c1]; /* then swap the index values */ itmp[c1] = itmp[spos]; itmp[spos] = tmp; } } if( g_opts.debug > 2 ){ fprintf(stderr, "+d sorted indexing list:\n"); fprintf(stderr, " orig : "); for( c1 = 0; c1 < nbricks; c1++ ) fprintf(stderr," %d",blist[c1]); fprintf(stderr,"\n new : "); for( c1 = 0; c1 < nbricks; c1++ ) fprintf(stderr," %d",stmp[c1]); fprintf(stderr,"\n indices: "); for( c1 = 0; c1 < nbricks; c1++ ) fprintf(stderr," %d",itmp[c1]); fputc('\n', stderr); } /* check the sort (why not? I've got time...) */ for( c1 = 0; c1 < nbricks-1; c1++ ){ if( (stmp[c1] > stmp[c1+1]) || (blist[itmp[c1]] != stmp[c1]) ){ fprintf(stderr,"** sorting screw-up, way to go, rick!\n"); free(stmp); free(itmp); *slist = NULL; *sindex = NULL; return -1; } } if( g_opts.debug > 2 ) fprintf(stderr,"-d sorting is okay\n"); return 0; } /*----------------------------------------------------------------------*/ /*! valid_nifti_brick_list - check sub-brick list for image * * This function verifies that nbricks and blist are appropriate * for use with this nim, based on the dimensions. * * \param nim nifti_image to check against * \param nbricks number of brick indices in blist * \param blist list of brick indices to check in nim * \param disp_error if this flag is set, report errors to user * * \return 1 if valid, 0 if not *//*--------------------------------------------------------------------*/ int valid_nifti_brick_list(nifti_image * nim , int nbricks, const int * blist, int disp_error) { int c, nsubs; if( !nim ){ if( disp_error || g_opts.debug > 0 ) fprintf(stderr,"** valid_nifti_brick_list: missing nifti image\n"); return 0; } if( nbricks <= 0 || !blist ){ if( disp_error || g_opts.debug > 1 ) fprintf(stderr,"** valid_nifti_brick_list: no brick list to check\n"); return 0; } if( nim->dim[0] < 3 ){ if( disp_error || g_opts.debug > 1 ) fprintf(stderr,"** cannot read explict brick list from %d-D dataset\n", nim->dim[0]); return 0; } /* nsubs sub-brick is nt*nu*nv*nw */ for( c = 4, nsubs = 1; c <= nim->dim[0]; c++ ) nsubs *= nim->dim[c]; if( nsubs <= 0 ){ fprintf(stderr,"** VNBL warning: bad dim list (%d,%d,%d,%d)\n", nim->dim[4], nim->dim[5], nim->dim[6], nim->dim[7]); return 0; } for( c = 0; c < nbricks; c++ ) if( (blist[c] < 0) || (blist[c] >= nsubs) ){ if( disp_error || g_opts.debug > 1 ) fprintf(stderr, "** volume index %d (#%d) is out of range [0,%d]\n", blist[c], c, nsubs-1); return 0; } return 1; /* all is well */ } /*----------------------------------------------------------------------*/ /* verify that NBL struct is a valid data source for the image * * return 1 if so, 0 otherwise *//*--------------------------------------------------------------------*/ static int nifti_NBL_matches_nim(const nifti_image *nim, const nifti_brick_list *NBL) { size_t volbytes = 0; /* bytes per volume */ int ind, errs = 0, nvols = 0; if( !nim || !NBL ) { if( g_opts.debug > 0 ) fprintf(stderr,"** nifti_NBL_matches_nim: NULL pointer(s)\n"); return 0; } /* for nim, compute volbytes and nvols */ if( nim->ndim > 0 ) { /* first 3 indices are over a single volume */ volbytes = (size_t)nim->nbyper; for( ind = 1; ind <= nim->ndim && ind < 4; ind++ ) volbytes *= (size_t)nim->dim[ind]; for( ind = 4, nvols = 1; ind <= nim->ndim; ind++ ) nvols *= nim->dim[ind]; } if( volbytes != NBL->bsize ) { if( g_opts.debug > 1 ) fprintf(stderr,"** NBL/nim mismatch, volbytes = %u, %u\n", (unsigned)NBL->bsize, (unsigned)volbytes); errs++; } if( nvols != NBL->nbricks ) { if( g_opts.debug > 1 ) fprintf(stderr,"** NBL/nim mismatch, nvols = %d, %d\n", NBL->nbricks, nvols); errs++; } if( errs ) return 0; else if ( g_opts.debug > 2 ) fprintf(stderr,"-- nim/NBL agree: nvols = %d, nbytes = %u\n", nvols, (unsigned)volbytes); return 1; } /* end of new nifti_image_read_bricks() functionality */ /*----------------------------------------------------------------------*/ /*! display the orientation from the quaternian fields * * \param mesg if non-NULL, display this message first * \param mat the matrix to convert to "nearest" orientation * * \return -1 if results cannot be determined, 0 if okay *//*--------------------------------------------------------------------*/ int nifti_disp_matrix_orient( const char * mesg, mat44 mat ) { int i, j, k; if ( mesg ) fputs( mesg, stderr ); /* use stdout? */ nifti_mat44_to_orientation( mat, &i,&j,&k ); if ( i <= 0 || j <= 0 || k <= 0 ) return -1; /* so we have good codes */ fprintf(stderr, " i orientation = '%s'\n" " j orientation = '%s'\n" " k orientation = '%s'\n", nifti_orientation_string(i), nifti_orientation_string(j), nifti_orientation_string(k) ); return 0; } /*----------------------------------------------------------------------*/ /*! duplicate the given string (alloc length+1) * * \return allocated pointer (or NULL on failure) *//*--------------------------------------------------------------------*/ char *nifti_strdup(const char *str) { char *dup; if( !str ) return NULL; /* allow calls passing NULL */ dup = (char *)malloc(strlen(str) + 1); /* check for failure */ if( dup ) strcpy(dup, str); else fprintf(stderr,"** nifti_strdup: failed to alloc %u bytes\n", (unsigned int)strlen(str)+1); return dup; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI datatype. \param dt NIfTI-1 datatype \return pointer to static string holding the datatype name \warning Do not free() or modify this string! It points to static storage. \sa NIFTI1_DATATYPES group in nifti1.h *//*-------------------------------------------------------------------------*/ char *nifti_datatype_string( int dt ) { switch( dt ){ case DT_UNKNOWN: return "UNKNOWN" ; case DT_BINARY: return "BINARY" ; case DT_INT8: return "INT8" ; case DT_UINT8: return "UINT8" ; case DT_INT16: return "INT16" ; case DT_UINT16: return "UINT16" ; case DT_INT32: return "INT32" ; case DT_UINT32: return "UINT32" ; case DT_INT64: return "INT64" ; case DT_UINT64: return "UINT64" ; case DT_FLOAT32: return "FLOAT32" ; case DT_FLOAT64: return "FLOAT64" ; case DT_FLOAT128: return "FLOAT128" ; case DT_COMPLEX64: return "COMPLEX64" ; case DT_COMPLEX128: return "COMPLEX128" ; case DT_COMPLEX256: return "COMPLEX256" ; case DT_RGB24: return "RGB24" ; case DT_RGBA32: return "RGBA32" ; } return "**ILLEGAL**" ; } /*----------------------------------------------------------------------*/ /*! Determine if the datatype code dt is an integer type (1=YES, 0=NO). \return whether the given NIfTI-1 datatype code is valid \sa NIFTI1_DATATYPES group in nifti1.h *//*--------------------------------------------------------------------*/ int nifti_is_inttype( int dt ) { switch( dt ){ case DT_UNKNOWN: return 0 ; case DT_BINARY: return 0 ; case DT_INT8: return 1 ; case DT_UINT8: return 1 ; case DT_INT16: return 1 ; case DT_UINT16: return 1 ; case DT_INT32: return 1 ; case DT_UINT32: return 1 ; case DT_INT64: return 1 ; case DT_UINT64: return 1 ; case DT_FLOAT32: return 0 ; case DT_FLOAT64: return 0 ; case DT_FLOAT128: return 0 ; case DT_COMPLEX64: return 0 ; case DT_COMPLEX128: return 0 ; case DT_COMPLEX256: return 0 ; case DT_RGB24: return 1 ; case DT_RGBA32: return 1 ; } return 0 ; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI units type. \param uu NIfTI-1 unit code \return pointer to static string for the given unit type \warning Do not free() or modify this string! It points to static storage. \sa NIFTI1_UNITS group in nifti1.h *//*-------------------------------------------------------------------------*/ char *nifti_units_string( int uu ) { switch( uu ){ case NIFTI_UNITS_METER: return "m" ; case NIFTI_UNITS_MM: return "mm" ; case NIFTI_UNITS_MICRON: return "um" ; case NIFTI_UNITS_SEC: return "s" ; case NIFTI_UNITS_MSEC: return "ms" ; case NIFTI_UNITS_USEC: return "us" ; case NIFTI_UNITS_HZ: return "Hz" ; case NIFTI_UNITS_PPM: return "ppm" ; case NIFTI_UNITS_RADS: return "rad/s" ; } return "Unknown" ; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI transform type. \param xx NIfTI-1 xform code \return pointer to static string describing xform code \warning Do not free() or modify this string! It points to static storage. \sa NIFTI1_XFORM_CODES group in nifti1.h *//*-------------------------------------------------------------------------*/ char *nifti_xform_string( int xx ) { switch( xx ){ case NIFTI_XFORM_SCANNER_ANAT: return "Scanner Anat" ; case NIFTI_XFORM_ALIGNED_ANAT: return "Aligned Anat" ; case NIFTI_XFORM_TALAIRACH: return "Talairach" ; case NIFTI_XFORM_MNI_152: return "MNI_152" ; } return "Unknown" ; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI intent type. \param ii NIfTI-1 intent code \return pointer to static string describing code \warning Do not free() or modify this string! It points to static storage. \sa NIFTI1_INTENT_CODES group in nifti1.h *//*-------------------------------------------------------------------------*/ char *nifti_intent_string( int ii ) { switch( ii ){ case NIFTI_INTENT_CORREL: return "Correlation statistic" ; case NIFTI_INTENT_TTEST: return "T-statistic" ; case NIFTI_INTENT_FTEST: return "F-statistic" ; case NIFTI_INTENT_ZSCORE: return "Z-score" ; case NIFTI_INTENT_CHISQ: return "Chi-squared distribution" ; case NIFTI_INTENT_BETA: return "Beta distribution" ; case NIFTI_INTENT_BINOM: return "Binomial distribution" ; case NIFTI_INTENT_GAMMA: return "Gamma distribution" ; case NIFTI_INTENT_POISSON: return "Poisson distribution" ; case NIFTI_INTENT_NORMAL: return "Normal distribution" ; case NIFTI_INTENT_FTEST_NONC: return "F-statistic noncentral" ; case NIFTI_INTENT_CHISQ_NONC: return "Chi-squared noncentral" ; case NIFTI_INTENT_LOGISTIC: return "Logistic distribution" ; case NIFTI_INTENT_LAPLACE: return "Laplace distribution" ; case NIFTI_INTENT_UNIFORM: return "Uniform distribition" ; case NIFTI_INTENT_TTEST_NONC: return "T-statistic noncentral" ; case NIFTI_INTENT_WEIBULL: return "Weibull distribution" ; case NIFTI_INTENT_CHI: return "Chi distribution" ; case NIFTI_INTENT_INVGAUSS: return "Inverse Gaussian distribution" ; case NIFTI_INTENT_EXTVAL: return "Extreme Value distribution" ; case NIFTI_INTENT_PVAL: return "P-value" ; case NIFTI_INTENT_LOGPVAL: return "Log P-value" ; case NIFTI_INTENT_LOG10PVAL: return "Log10 P-value" ; case NIFTI_INTENT_ESTIMATE: return "Estimate" ; case NIFTI_INTENT_LABEL: return "Label index" ; case NIFTI_INTENT_NEURONAME: return "NeuroNames index" ; case NIFTI_INTENT_GENMATRIX: return "General matrix" ; case NIFTI_INTENT_SYMMATRIX: return "Symmetric matrix" ; case NIFTI_INTENT_DISPVECT: return "Displacement vector" ; case NIFTI_INTENT_VECTOR: return "Vector" ; case NIFTI_INTENT_POINTSET: return "Pointset" ; case NIFTI_INTENT_TRIANGLE: return "Triangle" ; case NIFTI_INTENT_QUATERNION: return "Quaternion" ; case NIFTI_INTENT_DIMLESS: return "Dimensionless number" ; } return "Unknown" ; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI slice_code. \param ss NIfTI-1 slice order code \return pointer to static string describing code \warning Do not free() or modify this string! It points to static storage. \sa NIFTI1_SLICE_ORDER group in nifti1.h *//*-------------------------------------------------------------------------*/ char *nifti_slice_string( int ss ) { switch( ss ){ case NIFTI_SLICE_SEQ_INC: return "sequential_increasing" ; case NIFTI_SLICE_SEQ_DEC: return "sequential_decreasing" ; case NIFTI_SLICE_ALT_INC: return "alternating_increasing" ; case NIFTI_SLICE_ALT_DEC: return "alternating_decreasing" ; case NIFTI_SLICE_ALT_INC2: return "alternating_increasing_2" ; case NIFTI_SLICE_ALT_DEC2: return "alternating_decreasing_2" ; } return "Unknown" ; } /*---------------------------------------------------------------------------*/ /*! Return a pointer to a string holding the name of a NIFTI orientation. \param ii orientation code \return pointer to static string holding the orientation information \warning Do not free() or modify the return string! It points to static storage. \sa NIFTI_L2R in nifti1_io.h *//*-------------------------------------------------------------------------*/ char *nifti_orientation_string( int ii ) { switch( ii ){ case NIFTI_L2R: return "Left-to-Right" ; case NIFTI_R2L: return "Right-to-Left" ; case NIFTI_P2A: return "Posterior-to-Anterior" ; case NIFTI_A2P: return "Anterior-to-Posterior" ; case NIFTI_I2S: return "Inferior-to-Superior" ; case NIFTI_S2I: return "Superior-to-Inferior" ; } return "Unknown" ; } /*--------------------------------------------------------------------------*/ /*! Given a datatype code, set number of bytes per voxel and the swapsize. \param datatype nifti1 datatype code \param nbyper pointer to return value: number of bytes per voxel \param swapsize pointer to return value: size of swap blocks The swapsize is set to 0 if this datatype doesn't ever need swapping. \sa NIFTI1_DATATYPES in nifti1.h *//*------------------------------------------------------------------------*/ void nifti_datatype_sizes( int datatype , int *nbyper, int *swapsize ) { int nb=0, ss=0 ; switch( datatype ){ case DT_INT8: case DT_UINT8: nb = 1 ; ss = 0 ; break ; case DT_INT16: case DT_UINT16: nb = 2 ; ss = 2 ; break ; case DT_RGB24: nb = 3 ; ss = 0 ; break ; case DT_RGBA32: nb = 4 ; ss = 0 ; break ; case DT_INT32: case DT_UINT32: case DT_FLOAT32: nb = 4 ; ss = 4 ; break ; case DT_COMPLEX64: nb = 8 ; ss = 4 ; break ; case DT_FLOAT64: case DT_INT64: case DT_UINT64: nb = 8 ; ss = 8 ; break ; case DT_FLOAT128: nb = 16 ; ss = 16 ; break ; case DT_COMPLEX128: nb = 16 ; ss = 8 ; break ; case DT_COMPLEX256: nb = 32 ; ss = 16 ; break ; } ASSIF(nbyper,nb) ; ASSIF(swapsize,ss) ; return ; } /*---------------------------------------------------------------------------*/ /*! Given the quaternion parameters (etc.), compute a transformation matrix. See comments in nifti1.h for details. - qb,qc,qd = quaternion parameters - qx,qy,qz = offset parameters - dx,dy,dz = grid stepsizes (non-negative inputs are set to 1.0) - qfac = sign of dz step (< 0 is negative; >= 0 is positive)
   If qx=qy=qz=0, dx=dy=dz=1, then the output is a rotation matrix.
   For qfac >= 0, the rotation is proper.
   For qfac <  0, the rotation is improper.
   
\see "QUATERNION REPRESENTATION OF ROTATION MATRIX" in nifti1.h \see nifti_mat44_to_quatern, nifti_make_orthog_mat44, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ) { mat44 R ; double a,b=qb,c=qc,d=qd , xd,yd,zd ; /* last row is always [ 0 0 0 1 ] */ R.m[3][0]=R.m[3][1]=R.m[3][2] = 0.0 ; R.m[3][3]= 1.0 ; /* compute a parameter from b,c,d */ a = 1.0l - (b*b + c*c + d*d) ; if( a < 1.e-7l ){ /* special case */ a = 1.0l / sqrt(b*b+c*c+d*d) ; b *= a ; c *= a ; d *= a ; /* normalize (b,c,d) vector */ a = 0.0l ; /* a = 0 ==> 180 degree rotation */ } else{ a = sqrt(a) ; /* angle = 2*arccos(a) */ } /* load rotation matrix, including scaling factors for voxel sizes */ xd = (dx > 0.0) ? dx : 1.0l ; /* make sure are positive */ yd = (dy > 0.0) ? dy : 1.0l ; zd = (dz > 0.0) ? dz : 1.0l ; if( qfac < 0.0 ) zd = -zd ; /* left handedness? */ R.m[0][0] = (a*a+b*b-c*c-d*d) * xd ; R.m[0][1] = 2.0l * (b*c-a*d ) * yd ; R.m[0][2] = 2.0l * (b*d+a*c ) * zd ; R.m[1][0] = 2.0l * (b*c+a*d ) * xd ; R.m[1][1] = (a*a+c*c-b*b-d*d) * yd ; R.m[1][2] = 2.0l * (c*d-a*b ) * zd ; R.m[2][0] = 2.0l * (b*d-a*c ) * xd ; R.m[2][1] = 2.0l * (c*d+a*b ) * yd ; R.m[2][2] = (a*a+d*d-c*c-b*b) * zd ; /* load offsets */ R.m[0][3] = qx ; R.m[1][3] = qy ; R.m[2][3] = qz ; return R ; } /*---------------------------------------------------------------------------*/ /*! Given the 3x4 upper corner of the matrix R, compute the quaternion parameters that fit it. - Any NULL pointer on input won't get assigned (e.g., if you don't want dx,dy,dz, just pass NULL in for those pointers). - If the 3 input matrix columns are NOT orthogonal, they will be orthogonalized prior to calculating the parameters, using the polar decomposition to find the orthogonal matrix closest to the column-normalized input matrix. - However, if the 3 input matrix columns are NOT orthogonal, then the matrix produced by nifti_quatern_to_mat44 WILL have orthogonal columns, so it won't be the same as the matrix input here. This "feature" is because the NIFTI 'qform' transform is deliberately not fully general -- it is intended to model a volume with perpendicular axes. - If the 3 input matrix columns are not even linearly independent, you'll just have to take your luck, won't you? \see "QUATERNION REPRESENTATION OF ROTATION MATRIX" in nifti1.h \see nifti_quatern_to_mat44, nifti_make_orthog_mat44, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) { double r11,r12,r13 , r21,r22,r23 , r31,r32,r33 ; double xd,yd,zd , a,b,c,d ; mat33 P,Q ; /* offset outputs are read write out of input matrix */ ASSIF(qx,R.m[0][3]) ; ASSIF(qy,R.m[1][3]) ; ASSIF(qz,R.m[2][3]) ; /* load 3x3 matrix into local variables */ r11 = R.m[0][0] ; r12 = R.m[0][1] ; r13 = R.m[0][2] ; r21 = R.m[1][0] ; r22 = R.m[1][1] ; r23 = R.m[1][2] ; r31 = R.m[2][0] ; r32 = R.m[2][1] ; r33 = R.m[2][2] ; /* compute lengths of each column; these determine grid spacings */ xd = sqrt( r11*r11 + r21*r21 + r31*r31 ) ; yd = sqrt( r12*r12 + r22*r22 + r32*r32 ) ; zd = sqrt( r13*r13 + r23*r23 + r33*r33 ) ; /* if a column length is zero, patch the trouble */ if( xd == 0.0l ){ r11 = 1.0l ; r21 = r31 = 0.0l ; xd = 1.0l ; } if( yd == 0.0l ){ r22 = 1.0l ; r12 = r32 = 0.0l ; yd = 1.0l ; } if( zd == 0.0l ){ r33 = 1.0l ; r13 = r23 = 0.0l ; zd = 1.0l ; } /* assign the output lengths */ ASSIF(dx,xd) ; ASSIF(dy,yd) ; ASSIF(dz,zd) ; /* normalize the columns */ r11 /= xd ; r21 /= xd ; r31 /= xd ; r12 /= yd ; r22 /= yd ; r32 /= yd ; r13 /= zd ; r23 /= zd ; r33 /= zd ; /* At this point, the matrix has normal columns, but we have to allow for the fact that the hideous user may not have given us a matrix with orthogonal columns. So, now find the orthogonal matrix closest to the current matrix. One reason for using the polar decomposition to get this orthogonal matrix, rather than just directly orthogonalizing the columns, is so that inputting the inverse matrix to R will result in the inverse orthogonal matrix at this point. If we just orthogonalized the columns, this wouldn't necessarily hold. */ Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; /* load Q */ Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; P = nifti_mat33_polar(Q) ; /* P is orthog matrix closest to Q */ r11 = P.m[0][0] ; r12 = P.m[0][1] ; r13 = P.m[0][2] ; /* unload */ r21 = P.m[1][0] ; r22 = P.m[1][1] ; r23 = P.m[1][2] ; r31 = P.m[2][0] ; r32 = P.m[2][1] ; r33 = P.m[2][2] ; /* [ r11 r12 r13 ] */ /* at this point, the matrix [ r21 r22 r23 ] is orthogonal */ /* [ r31 r32 r33 ] */ /* compute the determinant to determine if it is proper */ zd = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; /* should be -1 or 1 */ if( zd > 0 ){ /* proper */ ASSIF(qfac,1.0) ; } else { /* improper ==> flip 3rd column */ ASSIF(qfac,-1.0) ; r13 = -r13 ; r23 = -r23 ; r33 = -r33 ; } /* now, compute quaternion parameters */ a = r11 + r22 + r33 + 1.0l ; if( a > 0.5l ){ /* simplest case */ a = 0.5l * sqrt(a) ; b = 0.25l * (r32-r23) / a ; c = 0.25l * (r13-r31) / a ; d = 0.25l * (r21-r12) / a ; } else { /* trickier case */ xd = 1.0 + r11 - (r22+r33) ; /* 4*b*b */ yd = 1.0 + r22 - (r11+r33) ; /* 4*c*c */ zd = 1.0 + r33 - (r11+r22) ; /* 4*d*d */ if( xd > 1.0 ){ b = 0.5l * sqrt(xd) ; c = 0.25l* (r12+r21) / b ; d = 0.25l* (r13+r31) / b ; a = 0.25l* (r32-r23) / b ; } else if( yd > 1.0 ){ c = 0.5l * sqrt(yd) ; b = 0.25l* (r12+r21) / c ; d = 0.25l* (r23+r32) / c ; a = 0.25l* (r13-r31) / c ; } else { d = 0.5l * sqrt(zd) ; b = 0.25l* (r13+r31) / d ; c = 0.25l* (r23+r32) / d ; a = 0.25l* (r21-r12) / d ; } if( a < 0.0l ){ b=-b ; c=-c ; d=-d; a=-a; } } ASSIF(qb,b) ; ASSIF(qc,c) ; ASSIF(qd,d) ; return ; } /*---------------------------------------------------------------------------*/ /*! Compute the inverse of a bordered 4x4 matrix.
   - Some numerical code fragments were generated by Maple 8.
   - If a singular matrix is input, the output matrix will be all zero.
   - You can check for this by examining the [3][3] element, which will
     be 1.0 for the normal case and 0.0 for the bad case.

     The input matrix should have the form:
        [ r11 r12 r13 v1 ]
        [ r21 r22 r23 v2 ]
        [ r31 r32 r33 v3 ]
        [  0   0   0   1 ]
     
*//*-------------------------------------------------------------------------*/ mat44 nifti_mat44_inverse( mat44 R ) { double r11,r12,r13,r21,r22,r23,r31,r32,r33,v1,v2,v3 , deti ; mat44 Q ; /* INPUT MATRIX IS: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 v1 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 v2 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 v3 ] */ v1 = R.m[0][3]; v2 = R.m[1][3]; v3 = R.m[2][3]; /* [ 0 0 0 1 ] */ deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[0][3] = deti*(-r12*r23*v3+r12*v2*r33+r22*r13*v3 -r22*v1*r33-r32*r13*v2+r32*v1*r23) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[1][3] = deti*( r11*r23*v3-r11*v2*r33-r21*r13*v3 +r21*v1*r33+r31*r13*v2-r31*v1*r23) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; Q.m[2][3] = deti*(-r11*r22*v3+r11*r32*v2+r21*r12*v3 -r21*r32*v1-r31*r12*v2+r31*r22*v1) ; Q.m[3][0] = Q.m[3][1] = Q.m[3][2] = 0.0l ; Q.m[3][3] = (deti == 0.0l) ? 0.0l : 1.0l ; /* failure flag if deti == 0 */ return Q ; } /*---------------------------------------------------------------------------*/ /*! Input 9 floats and make an orthgonal mat44 out of them. Each row is normalized, then nifti_mat33_polar() is used to orthogonalize them. If row #3 (r31,r32,r33) is input as zero, then it will be taken to be the cross product of rows #1 and #2. This function can be used to create a rotation matrix for transforming an oblique volume to anatomical coordinates. For this application: - row #1 (r11,r12,r13) is the direction vector along the image i-axis - row #2 (r21,r22,r23) is the direction vector along the image j-axis - row #3 (r31,r32,r33) is the direction vector along the slice direction (if available; otherwise enter it as 0's) The first 2 rows can be taken from the DICOM attribute (0020,0037) "Image Orientation (Patient)". After forming the rotation matrix, the complete affine transformation from (i,j,k) grid indexes to (x,y,z) spatial coordinates can be computed by multiplying each column by the appropriate grid spacing: - column #1 (R.m[0][0],R.m[1][0],R.m[2][0]) by delta-x - column #2 (R.m[0][1],R.m[1][1],R.m[2][1]) by delta-y - column #3 (R.m[0][2],R.m[1][2],R.m[2][2]) by delta-z and by then placing the center (x,y,z) coordinates of voxel (0,0,0) into the column #4 (R.m[0][3],R.m[1][3],R.m[2][3]). \sa nifti_quatern_to_mat44, nifti_mat44_to_quatern, nifti_mat44_to_orientation *//*-------------------------------------------------------------------------*/ mat44 nifti_make_orthog_mat44( float r11, float r12, float r13 , float r21, float r22, float r23 , float r31, float r32, float r33 ) { mat44 R ; mat33 Q , P ; double val ; R.m[3][0] = R.m[3][1] = R.m[3][2] = 0.0l ; R.m[3][3] = 1.0l ; Q.m[0][0] = r11 ; Q.m[0][1] = r12 ; Q.m[0][2] = r13 ; /* load Q */ Q.m[1][0] = r21 ; Q.m[1][1] = r22 ; Q.m[1][2] = r23 ; Q.m[2][0] = r31 ; Q.m[2][1] = r32 ; Q.m[2][2] = r33 ; /* normalize row 1 */ val = Q.m[0][0]*Q.m[0][0] + Q.m[0][1]*Q.m[0][1] + Q.m[0][2]*Q.m[0][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[0][0] *= val ; Q.m[0][1] *= val ; Q.m[0][2] *= val ; } else { Q.m[0][0] = 1.0l ; Q.m[0][1] = 0.0l ; Q.m[0][2] = 0.0l ; } /* normalize row 2 */ val = Q.m[1][0]*Q.m[1][0] + Q.m[1][1]*Q.m[1][1] + Q.m[1][2]*Q.m[1][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[1][0] *= val ; Q.m[1][1] *= val ; Q.m[1][2] *= val ; } else { Q.m[1][0] = 0.0l ; Q.m[1][1] = 1.0l ; Q.m[1][2] = 0.0l ; } /* normalize row 3 */ val = Q.m[2][0]*Q.m[2][0] + Q.m[2][1]*Q.m[2][1] + Q.m[2][2]*Q.m[2][2] ; if( val > 0.0l ){ val = 1.0l / sqrt(val) ; Q.m[2][0] *= val ; Q.m[2][1] *= val ; Q.m[2][2] *= val ; } else { Q.m[2][0] = Q.m[0][1]*Q.m[1][2] - Q.m[0][2]*Q.m[1][1] ; /* cross */ Q.m[2][1] = Q.m[0][2]*Q.m[1][0] - Q.m[0][0]*Q.m[1][2] ; /* product */ Q.m[2][2] = Q.m[0][0]*Q.m[1][1] - Q.m[0][1]*Q.m[1][0] ; } P = nifti_mat33_polar(Q) ; /* P is orthog matrix closest to Q */ R.m[0][0] = P.m[0][0] ; R.m[0][1] = P.m[0][1] ; R.m[0][2] = P.m[0][2] ; R.m[1][0] = P.m[1][0] ; R.m[1][1] = P.m[1][1] ; R.m[1][2] = P.m[1][2] ; R.m[2][0] = P.m[2][0] ; R.m[2][1] = P.m[2][1] ; R.m[2][2] = P.m[2][2] ; R.m[0][3] = R.m[1][3] = R.m[2][3] = 0.0 ; return R ; } /*----------------------------------------------------------------------*/ /*! compute the inverse of a 3x3 matrix *//*--------------------------------------------------------------------*/ mat33 nifti_mat33_inverse( mat33 R ) /* inverse of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 , deti ; mat33 Q ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ deti = r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; if( deti != 0.0l ) deti = 1.0l / deti ; Q.m[0][0] = deti*( r22*r33-r32*r23) ; Q.m[0][1] = deti*(-r12*r33+r32*r13) ; Q.m[0][2] = deti*( r12*r23-r22*r13) ; Q.m[1][0] = deti*(-r21*r33+r31*r23) ; Q.m[1][1] = deti*( r11*r33-r31*r13) ; Q.m[1][2] = deti*(-r11*r23+r21*r13) ; Q.m[2][0] = deti*( r21*r32-r31*r22) ; Q.m[2][1] = deti*(-r11*r32+r31*r12) ; Q.m[2][2] = deti*( r11*r22-r21*r12) ; return Q ; } /*----------------------------------------------------------------------*/ /*! compute the determinant of a 3x3 matrix *//*--------------------------------------------------------------------*/ float nifti_mat33_determ( mat33 R ) /* determinant of 3x3 matrix */ { double r11,r12,r13,r21,r22,r23,r31,r32,r33 ; /* INPUT MATRIX: */ r11 = R.m[0][0]; r12 = R.m[0][1]; r13 = R.m[0][2]; /* [ r11 r12 r13 ] */ r21 = R.m[1][0]; r22 = R.m[1][1]; r23 = R.m[1][2]; /* [ r21 r22 r23 ] */ r31 = R.m[2][0]; r32 = R.m[2][1]; r33 = R.m[2][2]; /* [ r31 r32 r33 ] */ return r11*r22*r33-r11*r32*r23-r21*r12*r33 +r21*r32*r13+r31*r12*r23-r31*r22*r13 ; } /*----------------------------------------------------------------------*/ /*! compute the max row norm of a 3x3 matrix *//*--------------------------------------------------------------------*/ float nifti_mat33_rownorm( mat33 A ) /* max row norm of 3x3 matrix */ { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[0][1])+fabs(A.m[0][2]) ; r2 = fabs(A.m[1][0])+fabs(A.m[1][1])+fabs(A.m[1][2]) ; r3 = fabs(A.m[2][0])+fabs(A.m[2][1])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } /*----------------------------------------------------------------------*/ /*! compute the max column norm of a 3x3 matrix *//*--------------------------------------------------------------------*/ float nifti_mat33_colnorm( mat33 A ) /* max column norm of 3x3 matrix */ { float r1,r2,r3 ; r1 = fabs(A.m[0][0])+fabs(A.m[1][0])+fabs(A.m[2][0]) ; r2 = fabs(A.m[0][1])+fabs(A.m[1][1])+fabs(A.m[2][1]) ; r3 = fabs(A.m[0][2])+fabs(A.m[1][2])+fabs(A.m[2][2]) ; if( r1 < r2 ) r1 = r2 ; if( r1 < r3 ) r1 = r3 ; return r1 ; } /*----------------------------------------------------------------------*/ /*! multiply 2 3x3 matrices *//*--------------------------------------------------------------------*/ mat33 nifti_mat33_mul( mat33 A , mat33 B ) /* multiply 2 3x3 matrices */ { mat33 C ; int i,j ; for( i=0 ; i < 3 ; i++ ) for( j=0 ; j < 3 ; j++ ) C.m[i][j] = A.m[i][0] * B.m[0][j] + A.m[i][1] * B.m[1][j] + A.m[i][2] * B.m[2][j] ; return C ; } /*---------------------------------------------------------------------------*/ /*! polar decomposition of a 3x3 matrix This finds the closest orthogonal matrix to input A (in both the Frobenius and L2 norms). Algorithm is that from NJ Higham, SIAM J Sci Stat Comput, 7:1160-1174. *//*-------------------------------------------------------------------------*/ mat33 nifti_mat33_polar( mat33 A ) { mat33 X , Y , Z ; float alp,bet,gam,gmi , dif=1.0 ; int k=0 ; X = A ; /* force matrix to be nonsingular */ gam = nifti_mat33_determ(X) ; while( gam == 0.0 ){ /* perturb matrix */ gam = 0.00001 * ( 0.001 + nifti_mat33_rownorm(X) ) ; X.m[0][0] += gam ; X.m[1][1] += gam ; X.m[2][2] += gam ; gam = nifti_mat33_determ(X) ; } while(1){ Y = nifti_mat33_inverse(X) ; if( dif > 0.3 ){ /* far from convergence */ alp = sqrt( nifti_mat33_rownorm(X) * nifti_mat33_colnorm(X) ) ; bet = sqrt( nifti_mat33_rownorm(Y) * nifti_mat33_colnorm(Y) ) ; gam = sqrt( bet / alp ) ; gmi = 1.0 / gam ; } else { gam = gmi = 1.0 ; /* close to convergence */ } Z.m[0][0] = 0.5 * ( gam*X.m[0][0] + gmi*Y.m[0][0] ) ; Z.m[0][1] = 0.5 * ( gam*X.m[0][1] + gmi*Y.m[1][0] ) ; Z.m[0][2] = 0.5 * ( gam*X.m[0][2] + gmi*Y.m[2][0] ) ; Z.m[1][0] = 0.5 * ( gam*X.m[1][0] + gmi*Y.m[0][1] ) ; Z.m[1][1] = 0.5 * ( gam*X.m[1][1] + gmi*Y.m[1][1] ) ; Z.m[1][2] = 0.5 * ( gam*X.m[1][2] + gmi*Y.m[2][1] ) ; Z.m[2][0] = 0.5 * ( gam*X.m[2][0] + gmi*Y.m[0][2] ) ; Z.m[2][1] = 0.5 * ( gam*X.m[2][1] + gmi*Y.m[1][2] ) ; Z.m[2][2] = 0.5 * ( gam*X.m[2][2] + gmi*Y.m[2][2] ) ; dif = fabs(Z.m[0][0]-X.m[0][0])+fabs(Z.m[0][1]-X.m[0][1]) +fabs(Z.m[0][2]-X.m[0][2])+fabs(Z.m[1][0]-X.m[1][0]) +fabs(Z.m[1][1]-X.m[1][1])+fabs(Z.m[1][2]-X.m[1][2]) +fabs(Z.m[2][0]-X.m[2][0])+fabs(Z.m[2][1]-X.m[2][1]) +fabs(Z.m[2][2]-X.m[2][2]) ; k = k+1 ; if( k > 100 || dif < 3.e-6 ) break ; /* convergence or exhaustion */ X = Z ; } return Z ; } /*---------------------------------------------------------------------------*/ /*! compute the (closest) orientation from a 4x4 ijk->xyz tranformation matrix
   Input:  4x4 matrix that transforms (i,j,k) indexes to (x,y,z) coordinates,
           where +x=Right, +y=Anterior, +z=Superior.
           (Only the upper-left 3x3 corner of R is used herein.)
   Output: 3 orientation codes that correspond to the closest "standard"
           anatomical orientation of the (i,j,k) axes.
   Method: Find which permutation of (x,y,z) has the smallest angle to the
           (i,j,k) axes directions, which are the columns of the R matrix.
   Errors: The codes returned will be zero.

   For example, an axial volume might get return values of
     *icod = NIFTI_R2L   (i axis is mostly Right to Left)
     *jcod = NIFTI_P2A   (j axis is mostly Posterior to Anterior)
     *kcod = NIFTI_I2S   (k axis is mostly Inferior to Superior)
   
\see "QUATERNION REPRESENTATION OF ROTATION MATRIX" in nifti1.h \see nifti_quatern_to_mat44, nifti_mat44_to_quatern, nifti_make_orthog_mat44 *//*-------------------------------------------------------------------------*/ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod ) { float xi,xj,xk , yi,yj,yk , zi,zj,zk , val,detQ,detP ; mat33 P , Q , M ; int i,j,k=0,p,q,r , ibest,jbest,kbest,pbest,qbest,rbest ; float vbest ; if( icod == NULL || jcod == NULL || kcod == NULL ) return ; /* bad */ *icod = *jcod = *kcod = 0 ; /* error returns, if sh*t happens */ /* load column vectors for each (i,j,k) direction from matrix */ /*-- i axis --*/ /*-- j axis --*/ /*-- k axis --*/ xi = R.m[0][0] ; xj = R.m[0][1] ; xk = R.m[0][2] ; yi = R.m[1][0] ; yj = R.m[1][1] ; yk = R.m[1][2] ; zi = R.m[2][0] ; zj = R.m[2][1] ; zk = R.m[2][2] ; /* normalize column vectors to get unit vectors along each ijk-axis */ /* normalize i axis */ val = sqrt( xi*xi + yi*yi + zi*zi ) ; if( val == 0.0 ) return ; /* stupid input */ xi /= val ; yi /= val ; zi /= val ; /* normalize j axis */ val = sqrt( xj*xj + yj*yj + zj*zj ) ; if( val == 0.0 ) return ; /* stupid input */ xj /= val ; yj /= val ; zj /= val ; /* orthogonalize j axis to i axis, if needed */ val = xi*xj + yi*yj + zi*zj ; /* dot product between i and j */ if( fabs(val) > 1.e-4 ){ xj -= val*xi ; yj -= val*yi ; zj -= val*zi ; val = sqrt( xj*xj + yj*yj + zj*zj ) ; /* must renormalize */ if( val == 0.0 ) return ; /* j was parallel to i? */ xj /= val ; yj /= val ; zj /= val ; } /* normalize k axis; if it is zero, make it the cross product i x j */ val = sqrt( xk*xk + yk*yk + zk*zk ) ; if( val == 0.0 ){ xk = yi*zj-zi*yj; yk = zi*xj-zj*xi ; zk=xi*yj-yi*xj ; } else { xk /= val ; yk /= val ; zk /= val ; } /* orthogonalize k to i */ val = xi*xk + yi*yk + zi*zk ; /* dot product between i and k */ if( fabs(val) > 1.e-4 ){ xk -= val*xi ; yk -= val*yi ; zk -= val*zi ; val = sqrt( xk*xk + yk*yk + zk*zk ) ; if( val == 0.0 ) return ; /* bad */ xk /= val ; yk /= val ; zk /= val ; } /* orthogonalize k to j */ val = xj*xk + yj*yk + zj*zk ; /* dot product between j and k */ if( fabs(val) > 1.e-4 ){ xk -= val*xj ; yk -= val*yj ; zk -= val*zj ; val = sqrt( xk*xk + yk*yk + zk*zk ) ; if( val == 0.0 ) return ; /* bad */ xk /= val ; yk /= val ; zk /= val ; } Q.m[0][0] = xi ; Q.m[0][1] = xj ; Q.m[0][2] = xk ; Q.m[1][0] = yi ; Q.m[1][1] = yj ; Q.m[1][2] = yk ; Q.m[2][0] = zi ; Q.m[2][1] = zj ; Q.m[2][2] = zk ; /* at this point, Q is the rotation matrix from the (i,j,k) to (x,y,z) axes */ detQ = nifti_mat33_determ( Q ) ; if( detQ == 0.0 ) return ; /* shouldn't happen unless user is a DUFIS */ /* Build and test all possible +1/-1 coordinate permutation matrices P; then find the P such that the rotation matrix M=PQ is closest to the identity, in the sense of M having the smallest total rotation angle. */ /* Despite the formidable looking 6 nested loops, there are only 3*3*3*2*2*2 = 216 passes, which will run very quickly. */ vbest = -666.0 ; ibest=pbest=qbest=rbest=1 ; jbest=2 ; kbest=3 ; for( i=1 ; i <= 3 ; i++ ){ /* i = column number to use for row #1 */ for( j=1 ; j <= 3 ; j++ ){ /* j = column number to use for row #2 */ if( i == j ) continue ; for( k=1 ; k <= 3 ; k++ ){ /* k = column number to use for row #3 */ if( i == k || j == k ) continue ; P.m[0][0] = P.m[0][1] = P.m[0][2] = P.m[1][0] = P.m[1][1] = P.m[1][2] = P.m[2][0] = P.m[2][1] = P.m[2][2] = 0.0 ; for( p=-1 ; p <= 1 ; p+=2 ){ /* p,q,r are -1 or +1 */ for( q=-1 ; q <= 1 ; q+=2 ){ /* and go into rows #1,2,3 */ for( r=-1 ; r <= 1 ; r+=2 ){ P.m[0][i-1] = p ; P.m[1][j-1] = q ; P.m[2][k-1] = r ; detP = nifti_mat33_determ(P) ; /* sign of permutation */ if( detP * detQ <= 0.0 ) continue ; /* doesn't match sign of Q */ M = nifti_mat33_mul(P,Q) ; /* angle of M rotation = 2.0*acos(0.5*sqrt(1.0+trace(M))) */ /* we want largest trace(M) == smallest angle == M nearest to I */ val = M.m[0][0] + M.m[1][1] + M.m[2][2] ; /* trace */ if( val > vbest ){ vbest = val ; ibest = i ; jbest = j ; kbest = k ; pbest = p ; qbest = q ; rbest = r ; } }}}}}} /* At this point ibest is 1 or 2 or 3; pbest is -1 or +1; etc. The matrix P that corresponds is the best permutation approximation to Q-inverse; that is, P (approximately) takes (x,y,z) coordinates to the (i,j,k) axes. For example, the first row of P (which contains pbest in column ibest) determines the way the i axis points relative to the anatomical (x,y,z) axes. If ibest is 2, then the i axis is along the y axis, which is direction P2A (if pbest > 0) or A2P (if pbest < 0). So, using ibest and pbest, we can assign the output code for the i axis. Mutatis mutandis for the j and k axes, of course. */ switch( ibest*pbest ){ case 1: i = NIFTI_L2R ; break ; case -1: i = NIFTI_R2L ; break ; case 2: i = NIFTI_P2A ; break ; case -2: i = NIFTI_A2P ; break ; case 3: i = NIFTI_I2S ; break ; case -3: i = NIFTI_S2I ; break ; } switch( jbest*qbest ){ case 1: j = NIFTI_L2R ; break ; case -1: j = NIFTI_R2L ; break ; case 2: j = NIFTI_P2A ; break ; case -2: j = NIFTI_A2P ; break ; case 3: j = NIFTI_I2S ; break ; case -3: j = NIFTI_S2I ; break ; } switch( kbest*rbest ){ case 1: k = NIFTI_L2R ; break ; case -1: k = NIFTI_R2L ; break ; case 2: k = NIFTI_P2A ; break ; case -2: k = NIFTI_A2P ; break ; case 3: k = NIFTI_I2S ; break ; case -3: k = NIFTI_S2I ; break ; } *icod = i ; *jcod = j ; *kcod = k ; return ; } /*---------------------------------------------------------------------------*/ /* Routines to swap byte arrays in various ways: - 2 at a time: ab -> ba [short] - 4 at a time: abcd -> dcba [int, float] - 8 at a time: abcdDCBA -> ABCDdcba [long long, double] - 16 at a time: abcdefghHGFEDCBA -> ABCDEFGHhgfedcba [long double] -----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /*! swap each byte pair from the given list of n pairs * * Due to alignment of structures at some architectures (e.g. on ARM), * stick to char varaibles. * Fixes http://bugs.debian.org/446893 Yaroslav * *//*--------------------------------------------------------------------*/ void nifti_swap_2bytes( size_t n , void *ar ) /* 2 bytes at a time */ { register size_t ii ; unsigned char * cp1 = (unsigned char *)ar, * cp2 ; unsigned char tval; for( ii=0 ; ii < n ; ii++ ){ cp2 = cp1 + 1; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1 += 2; } return ; } /*----------------------------------------------------------------------*/ /*! swap 4 bytes at a time from the given list of n sets of 4 bytes *//*--------------------------------------------------------------------*/ void nifti_swap_4bytes( size_t n , void *ar ) /* 4 bytes at a time */ { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+3; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp1++; cp2--; tval = *cp1; *cp1 = *cp2; *cp2 = tval; cp0 += 4; } return ; } /*----------------------------------------------------------------------*/ /*! swap 8 bytes at a time from the given list of n sets of 8 bytes * * perhaps use this style for the general Nbytes, as Yaroslav suggests *//*--------------------------------------------------------------------*/ void nifti_swap_8bytes( size_t n , void *ar ) /* 8 bytes at a time */ { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+7; while ( cp2 > cp1 ) /* unroll? */ { tval = *cp1 ; *cp1 = *cp2 ; *cp2 = tval ; cp1++; cp2--; } cp0 += 8; } return ; } /*----------------------------------------------------------------------*/ /*! swap 16 bytes at a time from the given list of n sets of 16 bytes *//*--------------------------------------------------------------------*/ void nifti_swap_16bytes( size_t n , void *ar ) /* 16 bytes at a time */ { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+15; while ( cp2 > cp1 ) { tval = *cp1 ; *cp1 = *cp2 ; *cp2 = tval ; cp1++; cp2--; } cp0 += 16; } return ; } #if 0 /* not important: save for version update 6 Jul 2010 [rickr] */ /*----------------------------------------------------------------------*/ /*! generic: swap siz bytes at a time from the given list of n sets *//*--------------------------------------------------------------------*/ void nifti_swap_bytes( size_t n , int siz , void *ar ) { register size_t ii ; unsigned char * cp0 = (unsigned char *)ar, * cp1, * cp2 ; register unsigned char tval ; for( ii=0 ; ii < n ; ii++ ){ cp1 = cp0; cp2 = cp0+(siz-1); while ( cp2 > cp1 ) { tval = *cp1 ; *cp1 = *cp2 ; *cp2 = tval ; cp1++; cp2--; } cp0 += siz; } return ; } #endif /*---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /*! based on siz, call the appropriate nifti_swap_Nbytes() function *//*--------------------------------------------------------------------*/ void nifti_swap_Nbytes( size_t n , int siz , void *ar ) /* subsuming case */ { switch( siz ){ case 2: nifti_swap_2bytes ( n , ar ) ; break ; case 4: nifti_swap_4bytes ( n , ar ) ; break ; case 8: nifti_swap_8bytes ( n , ar ) ; break ; case 16: nifti_swap_16bytes( n , ar ) ; break ; default: /* nifti_swap_bytes ( n , siz, ar ) ; */ fprintf(stderr,"** NIfTI: cannot swap in %d byte blocks\n", siz); break ; } return ; } /*-------------------------------------------------------------------------*/ /*! Byte swap NIFTI-1 file header in various places and ways. If is_nifti, swap all (even UNUSED) fields of NIfTI header. Else, swap as a nifti_analyze75 struct. *//*---------------------------------------------------------------------- */ void swap_nifti_header( struct nifti_1_header *h , int is_nifti ) { /* if ANALYZE, swap as such and return */ if( ! is_nifti ) { nifti_swap_as_analyze((nifti_analyze75 *)h); return; } /* otherwise, swap all NIFTI fields */ nifti_swap_4bytes(1, &h->sizeof_hdr); nifti_swap_4bytes(1, &h->extents); nifti_swap_2bytes(1, &h->session_error); nifti_swap_2bytes(8, h->dim); nifti_swap_4bytes(1, &h->intent_p1); nifti_swap_4bytes(1, &h->intent_p2); nifti_swap_4bytes(1, &h->intent_p3); nifti_swap_2bytes(1, &h->intent_code); nifti_swap_2bytes(1, &h->datatype); nifti_swap_2bytes(1, &h->bitpix); nifti_swap_2bytes(1, &h->slice_start); nifti_swap_4bytes(8, h->pixdim); nifti_swap_4bytes(1, &h->vox_offset); nifti_swap_4bytes(1, &h->scl_slope); nifti_swap_4bytes(1, &h->scl_inter); nifti_swap_2bytes(1, &h->slice_end); nifti_swap_4bytes(1, &h->cal_max); nifti_swap_4bytes(1, &h->cal_min); nifti_swap_4bytes(1, &h->slice_duration); nifti_swap_4bytes(1, &h->toffset); nifti_swap_4bytes(1, &h->glmax); nifti_swap_4bytes(1, &h->glmin); nifti_swap_2bytes(1, &h->qform_code); nifti_swap_2bytes(1, &h->sform_code); nifti_swap_4bytes(1, &h->quatern_b); nifti_swap_4bytes(1, &h->quatern_c); nifti_swap_4bytes(1, &h->quatern_d); nifti_swap_4bytes(1, &h->qoffset_x); nifti_swap_4bytes(1, &h->qoffset_y); nifti_swap_4bytes(1, &h->qoffset_z); nifti_swap_4bytes(4, h->srow_x); nifti_swap_4bytes(4, h->srow_y); nifti_swap_4bytes(4, h->srow_z); return ; } /*-------------------------------------------------------------------------*/ /*! Byte swap as an ANALYZE 7.5 header * * return non-zero on failure *//*---------------------------------------------------------------------- */ int nifti_swap_as_analyze( nifti_analyze75 * h ) { if( !h ) return 1; nifti_swap_4bytes(1, &h->sizeof_hdr); nifti_swap_4bytes(1, &h->extents); nifti_swap_2bytes(1, &h->session_error); nifti_swap_2bytes(8, h->dim); nifti_swap_2bytes(1, &h->unused8); nifti_swap_2bytes(1, &h->unused9); nifti_swap_2bytes(1, &h->unused10); nifti_swap_2bytes(1, &h->unused11); nifti_swap_2bytes(1, &h->unused12); nifti_swap_2bytes(1, &h->unused13); nifti_swap_2bytes(1, &h->unused14); nifti_swap_2bytes(1, &h->datatype); nifti_swap_2bytes(1, &h->bitpix); nifti_swap_2bytes(1, &h->dim_un0); nifti_swap_4bytes(8, h->pixdim); nifti_swap_4bytes(1, &h->vox_offset); nifti_swap_4bytes(1, &h->funused1); nifti_swap_4bytes(1, &h->funused2); nifti_swap_4bytes(1, &h->funused3); nifti_swap_4bytes(1, &h->cal_max); nifti_swap_4bytes(1, &h->cal_min); nifti_swap_4bytes(1, &h->compressed); nifti_swap_4bytes(1, &h->verified); nifti_swap_4bytes(1, &h->glmax); nifti_swap_4bytes(1, &h->glmin); nifti_swap_4bytes(1, &h->views); nifti_swap_4bytes(1, &h->vols_added); nifti_swap_4bytes(1, &h->start_field); nifti_swap_4bytes(1, &h->field_skip); nifti_swap_4bytes(1, &h->omax); nifti_swap_4bytes(1, &h->omin); nifti_swap_4bytes(1, &h->smax); nifti_swap_4bytes(1, &h->smin); return 0; } /*-------------------------------------------------------------------------*/ /*! OLD VERSION of swap_nifti_header (left for undo/compare operations) Byte swap NIFTI-1 file header in various places and ways. If is_nifti is nonzero, will also swap the NIFTI-specific components of the header; otherwise, only the components common to NIFTI and ANALYZE will be swapped. *//*---------------------------------------------------------------------- */ void old_swap_nifti_header( struct nifti_1_header *h , int is_nifti ) { /* this stuff is always present, for ANALYZE and NIFTI */ swap_4(h->sizeof_hdr) ; nifti_swap_2bytes( 8 , h->dim ) ; nifti_swap_4bytes( 8 , h->pixdim ) ; swap_2(h->datatype) ; swap_2(h->bitpix) ; swap_4(h->vox_offset); swap_4(h->cal_max); swap_4(h->cal_min); /* this stuff is NIFTI specific */ if( is_nifti ){ swap_4(h->intent_p1); swap_4(h->intent_p2); swap_4(h->intent_p3); swap_2(h->intent_code); swap_2(h->slice_start); swap_2(h->slice_end); swap_4(h->scl_slope); swap_4(h->scl_inter); swap_4(h->slice_duration); swap_4(h->toffset); swap_2(h->qform_code); swap_2(h->sform_code); swap_4(h->quatern_b); swap_4(h->quatern_c); swap_4(h->quatern_d); swap_4(h->qoffset_x); swap_4(h->qoffset_y); swap_4(h->qoffset_z); nifti_swap_4bytes(4,h->srow_x); nifti_swap_4bytes(4,h->srow_y); nifti_swap_4bytes(4,h->srow_z); } return ; } #define USE_STAT #ifdef USE_STAT /*---------------------------------------------------------------------------*/ /* Return the file length (0 if file not found or has no contents). This is a Unix-specific function, since it uses stat(). -----------------------------------------------------------------------------*/ #include #include /*---------------------------------------------------------------------------*/ /*! return the size of a file, in bytes \return size of file on success, -1 on error or no file changed to return int, -1 means no file or error 20 Dec 2004 [rickr] *//*-------------------------------------------------------------------------*/ int nifti_get_filesize( const char *pathname ) { struct stat buf ; int ii ; if( pathname == NULL || *pathname == '\0' ) return -1 ; ii = stat( pathname , &buf ); if( ii != 0 ) return -1 ; return (unsigned int)buf.st_size ; } #else /*---------- non-Unix version of the above, less efficient -----------*/ int nifti_get_filesize( const char *pathname ) { znzFile fp ; int len ; if( pathname == NULL || *pathname == '\0' ) return -1 ; fp = znzopen(pathname,"rb",0); if( znz_isnull(fp) ) return -1 ; znzseek(fp,0L,SEEK_END) ; len = znztell(fp) ; znzclose(fp) ; return len ; } #endif /* USE_STAT */ /*----------------------------------------------------------------------*/ /*! return the total volume size, in bytes This is computed as nvox * nbyper. *//*--------------------------------------------------------------------*/ size_t nifti_get_volsize(const nifti_image *nim) { return nim->nbyper * nim->nvox ; /* total bytes */ } /*--------------------------------------------------------------------------*/ /* Support functions for filenames in read and write - allows for gzipped files */ /*----------------------------------------------------------------------*/ /*! simple check for file existence \return 1 on existence, 0 otherwise *//*--------------------------------------------------------------------*/ static int nifti_fileexists(const char* fname) { znzFile fp; fp = znzopen( fname , "rb" , 1 ) ; if( !znz_isnull(fp) ) { znzclose(fp); return 1; } return 0; /* fp is NULL */ } /*----------------------------------------------------------------------*/ /*! return whether the filename is valid Note: uppercase extensions are now valid. 27 Apr 2009 [rickr] The name is considered valid if the file basename has length greater than zero, AND one of the valid nifti extensions is provided. fname input | return | =============================== "myimage" | 0 | "myimage.tif" | 0 | "myimage.tif.gz" | 0 | "myimage.nii" | 1 | ".nii" | 0 | ".myhiddenimage" | 0 | ".myhiddenimage.nii" | 1 | *//*--------------------------------------------------------------------*/ int nifti_is_complete_filename(const char* fname) { const char * ext; /* check input file(s) for sanity */ if( fname == NULL || *fname == '\0' ){ if ( g_opts.debug > 1 ) fprintf(stderr,"-- empty filename in nifti_validfilename()\n"); return 0; } ext = nifti_find_file_extension(fname); if ( ext == NULL ) { /*Invalid extension given */ if ( g_opts.debug > 0 ) fprintf(stderr,"-- no nifti valid extension for filename '%s'\n", fname); return 0; } if ( ext && ext == fname ) { /* then no filename prefix */ if ( g_opts.debug > 0 ) fprintf(stderr,"-- no prefix for filename '%s'\n", fname); return 0; } return 1; } /*----------------------------------------------------------------------*/ /*! return whether the filename is valid Allow uppercase extensions as valid. 27 Apr 2009 [rickr] Any .gz extension case must match the base extension case. The name is considered valid if its length is positive, excluding any nifti filename extension. fname input | return | result of nifti_makebasename ==================================================================== "myimage" | 1 | "myimage" "myimage.tif" | 1 | "myimage.tif" "myimage.tif.gz" | 1 | "myimage.tif" "myimage.nii" | 1 | "myimage" ".nii" | 0 | ".myhiddenimage" | 1 | ".myhiddenimage" ".myhiddenimage.nii | 1 | ".myhiddenimage" *//*--------------------------------------------------------------------*/ int nifti_validfilename(const char* fname) { const char * ext; /* check input file(s) for sanity */ if( fname == NULL || *fname == '\0' ){ if ( g_opts.debug > 1 ) fprintf(stderr,"-- empty filename in nifti_validfilename()\n"); return 0; } ext = nifti_find_file_extension(fname); if ( ext && ext == fname ) { /* then no filename prefix */ if ( g_opts.debug > 0 ) fprintf(stderr,"-- no prefix for filename '%s'\n", fname); return 0; } return 1; } /*----------------------------------------------------------------------*/ /*! check the end of the filename for a valid nifti extension Valid extensions are currently .nii, .hdr, .img, .nia, or any of them followed by .gz. Note that '.' is part of the extension. Uppercase extensions are also valid, but not mixed case. \return a pointer to the extension (within the filename), or NULL *//*--------------------------------------------------------------------*/ const char * nifti_find_file_extension( const char * name ) { const char * ext; char extcopy[MAX_EXT_LEN + 1]; int len; char extnii[MAX_EXT_LEN + 1] = ".nii"; /* modifiable, for possible uppercase */ char exthdr[MAX_EXT_LEN + 1] = ".hdr"; /* (leave space for .gz) */ char extimg[MAX_EXT_LEN + 1] = ".img"; char extnia[MAX_EXT_LEN + 1] = ".nia"; #ifdef HAVE_ZLIB const char extgz[] = ".gz"; #endif char * elist[NUM_EXT] = { NULL, NULL, NULL, NULL}; /* stupid compiler... */ elist[0] = extnii; elist[1] = exthdr; elist[2] = extimg; elist[3] = extnia; if ( ! name ) return NULL; len = (int)strlen(name); if ( len < MIN_EXT_LEN ) return NULL; ext = name + len - MIN_EXT_LEN; /* make manipulation copy, and possibly convert to lowercase */ strncpy(extcopy, ext, MAX_EXT_LEN); extcopy[MAX_EXT_LEN] = '\0'; if( g_opts.allow_upper_fext ) make_lowercase(extcopy); /* if it look like a basic extension, fail or return it */ if( compare_strlist(extcopy, elist, NUM_EXT) >= 0 ) { if( is_mixedcase(ext) ) { fprintf(stderr,"** mixed case extension '%s' is not valid\n", ext); return NULL; } else return ext; } #ifdef HAVE_ZLIB if ( len < MAX_EXT_LEN ) return NULL; ext = name + len - MAX_EXT_LEN; /* make manipulation copy, and possibly convert to lowercase */ strncpy(extcopy, ext, MAX_EXT_LEN); extcopy[MAX_EXT_LEN] = '\0'; if( g_opts.allow_upper_fext ) make_lowercase(extcopy); /* go after .gz extensions using the modifiable strings */ strcat(elist[0], extgz); strcat(elist[1], extgz); strcat(elist[2], extgz); if( compare_strlist(extcopy, elist, NUM_EXT - 1) >= 0 ) { if( is_mixedcase(ext) ) { fprintf(stderr,"** mixed case extension '%s' is not valid\n", ext); return NULL; } else return ext; } #endif if( g_opts.debug > 1 ) fprintf(stderr,"** find_file_ext: failed for name '%s'\n", name); return NULL; } /*----------------------------------------------------------------------*/ /*! return whether the filename ends in ".gz" *//*--------------------------------------------------------------------*/ int nifti_is_gzfile(const char* fname) { /* return true if the filename ends with .gz */ if (fname == NULL) { return 0; } #ifdef HAVE_ZLIB { /* just so len doesn't generate compile warning */ int len; len = (int)strlen(fname); if (len < 3) return 0; /* so we don't search before the name */ if (fileext_compare(fname + strlen(fname) - 3,".gz")==0) { return 1; } } #endif return 0; } /*----------------------------------------------------------------------*/ /*! return whether the given library was compiled with HAVE_ZLIB set *//*--------------------------------------------------------------------*/ int nifti_compiled_with_zlib(void) { #ifdef HAVE_ZLIB return 1; #else return 0; #endif } /*----------------------------------------------------------------------*/ /*! duplicate the filename, while clearing any extension This allocates memory for basename which should eventually be freed. *//*--------------------------------------------------------------------*/ char * nifti_makebasename(const char* fname) { char *basename; const char *ext; basename=nifti_strdup(fname); ext = nifti_find_file_extension(basename); if ( ext != NULL ) basename[ext - basename] = '\0'; /* clear out extension */ return basename; /* in either case */ } /*----------------------------------------------------------------------*/ /*! set nifti's global debug level, for status reporting - 0 : quiet, nothing is printed to the terminal, but errors - 1 : normal execution (the default) - 2, 3 : more details *//*--------------------------------------------------------------------*/ void nifti_set_debug_level( int level ) { g_opts.debug = level; } /*----------------------------------------------------------------------*/ /*! set nifti's global skip_blank_ext flag 5 Sep 2006 [rickr] explicitly set to 0 or 1 *//*--------------------------------------------------------------------*/ void nifti_set_skip_blank_ext( int skip ) { g_opts.skip_blank_ext = skip ? 1 : 0; } /*----------------------------------------------------------------------*/ /*! set nifti's global allow_upper_fext flag 28 Apr 2009 [rickr] explicitly set to 0 or 1 *//*--------------------------------------------------------------------*/ void nifti_set_allow_upper_fext( int allow ) { g_opts.allow_upper_fext = allow ? 1 : 0; } /*----------------------------------------------------------------------*/ /*! check current directory for existing header file \return filename of header on success and NULL if no appropriate file could be found If fname has an uppercase extension, check for uppercase files. NB: it allocates memory for hdrname which should be freed when no longer required *//*-------------------------------------------------------------------*/ char * nifti_findhdrname(const char* fname) { char *basename, *hdrname; const char *ext; char elist[2][5] = { ".hdr", ".nii" }; char extzip[4] = ".gz"; int efirst = 1; /* init to .nii extension */ int eisupper = 0; /* init to lowercase extensions */ /**- check input file(s) for sanity */ if( !nifti_validfilename(fname) ) return NULL; basename = nifti_makebasename(fname); if( !basename ) return NULL; /* only on string alloc failure */ /**- return filename if it has a valid extension and exists (except if it is an .img file (and maybe .gz)) */ ext = nifti_find_file_extension(fname); if( ext ) eisupper = is_uppercase(ext); /* do we look for uppercase? */ /* if the file exists and is a valid header name (not .img), return it */ if ( ext && nifti_fileexists(fname) ) { /* allow for uppercase extension */ if ( fileext_n_compare(ext,".img",4) != 0 ){ hdrname = nifti_strdup(fname); free(basename); return hdrname; } else efirst = 0; /* note for below */ } /* So the requested name is a basename, contains .img, or does not exist. */ /* In any case, use basename. */ /**- if .img, look for .hdr, .hdr.gz, .nii, .nii.gz, in that order */ /**- else, look for .nii, .nii.gz, .hdr, .hdr.gz, in that order */ /* if we get more extension choices, this could be a loop */ /* note: efirst is 0 in the case of ".img" */ /* if the user passed an uppercase entension (.IMG), search for uppercase */ if( eisupper ) { make_uppercase(elist[0]); make_uppercase(elist[1]); make_uppercase(extzip); } hdrname = (char *)calloc(sizeof(char),strlen(basename)+MAX_EXT_LEN+1); if( !hdrname ){ fprintf(stderr,"** nifti_findhdrname: failed to alloc hdrname\n"); free(basename); return NULL; } strcpy(hdrname,basename); strcat(hdrname,elist[efirst]); if (nifti_fileexists(hdrname)) { free(basename); return hdrname; } #ifdef HAVE_ZLIB strcat(hdrname,extzip); if (nifti_fileexists(hdrname)) { free(basename); return hdrname; } #endif /* okay, try the other possibility */ efirst = 1 - efirst; strcpy(hdrname,basename); strcat(hdrname,elist[efirst]); if (nifti_fileexists(hdrname)) { free(basename); return hdrname; } #ifdef HAVE_ZLIB strcat(hdrname,extzip); if (nifti_fileexists(hdrname)) { free(basename); return hdrname; } #endif /**- if nothing has been found, return NULL */ free(basename); free(hdrname); return NULL; } /*------------------------------------------------------------------------*/ /*! check current directory for existing image file \param fname filename to check for \param nifti_type nifti_type for dataset - this determines whether to first check for ".nii" or ".img" (since both may exist) \return filename of data/img file on success and NULL if no appropriate file could be found If fname has a valid, uppercase extension, apply all extensions as uppercase. NB: it allocates memory for the image filename, which should be freed when no longer required *//*---------------------------------------------------------------------*/ char * nifti_findimgname(const char* fname , int nifti_type) { /* store all extensions as strings, in case we need to go uppercase */ char *basename, *imgname, elist[2][5] = { ".nii", ".img" }; char extzip[4] = ".gz"; char extnia[5] = ".nia"; const char *ext; int first; /* first extension to use */ /* check input file(s) for sanity */ if( !nifti_validfilename(fname) ) return NULL; basename = nifti_makebasename(fname); imgname = (char *)calloc(sizeof(char),strlen(basename)+MAX_EXT_LEN+1); if( !imgname ){ fprintf(stderr,"** nifti_findimgname: failed to alloc imgname\n"); free(basename); return NULL; } /* if we are looking for uppercase, apply the fact now */ ext = nifti_find_file_extension(fname); if( ext && is_uppercase(ext) ) { make_uppercase(elist[0]); make_uppercase(elist[1]); make_uppercase(extzip); make_uppercase(extnia); } /* only valid extension for ASCII type is .nia, handle first */ if( nifti_type == NIFTI_FTYPE_ASCII ){ strcpy(imgname,basename); strcat(imgname,extnia); if (nifti_fileexists(imgname)) { free(basename); return imgname; } } else { /**- test for .nii and .img (don't assume input type from image type) */ /**- if nifti_type = 1, check for .nii first, else .img first */ /* if we get 3 or more extensions, can make a loop here... */ if (nifti_type == NIFTI_FTYPE_NIFTI1_1) first = 0; /* should match .nii */ else first = 1; /* should match .img */ strcpy(imgname,basename); strcat(imgname,elist[first]); if (nifti_fileexists(imgname)) { free(basename); return imgname; } #ifdef HAVE_ZLIB /* then also check for .gz */ strcat(imgname,extzip); if (nifti_fileexists(imgname)) { free(basename); return imgname; } #endif /* failed to find image file with expected extension, try the other */ strcpy(imgname,basename); strcat(imgname,elist[1-first]); /* can do this with only 2 choices */ if (nifti_fileexists(imgname)) { free(basename); return imgname; } #ifdef HAVE_ZLIB /* then also check for .gz */ strcat(imgname,extzip); if (nifti_fileexists(imgname)) { free(basename); return imgname; } #endif } /**- if nothing has been found, return NULL */ free(basename); free(imgname); return NULL; } /*----------------------------------------------------------------------*/ /*! creates a filename for storing the header, based on nifti_type \param prefix - this will be copied before the suffix is added \param nifti_type - determines the extension, unless one is in prefix \param check - check for existence (fail condition) \param comp - add .gz for compressed name Note that if prefix provides a file suffix, nifti_type is not used. NB: this allocates memory which should be freed \sa nifti_set_filenames *//*-------------------------------------------------------------------*/ char * nifti_makehdrname(const char * prefix, int nifti_type, int check, int comp) { char * iname; const char * ext; char extnii[5] = ".nii"; /* modifiable, for possible uppercase */ char exthdr[5] = ".hdr"; char extimg[5] = ".img"; char extnia[5] = ".nia"; char extgz[5] = ".gz"; if( !nifti_validfilename(prefix) ) return NULL; /* add space for extension, optional ".gz", and null char */ iname = (char *)calloc(sizeof(char),strlen(prefix)+MAX_EXT_LEN+1); if( !iname ){ fprintf(stderr,"** small malloc failure!\n"); return NULL; } strcpy(iname, prefix); /* use any valid extension */ if( (ext = nifti_find_file_extension(iname)) != NULL ){ /* if uppercase, convert all extensions */ if( is_uppercase(ext) ) { make_uppercase(extnii); make_uppercase(exthdr); make_uppercase(extimg); make_uppercase(extnia); make_uppercase(extgz); } if( strncmp(ext,extimg,MIN_EXT_LEN) == 0 ) memcpy(&iname[ext-iname],exthdr,MIN_EXT_LEN); /* then convert img name to hdr */ } /* otherwise, make one up */ else if( nifti_type == NIFTI_FTYPE_NIFTI1_1 ) strcat(iname, extnii); else if( nifti_type == NIFTI_FTYPE_ASCII ) strcat(iname, extnia); else strcat(iname, exthdr); #ifdef HAVE_ZLIB /* if compression is requested, make sure of suffix */ if( comp && (!ext || !strstr(iname,extgz)) ) strcat(iname,extgz); #endif /* check for existence failure */ if( check && nifti_fileexists(iname) ){ fprintf(stderr,"** failure: header file '%s' already exists\n",iname); free(iname); return NULL; } if(g_opts.debug > 2) fprintf(stderr,"+d made header filename '%s'\n", iname); return iname; } /*----------------------------------------------------------------------*/ /*! creates a filename for storing the image, based on nifti_type \param prefix - this will be copied before the suffix is added \param nifti_type - determines the extension, unless provided by prefix \param check - check for existence (fail condition) \param comp - add .gz for compressed name Note that if prefix provides a file suffix, nifti_type is not used. NB: it allocates memory which should be freed \sa nifti_set_filenames *//*-------------------------------------------------------------------*/ char * nifti_makeimgname(const char * prefix, int nifti_type, int check, int comp) { char * iname; const char * ext; char extnii[5] = ".nii"; /* modifiable, for possible uppercase */ char exthdr[5] = ".hdr"; char extimg[5] = ".img"; char extnia[5] = ".nia"; char extgz[5] = ".gz"; if( !nifti_validfilename(prefix) ) return NULL; /* add space for extension, optional ".gz", and null char */ iname = (char *)calloc(sizeof(char),strlen(prefix)+MAX_EXT_LEN+1); if( !iname ){ fprintf(stderr,"** small malloc failure!\n"); return NULL; } strcpy(iname, prefix); /* use any valid extension */ if( (ext = nifti_find_file_extension(iname)) != NULL ){ /* if uppercase, convert all extensions */ if( is_uppercase(ext) ) { make_uppercase(extnii); make_uppercase(exthdr); make_uppercase(extimg); make_uppercase(extnia); make_uppercase(extgz); } if( strncmp(ext,exthdr,MIN_EXT_LEN) == 0 ) memcpy(&iname[ext-iname],extimg,MIN_EXT_LEN); /* then convert hdr name to img */ } /* otherwise, make one up */ else if( nifti_type == NIFTI_FTYPE_NIFTI1_1 ) strcat(iname, extnii); else if( nifti_type == NIFTI_FTYPE_ASCII ) strcat(iname, extnia); else strcat(iname, extimg); #ifdef HAVE_ZLIB /* if compression is requested, make sure of suffix */ if( comp && (!ext || !strstr(iname,extgz)) ) strcat(iname,extgz); #endif /* check for existence failure */ if( check && nifti_fileexists(iname) ){ fprintf(stderr,"** failure: image file '%s' already exists\n",iname); free(iname); return NULL; } if( g_opts.debug > 2 ) fprintf(stderr,"+d made image filename '%s'\n",iname); return iname; } /*----------------------------------------------------------------------*/ /*! create and set new filenames, based on prefix and image type \param nim pointer to nifti_image in which to set filenames \param prefix (required) prefix for output filenames \param check check for previous existence of filename (existence is an error condition) \param set_byte_order flag to set nim->byteorder here (this is probably a logical place to do so) \return 0 on successful update \warning this will free() any existing names and create new ones \sa nifti_makeimgname, nifti_makehdrname, nifti_type_and_names_match *//*--------------------------------------------------------------------*/ int nifti_set_filenames( nifti_image * nim, const char * prefix, int check, int set_byte_order ) { int comp = nifti_is_gzfile(prefix); if( !nim || !prefix ){ fprintf(stderr,"** nifti_set_filenames, bad params %p, %p\n", (void *)nim, (const void *)prefix); return -1; } if( g_opts.debug > 1 ) fprintf(stderr,"+d modifying output filenames using prefix %s\n", prefix); if( nim->fname ) free(nim->fname); if( nim->iname ) free(nim->iname); nim->fname = nifti_makehdrname(prefix, nim->nifti_type, check, comp); nim->iname = nifti_makeimgname(prefix, nim->nifti_type, check, comp); if( !nim->fname || !nim->iname ){ LNI_FERR("nifti_set_filename","failed to set prefix for",prefix); return -1; } if( set_byte_order ) nim->byteorder = nifti_short_order() ; if( nifti_set_type_from_names(nim) < 0 ) return -1; if( g_opts.debug > 2 ) fprintf(stderr,"+d have new filenames %s and %s\n",nim->fname,nim->iname); return 0; } /*--------------------------------------------------------------------------*/ /*! check whether nifti_type matches fname and iname for the nifti_image - if type 0 or 2, expect .hdr/.img pair - if type 1, expect .nii (and names must match) \param nim given nifti_image \param show_warn if set, print a warning message for any mis-match \return - 1 if the values seem to match - 0 if there is a mis-match - -1 if there is not sufficient information to create file(s) \sa NIFTI_FTYPE_* codes in nifti1_io.h \sa nifti_set_type_from_names, is_valid_nifti_type *//*------------------------------------------------------------------------*/ int nifti_type_and_names_match( nifti_image * nim, int show_warn ) { char func[] = "nifti_type_and_names_match"; const char * ext_h, * ext_i; /* header and image filename extensions */ int errs = 0; /* error counter */ /* sanity checks */ if( !nim ){ if( show_warn ) fprintf(stderr,"** %s: missing nifti_image\n", func); return -1; } if( !nim->fname ){ if( show_warn ) fprintf(stderr,"** %s: missing header filename\n", func); errs++; } if( !nim->iname ){ if( show_warn ) fprintf(stderr,"** %s: missing image filename\n", func); errs++; } if( !is_valid_nifti_type(nim->nifti_type) ){ if( show_warn ) fprintf(stderr,"** %s: bad nifti_type %d\n", func, nim->nifti_type); errs++; } if( errs ) return -1; /* then do not proceed */ /* get pointers to extensions */ ext_h = nifti_find_file_extension( nim->fname ); ext_i = nifti_find_file_extension( nim->iname ); /* check for filename extensions */ if( !ext_h ){ if( show_warn ) fprintf(stderr,"-d missing NIFTI extension in header filename, %s\n", nim->fname); errs++; } if( !ext_i ){ if( show_warn ) fprintf(stderr,"-d missing NIFTI extension in image filename, %s\n", nim->iname); errs++; } if( errs ) return 0; /* do not proceed, but this is just a mis-match */ /* general tests */ if( nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ){ /* .nii */ if( fileext_n_compare(ext_h,".nii",MIN_EXT_LEN) ) { if( show_warn ) fprintf(stderr, "-d NIFTI_FTYPE 1, but no .nii extension in header filename, %s\n", nim->fname); errs++; } if( fileext_n_compare(ext_i,".nii",MIN_EXT_LEN) ) { if( show_warn ) fprintf(stderr, "-d NIFTI_FTYPE 1, but no .nii extension in image filename, %s\n", nim->iname); errs++; } if( strcmp(nim->fname, nim->iname) != 0 ){ if( show_warn ) fprintf(stderr, "-d NIFTI_FTYPE 1, but header and image filenames differ: %s, %s\n", nim->fname, nim->iname); errs++; } } else if( (nim->nifti_type == NIFTI_FTYPE_NIFTI1_2) || /* .hdr/.img */ (nim->nifti_type == NIFTI_FTYPE_ANALYZE) ) { if( fileext_n_compare(ext_h,".hdr",MIN_EXT_LEN) != 0 ){ if( show_warn ) fprintf(stderr,"-d no '.hdr' extension, but NIFTI type is %d, %s\n", nim->nifti_type, nim->fname); errs++; } if( fileext_n_compare(ext_i,".img",MIN_EXT_LEN) != 0 ){ if( show_warn ) fprintf(stderr,"-d no '.img' extension, but NIFTI type is %d, %s\n", nim->nifti_type, nim->iname); errs++; } } /* ignore any other nifti_type */ return 1; } /* like strcmp, but also check against capitalization of known_ext * (test as local string, with max length 7) */ static int fileext_compare(const char * test_ext, const char * known_ext) { char caps[MAX_EXT_LEN + 1] = ""; int c, cmp, len; /* if anything odd, use default */ if( !test_ext || !known_ext ) return 0; /* if equal, don't need to check case (store to avoid multiple calls) */ cmp = strcmp(test_ext, known_ext); if( cmp == 0 ) return cmp; len = strlen(known_ext); if( len > MAX_EXT_LEN ) return cmp; /* if here, strings are different but need to check upper-case */ for(c = 0; c < len; c++ ) caps[c] = toupper(known_ext[c]); caps[c] = '\0'; return strcmp(test_ext, caps); } /* like strncmp, but also check against capitalization of known_ext * (test as local string, with max length 7) */ static int fileext_n_compare(const char * test_ext, const char * known_ext, int maxlen) { char caps[MAX_EXT_LEN + 1] = ""; int c, cmp, len; /* if anything odd, use default */ if( !test_ext || !known_ext ) return 0; /* if equal, don't need to check case (store to avoid multiple calls) */ cmp = strncmp(test_ext, known_ext, maxlen); if( cmp == 0 ) return cmp; len = strlen(known_ext); if( len > maxlen ) len = maxlen; /* ignore anything past maxlen */ if( len > MAX_EXT_LEN ) return cmp; /* if here, strings are different but need to check upper-case */ for(c = 0; c < len; c++ ) caps[c] = toupper(known_ext[c]); caps[c] = '\0'; return strncmp(test_ext, caps, maxlen); } /* return 1 if there are uppercase but no lowercase */ static int is_uppercase(const char * str) { int hasupper = 0; size_t c, n; if( !str || !*str ) return 0; n = strlen(str); for(c = 0; c < n; c++ ) { if( islower(str[c]) ) return 0; if( !hasupper && isupper(str[c]) ) hasupper = 1; } return hasupper; } /* return 1 if there are both uppercase and lowercase characters */ static int is_mixedcase(const char * str) { int hasupper = 0, haslower = 0; size_t c, n; if( !str || !*str ) return 0; n = strlen(str); for(c = 0; c < n; c++ ) { if( !haslower && islower(str[c]) ) haslower = 1; if( !hasupper && isupper(str[c]) ) hasupper = 1; if( haslower && hasupper ) return 1; } return 0; } /* convert any lowercase chars to uppercase */ static int make_uppercase(char * str) { size_t c, n; if( !str || !*str ) return 0; n = strlen(str); for(c = 0; c < n; c++ ) if( islower(str[c]) ) str[c] = toupper(str[c]); return 0; } /* convert any uppercase chars to lowercase */ static int make_lowercase(char * str) { size_t c, n; if( !str || !*str ) return 0; n = strlen(str); for(c = 0; c < n; c++ ) if( isupper(str[c]) ) str[c] = tolower(str[c]); return 0; } /* run strcmp against of list of strings * return index of equality, if found * else return -1 */ static int compare_strlist(const char * str, char ** strlist, int len) { int c; if( len <= 0 || !str || !strlist ) return -1; for( c = 0; c < len; c++ ) if( strlist[c] && !strcmp(str, strlist[c]) ) return c; return -1; } /*--------------------------------------------------------------------------*/ /*! check whether the given type is on the "approved" list The code is valid if it is non-negative, and does not exceed NIFTI_MAX_FTYPE. \return 1 if nifti_type is valid, 0 otherwise \sa NIFTI_FTYPE_* codes in nifti1_io.h *//*------------------------------------------------------------------------*/ int is_valid_nifti_type( int nifti_type ) { if( nifti_type >= NIFTI_FTYPE_ANALYZE && /* smallest type, 0 */ nifti_type <= NIFTI_MAX_FTYPE ) return 1; return 0; } /*--------------------------------------------------------------------------*/ /*! check whether the given type is on the "approved" list The type is explicitly checked against the NIFTI_TYPE_* list in nifti1.h. \return 1 if dtype is valid, 0 otherwise \sa NIFTI_TYPE_* codes in nifti1.h *//*------------------------------------------------------------------------*/ int nifti_is_valid_datatype( int dtype ) { if( dtype == NIFTI_TYPE_UINT8 || dtype == NIFTI_TYPE_INT16 || dtype == NIFTI_TYPE_INT32 || dtype == NIFTI_TYPE_FLOAT32 || dtype == NIFTI_TYPE_COMPLEX64 || dtype == NIFTI_TYPE_FLOAT64 || dtype == NIFTI_TYPE_RGB24 || dtype == NIFTI_TYPE_RGBA32 || dtype == NIFTI_TYPE_INT8 || dtype == NIFTI_TYPE_UINT16 || dtype == NIFTI_TYPE_UINT32 || dtype == NIFTI_TYPE_INT64 || dtype == NIFTI_TYPE_UINT64 || dtype == NIFTI_TYPE_FLOAT128 || dtype == NIFTI_TYPE_COMPLEX128 || dtype == NIFTI_TYPE_COMPLEX256 ) return 1; return 0; } /*--------------------------------------------------------------------------*/ /*! set the nifti_type field based on fname and iname Note that nifti_type is changed only when it does not match the filenames. \return 0 on success, -1 on error \sa is_valid_nifti_type, nifti_type_and_names_match *//*------------------------------------------------------------------------*/ int nifti_set_type_from_names( nifti_image * nim ) { /* error checking first */ if( !nim ){ fprintf(stderr,"** NSTFN: no nifti_image\n"); return -1; } if( !nim->fname || !nim->iname ){ fprintf(stderr,"** NSTFN: missing filename(s) fname @ %p, iname @ %p\n", (void *)nim->fname, (void *)nim->iname); return -1; } if( ! nifti_validfilename ( nim->fname ) || ! nifti_validfilename ( nim->iname ) || ! nifti_find_file_extension( nim->fname ) || ! nifti_find_file_extension( nim->iname ) ) { fprintf(stderr,"** NSTFN: invalid filename(s) fname='%s', iname='%s'\n", nim->fname, nim->iname); return -1; } if( g_opts.debug > 2 ) fprintf(stderr,"-d verify nifti_type from filenames: %d",nim->nifti_type); /* type should be NIFTI_FTYPE_ASCII if extension is .nia */ if( (fileext_compare(nifti_find_file_extension(nim->fname),".nia")==0)){ nim->nifti_type = NIFTI_FTYPE_ASCII; } else { /* not too picky here, do what must be done, and then verify */ if( strcmp(nim->fname, nim->iname) == 0 ) /* one file, type 1 */ nim->nifti_type = NIFTI_FTYPE_NIFTI1_1; else if( nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ) /* cannot be type 1 */ nim->nifti_type = NIFTI_FTYPE_NIFTI1_2; } if( g_opts.debug > 2 ) fprintf(stderr," -> %d\n",nim->nifti_type); if( g_opts.debug > 1 ) /* warn user about anything strange */ nifti_type_and_names_match(nim, 1); if( is_valid_nifti_type(nim->nifti_type) ) return 0; /* success! */ fprintf(stderr,"** NSTFN: bad nifti_type %d, for '%s' and '%s'\n", nim->nifti_type, nim->fname, nim->iname); return -1; } /*--------------------------------------------------------------------------*/ /*! Determine if this is a NIFTI-formatted file.
   \return  0 if file looks like ANALYZE 7.5 [checks sizeof_hdr field == 348]
            1 if file marked as NIFTI (header+data in 1 file)
            2 if file marked as NIFTI (header+data in 2 files)
           -1 if it can't tell, file doesn't exist, etc.
   
*//*------------------------------------------------------------------------*/ int is_nifti_file( const char *hname ) { struct nifti_1_header nhdr ; znzFile fp ; int ii ; char *tmpname; /* bad input name? */ if( !nifti_validfilename(hname) ) return -1 ; /* open file */ tmpname = nifti_findhdrname(hname); if( tmpname == NULL ){ if( g_opts.debug > 0 ) fprintf(stderr,"** no header file found for '%s'\n",hname); return -1; } fp = znzopen( tmpname , "rb" , nifti_is_gzfile(tmpname) ) ; free(tmpname); if (znz_isnull(fp)) return -1 ; /* bad open? */ /* read header, close file */ ii = (int)znzread( &nhdr , 1 , sizeof(nhdr) , fp ) ; znzclose( fp ) ; if( ii < (int) sizeof(nhdr) ) return -1 ; /* bad read? */ /* check for NIFTI-ness */ if( NIFTI_VERSION(nhdr) != 0 ){ return ( NIFTI_ONEFILE(nhdr) ) ? 1 : 2 ; } /* check for ANALYZE-ness (sizeof_hdr field == 348) */ ii = nhdr.sizeof_hdr ; if( ii == (int)sizeof(nhdr) ) return 0 ; /* matches */ /* try byte-swapping header */ swap_4(ii) ; if( ii == (int)sizeof(nhdr) ) return 0 ; /* matches */ return -1 ; /* not good */ } static int print_hex_vals( const char * data, int nbytes, FILE * fp ) { int c; if ( !data || nbytes < 1 || !fp ) return -1; fputs("0x", fp); for ( c = 0; c < nbytes; c++ ) fprintf(fp, " %x", data[c]); return 0; } /*----------------------------------------------------------------------*/ /*! display the contents of the nifti_1_header (send to stdout) \param info if non-NULL, print this character string \param hp pointer to nifti_1_header *//*--------------------------------------------------------------------*/ int disp_nifti_1_header( const char * info, const nifti_1_header * hp ) { int c; fputs( "-------------------------------------------------------\n", stdout ); if ( info ) fputs( info, stdout ); if ( !hp ){ fputs(" ** no nifti_1_header to display!\n",stdout); return 1; } fprintf(stdout," nifti_1_header :\n" " sizeof_hdr = %d\n" " data_type[10] = ", hp->sizeof_hdr); print_hex_vals(hp->data_type, 10, stdout); fprintf(stdout, "\n" " db_name[18] = "); print_hex_vals(hp->db_name, 18, stdout); fprintf(stdout, "\n" " extents = %d\n" " session_error = %d\n" " regular = 0x%x\n" " dim_info = 0x%x\n", hp->extents, hp->session_error, hp->regular, hp->dim_info ); fprintf(stdout, " dim[8] ="); for ( c = 0; c < 8; c++ ) fprintf(stdout," %d", hp->dim[c]); fprintf(stdout, "\n" " intent_p1 = %f\n" " intent_p2 = %f\n" " intent_p3 = %f\n" " intent_code = %d\n" " datatype = %d\n" " bitpix = %d\n" " slice_start = %d\n" " pixdim[8] =", hp->intent_p1, hp->intent_p2, hp->intent_p3, hp->intent_code, hp->datatype, hp->bitpix, hp->slice_start); /* break pixdim over 2 lines */ for ( c = 0; c < 4; c++ ) fprintf(stdout," %f", hp->pixdim[c]); fprintf(stdout, "\n "); for ( c = 4; c < 8; c++ ) fprintf(stdout," %f", hp->pixdim[c]); fprintf(stdout, "\n" " vox_offset = %f\n" " scl_slope = %f\n" " scl_inter = %f\n" " slice_end = %d\n" " slice_code = %d\n" " xyzt_units = 0x%x\n" " cal_max = %f\n" " cal_min = %f\n" " slice_duration = %f\n" " toffset = %f\n" " glmax = %d\n" " glmin = %d\n", hp->vox_offset, hp->scl_slope, hp->scl_inter, hp->slice_end, hp->slice_code, hp->xyzt_units, hp->cal_max, hp->cal_min, hp->slice_duration, hp->toffset, hp->glmax, hp->glmin); fprintf(stdout, " descrip = '%.80s'\n" " aux_file = '%.24s'\n" " qform_code = %d\n" " sform_code = %d\n" " quatern_b = %f\n" " quatern_c = %f\n" " quatern_d = %f\n" " qoffset_x = %f\n" " qoffset_y = %f\n" " qoffset_z = %f\n" " srow_x[4] = %f, %f, %f, %f\n" " srow_y[4] = %f, %f, %f, %f\n" " srow_z[4] = %f, %f, %f, %f\n" " intent_name = '%-.16s'\n" " magic = '%-.4s'\n", hp->descrip, hp->aux_file, hp->qform_code, hp->sform_code, hp->quatern_b, hp->quatern_c, hp->quatern_d, hp->qoffset_x, hp->qoffset_y, hp->qoffset_z, hp->srow_x[0], hp->srow_x[1], hp->srow_x[2], hp->srow_x[3], hp->srow_y[0], hp->srow_y[1], hp->srow_y[2], hp->srow_y[3], hp->srow_z[0], hp->srow_z[1], hp->srow_z[2], hp->srow_z[3], hp->intent_name, hp->magic); fputs( "-------------------------------------------------------\n", stdout ); fflush(stdout); return 0; } #undef ERREX #define ERREX(msg) \ do{ fprintf(stderr,"** ERROR: nifti_convert_nhdr2nim: %s\n", (msg) ) ; \ if (nim != NULL) free(nim); \ return NULL ; } while(0) /*----------------------------------------------------------------------*/ /*! convert a nifti_1_header into a nift1_image \return an allocated nifti_image, or NULL on failure *//*--------------------------------------------------------------------*/ nifti_image* nifti_convert_nhdr2nim(struct nifti_1_header nhdr, const char * fname) { int ii , doswap , ioff ; int is_nifti , is_onefile ; nifti_image *nim; nim = (nifti_image *)calloc( 1 , sizeof(nifti_image) ) ; if( !nim ) ERREX("failed to allocate nifti image"); /* be explicit with pointers */ nim->fname = NULL; nim->iname = NULL; nim->data = NULL; /**- check if we must swap bytes */ doswap = need_nhdr_swap(nhdr.dim[0], nhdr.sizeof_hdr); /* swap data flag */ if( doswap < 0 ){ if( doswap == -1 ) ERREX("bad dim[0]") ; ERREX("bad sizeof_hdr") ; /* else */ } /**- determine if this is a NIFTI-1 compliant header */ is_nifti = NIFTI_VERSION(nhdr) ; /* * before swapping header, record the Analyze75 orient code */ if(!is_nifti) { /**- in analyze75, the orient code is at the same address as * qform_code, but it's just one byte * the qform_code will be zero, at which point you can check * analyze75_orient if you care to. */ unsigned char c = *((char *)(&nhdr.qform_code)); nim->analyze75_orient = (analyze_75_orient_code)c; } if( doswap ) { if ( g_opts.debug > 3 ) disp_nifti_1_header("-d ni1 pre-swap: ", &nhdr); swap_nifti_header( &nhdr , is_nifti ) ; } if ( g_opts.debug > 2 ) disp_nifti_1_header("-d nhdr2nim : ", &nhdr); if( nhdr.datatype == DT_BINARY || nhdr.datatype == DT_UNKNOWN ) ERREX("bad datatype") ; if( nhdr.dim[1] <= 0 ) ERREX("bad dim[1]") ; /* fix bad dim[] values in the defined dimension range */ for( ii=2 ; ii <= nhdr.dim[0] ; ii++ ) if( nhdr.dim[ii] <= 0 ) nhdr.dim[ii] = 1 ; /* fix any remaining bad dim[] values, so garbage does not propagate */ /* (only values 0 or 1 seem rational, otherwise set to arbirary 1) */ for( ii=nhdr.dim[0]+1 ; ii <= 7 ; ii++ ) if( nhdr.dim[ii] != 1 && nhdr.dim[ii] != 0) nhdr.dim[ii] = 1 ; #if 0 /* rely on dim[0], do not attempt to modify it 16 Nov 2005 [rickr] */ /**- get number of dimensions (ignoring dim[0] now) */ for( ii=7 ; ii >= 2 ; ii-- ) /* loop backwards until we */ if( nhdr.dim[ii] > 1 ) break ; /* find a dim bigger than 1 */ ndim = ii ; #endif /**- set bad grid spacings to 1.0 */ for( ii=1 ; ii <= nhdr.dim[0] ; ii++ ){ if( nhdr.pixdim[ii] == 0.0 || !IS_GOOD_FLOAT(nhdr.pixdim[ii]) ) nhdr.pixdim[ii] = 1.0 ; } is_onefile = is_nifti && NIFTI_ONEFILE(nhdr) ; if( is_nifti ) nim->nifti_type = (is_onefile) ? NIFTI_FTYPE_NIFTI1_1 : NIFTI_FTYPE_NIFTI1_2 ; else nim->nifti_type = NIFTI_FTYPE_ANALYZE ; ii = nifti_short_order() ; if( doswap ) nim->byteorder = REVERSE_ORDER(ii) ; else nim->byteorder = ii ; /**- set dimensions of data array */ nim->ndim = nim->dim[0] = nhdr.dim[0]; nim->nx = nim->dim[1] = nhdr.dim[1]; nim->ny = nim->dim[2] = nhdr.dim[2]; nim->nz = nim->dim[3] = nhdr.dim[3]; nim->nt = nim->dim[4] = nhdr.dim[4]; nim->nu = nim->dim[5] = nhdr.dim[5]; nim->nv = nim->dim[6] = nhdr.dim[6]; nim->nw = nim->dim[7] = nhdr.dim[7]; for( ii=1, nim->nvox=1; ii <= nhdr.dim[0]; ii++ ) nim->nvox *= nhdr.dim[ii]; /**- set the type of data in voxels and how many bytes per voxel */ nim->datatype = nhdr.datatype ; nifti_datatype_sizes( nim->datatype , &(nim->nbyper) , &(nim->swapsize) ) ; if( nim->nbyper == 0 ){ ERREX("bad datatype"); } /**- set the grid spacings */ nim->dx = nim->pixdim[1] = nhdr.pixdim[1] ; nim->dy = nim->pixdim[2] = nhdr.pixdim[2] ; nim->dz = nim->pixdim[3] = nhdr.pixdim[3] ; nim->dt = nim->pixdim[4] = nhdr.pixdim[4] ; nim->du = nim->pixdim[5] = nhdr.pixdim[5] ; nim->dv = nim->pixdim[6] = nhdr.pixdim[6] ; nim->dw = nim->pixdim[7] = nhdr.pixdim[7] ; /**- compute qto_xyz transformation from pixel indexes (i,j,k) to (x,y,z) */ if( !is_nifti || nhdr.qform_code <= 0 ){ /**- if not nifti or qform_code <= 0, use grid spacing for qto_xyz */ nim->qto_xyz.m[0][0] = nim->dx ; /* grid spacings */ nim->qto_xyz.m[1][1] = nim->dy ; /* along diagonal */ nim->qto_xyz.m[2][2] = nim->dz ; /* off diagonal is zero */ nim->qto_xyz.m[0][1]=nim->qto_xyz.m[0][2]=nim->qto_xyz.m[0][3] = 0.0; nim->qto_xyz.m[1][0]=nim->qto_xyz.m[1][2]=nim->qto_xyz.m[1][3] = 0.0; nim->qto_xyz.m[2][0]=nim->qto_xyz.m[2][1]=nim->qto_xyz.m[2][3] = 0.0; /* last row is always [ 0 0 0 1 ] */ nim->qto_xyz.m[3][0]=nim->qto_xyz.m[3][1]=nim->qto_xyz.m[3][2] = 0.0; nim->qto_xyz.m[3][3]= 1.0 ; nim->qform_code = NIFTI_XFORM_UNKNOWN ; if( g_opts.debug > 1 ) fprintf(stderr,"-d no qform provided\n"); } else { /**- else NIFTI: use the quaternion-specified transformation */ nim->quatern_b = FIXED_FLOAT( nhdr.quatern_b ) ; nim->quatern_c = FIXED_FLOAT( nhdr.quatern_c ) ; nim->quatern_d = FIXED_FLOAT( nhdr.quatern_d ) ; nim->qoffset_x = FIXED_FLOAT(nhdr.qoffset_x) ; nim->qoffset_y = FIXED_FLOAT(nhdr.qoffset_y) ; nim->qoffset_z = FIXED_FLOAT(nhdr.qoffset_z) ; nim->qfac = (nhdr.pixdim[0] < 0.0) ? -1.0 : 1.0 ; /* left-handedness? */ nim->qto_xyz = nifti_quatern_to_mat44( nim->quatern_b, nim->quatern_c, nim->quatern_d, nim->qoffset_x, nim->qoffset_y, nim->qoffset_z, nim->dx , nim->dy , nim->dz , nim->qfac ) ; nim->qform_code = nhdr.qform_code ; if( g_opts.debug > 1 ) nifti_disp_matrix_orient("-d qform orientations:\n", nim->qto_xyz); } /**- load inverse transformation (x,y,z) -> (i,j,k) */ nim->qto_ijk = nifti_mat44_inverse( nim->qto_xyz ) ; /**- load sto_xyz affine transformation, if present */ if( !is_nifti || nhdr.sform_code <= 0 ){ /**- if not nifti or sform_code <= 0, then no sto transformation */ nim->sform_code = NIFTI_XFORM_UNKNOWN ; if( g_opts.debug > 1 ) fprintf(stderr,"-d no sform provided\n"); } else { /**- else set the sto transformation from srow_*[] */ nim->sto_xyz.m[0][0] = nhdr.srow_x[0] ; nim->sto_xyz.m[0][1] = nhdr.srow_x[1] ; nim->sto_xyz.m[0][2] = nhdr.srow_x[2] ; nim->sto_xyz.m[0][3] = nhdr.srow_x[3] ; nim->sto_xyz.m[1][0] = nhdr.srow_y[0] ; nim->sto_xyz.m[1][1] = nhdr.srow_y[1] ; nim->sto_xyz.m[1][2] = nhdr.srow_y[2] ; nim->sto_xyz.m[1][3] = nhdr.srow_y[3] ; nim->sto_xyz.m[2][0] = nhdr.srow_z[0] ; nim->sto_xyz.m[2][1] = nhdr.srow_z[1] ; nim->sto_xyz.m[2][2] = nhdr.srow_z[2] ; nim->sto_xyz.m[2][3] = nhdr.srow_z[3] ; /* last row is always [ 0 0 0 1 ] */ nim->sto_xyz.m[3][0]=nim->sto_xyz.m[3][1]=nim->sto_xyz.m[3][2] = 0.0; nim->sto_xyz.m[3][3]= 1.0 ; nim->sto_ijk = nifti_mat44_inverse( nim->sto_xyz ) ; nim->sform_code = nhdr.sform_code ; if( g_opts.debug > 1 ) nifti_disp_matrix_orient("-d sform orientations:\n", nim->sto_xyz); } /**- set miscellaneous NIFTI stuff */ if( is_nifti ){ nim->scl_slope = FIXED_FLOAT( nhdr.scl_slope ) ; nim->scl_inter = FIXED_FLOAT( nhdr.scl_inter ) ; nim->intent_code = nhdr.intent_code ; nim->intent_p1 = FIXED_FLOAT( nhdr.intent_p1 ) ; nim->intent_p2 = FIXED_FLOAT( nhdr.intent_p2 ) ; nim->intent_p3 = FIXED_FLOAT( nhdr.intent_p3 ) ; nim->toffset = FIXED_FLOAT( nhdr.toffset ) ; memcpy(nim->intent_name,nhdr.intent_name,15); nim->intent_name[15] = '\0'; nim->xyz_units = XYZT_TO_SPACE(nhdr.xyzt_units) ; nim->time_units = XYZT_TO_TIME (nhdr.xyzt_units) ; nim->freq_dim = DIM_INFO_TO_FREQ_DIM ( nhdr.dim_info ) ; nim->phase_dim = DIM_INFO_TO_PHASE_DIM( nhdr.dim_info ) ; nim->slice_dim = DIM_INFO_TO_SLICE_DIM( nhdr.dim_info ) ; nim->slice_code = nhdr.slice_code ; nim->slice_start = nhdr.slice_start ; nim->slice_end = nhdr.slice_end ; nim->slice_duration = FIXED_FLOAT(nhdr.slice_duration) ; } /**- set Miscellaneous ANALYZE stuff */ nim->cal_min = FIXED_FLOAT(nhdr.cal_min) ; nim->cal_max = FIXED_FLOAT(nhdr.cal_max) ; memcpy(nim->descrip ,nhdr.descrip ,79) ; nim->descrip [79] = '\0' ; memcpy(nim->aux_file,nhdr.aux_file,23) ; nim->aux_file[23] = '\0' ; /**- set ioff from vox_offset (but at least sizeof(header)) */ is_onefile = is_nifti && NIFTI_ONEFILE(nhdr) ; if( is_onefile ){ ioff = (int)nhdr.vox_offset ; if( ioff < (int) sizeof(nhdr) ) ioff = (int) sizeof(nhdr) ; } else { ioff = (int)nhdr.vox_offset ; } nim->iname_offset = ioff ; /**- deal with file names if set */ if (fname!=NULL) { nifti_set_filenames(nim,fname,0,0); if (nim->iname==NULL) { ERREX("bad filename"); } } else { nim->fname = NULL; nim->iname = NULL; } /* clear extension fields */ nim->num_ext = 0; nim->ext_list = NULL; return nim; } #undef ERREX #define ERREX(msg) \ do{ fprintf(stderr,"** ERROR: nifti_image_open(%s): %s\n", \ (hname != NULL) ? hname : "(null)" , (msg) ) ; \ return fptr ; } while(0) /*************************************************************** * nifti_image_open ***************************************************************/ /*! znzFile nifti_image_open( char *hname, char *opts , nifti_image **nim) \brief Read in NIFTI-1 or ANALYZE-7.5 file (pair) header information into a nifti_image struct. - The image data is not read from disk (it may be read later using nifti_image_load(), for example). - The image data will be stored in whatever data format the input data is; no scaling will be applied. - DT_BINARY data is not supported. - nifti_image_free() can be used to delete the returned struct, when you are done with it. \param hname filename of dataset .hdr or .nii file \param opts options string for opening the header file \param nim pointer to pointer to nifti_image struct (this routine allocates the nifti_image struct) \return file pointer (gzippable) to the file with the image data, ready for reading.
NULL if something fails badly. \sa nifti_image_load, nifti_image_free */ znzFile nifti_image_open(const char * hname, char * opts, nifti_image ** nim) { znzFile fptr=NULL; /* open the hdr and reading it in, but do not load the data */ *nim = nifti_image_read(hname,0); /* open the image file, ready for reading (compressed works for all reads) */ if( ((*nim) == NULL) || ((*nim)->iname == NULL) || ((*nim)->nbyper <= 0) || ((*nim)->nvox <= 0) ) ERREX("bad header info") ; /* open image data file */ fptr = znzopen( (*nim)->iname, opts, nifti_is_gzfile((*nim)->iname) ); if( znz_isnull(fptr) ) ERREX("Can't open data file") ; return fptr; } /*----------------------------------------------------------------------*/ /*! return an allocated and filled nifti_1_header struct Read the binary header from disk, and swap bytes if necessary. \return an allocated nifti_1_header struct, or NULL on failure \param hname name of file containing header \param swapped if not NULL, return whether header bytes were swapped \param check flag to check for invalid nifti_1_header \warning ASCII header type is not supported \sa nifti_image_read, nifti_image_free, nifti_image_read_bricks *//*--------------------------------------------------------------------*/ nifti_1_header * nifti_read_header(const char * hname, int * swapped, int check) { nifti_1_header nhdr, * hptr; znzFile fp; int bytes, lswap; char * hfile; char fname[] = { "nifti_read_header" }; /* determine file name to use for header */ hfile = nifti_findhdrname(hname); if( hfile == NULL ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"failed to find header file for", hname); return NULL; } else if( g_opts.debug > 1 ) fprintf(stderr,"-d %s: found header filename '%s'\n",fname,hfile); fp = znzopen( hfile, "rb", nifti_is_gzfile(hfile) ); if( znz_isnull(fp) ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"failed to open header file",hfile); free(hfile); return NULL; } free(hfile); /* done with filename */ if( has_ascii_header(fp) == 1 ){ znzclose( fp ); if( g_opts.debug > 0 ) LNI_FERR(fname,"ASCII header type not supported",hname); return NULL; } /* read the binary header */ bytes = (int)znzread( &nhdr, 1, sizeof(nhdr), fp ); znzclose( fp ); /* we are done with the file now */ if( bytes < (int)sizeof(nhdr) ){ if( g_opts.debug > 0 ){ LNI_FERR(fname,"bad binary header read for file", hname); fprintf(stderr," - read %d of %d bytes\n",bytes, (int)sizeof(nhdr)); } return NULL; } /* now just decide on byte swapping */ lswap = need_nhdr_swap(nhdr.dim[0], nhdr.sizeof_hdr); /* swap data flag */ if( check && lswap < 0 ){ LNI_FERR(fname,"bad nifti_1_header for file", hname); return NULL; } else if ( lswap < 0 ) { lswap = 0; /* if swapping does not help, don't do it */ if(g_opts.debug > 1) fprintf(stderr,"-- swap failure, none applied\n"); } if( lswap ) { if ( g_opts.debug > 3 ) disp_nifti_1_header("-d nhdr pre-swap: ", &nhdr); swap_nifti_header( &nhdr , NIFTI_VERSION(nhdr) ) ; } if ( g_opts.debug > 2 ) disp_nifti_1_header("-d nhdr post-swap: ", &nhdr); if ( check && ! nifti_hdr_looks_good(&nhdr) ){ LNI_FERR(fname,"nifti_1_header looks bad for file", hname); return NULL; } /* all looks good, so allocate memory for and return the header */ hptr = (nifti_1_header *)malloc(sizeof(nifti_1_header)); if( ! hptr ){ fprintf(stderr,"** nifti_read_hdr: failed to alloc nifti_1_header\n"); return NULL; } if( swapped ) *swapped = lswap; /* only if they care */ memcpy(hptr, &nhdr, sizeof(nifti_1_header)); return hptr; } /*----------------------------------------------------------------------*/ /*! decide if this nifti_1_header structure looks reasonable Check dim[0], dim[1], sizeof_hdr, and datatype. Check magic string for "n+1". Maybe more tests will follow. \return 1 if the header seems valid, 0 otherwise \sa nifti_nim_is_valid, valid_nifti_extensions *//*--------------------------------------------------------------------*/ int nifti_hdr_looks_good(const nifti_1_header * hdr) { int is_nifti, c, errs = 0; /* check dim[0] and sizeof_hdr */ if( need_nhdr_swap(hdr->dim[0], hdr->sizeof_hdr) < 0 ){ if( g_opts.debug > 0 ) fprintf(stderr,"** bad nhdr fields: dim0, sizeof_hdr = %d, %d\n", hdr->dim[0], hdr->sizeof_hdr); errs++; } /* check the valid dimension sizes (maybe dim[0] is bad) */ for( c = 1; c <= hdr->dim[0] && c <= 7; c++ ) if( hdr->dim[c] <= 0 ){ if( g_opts.debug > 0 ) fprintf(stderr,"** bad nhdr field: dim[%d] = %d\n",c,hdr->dim[c]); errs++; } is_nifti = NIFTI_VERSION(*hdr); /* determine header type */ if( is_nifti ){ /* NIFTI */ if( ! nifti_datatype_is_valid(hdr->datatype, 1) ){ if( g_opts.debug > 0 ) fprintf(stderr,"** bad NIFTI datatype in hdr, %d\n",hdr->datatype); errs++; } } else { /* ANALYZE 7.5 */ if( g_opts.debug > 1 ) /* maybe tell user it's an ANALYZE hdr */ fprintf(stderr, "-- nhdr magic field implies ANALYZE: magic = '%.4s'\n",hdr->magic); if( ! nifti_datatype_is_valid(hdr->datatype, 0) ){ if( g_opts.debug > 0 ) fprintf(stderr,"** bad ANALYZE datatype in hdr, %d\n",hdr->datatype); errs++; } } if( errs ) return 0; /* problems */ if( g_opts.debug > 2 ) fprintf(stderr,"-d nifti header looks good\n"); return 1; /* looks good */ } /*---------------------------------------------------------------------- * check whether byte swapping is needed * * dim[0] should be in [0,7], and sizeof_hdr should be accurate * * \returns > 0 : needs swap * 0 : does not need swap * < 0 : error condition *----------------------------------------------------------------------*/ static int need_nhdr_swap( short dim0, int hdrsize ) { short d0 = dim0; /* so we won't have to swap them on the stack */ int hsize = hdrsize; if( d0 != 0 ){ /* then use it for the check */ if( d0 > 0 && d0 <= 7 ) return 0; nifti_swap_2bytes(1, &d0); /* swap? */ if( d0 > 0 && d0 <= 7 ) return 1; if( g_opts.debug > 1 ){ fprintf(stderr,"** NIFTI: bad swapped d0 = %d, unswapped = ", d0); nifti_swap_2bytes(1, &d0); /* swap? */ fprintf(stderr,"%d\n", d0); } return -1; /* bad, naughty d0 */ } /* dim[0] == 0 should not happen, but could, so try hdrsize */ if( hsize == sizeof(nifti_1_header) ) return 0; nifti_swap_4bytes(1, &hsize); /* swap? */ if( hsize == sizeof(nifti_1_header) ) return 1; if( g_opts.debug > 1 ){ fprintf(stderr,"** NIFTI: bad swapped hsize = %d, unswapped = ", hsize); nifti_swap_4bytes(1, &hsize); /* swap? */ fprintf(stderr,"%d\n", hsize); } return -2; /* bad, naughty hsize */ } /* use macro LNI_FILE_ERROR instead of ERREX() #undef ERREX #define ERREX(msg) \ do{ fprintf(stderr,"** ERROR: nifti_image_read(%s): %s\n", \ (hname != NULL) ? hname : "(null)" , (msg) ) ; \ return NULL ; } while(0) */ /*************************************************************** * nifti_image_read ***************************************************************/ /*! \brief Read a nifti header and optionally the data, creating a nifti_image. - The data buffer will be byteswapped if necessary. - The data buffer will not be scaled. - The data buffer is allocated with calloc(). \param hname filename of the nifti dataset \param read_data Flag, true=read data blob, false=don't read blob. \return A pointer to the nifti_image data structure. \sa nifti_image_free, nifti_free_extensions, nifti_image_read_bricks */ nifti_image *nifti_image_read( const char *hname , int read_data ) { struct nifti_1_header nhdr ; nifti_image *nim ; znzFile fp ; int rv, ii , filesize, remaining; char fname[] = { "nifti_image_read" }; char *hfile=NULL; if( g_opts.debug > 1 ){ fprintf(stderr,"-d image_read from '%s', read_data = %d",hname,read_data); #ifdef HAVE_ZLIB fprintf(stderr,", HAVE_ZLIB = 1\n"); #else fprintf(stderr,", HAVE_ZLIB = 0\n"); #endif } /**- determine filename to use for header */ hfile = nifti_findhdrname(hname); if( hfile == NULL ){ if(g_opts.debug > 0) LNI_FERR(fname,"failed to find header file for", hname); return NULL; /* check return */ } else if( g_opts.debug > 1 ) fprintf(stderr,"-d %s: found header filename '%s'\n",fname,hfile); if( nifti_is_gzfile(hfile) ) filesize = -1; /* unknown */ else filesize = nifti_get_filesize(hfile); fp = znzopen(hfile, "rb", nifti_is_gzfile(hfile)); if( znz_isnull(fp) ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"failed to open header file",hfile); free(hfile); return NULL; } rv = has_ascii_header( fp ); if( rv < 0 ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"short header read",hfile); znzclose( fp ); free(hfile); return NULL; } else if ( rv == 1 ) /* process special file type */ return nifti_read_ascii_image( fp, hfile, filesize, read_data ); /* else, just process normally */ /**- read binary header */ ii = (int)znzread( &nhdr , 1 , sizeof(nhdr) , fp ) ; /* read the thing */ /* keep file open so we can check for exts. after nifti_convert_nhdr2nim() */ if( ii < (int) sizeof(nhdr) ){ if( g_opts.debug > 0 ){ LNI_FERR(fname,"bad binary header read for file", hfile); fprintf(stderr," - read %d of %d bytes\n",ii, (int)sizeof(nhdr)); } znzclose(fp) ; free(hfile); return NULL; } /* create output image struct and set it up */ /**- convert all nhdr fields to nifti_image fields */ nim = nifti_convert_nhdr2nim(nhdr,hfile); if( nim == NULL ){ znzclose( fp ) ; /* close the file */ if( g_opts.debug > 0 ) LNI_FERR(fname,"cannot create nifti image from header",hfile); free(hfile); /* had to save this for debug message */ return NULL; } if( g_opts.debug > 3 ){ fprintf(stderr,"+d nifti_image_read(), have nifti image:\n"); if( g_opts.debug > 2 ) nifti_image_infodump(nim); } /**- check for extensions (any errors here means no extensions) */ if( NIFTI_ONEFILE(nhdr) ) remaining = nim->iname_offset - sizeof(nhdr); else remaining = filesize - sizeof(nhdr); (void)nifti_read_extensions(nim, fp, remaining); znzclose( fp ) ; /* close the file */ free(hfile); /**- read the data if desired, then bug out */ if( read_data ){ if( nifti_image_load( nim ) < 0 ){ nifti_image_free(nim); /* take ball, go home. */ return NULL; } } else nim->data = NULL ; return nim ; } /*---------------------------------------------------------------------- * has_ascii_header - see if the NIFTI header is an ASCII format * * If the file starts with the ASCII string " 1 ) fprintf(stderr,"-d %s: have ASCII NIFTI file of size %d\n",fname,slen); if( slen > 65530 ) slen = 65530 ; sbuf = (char *)calloc(sizeof(char),slen+1) ; if( !sbuf ){ fprintf(stderr,"** %s: failed to alloc %d bytes for sbuf",lfunc,65530); free(fname); znzclose(fp); return NULL; } if (znzread( sbuf , 1 , slen - 1, fp ) <= 0) { free(fname); znzclose(fp); return NULL; } nim = nifti_image_from_ascii( sbuf, &txt_size ) ; free( sbuf ) ; if( nim == NULL ){ LNI_FERR(lfunc,"failed nifti_image_from_ascii()",fname); free(fname); znzclose(fp); return NULL; } nim->nifti_type = NIFTI_FTYPE_ASCII ; /* compute remaining space for extensions */ remain = flen - txt_size - (int)nifti_get_volsize(nim); if( remain > 4 ){ /* read extensions (reposition file pointer, first) */ if (znzseek(fp, txt_size, SEEK_SET) < 0) { free(fname); znzclose(fp); free(nim); return NULL; } (void) nifti_read_extensions(nim, fp, remain); } free(fname); znzclose( fp ) ; nim->iname_offset = -1 ; /* check from the end of the file */ if( read_data ) rv = nifti_image_load( nim ) ; else nim->data = NULL ; /* check for nifti_image_load() failure, maybe bail out */ if( read_data && rv != 0 ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d failed image_load, free nifti image struct\n"); free(nim); return NULL; } return nim ; } /*---------------------------------------------------------------------- * Read the extensions into the nifti_image struct 08 Dec 2004 [rickr] * * This function is called just after the header struct is read in, and * it is assumed the file pointer has not moved. The value in remain * is assumed to be accurate, reflecting the bytes of space for potential * extensions. * * return the number of extensions read in, or < 0 on error *----------------------------------------------------------------------*/ static int nifti_read_extensions( nifti_image *nim, znzFile fp, int remain ) { nifti1_extender extdr; /* defines extension existence */ nifti1_extension extn; /* single extension to process */ nifti1_extension * Elist; /* list of processed extensions */ int posn, count; if( !nim || znz_isnull(fp) ) { if( g_opts.debug > 0 ) fprintf(stderr,"** nifti_read_extensions: bad inputs (%p,%p)\n", (void *)nim, (void *)fp); return -1; } posn = znztell(fp); if( (posn != sizeof(nifti_1_header)) && (nim->nifti_type != NIFTI_FTYPE_ASCII) ) fprintf(stderr,"** WARNING: posn not header size (%d, %d)\n", posn, (int)sizeof(nifti_1_header)); if( g_opts.debug > 2 ) fprintf(stderr,"-d nre: posn = %d, offset = %d, type = %d, remain = %d\n", posn, nim->iname_offset, nim->nifti_type, remain); if( remain < 16 ){ if( g_opts.debug > 2 ){ if( g_opts.skip_blank_ext ) fprintf(stderr,"-d no extender in '%s' is okay, as " "skip_blank_ext is set\n",nim->fname); else fprintf(stderr,"-d remain=%d, no space for extensions\n",remain); } return 0; } count = (int)znzread( extdr.extension, 1, 4, fp ); /* get extender */ if( count < 4 ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d file '%s' is too short for an extender\n", nim->fname); return 0; } if( extdr.extension[0] != 1 ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d extender[0] (%d) shows no extensions for '%s'\n", extdr.extension[0], nim->fname); return 0; } remain -= 4; if( g_opts.debug > 2 ) fprintf(stderr,"-d found valid 4-byte extender, remain = %d\n", remain); /* so we expect extensions, but have no idea of how many there may be */ count = 0; Elist = NULL; while (nifti_read_next_extension(&extn, nim, remain, fp) > 0) { if( nifti_add_exten_to_list(&extn, &Elist, count+1) < 0 ){ if( g_opts.debug > 0 ) fprintf(stderr,"** failed adding ext %d to list\n", count); return -1; } /* we have a new extension */ if( g_opts.debug > 1 ){ fprintf(stderr,"+d found extension #%d, code = 0x%x, size = %d\n", count, extn.ecode, extn.esize); if( extn.ecode == NIFTI_ECODE_AFNI && g_opts.debug > 2 ) /* ~XML */ fprintf(stderr," AFNI extension: %.*s\n", extn.esize-8,extn.edata); else if( extn.ecode == NIFTI_ECODE_COMMENT && g_opts.debug > 2 ) fprintf(stderr," COMMENT extension: %.*s\n", /* TEXT */ extn.esize-8,extn.edata); } remain -= extn.esize; count++; } if( g_opts.debug > 2 ) fprintf(stderr,"+d found %d extension(s)\n", count); nim->num_ext = count; nim->ext_list = Elist; return count; } /*----------------------------------------------------------------------*/ /*! nifti_add_extension - add an extension, with a copy of the data Add an extension to the nim->ext_list array. Fill this extension with a copy of the data, noting the length and extension code. \param nim - nifti_image to add extension to \param data - raw extension data \param len - length of raw extension data \param ecode - extension code \sa extension codes NIFTI_ECODE_* in nifti1_io.h \sa nifti_free_extensions, valid_nifti_extensions, nifti_copy_extensions \return 0 on success, -1 on error (and free the entire list) *//*--------------------------------------------------------------------*/ int nifti_add_extension(nifti_image *nim, const char * data, int len, int ecode) { nifti1_extension ext; /* error are printed in functions */ if( nifti_fill_extension(&ext, data, len, ecode) ) return -1; if( nifti_add_exten_to_list(&ext, &nim->ext_list, nim->num_ext+1)) return -1; nim->num_ext++; /* success, so increment */ return 0; } /*----------------------------------------------------------------------*/ /* nifti_add_exten_to_list - add a new nifti1_extension to the list We will append via "malloc, copy and free", because on an error, the list will revert to the previous one (sorry realloc(), only quality dolphins get to become part of St@rk!st brand tunafish). return 0 on success, -1 on error (and free the entire list) *//*--------------------------------------------------------------------*/ static int nifti_add_exten_to_list( nifti1_extension * new_ext, nifti1_extension ** list, int new_length ) { nifti1_extension * tmplist; tmplist = *list; *list = (nifti1_extension *)malloc(new_length * sizeof(nifti1_extension)); /* check for failure first */ if( ! *list ){ fprintf(stderr,"** failed to alloc %d extension structs (%d bytes)\n", new_length, new_length*(int)sizeof(nifti1_extension)); if( !tmplist ) return -1; /* no old list to lose */ *list = tmplist; /* reset list to old one */ return -1; } /* if an old list exists, copy the pointers and free the list */ if( tmplist ){ memcpy(*list, tmplist, (new_length-1)*sizeof(nifti1_extension)); free(tmplist); } /* for some reason, I just don't like struct copy... */ (*list)[new_length-1].esize = new_ext->esize; (*list)[new_length-1].ecode = new_ext->ecode; (*list)[new_length-1].edata = new_ext->edata; if( g_opts.debug > 2 ) fprintf(stderr,"+d allocated and appended extension #%d to list\n", new_length); return 0; } /*----------------------------------------------------------------------*/ /* nifti_fill_extension - given data and length, fill an extension struct Allocate memory for data, copy data, set the size and code. return 0 on success, -1 on error (and free the entire list) *//*--------------------------------------------------------------------*/ static int nifti_fill_extension( nifti1_extension *ext, const char * data, int len, int ecode) { int esize; if( !ext || !data || len < 0 ){ fprintf(stderr,"** fill_ext: bad params (%p,%p,%d)\n", (void *)ext, (const void *)data, len); return -1; } else if( ! nifti_is_valid_ecode(ecode) ){ fprintf(stderr,"** fill_ext: invalid ecode %d\n", ecode); return -1; } /* compute esize, first : len+8, and take ceiling up to a mult of 16 */ esize = len+8; if( esize & 0xf ) esize = (esize + 0xf) & ~0xf; ext->esize = esize; /* allocate esize-8 (maybe more than len), using calloc for fill */ ext->edata = (char *)calloc(esize-8, sizeof(char)); if( !ext->edata ){ fprintf(stderr,"** NFE: failed to alloc %d bytes for extension\n",len); return -1; } memcpy(ext->edata, data, len); /* copy the data, using len */ ext->ecode = ecode; /* set the ecode */ if( g_opts.debug > 2 ) fprintf(stderr,"+d alloc %d bytes for ext len %d, ecode %d, esize %d\n", esize-8, len, ecode, esize); return 0; } /*---------------------------------------------------------------------- * nifti_read_next_extension - read a single extension from the file * * return (>= 0 is okay): * * success : esize * no extension : 0 * error : -1 *----------------------------------------------------------------------*/ static int nifti_read_next_extension( nifti1_extension * nex, nifti_image *nim, int remain, znzFile fp ) { int swap = nim->byteorder != nifti_short_order(); int count, size, code = 0; /* first clear nex */ nex->esize = nex->ecode = 0; nex->edata = NULL; if( remain < 16 ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d only %d bytes remain, so no extension\n", remain); return 0; } /* must start with 4-byte size and code */ count = (int)znzread( &size, 4, 1, fp ); if( count == 1 ) count += (int)znzread( &code, 4, 1, fp ); if( count != 2 ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d current extension read failed\n"); if (znzseek(fp, -4*count, SEEK_CUR) < 0) /* back up past any read */ return -1; return 0; /* no extension, no error condition */ } if( swap ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d pre-swap exts: code %d, size %d\n", code, size); nifti_swap_4bytes(1, &size); nifti_swap_4bytes(1, &code); } if( g_opts.debug > 2 ) fprintf(stderr,"-d potential extension: code %d, size %d\n", code, size); if( !nifti_check_extension(nim, size, code, remain) ){ if( znzseek(fp, -8, SEEK_CUR) < 0 ){ /* back up past any read */ fprintf(stderr,"** failure to back out of extension read!\n"); return -1; } return 0; } /* now get the actual data */ nex->esize = size; nex->ecode = code; size -= 8; /* subtract space for size and code in extension */ nex->edata = (char *)malloc(size * sizeof(char)); if( !nex->edata ){ fprintf(stderr,"** failed to allocate %d bytes for extension\n",size); return -1; } count = (int)znzread(nex->edata, 1, size, fp); if( count < size ){ if( g_opts.debug > 0 ) fprintf(stderr,"-d read only %d (of %d) bytes for extension\n", count, size); free(nex->edata); nex->edata = NULL; return -1; } /* success! */ if( g_opts.debug > 2 ) fprintf(stderr,"+d successfully read extension, code %d, size %d\n", nex->ecode, nex->esize); return nex->esize; } /*----------------------------------------------------------------------*/ /*! for each extension, check code, size and data pointer *//*--------------------------------------------------------------------*/ int valid_nifti_extensions(const nifti_image * nim) { nifti1_extension * ext; int c, errs; if( nim->num_ext <= 0 || nim->ext_list == NULL ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d empty extension list\n"); return 0; } /* for each extension, check code, size and data pointer */ ext = nim->ext_list; errs = 0; for ( c = 0; c < nim->num_ext; c++ ){ if( ! nifti_is_valid_ecode(ext->ecode) ) { if( g_opts.debug > 1 ) fprintf(stderr,"-d ext %d, invalid code %d\n", c, ext->ecode); errs++; } if( ext->esize <= 0 ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d ext %d, bad size = %d\n", c, ext->esize); errs++; } else if( ext->esize & 0xf ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d ext %d, size %d not multiple of 16\n", c, ext->esize); errs++; } if( ext->edata == NULL ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d ext %d, missing data\n", c); errs++; } ext++; } if( errs > 0 ){ if( g_opts.debug > 0 ) fprintf(stderr,"-d had %d extension errors, none will be written\n", errs); return 0; } /* if we're here, we're good */ return 1; } /*----------------------------------------------------------------------*/ /*! check whether the extension code is valid \return 1 if valid, 0 otherwise *//*--------------------------------------------------------------------*/ int nifti_is_valid_ecode( int ecode ) { if( ecode < NIFTI_ECODE_IGNORE || /* minimum code number (0) */ ecode > NIFTI_MAX_ECODE || /* maximum code number */ ecode & 1 ) /* cannot be odd */ return 0; return 1; } /*---------------------------------------------------------------------- * check for valid size and code, as well as can be done *----------------------------------------------------------------------*/ static int nifti_check_extension(nifti_image *nim, int size, int code, int rem) { /* check for bad code before bad size */ if( ! nifti_is_valid_ecode(code) ) { if( g_opts.debug > 2 ) fprintf(stderr,"-d invalid extension code %d\n",code); return 0; } if( size < 16 ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d ext size %d, no extension\n",size); return 0; } if( size > rem ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d ext size %d, space %d, no extension\n", size, rem); return 0; } if( size & 0xf ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d nifti extension size %d not multiple of 16\n",size); return 0; } if( nim->nifti_type == NIFTI_FTYPE_ASCII && size > LNI_MAX_NIA_EXT_LEN ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d NVE, bad nifti_type 3 size %d\n", size); return 0; } return 1; } /*---------------------------------------------------------------------- * nifti_image_load_prep - prepare to read data * * Check nifti_image fields, open the file and seek to the appropriate * offset for reading. * * return NULL on failure *----------------------------------------------------------------------*/ static znzFile nifti_image_load_prep( nifti_image *nim ) { /* set up data space, open data file and seek, then call nifti_read_buffer */ size_t ntot , ii , ioff; znzFile fp; char *tmpimgname; char fname[] = { "nifti_image_load_prep" }; /**- perform sanity checks */ if( nim == NULL || nim->iname == NULL || nim->nbyper <= 0 || nim->nvox <= 0 ) { if ( g_opts.debug > 0 ){ if( !nim ) fprintf(stderr,"** ERROR: N_image_load: no nifti image\n"); else fprintf(stderr,"** ERROR: N_image_load: bad params (%p,%d,%u)\n", (void *)nim->iname, nim->nbyper, (unsigned)nim->nvox); } return NULL; } ntot = nifti_get_volsize(nim) ; /* total bytes to read */ /**- open image data file */ tmpimgname = nifti_findimgname(nim->iname , nim->nifti_type); if( tmpimgname == NULL ){ if( g_opts.debug > 0 ) fprintf(stderr,"** no image file found for '%s'\n",nim->iname); return NULL; } fp = znzopen(tmpimgname, "rb", nifti_is_gzfile(tmpimgname)); if (znz_isnull(fp)){ if(g_opts.debug > 0) LNI_FERR(fname,"cannot open data file",tmpimgname); free(tmpimgname); return NULL; /* bad open? */ } free(tmpimgname); /**- get image offset: a negative offset means to figure from end of file */ if( nim->iname_offset < 0 ){ if( nifti_is_gzfile(nim->iname) ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"negative offset for compressed file",nim->iname); znzclose(fp); return NULL; } ii = nifti_get_filesize( nim->iname ) ; if( ii <= 0 ){ if( g_opts.debug > 0 ) LNI_FERR(fname,"empty data file",nim->iname); znzclose(fp); return NULL; } ioff = (ii > ntot) ? ii-ntot : 0 ; } else { /* non-negative offset */ ioff = nim->iname_offset ; /* means use it directly */ } /**- seek to the appropriate read position */ if( znzseek(fp , (long)ioff , SEEK_SET) < 0 ){ fprintf(stderr,"** could not seek to offset %u in file '%s'\n", (unsigned)ioff, nim->iname); znzclose(fp); return NULL; } /**- and return the File pointer */ return fp; } /*---------------------------------------------------------------------- * nifti_image_load *----------------------------------------------------------------------*/ /*! \fn int nifti_image_load( nifti_image *nim ) \brief Load the image blob into a previously initialized nifti_image. - If not yet set, the data buffer is allocated with calloc(). - The data buffer will be byteswapped if necessary. - The data buffer will not be scaled. This function is used to read the image from disk. It should be used after a function such as nifti_image_read(), so that the nifti_image structure is already initialized. \param nim pointer to a nifti_image (previously initialized) \return 0 on success, -1 on failure \sa nifti_image_read, nifti_image_free, nifti_image_unload */ int nifti_image_load( nifti_image *nim ) { /* set up data space, open data file and seek, then call nifti_read_buffer */ size_t ntot , ii ; znzFile fp ; /**- open the file and position the FILE pointer */ fp = nifti_image_load_prep( nim ); if( fp == NULL ){ if( g_opts.debug > 0 ) fprintf(stderr,"** nifti_image_load, failed load_prep\n"); return -1; } ntot = nifti_get_volsize(nim); /**- if the data pointer is not yet set, get memory space for the image */ if( nim->data == NULL ) { nim->data = (void *)calloc(1,ntot) ; /* create image memory */ if( nim->data == NULL ){ if( g_opts.debug > 0 ) fprintf(stderr,"** failed to alloc %d bytes for image data\n", (int)ntot); znzclose(fp); return -1; } } /**- now that everything is set up, do the reading */ ii = nifti_read_buffer(fp,nim->data,ntot,nim); if( ii < ntot ){ znzclose(fp) ; free(nim->data) ; nim->data = NULL ; return -1 ; /* errors were printed in nifti_read_buffer() */ } /**- close the file */ znzclose( fp ) ; return 0 ; } /* 30 Nov 2004 [rickr] #undef ERREX #define ERREX(msg) \ do{ fprintf(stderr,"** ERROR: nifti_read_buffer: %s\n",(msg)) ; \ return 0; } while(0) */ /*----------------------------------------------------------------------*/ /*! read ntot bytes of data from an open file and byte swaps if necessary note that nifti_image is required for information on datatype, bsize (for any needed byte swapping), etc. This function does not allocate memory, so dataptr must be valid. *//*--------------------------------------------------------------------*/ size_t nifti_read_buffer(znzFile fp, void* dataptr, size_t ntot, nifti_image *nim) { size_t ii; if( dataptr == NULL ){ if( g_opts.debug > 0 ) fprintf(stderr,"** ERROR: nifti_read_buffer: NULL dataptr\n"); return -1; } ii = znzread( dataptr , 1 , ntot , fp ) ; /* data input */ /* if read was short, fail */ if( ii < ntot ){ if( g_opts.debug > 0 ) fprintf(stderr,"++ WARNING: nifti_read_buffer(%s):\n" " data bytes needed = %u\n" " data bytes input = %u\n" " number missing = %u (set to 0)\n", nim->iname , (unsigned int)ntot , (unsigned int)ii , (unsigned int)(ntot-ii) ) ; /* memset( (char *)(dataptr)+ii , 0 , ntot-ii ) ; now failure [rickr] */ return -1 ; } if( g_opts.debug > 2 ) fprintf(stderr,"+d nifti_read_buffer: read %u bytes\n", (unsigned)ii); /* byte swap array if needed */ /* ntot/swapsize might not fit as int, use size_t 6 Jul 2010 [rickr] */ if( nim->swapsize > 1 && nim->byteorder != nifti_short_order() ) { if( g_opts.debug > 1 ) fprintf(stderr,"+d nifti_read_buffer: swapping data bytes...\n"); nifti_swap_Nbytes( ntot / nim->swapsize, nim->swapsize , dataptr ) ; } #ifdef isfinite { /* check input float arrays for goodness, and fix bad floats */ int fix_count = 0 ; switch( nim->datatype ){ case NIFTI_TYPE_FLOAT32: case NIFTI_TYPE_COMPLEX64:{ register float *far = (float *)dataptr ; register size_t jj,nj ; nj = ntot / sizeof(float) ; for( jj=0 ; jj < nj ; jj++ ) /* count fixes 30 Nov 2004 [rickr] */ if( !IS_GOOD_FLOAT(far[jj]) ){ far[jj] = 0 ; fix_count++ ; } } break ; case NIFTI_TYPE_FLOAT64: case NIFTI_TYPE_COMPLEX128:{ register double *far = (double *)dataptr ; register size_t jj,nj ; nj = ntot / sizeof(double) ; for( jj=0 ; jj < nj ; jj++ ) /* count fixes 30 Nov 2004 [rickr] */ if( !IS_GOOD_FLOAT(far[jj]) ){ far[jj] = 0 ; fix_count++ ; } } break ; } if( g_opts.debug > 1 ) fprintf(stderr,"+d in image, %d bad floats were set to 0\n", fix_count); } #endif return ii; } /*--------------------------------------------------------------------------*/ /*! Unload the data in a nifti_image struct, but keep the metadata. *//*------------------------------------------------------------------------*/ void nifti_image_unload( nifti_image *nim ) { if( nim != NULL && nim->data != NULL ){ free(nim->data) ; nim->data = NULL ; } return ; } /*--------------------------------------------------------------------------*/ /*! free 'everything' about a nifti_image struct (including the passed struct) free (only fields which are not NULL): - fname and iname - data - any ext_list[i].edata - ext_list - nim *//*------------------------------------------------------------------------*/ void nifti_image_free( nifti_image *nim ) { if( nim == NULL ) return ; if( nim->fname != NULL ) free(nim->fname) ; if( nim->iname != NULL ) free(nim->iname) ; if( nim->data != NULL ) free(nim->data ) ; (void)nifti_free_extensions( nim ) ; free(nim) ; return ; } /*--------------------------------------------------------------------------*/ /*! free the nifti extensions - If any edata pointer is set in the extension list, free() it. - Free ext_list, if it is set. - Clear num_ext and ext_list from nim. \return 0 on success, -1 on error \sa nifti_add_extension, nifti_copy_extensions *//*------------------------------------------------------------------------*/ int nifti_free_extensions( nifti_image *nim ) { int c ; if( nim == NULL ) return -1; if( nim->num_ext > 0 && nim->ext_list ){ for( c = 0; c < nim->num_ext; c++ ) if ( nim->ext_list[c].edata ) free(nim->ext_list[c].edata); free(nim->ext_list); } /* or if it is inconsistent, warn the user (if we are not in quiet mode) */ else if ( (nim->num_ext > 0 || nim->ext_list != NULL) && (g_opts.debug > 0) ) fprintf(stderr,"** warning: nifti extension num/ptr mismatch (%d,%p)\n", nim->num_ext, (void *)nim->ext_list); if( g_opts.debug > 2 ) fprintf(stderr,"+d free'd %d extension(s)\n", nim->num_ext); nim->num_ext = 0; nim->ext_list = NULL; return 0; } /*--------------------------------------------------------------------------*/ /*! Print to stdout some info about a nifti_image struct. *//*------------------------------------------------------------------------*/ void nifti_image_infodump( const nifti_image *nim ) { char *str = nifti_image_to_ascii( nim ) ; /* stdout -> stderr 2 Dec 2004 [rickr] */ if( str != NULL ){ fputs(str,stderr) ; free(str) ; } return ; } /*-------------------------------------------------------------------------- * nifti_write_buffer just check for a null znzFile and call znzwrite *--------------------------------------------------------------------------*/ /*! \fn size_t nifti_write_buffer(znzFile fp, void *buffer, size_t numbytes) \brief write numbytes of buffer to file, fp \param fp File pointer (from znzopen) to gzippable nifti datafile \param buffer data buffer to be written \param numbytes number of bytes in buffer to write \return number of bytes successfully written */ size_t nifti_write_buffer(znzFile fp, const void *buffer, size_t numbytes) { /* Write all the image data at once (no swapping here) */ size_t ss; if (znz_isnull(fp)){ fprintf(stderr,"** ERROR: nifti_write_buffer: null file pointer\n"); return 0; } ss = znzwrite( buffer , 1 , numbytes , fp ) ; return ss; } /*----------------------------------------------------------------------*/ /*! write the nifti_image data to file (from nim->data or from NBL) If NBL is not NULL, write the data from that structure. Otherwise, write it out from nim->data. No swapping is done here. \param fp : File pointer \param nim : nifti_image corresponding to the data \param NBL : optional source of write data (if NULL use nim->data) \return 0 on success, -1 on failure Note: the nifti_image byte_order is set as that of the current CPU. This is because such a conversion was made to the data upon reading, while byte_order was not set (so the programs would know what format the data was on disk). Effectively, since byte_order should match what is on disk, it should bet set to that of the current CPU whenever new filenames are assigned. *//*--------------------------------------------------------------------*/ int nifti_write_all_data(znzFile fp, nifti_image * nim, const nifti_brick_list * NBL) { size_t ss; int bnum; if( !NBL ){ /* just write one buffer and get out of here */ if( nim->data == NULL ){ fprintf(stderr,"** NWAD: no image data to write\n"); return -1; } ss = nifti_write_buffer(fp,nim->data,nim->nbyper * nim->nvox); if (ss < nim->nbyper * nim->nvox){ fprintf(stderr, "** ERROR: NWAD: wrote only %u of %u bytes to file\n", (unsigned)ss, (unsigned)(nim->nbyper * nim->nvox)); return -1; } if( g_opts.debug > 1 ) fprintf(stderr,"+d wrote single image of %u bytes\n", (unsigned)ss); } else { if( ! NBL->bricks || NBL->nbricks <= 0 || NBL->bsize <= 0 ){ fprintf(stderr,"** NWAD: no brick data to write (%p,%d,%u)\n", (void *)NBL->bricks, NBL->nbricks, (unsigned)NBL->bsize); return -1; } for( bnum = 0; bnum < NBL->nbricks; bnum++ ){ ss = nifti_write_buffer(fp, NBL->bricks[bnum], NBL->bsize); if( ss < NBL->bsize ){ fprintf(stderr, "** NWAD ERROR: wrote %u of %u bytes of brick %d of %d to file", (unsigned)ss, (unsigned)NBL->bsize, bnum+1, NBL->nbricks); return -1; } } if( g_opts.debug > 1 ) fprintf(stderr,"+d wrote image of %d brick(s), each of %u bytes\n", NBL->nbricks, (unsigned int)NBL->bsize); } /* mark as being in this CPU byte order */ nim->byteorder = nifti_short_order() ; return 0; } /* return number of extensions written, or -1 on error */ static int nifti_write_extensions(znzFile fp, nifti_image *nim) { nifti1_extension * list; char extdr[4] = { 0, 0, 0, 0 }; int c, size, ok = 1; if( znz_isnull(fp) || !nim || nim->num_ext < 0 ){ if( g_opts.debug > 0 ) fprintf(stderr,"** nifti_write_extensions, bad params\n"); return -1; } /* if no extensions and user requests it, skip extender */ if( g_opts.skip_blank_ext && (nim->num_ext == 0 || ! nim->ext_list ) ){ if( g_opts.debug > 1 ) fprintf(stderr,"-d no exts and skip_blank_ext set, " "so skipping 4-byte extender\n"); return 0; } /* if invalid extension list, clear num_ext */ if( ! valid_nifti_extensions(nim) ) nim->num_ext = 0; /* write out extender block */ if( nim->num_ext > 0 ) extdr[0] = 1; if( nifti_write_buffer(fp, extdr, 4) != 4 ){ fprintf(stderr,"** failed to write extender\n"); return -1; } list = nim->ext_list; for ( c = 0; c < nim->num_ext; c++ ){ size = (int)nifti_write_buffer(fp, &list->esize, sizeof(int)); ok = (size == (int)sizeof(int)); if( ok ){ size = (int)nifti_write_buffer(fp, &list->ecode, sizeof(int)); ok = (size == (int)sizeof(int)); } if( ok ){ size = (int)nifti_write_buffer(fp, list->edata, list->esize - 8); ok = (size == list->esize - 8); } if( !ok ){ fprintf(stderr,"** failed while writing extension #%d\n",c); return -1; } else if ( g_opts.debug > 2 ) fprintf(stderr,"+d wrote extension %d of %d bytes\n", c, size); list++; } if( g_opts.debug > 1 ) fprintf(stderr,"+d wrote out %d extension(s)\n", nim->num_ext); return nim->num_ext; } /*----------------------------------------------------------------------*/ /*! basic initialization of a nifti_image struct (to a 1x1x1 image) *//*--------------------------------------------------------------------*/ nifti_image* nifti_simple_init_nim(void) { nifti_image *nim; struct nifti_1_header nhdr; int nbyper, swapsize; memset(&nhdr,0,sizeof(nhdr)) ; /* zero out header, to be safe */ nhdr.sizeof_hdr = sizeof(nhdr) ; nhdr.regular = 'r' ; /* for some stupid reason */ nhdr.dim[0] = 3 ; nhdr.dim[1] = 1 ; nhdr.dim[2] = 1 ; nhdr.dim[3] = 1 ; nhdr.dim[4] = 0 ; nhdr.pixdim[0] = 0.0 ; nhdr.pixdim[1] = 1.0 ; nhdr.pixdim[2] = 1.0 ; nhdr.pixdim[3] = 1.0 ; nhdr.datatype = DT_FLOAT32 ; nifti_datatype_sizes( nhdr.datatype , &nbyper, &swapsize ); nhdr.bitpix = 8 * nbyper ; strcpy(nhdr.magic, "n+1"); /* init to single file */ nim = nifti_convert_nhdr2nim(nhdr,NULL); nim->fname = NULL; nim->iname = NULL; return nim; } /*----------------------------------------------------------------------*/ /*! basic initialization of a nifti_1_header struct (with given dimensions) Return an allocated nifti_1_header struct, based on the given dimensions and datatype. \param arg_dims : optional dim[8] array (default {3,1,1,1,0,0,0,0}) \param arg_dtype : optional datatype (default DT_FLOAT32) \return pointer to allocated nifti_1_header struct *//*--------------------------------------------------------------------*/ nifti_1_header * nifti_make_new_header(const int arg_dims[], int arg_dtype) { nifti_1_header * nhdr; const int default_dims[8] = { 3, 1, 1, 1, 0, 0, 0, 0 }; const int * dim; /* either passed or default dims */ int dtype; /* either passed or default dtype */ int c, nbyper, swapsize; /* if arg_dims is passed, apply it */ if( arg_dims ) dim = arg_dims; else dim = default_dims; /* validate dim: if there is any problem, apply default_dims */ if( dim[0] < 1 || dim[0] > 7 ) { fprintf(stderr,"** nifti_simple_hdr_with_dims: bad dim[0]=%d\n",dim[0]); dim = default_dims; } else { for( c = 1; c <= dim[0]; c++ ) if( dim[c] < 1 ) { fprintf(stderr, "** nifti_simple_hdr_with_dims: bad dim[%d]=%d\n",c,dim[c]); dim = default_dims; break; } } /* validate dtype, too */ dtype = arg_dtype; if( ! nifti_is_valid_datatype(dtype) ) { fprintf(stderr,"** nifti_simple_hdr_with_dims: bad dtype %d\n",dtype); dtype = DT_FLOAT32; } /* now populate the header struct */ if( g_opts.debug > 1 ) fprintf(stderr,"+d nifti_make_new_header, dim[0] = %d, datatype = %d\n", dim[0], dtype); nhdr = (nifti_1_header *)calloc(1,sizeof(nifti_1_header)); if( !nhdr ){ fprintf(stderr,"** nifti_make_new_header: failed to alloc hdr\n"); return NULL; } nhdr->sizeof_hdr = sizeof(nifti_1_header) ; nhdr->regular = 'r' ; /* for some stupid reason */ /* init dim and pixdim */ nhdr->dim[0] = dim[0] ; nhdr->pixdim[0] = 0.0; for( c = 1; c <= dim[0]; c++ ) { nhdr->dim[c] = dim[c]; nhdr->pixdim[c] = 1.0; } nhdr->datatype = dtype ; nifti_datatype_sizes( nhdr->datatype , &nbyper, &swapsize ); nhdr->bitpix = 8 * nbyper ; strcpy(nhdr->magic, "n+1"); /* init to single file */ return nhdr; } /*----------------------------------------------------------------------*/ /*! basic creation of a nifti_image struct Create a nifti_image from the given dimensions and data type. Optinally, allocate zero-filled data. \param dims : optional dim[8] (default {3,1,1,1,0,0,0,0}) \param datatype : optional datatype (default DT_FLOAT32) \param data_fill : if flag is set, allocate zero-filled data for image \return pointer to allocated nifti_image struct *//*--------------------------------------------------------------------*/ nifti_image * nifti_make_new_nim(const int dims[], int datatype, int data_fill) { nifti_image * nim; nifti_1_header * nhdr; nhdr = nifti_make_new_header(dims, datatype); if( !nhdr ) return NULL; /* error already printed */ nim = nifti_convert_nhdr2nim(*nhdr,NULL); free(nhdr); /* in any case, we are done with this */ if( !nim ){ fprintf(stderr,"** NMNN: nifti_convert_nhdr2nim failure\n"); return NULL; } if( g_opts.debug > 1 ) fprintf(stderr,"+d nifti_make_new_nim, data_fill = %d\n",data_fill); if( data_fill ) { nim->data = calloc(nim->nvox, nim->nbyper); /* if we cannot allocate data, take ball and go home */ if( !nim->data ) { fprintf(stderr,"** NMNN: failed to alloc %u bytes for data\n", (unsigned)(nim->nvox*nim->nbyper)); nifti_image_free(nim); nim = NULL; } } return nim; } /*----------------------------------------------------------------------*/ /*! convert a nifti_image structure to a nifti_1_header struct No allocation is done, this should be used via structure copy. As in:
    nifti_1_header my_header;
    my_header = nifti_convert_nim2nhdr(my_nim_pointer);
    
*//*--------------------------------------------------------------------*/ struct nifti_1_header nifti_convert_nim2nhdr(const nifti_image * nim) { struct nifti_1_header nhdr; memset(&nhdr,0,sizeof(nhdr)) ; /* zero out header, to be safe */ /**- load the ANALYZE-7.5 generic parts of the header struct */ nhdr.sizeof_hdr = sizeof(nhdr) ; nhdr.regular = 'r' ; /* for some stupid reason */ nhdr.dim[0] = nim->ndim ; nhdr.dim[1] = nim->nx ; nhdr.dim[2] = nim->ny ; nhdr.dim[3] = nim->nz ; nhdr.dim[4] = nim->nt ; nhdr.dim[5] = nim->nu ; nhdr.dim[6] = nim->nv ; nhdr.dim[7] = nim->nw ; nhdr.pixdim[0] = 0.0 ; nhdr.pixdim[1] = nim->dx ; nhdr.pixdim[2] = nim->dy ; nhdr.pixdim[3] = nim->dz ; nhdr.pixdim[4] = nim->dt ; nhdr.pixdim[5] = nim->du ; nhdr.pixdim[6] = nim->dv ; nhdr.pixdim[7] = nim->dw ; nhdr.datatype = nim->datatype ; nhdr.bitpix = 8 * nim->nbyper ; if( nim->cal_max > nim->cal_min ){ nhdr.cal_max = nim->cal_max ; nhdr.cal_min = nim->cal_min ; } if( nim->scl_slope != 0.0 ){ nhdr.scl_slope = nim->scl_slope ; nhdr.scl_inter = nim->scl_inter ; } if( nim->descrip[0] != '\0' ){ memcpy(nhdr.descrip ,nim->descrip ,79) ; nhdr.descrip[79] = '\0' ; } if( nim->aux_file[0] != '\0' ){ memcpy(nhdr.aux_file ,nim->aux_file ,23) ; nhdr.aux_file[23] = '\0' ; } /**- Load NIFTI specific stuff into the header */ if( nim->nifti_type > NIFTI_FTYPE_ANALYZE ){ /* then not ANALYZE */ if( nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ) strcpy(nhdr.magic,"n+1") ; else strcpy(nhdr.magic,"ni1") ; nhdr.pixdim[1] = fabs(nhdr.pixdim[1]) ; nhdr.pixdim[2] = fabs(nhdr.pixdim[2]) ; nhdr.pixdim[3] = fabs(nhdr.pixdim[3]) ; nhdr.pixdim[4] = fabs(nhdr.pixdim[4]) ; nhdr.pixdim[5] = fabs(nhdr.pixdim[5]) ; nhdr.pixdim[6] = fabs(nhdr.pixdim[6]) ; nhdr.pixdim[7] = fabs(nhdr.pixdim[7]) ; nhdr.intent_code = nim->intent_code ; nhdr.intent_p1 = nim->intent_p1 ; nhdr.intent_p2 = nim->intent_p2 ; nhdr.intent_p3 = nim->intent_p3 ; if( nim->intent_name[0] != '\0' ){ memcpy(nhdr.intent_name,nim->intent_name,15) ; nhdr.intent_name[15] = '\0' ; } nhdr.vox_offset = (float) nim->iname_offset ; nhdr.xyzt_units = SPACE_TIME_TO_XYZT( nim->xyz_units, nim->time_units ) ; nhdr.toffset = nim->toffset ; if( nim->qform_code > 0 ){ nhdr.qform_code = nim->qform_code ; nhdr.quatern_b = nim->quatern_b ; nhdr.quatern_c = nim->quatern_c ; nhdr.quatern_d = nim->quatern_d ; nhdr.qoffset_x = nim->qoffset_x ; nhdr.qoffset_y = nim->qoffset_y ; nhdr.qoffset_z = nim->qoffset_z ; nhdr.pixdim[0] = (nim->qfac >= 0.0) ? 1.0 : -1.0 ; } if( nim->sform_code > 0 ){ nhdr.sform_code = nim->sform_code ; nhdr.srow_x[0] = nim->sto_xyz.m[0][0] ; nhdr.srow_x[1] = nim->sto_xyz.m[0][1] ; nhdr.srow_x[2] = nim->sto_xyz.m[0][2] ; nhdr.srow_x[3] = nim->sto_xyz.m[0][3] ; nhdr.srow_y[0] = nim->sto_xyz.m[1][0] ; nhdr.srow_y[1] = nim->sto_xyz.m[1][1] ; nhdr.srow_y[2] = nim->sto_xyz.m[1][2] ; nhdr.srow_y[3] = nim->sto_xyz.m[1][3] ; nhdr.srow_z[0] = nim->sto_xyz.m[2][0] ; nhdr.srow_z[1] = nim->sto_xyz.m[2][1] ; nhdr.srow_z[2] = nim->sto_xyz.m[2][2] ; nhdr.srow_z[3] = nim->sto_xyz.m[2][3] ; } nhdr.dim_info = FPS_INTO_DIM_INFO( nim->freq_dim , nim->phase_dim , nim->slice_dim ) ; nhdr.slice_code = nim->slice_code ; nhdr.slice_start = nim->slice_start ; nhdr.slice_end = nim->slice_end ; nhdr.slice_duration = nim->slice_duration ; } return nhdr; } /*----------------------------------------------------------------------*/ /*! \fn int nifti_copy_extensions(nifti_image * nim_dest, nifti_image * nim_src) \brief copy the nifti1_extension list from src to dest Duplicate the list of nifti1_extensions. The dest structure must be clear of extensions. \return 0 on success, -1 on failure \sa nifti_add_extension, nifti_free_extensions */ int nifti_copy_extensions(nifti_image * nim_dest, const nifti_image * nim_src) { char * data; size_t bytes; int c, size, old_size; if( nim_dest->num_ext > 0 || nim_dest->ext_list != NULL ){ fprintf(stderr,"** will not copy extensions over existing ones\n"); return -1; } if( g_opts.debug > 1 ) fprintf(stderr,"+d duplicating %d extension(s)\n", nim_src->num_ext); if( nim_src->num_ext <= 0 ) return 0; bytes = nim_src->num_ext * sizeof(nifti1_extension); /* I'm lazy */ nim_dest->ext_list = (nifti1_extension *)malloc(bytes); if( !nim_dest->ext_list ){ fprintf(stderr,"** failed to allocate %d nifti1_extension structs\n", nim_src->num_ext); return -1; } /* copy the extension data */ nim_dest->num_ext = 0; for( c = 0; c < nim_src->num_ext; c++ ){ size = old_size = nim_src->ext_list[c].esize; if( size & 0xf ) size = (size + 0xf) & ~0xf; /* make multiple of 16 */ if( g_opts.debug > 2 ) fprintf(stderr,"+d dup'ing ext #%d of size %d (from size %d)\n", c, size, old_size); /* data length is size-8, as esize includes space for esize and ecode */ data = (char *)calloc(size-8,sizeof(char)); /* maybe size > old */ if( !data ){ fprintf(stderr,"** failed to alloc %d bytes for extention\n", size); if( c == 0 ) { free(nim_dest->ext_list); nim_dest->ext_list = NULL; } /* otherwise, keep what we have (a.o.t. deleting them all) */ return -1; } /* finally, fill the new structure */ nim_dest->ext_list[c].esize = size; nim_dest->ext_list[c].ecode = nim_src->ext_list[c].ecode; nim_dest->ext_list[c].edata = data; memcpy(data, nim_src->ext_list[c].edata, old_size-8); nim_dest->num_ext++; } return 0; } /*----------------------------------------------------------------------*/ /*! compute the total size of all extensions \return the total of all esize fields Note that each esize includes 4 bytes for ecode, 4 bytes for esize, and the bytes used for the data. Each esize also needs to be a multiple of 16, so it may be greater than the sum of its 3 parts. *//*--------------------------------------------------------------------*/ static int nifti_extension_size(nifti_image *nim) { int c, size = 0; if( !nim || nim->num_ext <= 0 ) return 0; if( g_opts.debug > 2 ) fprintf(stderr,"-d ext sizes:"); for ( c = 0; c < nim->num_ext; c++ ){ size += nim->ext_list[c].esize; if( g_opts.debug > 2 ) fprintf(stderr," %d",nim->ext_list[c].esize); } if( g_opts.debug > 2 ) fprintf(stderr," (total = %d)\n",size); return size; } /*----------------------------------------------------------------------*/ /*! set the nifti_image iname_offset field, based on nifti_type - if writing to 2 files, set offset to 0 - if writing to a single NIFTI-1 file, set the offset to 352 + total extension size, then align to 16-byte boundary - if writing an ASCII header, set offset to -1 *//*--------------------------------------------------------------------*/ void nifti_set_iname_offset(nifti_image *nim) { int offset; switch( nim->nifti_type ){ default: /* writing into 2 files */ /* we only write files with 0 offset in the 2 file format */ nim->iname_offset = 0 ; break ; /* NIFTI-1 single binary file - always update */ case NIFTI_FTYPE_NIFTI1_1: offset = nifti_extension_size(nim)+sizeof(struct nifti_1_header)+4; /* be sure offset is aligned to a 16 byte boundary */ if ( ( offset % 16 ) != 0 ) offset = ((offset + 0xf) & ~0xf); if( nim->iname_offset != offset ){ if( g_opts.debug > 1 ) fprintf(stderr,"+d changing offset from %d to %d\n", nim->iname_offset, offset); nim->iname_offset = offset; } break ; /* non-standard case: NIFTI-1 ASCII header + binary data (single file) */ case NIFTI_FTYPE_ASCII: nim->iname_offset = -1 ; /* compute offset from filesize */ break ; } } /*----------------------------------------------------------------------*/ /*! write the nifti_image dataset to disk, optionally including data This is just a front-end for nifti_image_write_hdr_img2. \param nim nifti_image to write to disk \param write_data write options (see nifti_image_write_hdr_img2) \param opts file open options ("wb" from nifti_image_write) \sa nifti_image_write, nifti_image_write_hdr_img2, nifti_image_free, nifti_set_filenames *//*--------------------------------------------------------------------*/ znzFile nifti_image_write_hdr_img( nifti_image *nim , int write_data , const char* opts ) { return nifti_image_write_hdr_img2(nim,write_data,opts,NULL,NULL); } #undef ERREX #define ERREX(msg) \ do{ fprintf(stderr,"** ERROR: nifti_image_write_hdr_img: %s\n",(msg)) ; \ return fp ; } while(0) /* ----------------------------------------------------------------------*/ /*! This writes the header (and optionally the image data) to file * * If the image data file is left open it returns a valid znzFile handle. * It also uses imgfile as the open image file is not null, and modifies * it inside. * * \param nim nifti_image to write to disk * \param write_opts flags whether to write data and/or close file (see below) * \param opts file-open options, probably "wb" from nifti_image_write() * \param imgfile optional open znzFile struct, for writing image data (may be NULL) * \param NBL optional nifti_brick_list, containing the image data (may be NULL) * * Values for write_opts mode are based on two binary flags * ( 0/1 for no-write/write data, and 0/2 for close/leave-open files ) : * - 0 = do not write data and close (do not open data file) * - 1 = write data and close * - 2 = do not write data and leave data file open * - 3 = write data and leave data file open * * \sa nifti_image_write, nifti_image_write_hdr_img, nifti_image_free, * nifti_set_filenames *//*---------------------------------------------------------------------*/ znzFile nifti_image_write_hdr_img2(nifti_image *nim, int write_opts, const char * opts, znzFile imgfile, const nifti_brick_list * NBL) { struct nifti_1_header nhdr ; znzFile fp=NULL; size_t ss ; int write_data, leave_open; char func[] = { "nifti_image_write_hdr_img2" }; write_data = write_opts & 1; /* just separate the bits now */ leave_open = write_opts & 2; if( ! nim ) ERREX("NULL input") ; if( ! nifti_validfilename(nim->fname) ) ERREX("bad fname input") ; if( write_data && ! nim->data && ! NBL ) ERREX("no image data") ; if( write_data && NBL && ! nifti_NBL_matches_nim(nim, NBL) ) ERREX("NBL does not match nim"); nifti_set_iname_offset(nim); if( g_opts.debug > 1 ){ fprintf(stderr,"-d writing nifti file '%s'...\n", nim->fname); if( g_opts.debug > 2 ) fprintf(stderr,"-d nifti type %d, offset %d\n", nim->nifti_type, nim->iname_offset); } if( nim->nifti_type == NIFTI_FTYPE_ASCII ) /* non-standard case */ return nifti_write_ascii_image(nim,NBL,opts,write_data,leave_open); nhdr = nifti_convert_nim2nhdr(nim); /* create the nifti1_header struct */ /* if writing to 2 files, make sure iname is set and different from fname */ if( nim->nifti_type != NIFTI_FTYPE_NIFTI1_1 ){ if( nim->iname && strcmp(nim->iname,nim->fname) == 0 ){ free(nim->iname) ; nim->iname = NULL ; } if( nim->iname == NULL ){ /* then make a new one */ nim->iname = nifti_makeimgname(nim->fname,nim->nifti_type,0,0); if( nim->iname == NULL ) return NULL; } } /* if we have an imgfile and will write the header there, use it */ if( ! znz_isnull(imgfile) && nim->nifti_type == NIFTI_FTYPE_NIFTI1_1 ){ if( g_opts.debug > 2 ) fprintf(stderr,"+d using passed file for hdr\n"); fp = imgfile; } else { if( g_opts.debug > 2 ) fprintf(stderr,"+d opening output file %s [%s]\n",nim->fname,opts); fp = znzopen( nim->fname , opts , nifti_is_gzfile(nim->fname) ) ; if( znz_isnull(fp) ){ LNI_FERR(func,"cannot open output file",nim->fname); return fp; } } /* write the header and extensions */ ss = znzwrite(&nhdr , 1 , sizeof(nhdr) , fp); /* write header */ if( ss < sizeof(nhdr) ){ LNI_FERR(func,"bad header write to output file",nim->fname); znzclose(fp); return fp; } /* partial file exists, and errors have been printed, so ignore return */ if( nim->nifti_type != NIFTI_FTYPE_ANALYZE ) (void)nifti_write_extensions(fp,nim); /* if the header is all we want, we are done */ if( ! write_data && ! leave_open ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d header is all we want: done\n"); znzclose(fp); return(fp); } if( nim->nifti_type != NIFTI_FTYPE_NIFTI1_1 ){ /* get a new file pointer */ znzclose(fp); /* first, close header file */ if( ! znz_isnull(imgfile) ){ if(g_opts.debug > 2) fprintf(stderr,"+d using passed file for img\n"); fp = imgfile; } else { if( g_opts.debug > 2 ) fprintf(stderr,"+d opening img file '%s'\n", nim->iname); fp = znzopen( nim->iname , opts , nifti_is_gzfile(nim->iname) ) ; if( znz_isnull(fp) ) ERREX("cannot open image file") ; } } /* in any case, seek to offset */ if (znzseek(fp, nim->iname_offset, SEEK_SET) < 0) { znzclose(fp); return NULL; } if( write_data ) nifti_write_all_data(fp,nim,NBL); if( ! leave_open ) znzclose(fp); return fp; } /*----------------------------------------------------------------------*/ /*! write a nifti_image to disk in ASCII format *//*--------------------------------------------------------------------*/ znzFile nifti_write_ascii_image(nifti_image *nim, const nifti_brick_list * NBL, const char *opts, int write_data, int leave_open) { znzFile fp; char * hstr; hstr = nifti_image_to_ascii( nim ) ; /* get header in ASCII form */ if( ! hstr ){ fprintf(stderr,"** failed image_to_ascii()\n"); return NULL; } fp = znzopen( nim->fname , opts , nifti_is_gzfile(nim->fname) ) ; if( znz_isnull(fp) ){ free(hstr); fprintf(stderr,"** failed to open '%s' for ascii write\n",nim->fname); return fp; } znzputs(hstr,fp); /* header */ nifti_write_extensions(fp,nim); /* extensions */ if ( write_data ) { nifti_write_all_data(fp,nim,NBL); } /* data */ if ( ! leave_open ) { znzclose(fp); } free(hstr); return fp; /* returned but may be closed */ } /*--------------------------------------------------------------------------*/ /*! Write a nifti_image to disk. Since data is properly byte-swapped upon reading, it is assumed to be in the byte-order of the current CPU at write time. Thus, nim->byte_order should match that of the current CPU. Note that the nifti_set_filenames() function takes the flag, set_byte_order. The following fields of nim affect how the output appears: - nifti_type = 0 ==> ANALYZE-7.5 format file pair will be written - nifti_type = 1 ==> NIFTI-1 format single file will be written (data offset will be 352+extensions) - nifti_type = 2 ==> NIFTI_1 format file pair will be written - nifti_type = 3 ==> NIFTI_1 ASCII single file will be written - fname is the name of the output file (header or header+data) - if a file pair is being written, iname is the name of the data file - existing files WILL be overwritten with extreme prejudice - if qform_code > 0, the quatern_*, qoffset_*, and qfac fields determine the qform output, NOT the qto_xyz matrix; if you want to compute these fields from the qto_xyz matrix, you can use the utility function nifti_mat44_to_quatern() \sa nifti_image_write_bricks, nifti_image_free, nifti_set_filenames, nifti_image_write_hdr_img *//*------------------------------------------------------------------------*/ void nifti_image_write( nifti_image *nim ) { znzFile fp = nifti_image_write_hdr_img(nim,1,"wb"); if( fp ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d niw: done with znzFile\n"); free(fp); } if( g_opts.debug > 1 ) fprintf(stderr,"-d nifti_image_write: done\n"); } /*----------------------------------------------------------------------*/ /*! similar to nifti_image_write, but data is in NBL struct, not nim->data \sa nifti_image_write, nifti_image_free, nifti_set_filenames, nifti_free_NBL *//*--------------------------------------------------------------------*/ void nifti_image_write_bricks( nifti_image *nim, const nifti_brick_list * NBL ) { znzFile fp = nifti_image_write_hdr_img2(nim,1,"wb",NULL,NBL); if( fp ){ if( g_opts.debug > 2 ) fprintf(stderr,"-d niwb: done with znzFile\n"); free(fp); } if( g_opts.debug > 1 ) fprintf(stderr,"-d niwb: done writing bricks\n"); } /*----------------------------------------------------------------------*/ /*! copy the nifti_image structure, without data Duplicate the structure, including fname, iname and extensions. Leave the data pointer as NULL. *//*--------------------------------------------------------------------*/ nifti_image * nifti_copy_nim_info(const nifti_image * src) { nifti_image *dest; dest = (nifti_image *)calloc(1,sizeof(nifti_image)); if( !dest ){ fprintf(stderr,"** NCNI: failed to alloc nifti_image\n"); return NULL; } memcpy(dest, src, sizeof(nifti_image)); if( src->fname ) dest->fname = nifti_strdup(src->fname); if( src->iname ) dest->iname = nifti_strdup(src->iname); dest->num_ext = 0; dest->ext_list = NULL; /* errors will be printed in NCE(), continue in either case */ (void)nifti_copy_extensions(dest, src); dest->data = NULL; return dest; } /*------------------------------------------------------------------------*/ /* Un-escape a C string in place -- that is, convert XML escape sequences back into their characters. (This can be done in place since the replacement is always smaller than the input.) Escapes recognized are: - < -> < - > -> > - " -> " - ' -> ' - & -> & Also replace CR LF pair (Microsoft), or CR alone (Macintosh) with LF (Unix), per the XML standard. Return value is number of replacements made (if you care). --------------------------------------------------------------------------*/ #undef CR #undef LF #define CR 0x0D #define LF 0x0A static int unescape_string( char *str ) { int ii,jj , nn,ll ; if( str == NULL ) return 0 ; /* no string? */ ll = (int)strlen(str) ; if( ll == 0 ) return 0 ; /* scan for escapes: &something; */ for( ii=jj=nn=0 ; ii': lout += 4 ; break ; /* replace '<' with "<" */ case '"' : case '\'': lout += 6 ; break ; /* replace '"' with """ */ case CR: case LF: lout += 6 ; break ; /* replace CR with " " LF with " " */ default: lout++ ; break ; /* copy all other chars */ } } out = (char *)calloc(1,lout) ; /* allocate output string */ if( !out ){ fprintf(stderr,"** escapize_string: failed to alloc %d bytes\n",lout); return NULL; } out[0] = '\'' ; /* opening quote mark */ for( ii=0,jj=1 ; ii < lstr ; ii++ ){ switch( str[ii] ){ default: out[jj++] = str[ii] ; break ; /* normal characters */ case '&': memcpy(out+jj,"&",5) ; jj+=5 ; break ; case '<': memcpy(out+jj,"<",4) ; jj+=4 ; break ; case '>': memcpy(out+jj,">",4) ; jj+=4 ; break ; case '"' : memcpy(out+jj,""",6) ; jj+=6 ; break ; case '\'': memcpy(out+jj,"'",6) ; jj+=6 ; break ; case CR: memcpy(out+jj," ",6) ; jj+=6 ; break ; case LF: memcpy(out+jj," ",6) ; jj+=6 ; break ; } } out[jj++] = '\'' ; /* closing quote mark */ out[jj] = '\0' ; /* terminate the string */ return out ; } /*---------------------------------------------------------------------------*/ /*! Dump the information in a NIFTI image header to an XML-ish ASCII string that can later be converted back into a NIFTI header in nifti_image_from_ascii(). The resulting string can be free()-ed when you are done with it. *//*-------------------------------------------------------------------------*/ char *nifti_image_to_ascii( const nifti_image *nim ) { char *buf , *ebuf ; int nbuf ; if( nim == NULL ) return NULL ; /* stupid caller */ buf = (char *)calloc(1,65534); nbuf = 0; /* longer than needed, to be safe */ if( !buf ){ fprintf(stderr,"** NITA: failed to alloc %d bytes\n",65534); return NULL; } sprintf( buf , "nifti_type == NIFTI_FTYPE_NIFTI1_1) ? "NIFTI-1+" :(nim->nifti_type == NIFTI_FTYPE_NIFTI1_2) ? "NIFTI-1" :(nim->nifti_type == NIFTI_FTYPE_ASCII ) ? "NIFTI-1A" : "ANALYZE-7.5" ) ; /** Strings that we don't control (filenames, etc.) that might contain "weird" characters (like quotes) are "escaped": - A few special characters are replaced by XML-style escapes, using the function escapize_string(). - On input, function unescape_string() reverses this process. - The result is that the NIFTI ASCII-format header is XML-compliant. */ ebuf = escapize_string(nim->fname) ; sprintf( buf+strlen(buf) , " header_filename = %s\n",ebuf); free(ebuf); ebuf = escapize_string(nim->iname) ; sprintf( buf+strlen(buf) , " image_filename = %s\n", ebuf); free(ebuf); sprintf( buf+strlen(buf) , " image_offset = '%d'\n" , nim->iname_offset ); sprintf( buf+strlen(buf), " ndim = '%d'\n", nim->ndim); sprintf( buf+strlen(buf), " nx = '%d'\n", nim->nx ); if( nim->ndim > 1 ) sprintf( buf+strlen(buf), " ny = '%d'\n", nim->ny ); if( nim->ndim > 2 ) sprintf( buf+strlen(buf), " nz = '%d'\n", nim->nz ); if( nim->ndim > 3 ) sprintf( buf+strlen(buf), " nt = '%d'\n", nim->nt ); if( nim->ndim > 4 ) sprintf( buf+strlen(buf), " nu = '%d'\n", nim->nu ); if( nim->ndim > 5 ) sprintf( buf+strlen(buf), " nv = '%d'\n", nim->nv ); if( nim->ndim > 6 ) sprintf( buf+strlen(buf), " nw = '%d'\n", nim->nw ); sprintf( buf+strlen(buf), " dx = '%g'\n", nim->dx ); if( nim->ndim > 1 ) sprintf( buf+strlen(buf), " dy = '%g'\n", nim->dy ); if( nim->ndim > 2 ) sprintf( buf+strlen(buf), " dz = '%g'\n", nim->dz ); if( nim->ndim > 3 ) sprintf( buf+strlen(buf), " dt = '%g'\n", nim->dt ); if( nim->ndim > 4 ) sprintf( buf+strlen(buf), " du = '%g'\n", nim->du ); if( nim->ndim > 5 ) sprintf( buf+strlen(buf), " dv = '%g'\n", nim->dv ); if( nim->ndim > 6 ) sprintf( buf+strlen(buf), " dw = '%g'\n", nim->dw ); sprintf( buf+strlen(buf) , " datatype = '%d'\n" , nim->datatype ) ; sprintf( buf+strlen(buf) , " datatype_name = '%s'\n" , nifti_datatype_string(nim->datatype) ) ; sprintf( buf+strlen(buf) , " nvox = '%u'\n" , (unsigned)nim->nvox ) ; sprintf( buf+strlen(buf) , " nbyper = '%d'\n" , nim->nbyper ) ; sprintf( buf+strlen(buf) , " byteorder = '%s'\n" , (nim->byteorder==MSB_FIRST) ? "MSB_FIRST" : "LSB_FIRST" ) ; if( nim->cal_min < nim->cal_max ){ sprintf( buf+strlen(buf) , " cal_min = '%g'\n", nim->cal_min ) ; sprintf( buf+strlen(buf) , " cal_max = '%g'\n", nim->cal_max ) ; } if( nim->scl_slope != 0.0 ){ sprintf( buf+strlen(buf) , " scl_slope = '%g'\n" , nim->scl_slope ) ; sprintf( buf+strlen(buf) , " scl_inter = '%g'\n" , nim->scl_inter ) ; } if( nim->intent_code > 0 ){ sprintf( buf+strlen(buf) , " intent_code = '%d'\n", nim->intent_code ) ; sprintf( buf+strlen(buf) , " intent_code_name = '%s'\n" , nifti_intent_string(nim->intent_code) ) ; sprintf( buf+strlen(buf) , " intent_p1 = '%g'\n" , nim->intent_p1 ) ; sprintf( buf+strlen(buf) , " intent_p2 = '%g'\n" , nim->intent_p2 ) ; sprintf( buf+strlen(buf) , " intent_p3 = '%g'\n" , nim->intent_p3 ) ; if( nim->intent_name[0] != '\0' ){ ebuf = escapize_string(nim->intent_name) ; sprintf( buf+strlen(buf) , " intent_name = %s\n",ebuf) ; free(ebuf) ; } } if( nim->toffset != 0.0 ) sprintf( buf+strlen(buf) , " toffset = '%g'\n",nim->toffset ) ; if( nim->xyz_units > 0 ) sprintf( buf+strlen(buf) , " xyz_units = '%d'\n" " xyz_units_name = '%s'\n" , nim->xyz_units , nifti_units_string(nim->xyz_units) ) ; if( nim->time_units > 0 ) sprintf( buf+strlen(buf) , " time_units = '%d'\n" " time_units_name = '%s'\n" , nim->time_units , nifti_units_string(nim->time_units) ) ; if( nim->freq_dim > 0 ) sprintf( buf+strlen(buf) , " freq_dim = '%d'\n",nim->freq_dim ) ; if( nim->phase_dim > 0 ) sprintf( buf+strlen(buf) , " phase_dim = '%d'\n",nim->phase_dim ) ; if( nim->slice_dim > 0 ) sprintf( buf+strlen(buf) , " slice_dim = '%d'\n",nim->slice_dim ) ; if( nim->slice_code > 0 ) sprintf( buf+strlen(buf) , " slice_code = '%d'\n" " slice_code_name = '%s'\n" , nim->slice_code , nifti_slice_string(nim->slice_code) ) ; if( nim->slice_start >= 0 && nim->slice_end > nim->slice_start ) sprintf( buf+strlen(buf) , " slice_start = '%d'\n" " slice_end = '%d'\n" , nim->slice_start , nim->slice_end ) ; if( nim->slice_duration != 0.0 ) sprintf( buf+strlen(buf) , " slice_duration = '%g'\n", nim->slice_duration ) ; if( nim->descrip[0] != '\0' ){ ebuf = escapize_string(nim->descrip) ; sprintf( buf+strlen(buf) , " descrip = %s\n",ebuf) ; free(ebuf) ; } if( nim->aux_file[0] != '\0' ){ ebuf = escapize_string(nim->aux_file) ; sprintf( buf+strlen(buf) , " aux_file = %s\n",ebuf) ; free(ebuf) ; } if( nim->qform_code > 0 ){ int i,j,k ; sprintf( buf+strlen(buf) , " qform_code = '%d'\n" " qform_code_name = '%s'\n" " qto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" , nim->qform_code , nifti_xform_string(nim->qform_code) , nim->qto_xyz.m[0][0] , nim->qto_xyz.m[0][1] , nim->qto_xyz.m[0][2] , nim->qto_xyz.m[0][3] , nim->qto_xyz.m[1][0] , nim->qto_xyz.m[1][1] , nim->qto_xyz.m[1][2] , nim->qto_xyz.m[1][3] , nim->qto_xyz.m[2][0] , nim->qto_xyz.m[2][1] , nim->qto_xyz.m[2][2] , nim->qto_xyz.m[2][3] , nim->qto_xyz.m[3][0] , nim->qto_xyz.m[3][1] , nim->qto_xyz.m[3][2] , nim->qto_xyz.m[3][3] ) ; sprintf( buf+strlen(buf) , " qto_ijk_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" , nim->qto_ijk.m[0][0] , nim->qto_ijk.m[0][1] , nim->qto_ijk.m[0][2] , nim->qto_ijk.m[0][3] , nim->qto_ijk.m[1][0] , nim->qto_ijk.m[1][1] , nim->qto_ijk.m[1][2] , nim->qto_ijk.m[1][3] , nim->qto_ijk.m[2][0] , nim->qto_ijk.m[2][1] , nim->qto_ijk.m[2][2] , nim->qto_ijk.m[2][3] , nim->qto_ijk.m[3][0] , nim->qto_ijk.m[3][1] , nim->qto_ijk.m[3][2] , nim->qto_ijk.m[3][3] ) ; sprintf( buf+strlen(buf) , " quatern_b = '%g'\n" " quatern_c = '%g'\n" " quatern_d = '%g'\n" " qoffset_x = '%g'\n" " qoffset_y = '%g'\n" " qoffset_z = '%g'\n" " qfac = '%g'\n" , nim->quatern_b , nim->quatern_c , nim->quatern_d , nim->qoffset_x , nim->qoffset_y , nim->qoffset_z , nim->qfac ) ; nifti_mat44_to_orientation( nim->qto_xyz , &i,&j,&k ) ; if( i > 0 && j > 0 && k > 0 ) sprintf( buf+strlen(buf) , " qform_i_orientation = '%s'\n" " qform_j_orientation = '%s'\n" " qform_k_orientation = '%s'\n" , nifti_orientation_string(i) , nifti_orientation_string(j) , nifti_orientation_string(k) ) ; } if( nim->sform_code > 0 ){ int i,j,k ; sprintf( buf+strlen(buf) , " sform_code = '%d'\n" " sform_code_name = '%s'\n" " sto_xyz_matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" , nim->sform_code , nifti_xform_string(nim->sform_code) , nim->sto_xyz.m[0][0] , nim->sto_xyz.m[0][1] , nim->sto_xyz.m[0][2] , nim->sto_xyz.m[0][3] , nim->sto_xyz.m[1][0] , nim->sto_xyz.m[1][1] , nim->sto_xyz.m[1][2] , nim->sto_xyz.m[1][3] , nim->sto_xyz.m[2][0] , nim->sto_xyz.m[2][1] , nim->sto_xyz.m[2][2] , nim->sto_xyz.m[2][3] , nim->sto_xyz.m[3][0] , nim->sto_xyz.m[3][1] , nim->sto_xyz.m[3][2] , nim->sto_xyz.m[3][3] ) ; sprintf( buf+strlen(buf) , " sto_ijk matrix = '%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g'\n" , nim->sto_ijk.m[0][0] , nim->sto_ijk.m[0][1] , nim->sto_ijk.m[0][2] , nim->sto_ijk.m[0][3] , nim->sto_ijk.m[1][0] , nim->sto_ijk.m[1][1] , nim->sto_ijk.m[1][2] , nim->sto_ijk.m[1][3] , nim->sto_ijk.m[2][0] , nim->sto_ijk.m[2][1] , nim->sto_ijk.m[2][2] , nim->sto_ijk.m[2][3] , nim->sto_ijk.m[3][0] , nim->sto_ijk.m[3][1] , nim->sto_ijk.m[3][2] , nim->sto_ijk.m[3][3] ) ; nifti_mat44_to_orientation( nim->sto_xyz , &i,&j,&k ) ; if( i > 0 && j > 0 && k > 0 ) sprintf( buf+strlen(buf) , " sform_i_orientation = '%s'\n" " sform_j_orientation = '%s'\n" " sform_k_orientation = '%s'\n" , nifti_orientation_string(i) , nifti_orientation_string(j) , nifti_orientation_string(k) ) ; } sprintf( buf+strlen(buf) , " num_ext = '%d'\n", nim->num_ext ) ; sprintf( buf+strlen(buf) , "/>\n" ) ; /* XML-ish closer */ nbuf = (int)strlen(buf) ; buf = (char *)realloc((void *)buf, nbuf+1); /* cut back to proper length */ if( !buf ) fprintf(stderr,"** NITA: failed to realloc %d bytes\n",nbuf+1); return buf ; } /*---------------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /*! get the byte order for this CPU - LSB_FIRST means least significant byte, first (little endian) - MSB_FIRST means most significant byte, first (big endian) *//*--------------------------------------------------------------------*/ int nifti_short_order(void) /* determine this CPU's byte order */ { union { unsigned char bb[2] ; short ss ; } fred ; fred.bb[0] = 1 ; fred.bb[1] = 0 ; return (fred.ss == 1) ? LSB_FIRST : MSB_FIRST ; } /*---------------------------------------------------------------------------*/ #undef QQNUM #undef QNUM #undef QSTR /* macro to check lhs string against "n1"; if it matches, interpret rhs string as a number, and put it into nim->"n2" */ #define QQNUM(n1,n2) if( strcmp(lhs,#n1)==0 ) nim->n2=strtod(rhs,NULL) /* same, but where "n1" == "n2" */ #define QNUM(nam) QQNUM(nam,nam) /* macro to check lhs string against "nam"; if it matches, put rhs string into nim->"nam" string, with max length = "ml" */ #define QSTR(nam,ml) if( strcmp(lhs,#nam) == 0 ) \ strncpy(nim->nam,rhs,ml), nim->nam[ml]='\0' /*---------------------------------------------------------------------------*/ /*! Take an XML-ish ASCII string and create a NIFTI image header to match. NULL is returned if enough information isn't present in the input string. - The image data can later be loaded with nifti_image_load(). - The struct returned here can be liberated with nifti_image_free(). - Not a lot of error checking is done here to make sure that the input values are reasonable! *//*-------------------------------------------------------------------------*/ nifti_image *nifti_image_from_ascii( const char *str, int * bytes_read ) { char lhs[1024] , rhs[1024] ; int ii , spos, nn ; nifti_image *nim ; /* will be output */ if( str == NULL || *str == '\0' ) return NULL ; /* bad input!? */ /* scan for opening string */ spos = 0 ; ii = sscanf( str+spos , "%1023s%n" , lhs , &nn ) ; spos += nn ; if( ii == 0 || strcmp(lhs,"nx = nim->ny = nim->nz = nim->nt = nim->nu = nim->nv = nim->nw = 1 ; nim->dx = nim->dy = nim->dz = nim->dt = nim->du = nim->dv = nim->dw = 0 ; nim->qfac = 1.0 ; nim->byteorder = nifti_short_order() ; /* starting at str[spos], scan for "equations" of the form lhs = 'rhs' and assign rhs values into the struct component named by lhs */ while(1){ while( isspace((int) str[spos]) ) spos++ ; /* skip whitespace */ if( str[spos] == '\0' ) break ; /* end of string? */ /* get lhs string */ ii = sscanf( str+spos , "%1023s%n" , lhs , &nn ) ; spos += nn ; if( ii == 0 || strcmp(lhs,"/>") == 0 ) break ; /* end of input? */ /* skip whitespace and the '=' marker */ while( isspace((int) str[spos]) || str[spos] == '=' ) spos++ ; if( str[spos] == '\0' ) break ; /* end of string? */ /* if next character is a quote ', copy everything up to next ' otherwise, copy everything up to next nonblank */ if( str[spos] == '\'' ){ ii = spos+1 ; while( str[ii] != '\0' && str[ii] != '\'' ) ii++ ; nn = ii-spos-1 ; if( nn > 1023 ) nn = 1023 ; memcpy(rhs,str+spos+1,nn) ; rhs[nn] = '\0' ; spos = (str[ii] == '\'') ? ii+1 : ii ; } else { ii = sscanf( str+spos , "%1023s%n" , rhs , &nn ) ; spos += nn ; if( ii == 0 ) break ; /* nothing found? */ } unescape_string(rhs) ; /* remove any XML escape sequences */ /* Now can do the assignment, based on lhs string. Start with special cases that don't fit the QNUM/QSTR macros. */ if( strcmp(lhs,"nifti_type") == 0 ){ if( strcmp(rhs,"ANALYZE-7.5") == 0 ) nim->nifti_type = NIFTI_FTYPE_ANALYZE ; else if( strcmp(rhs,"NIFTI-1+") == 0 ) nim->nifti_type = NIFTI_FTYPE_NIFTI1_1 ; else if( strcmp(rhs,"NIFTI-1") == 0 ) nim->nifti_type = NIFTI_FTYPE_NIFTI1_2 ; else if( strcmp(rhs,"NIFTI-1A") == 0 ) nim->nifti_type = NIFTI_FTYPE_ASCII ; } else if( strcmp(lhs,"header_filename") == 0 ){ nim->fname = nifti_strdup(rhs) ; } else if( strcmp(lhs,"image_filename") == 0 ){ nim->iname = nifti_strdup(rhs) ; } else if( strcmp(lhs,"sto_xyz_matrix") == 0 ){ sscanf( rhs , "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f" , &(nim->sto_xyz.m[0][0]) , &(nim->sto_xyz.m[0][1]) , &(nim->sto_xyz.m[0][2]) , &(nim->sto_xyz.m[0][3]) , &(nim->sto_xyz.m[1][0]) , &(nim->sto_xyz.m[1][1]) , &(nim->sto_xyz.m[1][2]) , &(nim->sto_xyz.m[1][3]) , &(nim->sto_xyz.m[2][0]) , &(nim->sto_xyz.m[2][1]) , &(nim->sto_xyz.m[2][2]) , &(nim->sto_xyz.m[2][3]) , &(nim->sto_xyz.m[3][0]) , &(nim->sto_xyz.m[3][1]) , &(nim->sto_xyz.m[3][2]) , &(nim->sto_xyz.m[3][3]) ) ; } else if( strcmp(lhs,"byteorder") == 0 ){ if( strcmp(rhs,"MSB_FIRST") == 0 ) nim->byteorder = MSB_FIRST ; if( strcmp(rhs,"LSB_FIRST") == 0 ) nim->byteorder = LSB_FIRST ; } else QQNUM(image_offset,iname_offset) ; else QNUM(datatype) ; else QNUM(ndim) ; else QNUM(nx) ; else QNUM(ny) ; else QNUM(nz) ; else QNUM(nt) ; else QNUM(nu) ; else QNUM(nv) ; else QNUM(nw) ; else QNUM(dx) ; else QNUM(dy) ; else QNUM(dz) ; else QNUM(dt) ; else QNUM(du) ; else QNUM(dv) ; else QNUM(dw) ; else QNUM(cal_min) ; else QNUM(cal_max) ; else QNUM(scl_slope) ; else QNUM(scl_inter) ; else QNUM(intent_code) ; else QNUM(intent_p1) ; else QNUM(intent_p2) ; else QNUM(intent_p3) ; else QSTR(intent_name,15) ; else QNUM(toffset) ; else QNUM(xyz_units) ; else QNUM(time_units) ; else QSTR(descrip,79) ; else QSTR(aux_file,23) ; else QNUM(qform_code) ; else QNUM(quatern_b) ; else QNUM(quatern_c) ; else QNUM(quatern_d) ; else QNUM(qoffset_x) ; else QNUM(qoffset_y) ; else QNUM(qoffset_z) ; else QNUM(qfac) ; else QNUM(sform_code) ; else QNUM(freq_dim) ; else QNUM(phase_dim) ; else QNUM(slice_dim) ; else QNUM(slice_code) ; else QNUM(slice_start) ; else QNUM(slice_end) ; else QNUM(slice_duration) ; else QNUM(num_ext) ; } /* end of while loop */ if( bytes_read ) *bytes_read = spos+1; /* "process" last '\n' */ /* do miscellaneous checking and cleanup */ if( nim->ndim <= 0 ){ nifti_image_free(nim); return NULL; } /* bad! */ nifti_datatype_sizes( nim->datatype, &(nim->nbyper), &(nim->swapsize) ); if( nim->nbyper == 0 ){ nifti_image_free(nim); return NULL; } /* bad! */ nim->dim[0] = nim->ndim ; nim->dim[1] = nim->nx ; nim->pixdim[1] = nim->dx ; nim->dim[2] = nim->ny ; nim->pixdim[2] = nim->dy ; nim->dim[3] = nim->nz ; nim->pixdim[3] = nim->dz ; nim->dim[4] = nim->nt ; nim->pixdim[4] = nim->dt ; nim->dim[5] = nim->nu ; nim->pixdim[5] = nim->du ; nim->dim[6] = nim->nv ; nim->pixdim[6] = nim->dv ; nim->dim[7] = nim->nw ; nim->pixdim[7] = nim->dw ; nim->nvox = (size_t)nim->nx * nim->ny * nim->nz * nim->nt * nim->nu * nim->nv * nim->nw ; if( nim->qform_code > 0 ) nim->qto_xyz = nifti_quatern_to_mat44( nim->quatern_b, nim->quatern_c, nim->quatern_d, nim->qoffset_x, nim->qoffset_y, nim->qoffset_z, nim->dx , nim->dy , nim->dz , nim->qfac ) ; else nim->qto_xyz = nifti_quatern_to_mat44( 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , 0.0 , nim->dx , nim->dy , nim->dz , 0.0 ) ; nim->qto_ijk = nifti_mat44_inverse( nim->qto_xyz ) ; if( nim->sform_code > 0 ) nim->sto_ijk = nifti_mat44_inverse( nim->sto_xyz ) ; return nim ; } /*---------------------------------------------------------------------------*/ /*! validate the nifti_image \return 1 if the structure seems valid, otherwise 0 \sa nifti_nim_has_valid_dims, nifti_hdr_looks_good *//*-------------------------------------------------------------------------*/ int nifti_nim_is_valid(nifti_image * nim, int complain) { int errs = 0; if( !nim ){ fprintf(stderr,"** is_valid_nim: nim is NULL\n"); return 0; } if( g_opts.debug > 2 ) fprintf(stderr,"-d nim_is_valid check...\n"); /**- check that dim[] matches the individual values ndim, nx, ny, ... */ if( ! nifti_nim_has_valid_dims(nim,complain) ){ if( !complain ) return 0; errs++; } /* might check nbyper, pixdim, q/sforms, swapsize, nifti_type, ... */ /**- be explicit in return of 0 or 1 */ if( errs > 0 ) return 0; else return 1; } /*---------------------------------------------------------------------------*/ /*! validate nifti dimensions \return 1 if valid, 0 if not \sa nifti_nim_is_valid, nifti_hdr_looks_good rely on dim[] as the master *//*-------------------------------------------------------------------------*/ int nifti_nim_has_valid_dims(nifti_image * nim, int complain) { size_t prod; int c, errs = 0; /**- start with dim[0]: failure here is considered terminal */ if( nim->dim[0] <= 0 || nim->dim[0] > 7 ){ errs++; if( complain ) fprintf(stderr,"** NVd: dim[0] (%d) out of range [1,7]\n",nim->dim[0]); return 0; } /**- check whether ndim equals dim[0] */ if( nim->ndim != nim->dim[0] ){ errs++; if( ! complain ) return 0; fprintf(stderr,"** NVd: ndim != dim[0] (%d,%d)\n",nim->ndim,nim->dim[0]); } /**- compare each dim[i] to the proper nx, ny, ... */ if( ( (nim->dim[0] >= 1) && (nim->dim[1] != nim->nx) ) || ( (nim->dim[0] >= 2) && (nim->dim[2] != nim->ny) ) || ( (nim->dim[0] >= 3) && (nim->dim[3] != nim->nz) ) || ( (nim->dim[0] >= 4) && (nim->dim[4] != nim->nt) ) || ( (nim->dim[0] >= 5) && (nim->dim[5] != nim->nu) ) || ( (nim->dim[0] >= 6) && (nim->dim[6] != nim->nv) ) || ( (nim->dim[0] >= 7) && (nim->dim[7] != nim->nw) ) ){ errs++; if( !complain ) return 0; fprintf(stderr,"** NVd mismatch: dims = %d,%d,%d,%d,%d,%d,%d\n" " nxyz... = %d,%d,%d,%d,%d,%d,%d\n", nim->dim[1], nim->dim[2], nim->dim[3], nim->dim[4], nim->dim[5], nim->dim[6], nim->dim[7], nim->nx, nim->ny, nim->nz, nim->nt, nim->nu, nim->nv, nim->nw ); } if( g_opts.debug > 2 ){ fprintf(stderr,"-d check dim[%d] =", nim->dim[0]); for( c = 0; c < 7; c++ ) fprintf(stderr," %d", nim->dim[c]); fputc('\n', stderr); } /**- check the dimensions, and that their product matches nvox */ prod = 1; for( c = 1; c <= nim->dim[0]; c++ ){ if( nim->dim[c] > 0) prod *= nim->dim[c]; else if( nim->dim[c] <= 0 ){ if( !complain ) return 0; fprintf(stderr,"** NVd: dim[%d] (=%d) <= 0\n",c, nim->dim[c]); errs++; } } if( prod != nim->nvox ){ if( ! complain ) return 0; fprintf(stderr,"** NVd: nvox does not match %d-dim product (%u, %u)\n", nim->dim[0], (unsigned)nim->nvox, (unsigned)prod); errs++; } /**- if debug, warn about any remaining dim that is neither 0, nor 1 */ /* (values in dims above dim[0] are undefined, as reminded by Cinly Ooi and Alle Meije Wink) 16 Nov 2005 [rickr] */ if( g_opts.debug > 1 ) for( c = nim->dim[0]+1; c <= 7; c++ ) if( nim->dim[c] != 0 && nim->dim[c] != 1 ) fprintf(stderr,"** NVd warning: dim[%d] = %d, but ndim = %d\n", c, nim->dim[c], nim->dim[0]); if( g_opts.debug > 2 ) fprintf(stderr,"-d nim_has_valid_dims check, errs = %d\n", errs); /**- return invalid or valid */ if( errs > 0 ) return 0; else return 1; } /*---------------------------------------------------------------------------*/ /*! read a nifti image, collapsed across dimensions according to dims[8]

    This function may be used to read parts of a nifti dataset, such as
    the time series for a single voxel, or perhaps a slice.  It is similar
    to nifti_image_load(), though the passed 'data' parameter is used for
    returning the image, not nim->data.

    \param nim  given nifti_image struct, corresponding to the data file
    \param dims given list of dimensions (see below)
    \param data pointer to data pointer (if *data is NULL, data will be
                allocated, otherwise not)

    Here, dims is an array of 8 ints, similar to nim->dim[8].  While dims[0]
    is unused at this point, the other indices specify which dimensions to
    collapse (and at which index), and which not to collapse.  If dims[i] is
    set to -1, then that entire dimension will be read in, from index 0 to
    index (nim->dim[i] - 1).  If dims[i] >= 0, then only that index will be
    read in (so dims[i] must also be < nim->dim[i]).

    Example: given  nim->dim[8] = { 4, 64, 64, 21, 80, 1, 1, 1 } (4-D dataset)

      if dims[8] = { 0,  5,  4, 17, -1, -1, -1, -1 }
         -> read time series for voxel i,j,k = 5,4,17

      if dims[8] = { 0, -1, -1, -1, 17, -1, -1, -1 }
         -> read single volume at time point 17

    Example: given  nim->dim[8] = { 6, 64, 64, 21, 80, 4, 3, 1 } (6-D dataset)

      if dims[8] = { 0, 5, 4, 17, -1, 2, 1, 0 }
         -> read time series for the voxel i,j,k = 5,4,17, and dim 5,6 = 2,1

      if dims[8] = { 0, 5, 4, -1, -1, 0, 0, 0 }
         -> read time series for slice at i,j = 5,4, and dim 5,6,7 = 0,0,0
            (note that dims[7] is not relevant, but must be 0 or -1)

    If *data is NULL, then *data will be set as a pointer to new memory,
    allocated here for the resulting collapsed image data.

      e.g. { int    dims[8] = { 0,  5,  4, 17, -1, -1, -1, -1 };
             void * data    = NULL;
             ret_val = nifti_read_collapsed_image(nim, dims, &data);
             if( ret_val > 0 ){
                process_time_series(data);
                if( data != NULL ) free(data);
             }
           }

    NOTE: If *data is not NULL, then it will be assumed that it points to
          valid memory, sufficient to hold the results.  This is done for
          speed and possibly repeated calls to this function.

      e.g. { int    dims[8] = { 0,  -1, -1, -1, -1, -1, -1, -1 };
             void * data    = NULL;
             for( zslice = 0; zslice < nzslices; zslice++ ){
                dims[3] = zslice;
                ret_val = nifti_read_collapsed_image(nim, dims, &data);
                if( ret_val > 0 ) process_slice(zslice, data);
             }
             if( data != NULL ) free(data);
           }

    \return
        -  the total number of bytes read, or < 0 on failure
        -  the read and byte-swapped data, in 'data'            
\sa nifti_image_read, nifti_image_free, nifti_image_read_bricks nifti_image_load *//*-------------------------------------------------------------------------*/ int nifti_read_collapsed_image( nifti_image * nim, const int dims [8], void ** data ) { znzFile fp; int pivots[8], prods[8], nprods; /* sizes are bounded by dims[], so 8 */ int c, bytes; /** - check pointers for sanity */ if( !nim || !dims || !data ){ fprintf(stderr,"** nifti_RCI: bad params %p, %p, %p\n", (void *) nim, (const void *)dims, (void *)data); return -1; } if( g_opts.debug > 2 ){ fprintf(stderr,"-d read_collapsed_image:\n dims ="); for(c = 0; c < 8; c++) fprintf(stderr," %3d", dims[c]); fprintf(stderr,"\n nim->dims ="); for(c = 0; c < 8; c++) fprintf(stderr," %3d", nim->dim[c]); fputc('\n', stderr); } /** - verify that dim[] makes sense */ if( ! nifti_nim_is_valid(nim, g_opts.debug > 0) ){ fprintf(stderr,"** invalid nim (file is '%s')\n", nim->fname ); return -1; } /** - verify that dims[] makes sense for this dataset */ for( c = 1; c <= nim->dim[0]; c++ ){ if( dims[c] >= nim->dim[c] ){ fprintf(stderr,"** nifti_RCI: dims[%d] >= nim->dim[%d] (%d,%d)\n", c, c, dims[c], nim->dim[c]); return -1; } } /** - prepare pivot list - pivots are fixed indices */ if( make_pivot_list(nim, dims, pivots, prods, &nprods) < 0 ) return -1; bytes = rci_alloc_mem(data, prods, nprods, nim->nbyper); if( bytes < 0 ) return -1; /** - open the image file for reading at the appropriate offset */ fp = nifti_image_load_prep( nim ); if( ! fp ){ free(*data); *data = NULL; return -1; } /* failure */ /** - call the recursive reading function, passing nim, the pivot info, location to store memory, and file pointer and position */ c = rci_read_data(nim, pivots,prods,nprods,dims, (char *)*data, fp, znztell(fp)); znzclose(fp); /* in any case, close the file */ if( c < 0 ){ free(*data); *data = NULL; return -1; } /* failure */ if( g_opts.debug > 1 ) fprintf(stderr,"+d read %d bytes of collapsed image from %s\n", bytes, nim->fname); return bytes; } /* local function to find strides per dimension. assumes 7D size and ** stride array. */ static void compute_strides(int *strides,const int *size,int nbyper) { int i; strides[0] = nbyper; for(i = 1; i < 7; i++) { strides[i] = size[i-1] * strides[i-1]; } } /*---------------------------------------------------------------------------*/ /*! read an arbitrary subregion from a nifti image

    This function may be used to read a single arbitary subregion of any
    rectangular size from a nifti dataset, such as a small 5x5x5 subregion
    around the center of a 3D image.

    \param nim  given nifti_image struct, corresponding to the data file
    \param start_index the index location of the first voxel that will be returned
    \param region_size the size of the subregion to be returned
    \param data pointer to data pointer (if *data is NULL, data will be
                allocated, otherwise not)

    Example: given  nim->dim[8] = {3, 64, 64, 64, 1, 1, 1, 1 } (3-D dataset)

      if start_index[7] = { 29,  29, 29, 0, 0, 0, 0 } and
         region_size[7] = {  5,   5,  5, 1, 1, 1, 1 }
         -> read 5x5x5 region starting with the first voxel location at (29,29,29)

    NOTE: If *data is not NULL, then it will be assumed that it points to
          valid memory, sufficient to hold the results.  This is done for
          speed and possibly repeated calls to this function.
    \return
        -  the total number of bytes read, or < 0 on failure
        -  the read and byte-swapped data, in 'data'            
\sa nifti_image_read, nifti_image_free, nifti_image_read_bricks nifti_image_load, nifti_read_collapsed_image *//*-------------------------------------------------------------------------*/ int nifti_read_subregion_image( nifti_image * nim, int *start_index, int *region_size, void ** data ) { znzFile fp; /* file to read */ int i,j,k,l,m,n; /* indices for dims */ long int bytes = 0; /* total # bytes read */ int total_alloc_size; /* size of buffer allocation */ char *readptr; /* where in *data to read next */ int strides[7]; /* strides between dimensions */ int collapsed_dims[8]; /* for read_collapsed_image */ int *image_size; /* pointer to dimensions in header */ long int initial_offset; long int offset; /* seek offset for reading current row */ /* probably ignored, but set to ndim for consistency*/ collapsed_dims[0] = nim->ndim; /* build a dims array for collapsed image read */ for(i = 0; i < nim->ndim; i++) { /* if you take the whole extent in this dimension */ if(start_index[i] == 0 && region_size[i] == nim->dim[i+1]) { collapsed_dims[i+1] = -1; } /* if you specify a single element in this dimension */ else if(region_size[i] == 1) { collapsed_dims[i+1] = start_index[i]; } else { collapsed_dims[i+1] = -2; /* sentinel value */ } } /* fill out end of collapsed_dims */ for(i = nim->ndim ; i < 7; i++) { collapsed_dims[i+1] = -1; } /* check to see whether collapsed read is possible */ for(i = 1; i <= nim->ndim; i++) { if(collapsed_dims[i] == -2) { break; } } /* if you get through all the dimensions without hitting ** a subrange of size > 1, a collapsed read is possible */ if(i > nim->ndim) { return nifti_read_collapsed_image(nim, collapsed_dims, data); } /* point past first element of dim, which holds nim->ndim */ image_size = &(nim->dim[1]); /* check region sizes for sanity */ for(i = 0; i < nim->ndim; i++) { if(start_index[i] + region_size[i] > image_size[i]) { if(g_opts.debug > 1) { fprintf(stderr,"region doesn't fit within image size\n"); } return -1; } } /* get the file open */ fp = nifti_image_load_prep( nim ); /* the current offset is just past the nifti header, save * location so that SEEK_SET can be used below */ initial_offset = znztell(fp); /* get strides*/ compute_strides(strides,image_size,nim->nbyper); total_alloc_size = nim->nbyper; /* size of pixel */ /* find alloc size */ for(i = 0; i < nim->ndim; i++) { total_alloc_size *= region_size[i]; } /* allocate buffer, if necessary */ if(*data == 0) { *data = (void *)malloc(total_alloc_size); } if(*data == 0) { if(g_opts.debug > 1) { fprintf(stderr,"allocation of %d bytes failed\n",total_alloc_size); } znzclose(fp); return -1; } /* point to start of data buffer as char * */ readptr = *((char **)data); { /* can't assume that start_index and region_size have any more than ** nim->ndim elements so make local copies, filled out to seven elements */ int si[7], rs[7]; for(i = 0; i < nim->ndim; i++) { si[i] = start_index[i]; rs[i] = region_size[i]; } for(i = nim->ndim; i < 7; i++) { si[i] = 0; rs[i] = 1; } /* loop through subregion and read a row at a time */ for(i = si[6]; i < (si[6] + rs[6]); i++) { for(j = si[5]; j < (si[5] + rs[5]); j++) { for(k = si[4]; k < (si[4] + rs[4]); k++) { for(l = si[3]; l < (si[3] + rs[3]); l++) { for(m = si[2]; m < (si[2] + rs[2]); m++) { for(n = si[1]; n < (si[1] + rs[1]); n++) { int nread,read_amount; offset = initial_offset + (i * strides[6]) + (j * strides[5]) + (k * strides[4]) + (l * strides[3]) + (m * strides[2]) + (n * strides[1]) + (si[0] * strides[0]); if (znzseek(fp, offset, SEEK_SET) < 0) { /* seek to current row */ if (g_opts.debug > 1) { fprintf(stderr,"read failed on seek\n"); } return -1; } read_amount = rs[0] * nim->nbyper; /* read a row of the subregion*/ nread = (int)nifti_read_buffer(fp, readptr, read_amount, nim); if(nread != read_amount) { if(g_opts.debug > 1) { fprintf(stderr,"read of %d bytes failed\n",read_amount); return -1; } } bytes += nread; readptr += read_amount; } } } } } } } znzclose(fp); return bytes; } /* read the data from the file pointed to by fp - this a recursive function, so start with the base case - data is now (char *) for easy incrementing return 0 on success, < 0 on failure */ static int rci_read_data(nifti_image * nim, int * pivots, int * prods, int nprods, const int dims[], char * data, znzFile fp, size_t base_offset) { size_t sublen, offset, read_size; int c; /* bad check first - base_offset may not have been checked */ if( nprods <= 0 ){ fprintf(stderr,"** rci_read_data, bad prods, %d\n", nprods); return -1; } /* base case: actually read the data */ if( nprods == 1 ){ size_t nread, bytes; /* make sure things look good here */ if( *pivots != 0 ){ fprintf(stderr,"** rciRD: final pivot == %d!\n", *pivots); return -1; } /* so just seek and read (prods[0] * nbyper) bytes from the file */ if (znzseek(fp, (long)base_offset, SEEK_SET) < 0) return -1; bytes = (size_t)prods[0] * nim->nbyper; nread = nifti_read_buffer(fp, data, bytes, nim); if( nread != bytes ){ fprintf(stderr,"** rciRD: read only %u of %u bytes from '%s'\n", (unsigned)nread, (unsigned)bytes, nim->fname); return -1; } else if( g_opts.debug > 3 ) fprintf(stderr,"+d successful read of %u bytes at offset %u\n", (unsigned)bytes, (unsigned)base_offset); return 0; /* done with base case - return success */ } /* not the base case, so do a set of reduced reads */ /* compute size of sub-brick: all dimensions below pivot */ for( c = 1, sublen = 1; c < *pivots; c++ ) sublen *= nim->dim[c]; /* compute number of values to read, i.e. remaining prods */ for( c = 1, read_size = 1; c < nprods; c++ ) read_size *= prods[c]; read_size *= nim->nbyper; /* and multiply by bytes per voxel */ /* now repeatedly compute offsets, and recursively read */ for( c = 0; c < prods[0]; c++ ){ /* offset is (c * sub-block size (including pivot dim)) */ /* + (dims[] index into pivot sub-block) */ /* the unneeded multiplication is to make this more clear */ offset = (size_t)c * sublen * nim->dim[*pivots] + (size_t)sublen * dims[*pivots]; offset *= nim->nbyper; if( g_opts.debug > 3 ) fprintf(stderr,"-d reading %u bytes, foff %u + %u, doff %u\n", (unsigned)read_size, (unsigned)base_offset, (unsigned)offset, (unsigned)(c*read_size)); /* now read the next level down, adding this offset */ if( rci_read_data(nim, pivots+1, prods+1, nprods-1, dims, data + c * read_size, fp, base_offset + offset) < 0 ) return -1; } return 0; } /* allocate memory for all collapsed image data If *data is already set, do not allocate, but still calculate size for debug report. return total size on success, and < 0 on failure */ static int rci_alloc_mem(void ** data, int prods[8], int nprods, int nbyper ) { int size, index; if( nbyper < 0 || nprods < 1 || nprods > 8 ){ fprintf(stderr,"** rci_am: bad params, %d, %d\n", nbyper, nprods); return -1; } for( index = 0, size = 1; index < nprods; index++ ) size *= prods[index]; size *= nbyper; if( ! *data ){ /* then allocate what is needed */ if( g_opts.debug > 1 ) fprintf(stderr,"+d alloc %d (= %d x %d) bytes for collapsed image\n", size, size/nbyper, nbyper); *data = malloc(size); /* actually allocate the memory */ if( ! *data ){ fprintf(stderr,"** rci_am: failed to alloc %d bytes for data\n", size); return -1; } } else if( g_opts.debug > 1 ) fprintf(stderr,"-d rci_am: *data already set, need %d (%d x %d) bytes\n", size, size/nbyper, nbyper); return size; } /* prepare a pivot list for reading The pivot points are the indices into dims where the calling function wants to collapse a dimension. The last pivot should always be zero (note that we have space for that in the lists). */ static int make_pivot_list(nifti_image * nim, const int dims[], int pivots[], int prods[], int * nprods ) { int len, index; len = 0; index = nim->dim[0]; if (index <= 0 || index >= 8) return -1; while( index > 0 ){ prods[len] = 1; while( index > 0 && (nim->dim[index] == 1 || dims[index] == -1) ){ prods[len] *= nim->dim[index]; index--; } pivots[len] = index; len++; index--; /* fine, let it drop out at -1 */ } /* make sure to include 0 as a pivot (instead of just 1, if it is) */ if( pivots[len-1] != 0 ){ pivots[len] = 0; prods[len] = 1; len++; } *nprods = len; if( g_opts.debug > 2 ){ fprintf(stderr,"+d pivot list created, pivots :"); for(index = 0; index < len; index++) fprintf(stderr," %d", pivots[index]); fprintf(stderr,", prods :"); for(index = 0; index < len; index++) fprintf(stderr," %d", prods[index]); fputc('\n',stderr); } return 0; } #undef ISEND #define ISEND(c) ( (c)==']' || (c)=='}' || (c)=='\0' ) /*---------------------------------------------------------------------*/ /*! Get an integer list in the range 0..(nvals-1), from the character string str. If we call the output pointer fred, then fred[0] = number of integers in the list (> 0), and fred[i] = i-th integer in the list for i=1..fred[0]. If on return, fred == NULL or fred[0] == 0, then something is wrong, and the caller must deal with that. Syntax of input string: - initial '{' or '[' is skipped, if present - ends when '}' or ']' or end of string is found - contains entries separated by commas - entries have one of these forms: - a single number - a dollar sign '$', which means nvals-1 - a sequence of consecutive numbers in the form "a..b" or "a-b", where "a" and "b" are single numbers (or '$') - a sequence of evenly spaced numbers in the form "a..b(c)" or "a-b(c)", where "c" encodes the step - Example: "[2,7..4,3..9(2)]" decodes to the list 2 7 6 5 4 3 5 7 9 - entries should be in the range 0..nvals-1 (borrowed, with permission, from thd_intlist.c) *//*-------------------------------------------------------------------*/ int * nifti_get_intlist( int nvals , const char * str ) { int *subv = NULL ; int ii , ipos , nout , slen ; int ibot,itop,istep , nused ; char *cpt ; /* Meaningless input? */ if( nvals < 1 ) return NULL ; /* No selection list? */ if( str == NULL || str[0] == '\0' ) return NULL ; /* skip initial '[' or '{' */ subv = (int *)malloc( sizeof(int) * 2 ) ; if( !subv ) { fprintf(stderr,"** nifti_get_intlist: failed alloc of 2 ints\n"); return NULL; } subv[0] = nout = 0 ; ipos = 0 ; if( str[ipos] == '[' || str[ipos] == '{' ) ipos++ ; if( g_opts.debug > 1 ) fprintf(stderr,"-d making int_list (vals = %d) from '%s'\n", nvals, str); /**- for each sub-selector until end of input... */ slen = (int)strlen(str) ; while( ipos < slen && !ISEND(str[ipos]) ){ while( isspace((int) str[ipos]) ) ipos++ ; /* skip blanks */ if( ISEND(str[ipos]) ) break ; /* done */ /**- get starting value */ if( str[ipos] == '$' ){ /* special case */ ibot = nvals-1 ; ipos++ ; } else { /* decode an integer */ ibot = strtol( str+ipos , &cpt , 10 ) ; if( ibot < 0 ){ fprintf(stderr,"** ERROR: list index %d is out of range 0..%d\n", ibot,nvals-1) ; free(subv) ; return NULL ; } if( ibot >= nvals ){ fprintf(stderr,"** ERROR: list index %d is out of range 0..%d\n", ibot,nvals-1) ; free(subv) ; return NULL ; } nused = (cpt-(str+ipos)) ; if( ibot == 0 && nused == 0 ){ fprintf(stderr,"** ERROR: list syntax error '%s'\n",str+ipos) ; free(subv) ; return NULL ; } ipos += nused ; } while( isspace((int) str[ipos]) ) ipos++ ; /* skip blanks */ /**- if that's it for this sub-selector, add one value to list */ if( str[ipos] == ',' || ISEND(str[ipos]) ){ nout++ ; subv = (int *)realloc( (char *)subv , sizeof(int) * (nout+1) ) ; if( !subv ) { fprintf(stderr,"** nifti_get_intlist: failed realloc of %d ints\n", nout+1); return NULL; } subv[0] = nout ; subv[nout] = ibot ; if( ISEND(str[ipos]) ) break ; /* done */ ipos++ ; continue ; /* re-start loop at next sub-selector */ } /**- otherwise, must have '..' or '-' as next inputs */ if( str[ipos] == '-' ){ ipos++ ; } else if( str[ipos] == '.' && str[ipos+1] == '.' ){ ipos++ ; ipos++ ; } else { fprintf(stderr,"** ERROR: index list syntax is bad: '%s'\n", str+ipos) ; free(subv) ; return NULL ; } /**- get ending value for loop now */ if( str[ipos] == '$' ){ /* special case */ itop = nvals-1 ; ipos++ ; } else { /* decode an integer */ itop = strtol( str+ipos , &cpt , 10 ) ; if( itop < 0 ){ fprintf(stderr,"** ERROR: index %d is out of range 0..%d\n", itop,nvals-1) ; free(subv) ; return NULL ; } if( itop >= nvals ){ fprintf(stderr,"** ERROR: index %d is out of range 0..%d\n", itop,nvals-1) ; free(subv) ; return NULL ; } nused = (cpt-(str+ipos)) ; if( itop == 0 && nused == 0 ){ fprintf(stderr,"** ERROR: index list syntax error '%s'\n",str+ipos) ; free(subv) ; return NULL ; } ipos += nused ; } /**- set default loop step */ istep = (ibot <= itop) ? 1 : -1 ; while( isspace((int) str[ipos]) ) ipos++ ; /* skip blanks */ /**- check if we have a non-default loop step */ if( str[ipos] == '(' ){ /* decode an integer */ ipos++ ; istep = strtol( str+ipos , &cpt , 10 ) ; if( istep == 0 ){ fprintf(stderr,"** ERROR: index loop step is 0!\n") ; free(subv) ; return NULL ; } nused = (cpt-(str+ipos)) ; ipos += nused ; if( str[ipos] == ')' ) ipos++ ; if( (ibot-itop)*istep > 0 ){ fprintf(stderr,"** WARNING: index list '%d..%d(%d)' means nothing\n", ibot,itop,istep ) ; } } /**- add values to output */ for( ii=ibot ; (ii-itop)*istep <= 0 ; ii += istep ){ nout++ ; subv = (int *)realloc( (char *)subv , sizeof(int) * (nout+1) ) ; if( !subv ) { fprintf(stderr,"** nifti_get_intlist: failed realloc of %d ints\n", nout+1); return NULL; } subv[0] = nout ; subv[nout] = ii ; } /**- check if we have a comma to skip over */ while( isspace((int) str[ipos]) ) ipos++ ; /* skip blanks */ if( str[ipos] == ',' ) ipos++ ; /* skip commas */ } /* end of loop through selector string */ if( g_opts.debug > 1 ) { fprintf(stderr,"+d int_list (vals = %d): ", subv[0]); for( ii = 1; ii <= subv[0]; ii++ ) fprintf(stderr,"%d ", subv[ii]); fputc('\n',stderr); } if( subv[0] == 0 ){ free(subv); subv = NULL; } return subv ; } /*---------------------------------------------------------------------*/ /*! Given a NIFTI_TYPE string, such as "NIFTI_TYPE_INT16", return the * corresponding integral type code. The type code is the macro * value defined in nifti1.h. *//*-------------------------------------------------------------------*/ int nifti_datatype_from_string( const char * name ) { int tablen = sizeof(nifti_type_list)/sizeof(nifti_type_ele); int c; if( !name ) return DT_UNKNOWN; for( c = tablen-1; c > 0; c-- ) if( !strcmp(name, nifti_type_list[c].name) ) break; return nifti_type_list[c].type; } /*---------------------------------------------------------------------*/ /*! Given a NIFTI_TYPE value, such as NIFTI_TYPE_INT16, return the * corresponding macro label as a string. The dtype code is the * macro value defined in nifti1.h. *//*-------------------------------------------------------------------*/ char * nifti_datatype_to_string( int dtype ) { int tablen = sizeof(nifti_type_list)/sizeof(nifti_type_ele); int c; for( c = tablen-1; c > 0; c-- ) if( nifti_type_list[c].type == dtype ) break; return nifti_type_list[c].name; } /*---------------------------------------------------------------------*/ /*! Determine whether dtype is a valid NIFTI_TYPE. * * DT_UNKNOWN is considered invalid * * The only difference 'for_nifti' makes is that DT_BINARY * should be invalid for a NIfTI dataset. *//*-------------------------------------------------------------------*/ int nifti_datatype_is_valid( int dtype, int for_nifti ) { int tablen = sizeof(nifti_type_list)/sizeof(nifti_type_ele); int c; /* special case */ if( for_nifti && dtype == DT_BINARY ) return 0; for( c = tablen-1; c > 0; c-- ) if( nifti_type_list[c].type == dtype ) return 1; return 0; } /*---------------------------------------------------------------------*/ /*! Only as a test, verify that the new nifti_type_list table matches * the the usage of nifti_datatype_sizes (which could be changed to * use the table, if there were interest). * * return the number of errors (so 0 is success, as usual) *//*-------------------------------------------------------------------*/ int nifti_test_datatype_sizes(int verb) { int tablen = sizeof(nifti_type_list)/sizeof(nifti_type_ele); int nbyper, ssize; int c, errs = 0; for( c = 0; c < tablen; c++ ) { nbyper = ssize = -1; nifti_datatype_sizes(nifti_type_list[c].type, &nbyper, &ssize); if( nbyper < 0 || ssize < 0 || nbyper != nifti_type_list[c].nbyper || ssize != nifti_type_list[c].swapsize ) { if( verb || g_opts.debug > 2 ) fprintf(stderr, "** type mismatch: %s, %d, %d, %d : %d, %d\n", nifti_type_list[c].name, nifti_type_list[c].type, nifti_type_list[c].nbyper, nifti_type_list[c].swapsize, nbyper, ssize); errs++; } } if( errs ) fprintf(stderr,"** nifti_test_datatype_sizes: found %d errors\n",errs); else if( verb || g_opts.debug > 1 ) fprintf(stderr,"-- nifti_test_datatype_sizes: all OK\n"); return errs; } /*---------------------------------------------------------------------*/ /*! Display the nifti_type_list table. * * if which == 1 : display DT_* * if which == 2 : display NIFTI_TYPE* * else : display all *//*-------------------------------------------------------------------*/ int nifti_disp_type_list( int which ) { char * style; int tablen = sizeof(nifti_type_list)/sizeof(nifti_type_ele); int lwhich, c; if ( which == 1 ){ lwhich = 1; style = "DT_"; } else if( which == 2 ){ lwhich = 2; style = "NIFTI_TYPE_"; } else { lwhich = 3; style = "ALL"; } printf("nifti_type_list entries (%s) :\n" " name type nbyper swapsize\n" " --------------------- ---- ------ --------\n", style); for( c = 0; c < tablen; c++ ) if( (lwhich & 1 && nifti_type_list[c].name[0] == 'D') || (lwhich & 2 && nifti_type_list[c].name[0] == 'N') ) printf(" %-22s %5d %3d %5d\n", nifti_type_list[c].name, nifti_type_list[c].type, nifti_type_list[c].nbyper, nifti_type_list[c].swapsize); return 0; } libminc-libminc-2-3-00/nifti/nifti1_io.h000066400000000000000000000620261257462267400200120ustar00rootroot00000000000000/** \file nifti1_io.h \brief Data structures for using nifti1_io API. - Written by Bob Cox, SSCC NIMH - Revisions by Rick Reynolds, SSCC NIMH */ #ifndef _NIFTI_IO_HEADER_ #define _NIFTI_IO_HEADER_ #include #include #include #include #include #ifndef DONT_INCLUDE_ANALYZE_STRUCT #define DONT_INCLUDE_ANALYZE_STRUCT /*** not needed herein ***/ #endif #include "nifti1.h" /*** NIFTI-1 header specification ***/ #include /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ /*****===================================================================*****/ /***** File nifti1_io.h == Declarations for nifti1_io.c *****/ /*****...................................................................*****/ /***** This code is released to the public domain. *****/ /*****...................................................................*****/ /***** Author: Robert W Cox, SSCC/DIRP/NIMH/NIH/DHHS/USA/EARTH *****/ /***** Date: August 2003 *****/ /*****...................................................................*****/ /***** Neither the National Institutes of Health (NIH), nor any of its *****/ /***** employees imply any warranty of usefulness of this software for *****/ /***** any purpose, and do not assume any liability for damages, *****/ /***** incidental or otherwise, caused by any use of this document. *****/ /*****===================================================================*****/ /* Modified by: Mark Jenkinson (FMRIB Centre, University of Oxford, UK) Date: July/August 2004 Mainly adding low-level IO and changing things to allow gzipped files to be read and written Full backwards compatability should have been maintained Modified by: Rick Reynolds (SSCC/DIRP/NIMH, National Institutes of Health) Date: December 2004 Modified and added many routines for I/O. */ /********************** Some sample data structures **************************/ typedef struct { /** 4x4 matrix struct **/ float m[4][4] ; } mat44 ; typedef struct { /** 3x3 matrix struct **/ float m[3][3] ; } mat33 ; /*...........................................................................*/ /*! \enum analyze_75_orient_code * \brief Old-style analyze75 orientation * codes. */ typedef enum _analyze75_orient_code { a75_transverse_unflipped = 0, a75_coronal_unflipped = 1, a75_sagittal_unflipped = 2, a75_transverse_flipped = 3, a75_coronal_flipped = 4, a75_sagittal_flipped = 5, a75_orient_unknown = 6 } analyze_75_orient_code; /*! \struct nifti_image \brief High level data structure for open nifti datasets in the nifti1_io API. Note that this structure is not part of the nifti1 format definition; it is used to implement one API for reading/writing formats in the nifti1 format. */ typedef struct { /*!< Image storage struct **/ int ndim ; /*!< last dimension greater than 1 (1..7) */ int nx ; /*!< dimensions of grid array */ int ny ; /*!< dimensions of grid array */ int nz ; /*!< dimensions of grid array */ int nt ; /*!< dimensions of grid array */ int nu ; /*!< dimensions of grid array */ int nv ; /*!< dimensions of grid array */ int nw ; /*!< dimensions of grid array */ int dim[8] ; /*!< dim[0]=ndim, dim[1]=nx, etc. */ size_t nvox ; /*!< number of voxels = nx*ny*nz*...*nw */ int nbyper ; /*!< bytes per voxel, matches datatype */ int datatype ; /*!< type of data in voxels: DT_* code */ float dx ; /*!< grid spacings */ float dy ; /*!< grid spacings */ float dz ; /*!< grid spacings */ float dt ; /*!< grid spacings */ float du ; /*!< grid spacings */ float dv ; /*!< grid spacings */ float dw ; /*!< grid spacings */ float pixdim[8] ; /*!< pixdim[1]=dx, etc. */ float scl_slope ; /*!< scaling parameter - slope */ float scl_inter ; /*!< scaling parameter - intercept */ float cal_min ; /*!< calibration parameter, minimum */ float cal_max ; /*!< calibration parameter, maximum */ int qform_code ; /*!< codes for (x,y,z) space meaning */ int sform_code ; /*!< codes for (x,y,z) space meaning */ int freq_dim ; /*!< indexes (1,2,3, or 0) for MRI */ int phase_dim ; /*!< directions in dim[]/pixdim[] */ int slice_dim ; /*!< directions in dim[]/pixdim[] */ int slice_code ; /*!< code for slice timing pattern */ int slice_start ; /*!< index for start of slices */ int slice_end ; /*!< index for end of slices */ float slice_duration ; /*!< time between individual slices */ /*! quaternion transform parameters [when writing a dataset, these are used for qform, NOT qto_xyz] */ float quatern_b , quatern_c , quatern_d , qoffset_x , qoffset_y , qoffset_z , qfac ; mat44 qto_xyz ; /*!< qform: transform (i,j,k) to (x,y,z) */ mat44 qto_ijk ; /*!< qform: transform (x,y,z) to (i,j,k) */ mat44 sto_xyz ; /*!< sform: transform (i,j,k) to (x,y,z) */ mat44 sto_ijk ; /*!< sform: transform (x,y,z) to (i,j,k) */ float toffset ; /*!< time coordinate offset */ int xyz_units ; /*!< dx,dy,dz units: NIFTI_UNITS_* code */ int time_units ; /*!< dt units: NIFTI_UNITS_* code */ int nifti_type ; /*!< 0==ANALYZE, 1==NIFTI-1 (1 file), 2==NIFTI-1 (2 files), 3==NIFTI-ASCII (1 file) */ int intent_code ; /*!< statistic type (or something) */ float intent_p1 ; /*!< intent parameters */ float intent_p2 ; /*!< intent parameters */ float intent_p3 ; /*!< intent parameters */ char intent_name[16] ; /*!< optional description of intent data */ char descrip[80] ; /*!< optional text to describe dataset */ char aux_file[24] ; /*!< auxiliary filename */ char *fname ; /*!< header filename (.hdr or .nii) */ char *iname ; /*!< image filename (.img or .nii) */ int iname_offset ; /*!< offset into iname where data starts */ int swapsize ; /*!< swap unit in image data (might be 0) */ int byteorder ; /*!< byte order on disk (MSB_ or LSB_FIRST) */ void *data ; /*!< pointer to data: nbyper*nvox bytes */ int num_ext ; /*!< number of extensions in ext_list */ nifti1_extension * ext_list ; /*!< array of extension structs (with data) */ analyze_75_orient_code analyze75_orient; /*!< for old analyze files, orient */ } nifti_image ; /* struct for return from nifti_image_read_bricks() */ typedef struct { int nbricks; /* the number of allocated pointers in 'bricks' */ size_t bsize; /* the length of each data block, in bytes */ void ** bricks; /* array of pointers to data blocks */ } nifti_brick_list; /*****************************************************************************/ /*------------------ NIfTI version of ANALYZE 7.5 structure -----------------*/ /* (based on fsliolib/dbh.h, but updated for version 7.5) */ typedef struct { /* header info fields - describes the header overlap with NIfTI */ /* ------------------ */ int sizeof_hdr; /* 0 + 4 same */ char data_type[10]; /* 4 + 10 same */ char db_name[18]; /* 14 + 18 same */ int extents; /* 32 + 4 same */ short int session_error; /* 36 + 2 same */ char regular; /* 38 + 1 same */ char hkey_un0; /* 39 + 1 40 bytes */ /* image dimension fields - describes image sizes */ short int dim[8]; /* 0 + 16 same */ short int unused8; /* 16 + 2 intent_p1... */ short int unused9; /* 18 + 2 ... */ short int unused10; /* 20 + 2 intent_p2... */ short int unused11; /* 22 + 2 ... */ short int unused12; /* 24 + 2 intent_p3... */ short int unused13; /* 26 + 2 ... */ short int unused14; /* 28 + 2 intent_code */ short int datatype; /* 30 + 2 same */ short int bitpix; /* 32 + 2 same */ short int dim_un0; /* 34 + 2 slice_start */ float pixdim[8]; /* 36 + 32 same */ float vox_offset; /* 68 + 4 same */ float funused1; /* 72 + 4 scl_slope */ float funused2; /* 76 + 4 scl_inter */ float funused3; /* 80 + 4 slice_end, */ /* slice_code, */ /* xyzt_units */ float cal_max; /* 84 + 4 same */ float cal_min; /* 88 + 4 same */ float compressed; /* 92 + 4 slice_duration */ float verified; /* 96 + 4 toffset */ int glmax,glmin; /* 100 + 8 108 bytes */ /* data history fields - optional */ char descrip[80]; /* 0 + 80 same */ char aux_file[24]; /* 80 + 24 same */ char orient; /* 104 + 1 NO GOOD OVERLAP */ char originator[10]; /* 105 + 10 FROM HERE DOWN... */ 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, omin; /* 184 + 8 */ int smax, smin; /* 192 + 8 200 bytes */ } nifti_analyze75; /* total: 348 bytes */ /*****************************************************************************/ /*--------------- Prototypes of functions defined in this file --------------*/ char *nifti_datatype_string ( int dt ) ; char *nifti_units_string ( int uu ) ; char *nifti_intent_string ( int ii ) ; char *nifti_xform_string ( int xx ) ; char *nifti_slice_string ( int ss ) ; char *nifti_orientation_string( int ii ) ; int nifti_is_inttype( int dt ) ; mat44 nifti_mat44_inverse( mat44 R ) ; mat33 nifti_mat33_inverse( mat33 R ) ; mat33 nifti_mat33_polar ( mat33 A ) ; float nifti_mat33_rownorm( mat33 A ) ; float nifti_mat33_colnorm( mat33 A ) ; float nifti_mat33_determ ( mat33 R ) ; mat33 nifti_mat33_mul ( mat33 A , mat33 B ) ; void nifti_swap_2bytes ( size_t n , void *ar ) ; void nifti_swap_4bytes ( size_t n , void *ar ) ; void nifti_swap_8bytes ( size_t n , void *ar ) ; void nifti_swap_16bytes( size_t n , void *ar ) ; void nifti_swap_Nbytes ( size_t n , int siz , void *ar ) ; int nifti_datatype_is_valid (int dtype, int for_nifti); int nifti_datatype_from_string(const char * name); char * nifti_datatype_to_string (int dtype); int nifti_get_filesize( const char *pathname ) ; void swap_nifti_header ( struct nifti_1_header *h , int is_nifti ) ; void old_swap_nifti_header( struct nifti_1_header *h , int is_nifti ); int nifti_swap_as_analyze( nifti_analyze75 *h ); /* main read/write routines */ nifti_image *nifti_image_read_bricks(const char *hname , int nbricks, const int *blist, nifti_brick_list * NBL); int nifti_image_load_bricks(nifti_image *nim , int nbricks, const int *blist, nifti_brick_list * NBL); void nifti_free_NBL( nifti_brick_list * NBL ); nifti_image *nifti_image_read ( const char *hname , int read_data ) ; int nifti_image_load ( nifti_image *nim ) ; void nifti_image_unload ( nifti_image *nim ) ; void nifti_image_free ( nifti_image *nim ) ; int nifti_read_collapsed_image( nifti_image * nim, const int dims [8], void ** data ); int nifti_read_subregion_image( nifti_image * nim, int *start_index, int *region_size, void ** data ); void nifti_image_write ( nifti_image * nim ) ; void nifti_image_write_bricks(nifti_image * nim, const nifti_brick_list * NBL); void nifti_image_infodump( const nifti_image * nim ) ; void nifti_disp_lib_hist( void ) ; /* to display library history */ void nifti_disp_lib_version( void ) ; /* to display library version */ int nifti_disp_matrix_orient( const char * mesg, mat44 mat ); int nifti_disp_type_list( int which ); char * nifti_image_to_ascii ( const nifti_image * nim ) ; nifti_image *nifti_image_from_ascii( const char * str, int * bytes_read ) ; size_t nifti_get_volsize(const nifti_image *nim) ; /* basic file operations */ int nifti_set_filenames(nifti_image * nim, const char * prefix, int check, int set_byte_order); char * nifti_makehdrname (const char * prefix, int nifti_type, int check, int comp); char * nifti_makeimgname (const char * prefix, int nifti_type, int check, int comp); int is_nifti_file (const char *hname); const char * nifti_find_file_extension(const char * name); int nifti_is_complete_filename(const char* fname); int nifti_validfilename(const char* fname); int disp_nifti_1_header(const char * info, const nifti_1_header * hp ) ; void nifti_set_debug_level( int level ) ; void nifti_set_skip_blank_ext( int skip ) ; void nifti_set_allow_upper_fext( int allow ) ; int valid_nifti_brick_list(nifti_image * nim , int nbricks, const int * blist, int disp_error); /* znzFile operations */ znzFile nifti_image_open(const char * hname, char * opts, nifti_image ** nim); znzFile nifti_image_write_hdr_img(nifti_image *nim, int write_data, const char* opts); znzFile nifti_image_write_hdr_img2( nifti_image *nim , int write_opts , const char* opts, znzFile imgfile, const nifti_brick_list * NBL); size_t nifti_read_buffer(znzFile fp, void* datatptr, size_t ntot, nifti_image *nim); int nifti_write_all_data(znzFile fp, nifti_image * nim, const nifti_brick_list * NBL); size_t nifti_write_buffer(znzFile fp, const void * buffer, size_t numbytes); nifti_image *nifti_read_ascii_image(znzFile fp, char *fname, int flen, int read_data); znzFile nifti_write_ascii_image(nifti_image *nim, const nifti_brick_list * NBL, const char * opts, int write_data, int leave_open); void nifti_datatype_sizes( int datatype , int *nbyper, int *swapsize ) ; void nifti_mat44_to_quatern( mat44 R , float *qb, float *qc, float *qd, float *qx, float *qy, float *qz, float *dx, float *dy, float *dz, float *qfac ) ; mat44 nifti_quatern_to_mat44( float qb, float qc, float qd, float qx, float qy, float qz, float dx, float dy, float dz, float qfac ); mat44 nifti_make_orthog_mat44( float r11, float r12, float r13 , float r21, float r22, float r23 , float r31, float r32, float r33 ) ; int nifti_short_order(void) ; /* CPU byte order */ /* Orientation codes that might be returned from nifti_mat44_to_orientation().*/ #define NIFTI_L2R 1 /* Left to Right */ #define NIFTI_R2L 2 /* Right to Left */ #define NIFTI_P2A 3 /* Posterior to Anterior */ #define NIFTI_A2P 4 /* Anterior to Posterior */ #define NIFTI_I2S 5 /* Inferior to Superior */ #define NIFTI_S2I 6 /* Superior to Inferior */ void nifti_mat44_to_orientation( mat44 R , int *icod, int *jcod, int *kcod ) ; /*--------------------- Low level IO routines ------------------------------*/ char * nifti_findhdrname (const char* fname); char * nifti_findimgname (const char* fname , int nifti_type); int nifti_is_gzfile (const char* fname); char * nifti_makebasename(const char* fname); /* other routines */ struct nifti_1_header nifti_convert_nim2nhdr(const nifti_image* nim); nifti_1_header * nifti_make_new_header(const int arg_dims[], int arg_dtype); nifti_1_header * nifti_read_header(const char *hname, int *swapped, int check); nifti_image * nifti_copy_nim_info(const nifti_image * src); nifti_image * nifti_make_new_nim(const int dims[], int datatype, int data_fill); nifti_image * nifti_simple_init_nim(void); nifti_image * nifti_convert_nhdr2nim(struct nifti_1_header nhdr, const char * fname); int nifti_hdr_looks_good (const nifti_1_header * hdr); int nifti_is_valid_datatype (int dtype); int nifti_is_valid_ecode (int ecode); int nifti_nim_is_valid (nifti_image * nim, int complain); int nifti_nim_has_valid_dims (nifti_image * nim, int complain); int is_valid_nifti_type (int nifti_type); int nifti_test_datatype_sizes (int verb); int nifti_type_and_names_match (nifti_image * nim, int show_warn); int nifti_update_dims_from_array(nifti_image * nim); void nifti_set_iname_offset (nifti_image *nim); int nifti_set_type_from_names (nifti_image * nim); int nifti_add_extension(nifti_image * nim, const char * data, int len, int ecode ); int nifti_compiled_with_zlib (void); int nifti_copy_extensions (nifti_image *nim_dest,const nifti_image *nim_src); int nifti_free_extensions (nifti_image *nim); int * nifti_get_intlist (int nvals , const char *str); char * nifti_strdup (const char *str); int valid_nifti_extensions(const nifti_image *nim); /*-------------------- Some C convenience macros ----------------------------*/ /* NIfTI-1.1 extension codes: see http://nifti.nimh.nih.gov/nifti-1/documentation/faq#Q21 */ #define NIFTI_ECODE_IGNORE 0 /* changed from UNKNOWN, 29 June 2005 */ #define NIFTI_ECODE_DICOM 2 /* intended for raw DICOM attributes */ #define NIFTI_ECODE_AFNI 4 /* Robert W Cox: rwcox@nih.gov http://afni.nimh.nih.gov/afni */ #define NIFTI_ECODE_COMMENT 6 /* plain ASCII text only */ #define NIFTI_ECODE_XCEDE 8 /* David B Keator: dbkeator@uci.edu http://www.nbirn.net/Resources /Users/Applications/ /xcede/index.htm */ #define NIFTI_ECODE_JIMDIMINFO 10 /* Mark A Horsfield: mah5@leicester.ac.uk http://someplace/something */ #define NIFTI_ECODE_WORKFLOW_FWDS 12 /* Kate Fissell: fissell@pitt.edu http://kraepelin.wpic.pitt.edu /~fissell/NIFTI_ECODE_WORKFLOW_FWDS /NIFTI_ECODE_WORKFLOW_FWDS.html */ #define NIFTI_ECODE_FREESURFER 14 /* http://surfer.nmr.mgh.harvard.edu */ #define NIFTI_ECODE_PYPICKLE 16 /* embedded Python objects http://niftilib.sourceforge.net /pynifti */ /* LONI MiND codes: http://www.loni.ucla.edu/twiki/bin/view/Main/MiND */ #define NIFTI_ECODE_MIND_IDENT 18 /* Vishal Patel: vishal.patel@ucla.edu*/ #define NIFTI_ECODE_B_VALUE 20 #define NIFTI_ECODE_SPHERICAL_DIRECTION 22 #define NIFTI_ECODE_DT_COMPONENT 24 #define NIFTI_ECODE_SHC_DEGREEORDER 26 /* end LONI MiND codes */ #define NIFTI_ECODE_VOXBO 28 /* Dan Kimberg: www.voxbo.org */ #define NIFTI_ECODE_CARET 30 /* John Harwell: john@brainvis.wustl.edu http://brainvis.wustl.edu/wiki /index.php/Caret:Documentation :CaretNiftiExtension */ #define NIFTI_MAX_ECODE 30 /******* maximum extension code *******/ /* nifti_type file codes */ #define NIFTI_FTYPE_ANALYZE 0 #define NIFTI_FTYPE_NIFTI1_1 1 #define NIFTI_FTYPE_NIFTI1_2 2 #define NIFTI_FTYPE_ASCII 3 #define NIFTI_MAX_FTYPE 3 /* this should match the maximum code */ /*------------------------------------------------------------------------*/ /*-- the rest of these apply only to nifti1_io.c, check for _NIFTI1_IO_C_ */ /* Feb 9, 2005 [rickr] */ #ifdef _NIFTI1_IO_C_ typedef struct { int debug; /*!< debug level for status reports */ int skip_blank_ext; /*!< skip extender if no extensions */ int allow_upper_fext; /*!< allow uppercase file extensions */ } nifti_global_options; typedef struct { int type; /* should match the NIFTI_TYPE_ #define */ int nbyper; /* bytes per value, matches nifti_image */ int swapsize; /* bytes per swap piece, matches nifti_image */ char * name; /* text string to match #define */ } nifti_type_ele; #undef LNI_FERR /* local nifti file error, to be compact and repetative */ #define LNI_FERR(func,msg,file) \ fprintf(stderr,"** ERROR (%s): %s '%s'\n",func,msg,file) #undef swap_2 #undef swap_4 #define swap_2(s) nifti_swap_2bytes(1,&(s)) /* s: 2-byte short; swap in place */ #define swap_4(v) nifti_swap_4bytes(1,&(v)) /* v: 4-byte value; swap in place */ /***** isfinite() is a C99 macro, which is present in many C implementations already *****/ #undef IS_GOOD_FLOAT #undef FIXED_FLOAT #ifdef isfinite /* use isfinite() to check floats/doubles for goodness */ # define IS_GOOD_FLOAT(x) isfinite(x) /* check if x is a "good" float */ # define FIXED_FLOAT(x) (isfinite(x) ? (x) : 0) /* fixed if bad */ #else # define IS_GOOD_FLOAT(x) 1 /* don't check it */ # define FIXED_FLOAT(x) (x) /* don't fix it */ #endif #undef ASSIF /* assign v to *p, if possible */ #define ASSIF(p,v) if( (p)!=NULL ) *(p) = (v) #undef MSB_FIRST #undef LSB_FIRST #undef REVERSE_ORDER #define LSB_FIRST 1 #define MSB_FIRST 2 #define REVERSE_ORDER(x) (3-(x)) /* convert MSB_FIRST <--> LSB_FIRST */ #define LNI_MAX_NIA_EXT_LEN 100000 /* consider a longer extension invalid */ #endif /* _NIFTI1_IO_C_ section */ /*------------------------------------------------------------------------*/ /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif /* _NIFTI_IO_HEADER_ */ libminc-libminc-2-3-00/nifti/znzlib.c000066400000000000000000000201251257462267400174260ustar00rootroot00000000000000/** \file znzlib.c \brief Low level i/o interface to compressed and noncompressed files. Written by Mark Jenkinson, FMRIB This library provides an interface to both compressed (gzip/zlib) and uncompressed (normal) file IO. The functions are written to have the same interface as the standard file IO functions. To use this library instead of normal file IO, the following changes are required: - replace all instances of FILE* with znzFile - change the name of all function calls, replacing the initial character f with the znz (e.g. fseek becomes znzseek) one exception is rewind() -> znzrewind() - add a third parameter to all calls to znzopen (previously fopen) that specifies whether to use compression (1) or not (0) - use znz_isnull rather than any (pointer == NULL) comparisons in the code for znzfile types (normally done after a return from znzopen) NB: seeks for writable files with compression are quite restricted */ #include "znzlib.h" /* znzlib.c (zipped or non-zipped library) ***** This code is released to the public domain. ***** ***** Author: Mark Jenkinson, FMRIB Centre, University of Oxford ***** ***** Date: September 2004 ***** ***** Neither the FMRIB Centre, the University of Oxford, nor any of ***** ***** its employees imply any warranty of usefulness of this software ***** ***** for any purpose, and do not assume any liability for damages, ***** ***** incidental or otherwise, caused by any use of this document. ***** */ /* Note extra argument (use_compression) where use_compression==0 is no compression use_compression!=0 uses zlib (gzip) compression */ znzFile znzopen(const char *path, const char *mode, int use_compression) { znzFile file; file = (znzFile) calloc(1,sizeof(struct znzptr)); if( file == NULL ){ fprintf(stderr,"** ERROR: znzopen failed to alloc znzptr\n"); return NULL; } file->nzfptr = NULL; #ifdef HAVE_ZLIB file->zfptr = NULL; if (use_compression) { file->withz = 1; if((file->zfptr = gzopen(path,mode)) == NULL) { free(file); file = NULL; } } else { #endif file->withz = 0; if((file->nzfptr = fopen(path,mode)) == NULL) { free(file); file = NULL; } #ifdef HAVE_ZLIB } #endif return file; } znzFile znzdopen(int fd, const char *mode, int use_compression) { znzFile file; file = (znzFile) calloc(1,sizeof(struct znzptr)); if( file == NULL ){ fprintf(stderr,"** ERROR: znzdopen failed to alloc znzptr\n"); return NULL; } #ifdef HAVE_ZLIB if (use_compression) { file->withz = 1; file->zfptr = gzdopen(fd,mode); file->nzfptr = NULL; } else { #endif file->withz = 0; #ifdef HAVE_FDOPEN file->nzfptr = fdopen(fd,mode); #endif #ifdef HAVE_ZLIB file->zfptr = NULL; }; #endif return file; } int Xznzclose(znzFile * file) { int retval = 0; if (*file!=NULL) { #ifdef HAVE_ZLIB if ((*file)->zfptr!=NULL) { retval = gzclose((*file)->zfptr); } #endif if ((*file)->nzfptr!=NULL) { retval = fclose((*file)->nzfptr); } free(*file); *file = NULL; } return retval; } /* we already assume ints are 4 bytes */ #undef ZNZ_MAX_BLOCK_SIZE #define ZNZ_MAX_BLOCK_SIZE (1<<30) size_t znzread(void* buf, size_t size, size_t nmemb, znzFile file) { #ifdef HAVE_ZLIB size_t remain = size*nmemb; char * cbuf = (char *)buf; unsigned n2read; int nread; #endif if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) { /* gzread/write take unsigned int length, so maybe read in int pieces (noted by M Hanke, example given by M Adler) 6 July 2010 [rickr] */ while( remain > 0 ) { n2read = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE; nread = gzread(file->zfptr, (void *)cbuf, n2read); if( nread < 0 ) return nread; /* returns -1 on error */ remain -= nread; cbuf += nread; /* require reading n2read bytes, so we don't get stuck */ if( nread < (int)n2read ) break; /* return will be short */ } /* warn of a short read that will seem complete */ if( remain > 0 && remain < size ) fprintf(stderr,"** znzread: read short by %u bytes\n",(unsigned)remain); return nmemb - remain/size; /* return number of members processed */ } #endif return fread(buf,size,nmemb,file->nzfptr); } size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file) { #ifdef HAVE_ZLIB size_t remain = size*nmemb; const char * cbuf = buf; unsigned n2write; int nwritten; #endif if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) { while( remain > 0 ) { n2write = (remain < ZNZ_MAX_BLOCK_SIZE) ? remain : ZNZ_MAX_BLOCK_SIZE; nwritten = gzwrite(file->zfptr, cbuf, n2write); /* gzread returns 0 on error, but in case that ever changes... */ if( nwritten < 0 ) return nwritten; remain -= nwritten; cbuf += nwritten; /* require writing n2write bytes, so we don't get stuck */ if( nwritten < (int)n2write ) break; } /* warn of a short write that will seem complete */ if( remain > 0 && remain < size ) fprintf(stderr,"** znzwrite: write short by %u bytes\n",(unsigned)remain); return nmemb - remain/size; /* return number of members processed */ } #endif return fwrite(buf,size,nmemb,file->nzfptr); } long znzseek(znzFile file, long offset, int whence) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return (long) gzseek(file->zfptr,offset,whence); #endif return fseek(file->nzfptr,offset,whence); } int znzrewind(znzFile stream) { if (stream==NULL) { return 0; } #ifdef HAVE_ZLIB /* On some systems, gzrewind() fails for uncompressed files. Use gzseek(), instead. 10, May 2005 [rickr] if (stream->zfptr!=NULL) return gzrewind(stream->zfptr); */ if (stream->zfptr!=NULL) return (int)gzseek(stream->zfptr, 0L, SEEK_SET); #endif rewind(stream->nzfptr); return 0; } long znztell(znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return (long) gztell(file->zfptr); #endif return ftell(file->nzfptr); } int znzputs(const char * str, znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzputs(file->zfptr,str); #endif return fputs(str,file->nzfptr); } char * znzgets(char* str, int size, znzFile file) { if (file==NULL) { return NULL; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzgets(file->zfptr,str,size); #endif return fgets(str,size,file->nzfptr); } int znzflush(znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzflush(file->zfptr,Z_SYNC_FLUSH); #endif return fflush(file->nzfptr); } int znzeof(znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzeof(file->zfptr); #endif return feof(file->nzfptr); } int znzputc(int c, znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzputc(file->zfptr,c); #endif return fputc(c,file->nzfptr); } int znzgetc(znzFile file) { if (file==NULL) { return 0; } #ifdef HAVE_ZLIB if (file->zfptr!=NULL) return gzgetc(file->zfptr); #endif return fgetc(file->nzfptr); } #if !defined (WIN32) int znzprintf(znzFile stream, const char *format, ...) { int retval=0; #ifdef HAVE_ZLIB char *tmpstr; #endif va_list va; if (stream==NULL) { return 0; } va_start(va, format); #ifdef HAVE_ZLIB if (stream->zfptr!=NULL) { int size; /* local to HAVE_ZLIB block */ size = strlen(format) + 1000000; /* overkill I hope */ tmpstr = (char *)calloc(1, size); if( tmpstr == NULL ){ fprintf(stderr,"** ERROR: znzprintf failed to alloc %d bytes\n", size); va_end(va); return retval; } vsprintf(tmpstr,format,va); retval=gzprintf(stream->zfptr,"%s",tmpstr); free(tmpstr); } else #endif { retval=vfprintf(stream->nzfptr,format,va); } va_end(va); return retval; } #endif libminc-libminc-2-3-00/nifti/znzlib.h000066400000000000000000000057501257462267400174420ustar00rootroot00000000000000#ifndef _ZNZLIB_H_ #define _ZNZLIB_H_ /* znzlib.h (zipped or non-zipped library) ***** This code is released to the public domain. ***** ***** Author: Mark Jenkinson, FMRIB Centre, University of Oxford ***** ***** Date: September 2004 ***** ***** Neither the FMRIB Centre, the University of Oxford, nor any of ***** ***** its employees imply any warranty of usefulness of this software ***** ***** for any purpose, and do not assume any liability for damages, ***** ***** incidental or otherwise, caused by any use of this document. ***** */ /* This library provides an interface to both compressed (gzip/zlib) and uncompressed (normal) file IO. The functions are written to have the same interface as the standard file IO functions. To use this library instead of normal file IO, the following changes are required: - replace all instances of FILE* with znzFile - change the name of all function calls, replacing the initial character f with the znz (e.g. fseek becomes znzseek) - add a third parameter to all calls to znzopen (previously fopen) that specifies whether to use compression (1) or not (0) - use znz_isnull rather than any (pointer == NULL) comparisons in the code NB: seeks for writable files with compression are quite restricted */ /*=================*/ #ifdef __cplusplus extern "C" { #endif /*=================*/ #include #include #include #include #include "config.h" /* include optional check for HAVE_FDOPEN here, from deleted config.h: uncomment the following line if fdopen() exists for your compiler and compiler options */ /* #define HAVE_FDOPEN */ #ifdef HAVE_ZLIB #if defined(ITKZLIB) #include "itk_zlib.h" #else #include "zlib.h" #endif #endif struct znzptr { int withz; FILE* nzfptr; #ifdef HAVE_ZLIB gzFile zfptr; #endif } ; /* the type for all file pointers */ typedef struct znzptr * znzFile; /* int znz_isnull(znzFile f); */ /* int znzclose(znzFile f); */ #define znz_isnull(f) ((f) == NULL) #define znzclose(f) Xznzclose(&(f)) /* Note extra argument (use_compression) where use_compression==0 is no compression use_compression!=0 uses zlib (gzip) compression */ znzFile znzopen(const char *path, const char *mode, int use_compression); znzFile znzdopen(int fd, const char *mode, int use_compression); int Xznzclose(znzFile * file); size_t znzread(void* buf, size_t size, size_t nmemb, znzFile file); size_t znzwrite(const void* buf, size_t size, size_t nmemb, znzFile file); long znzseek(znzFile file, long offset, int whence); int znzrewind(znzFile stream); long znztell(znzFile file); int znzputs(const char *str, znzFile file); char * znzgets(char* str, int size, znzFile file); int znzputc(int c, znzFile file); int znzgetc(znzFile file); #if !defined(WIN32) int znzprintf(znzFile stream, const char *format, ...); #endif /*=================*/ #ifdef __cplusplus } #endif /*=================*/ #endif libminc-libminc-2-3-00/testdir/000077500000000000000000000000001257462267400163175ustar00rootroot00000000000000libminc-libminc-2-3-00/testdir/CMakeLists.txt000066400000000000000000000152241257462267400210630ustar00rootroot00000000000000LINK_LIBRARIES(${LIBMINC_LIBRARIES}) MACRO(minc_test cmd) # minc 1 version IF(HAVE_MINC1) ADD_TEST(${cmd}-1 ${CMAKE_CURRENT_SOURCE_DIR}/run_test_cmake.sh ${CMAKE_CURRENT_BINARY_DIR}/${cmd} ${CMAKE_CURRENT_SOURCE_DIR}/${cmd}.out) ENDIF(HAVE_MINC1) ADD_TEST(${cmd}-2 ${CMAKE_CURRENT_SOURCE_DIR}/run_test2_cmake.sh ${CMAKE_CURRENT_BINARY_DIR}/${cmd} ${CMAKE_CURRENT_SOURCE_DIR}/${cmd}.out) IF(MINC_TEST_ENVIRONMENT) set_tests_properties( ${cmd}-1 PROPERTIES ENVIRONMENT "${MINC_TEST_ENVIRONMENT}") ENDIF(MINC_TEST_ENVIRONMENT) IF(MINC_TEST_ENVIRONMENT) set_tests_properties( ${cmd}-2 PROPERTIES ENVIRONMENT "${MINC_TEST_ENVIRONMENT}") ENDIF(MINC_TEST_ENVIRONMENT) ENDMACRO(minc_test) MACRO(add_minc_test name cmd) ADD_TEST( ${name} ${cmd} ${ARGV2} ${ARGV3} ${ARGV4} ${ARGV5} ${ARGV6} ${ARGV7} ${ARGV8} ${ARGV9} ${ARGV10} ${ARGV11} ${ARGV12} ${ARGV13} ${ARGV14} ${ARGV15} ${ARGV16} ${ARGV17} ${ARGV18} ${ARGV19} ${ARGV20} ${ARGV21} ${ARGV22} ${ARGV23} ${ARGV24} ${ARGV25} ${ARGV26} ) IF(MINC_TEST_ENVIRONMENT) set_tests_properties( ${name} PROPERTIES ENVIRONMENT "${MINC_TEST_ENVIRONMENT}") ENDIF(MINC_TEST_ENVIRONMENT) ENDMACRO(add_minc_test) IF(LIBMINC_MINC1_SUPPORT) ADD_EXECUTABLE(minc_tst minc.c) ADD_EXECUTABLE(icv icv.c) ADD_EXECUTABLE(icv_vec icv_vec.c) ADD_EXECUTABLE(icv_dim1 icv_dim1.c) ADD_EXECUTABLE(icv_dim icv_dim.c) ADD_EXECUTABLE(icv_fillvalue icv_fillvalue.c) ADD_EXECUTABLE(icv_range icv_range.c) ADD_EXECUTABLE(mincapi mincapi.c) ADD_EXECUTABLE(minc_types minc_types.c) ADD_EXECUTABLE(test_mconv test_mconv.c) ADD_EXECUTABLE(minc_long_attr minc_long_attr.c) ADD_EXECUTABLE(minc_conversion minc_conversion.c) #ADD_EXECUTABLE(test_speed test_speed.c) # running tests minc_test(minc_types) minc_test(icv_dim1) minc_test(icv_dim) minc_test(icv_fillvalue) minc_test(icv_range) add_minc_test(arg_parse ${CMAKE_CURRENT_SOURCE_DIR}/run_test_arg_parse_cmake.sh ${CMAKE_CURRENT_BINARY_DIR}/test_arg_parse) add_minc_test(icv icv) add_minc_test(icv_vec icv_vec) add_minc_test(minc minc_tst) add_minc_test(mincapi mincapi) add_minc_test(test_mconv test_mconv) add_minc_test(minc_long_attr_10k minc_long_attr 10000) add_minc_test(minc_long_attr_100k minc_long_attr 100000) add_minc_test(minc_long_attr_1m minc_long_attr 1000000) add_minc_test(minc_conversion minc_conversion) ENDIF(LIBMINC_MINC1_SUPPORT) ADD_EXECUTABLE(nifti_test nifti_test.c) ADD_TEST(nifti_test-1 nifti_test) # Volume IO tests ADD_EXECUTABLE(test_xfm vio_xfm_test/test-xfm.c) TARGET_LINK_LIBRARIES(test_xfm ${VOLUME_IO_LIBRARY} ${LIBMINC_LIBRARIES}) ADD_EXECUTABLE(copy_xfm vio_xfm_test/copy-xfm.c) TARGET_LINK_LIBRARIES(copy_xfm ${VOLUME_IO_LIBRARY} ${LIBMINC_LIBRARIES}) ADD_EXECUTABLE(create_grid_xfm create_grid_xfm.c) TARGET_LINK_LIBRARIES(create_grid_xfm ${VOLUME_IO_LIBRARY} ${LIBMINC_LIBRARIES}) ADD_EXECUTABLE(verify_xfm vio_xfm_test/verify_xfm.c) TARGET_LINK_LIBRARIES(verify_xfm ${VOLUME_IO_LIBRARY} ${LIBMINC_LIBRARIES}) #ADD_TEST(create_grid_xfm create_grid_xfm) #ADD_TEST(test_speed test_speed) add_minc_test(test_xfm_1 test_xfm 10000 ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/t1.xfm) add_minc_test(test_xfm_2 test_xfm 10000 ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/t2.xfm) add_minc_test(test_xfm_3 test_xfm 10000 ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/t3.xfm 0.9) add_minc_test(copy_xfm copy_xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/t3.xfm ${CMAKE_CURRENT_BINARY_DIR}/t3_copy.xfm) add_minc_test(verify_xfm_1 verify_xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/t3.xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/random2 1e-6) add_minc_test(verify_xfm_float verify_xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/test_float.xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/verify_xfm_table.txt 1e-9) add_minc_test(verify_xfm_short verify_xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/test_short.xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/verify_xfm_table.txt 1e-4) add_minc_test(verify_xfm_byte verify_xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/test_byte.xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/verify_xfm_table.txt 1e-1) add_minc_test(verify_xfm_2 verify_xfm ${CMAKE_CURRENT_BINARY_DIR}/t3_copy.xfm ${CMAKE_CURRENT_SOURCE_DIR}/vio_xfm_test/random2 1e-3) set_property(TEST verify_xfm_2 APPEND PROPERTY DEPENDS copy_xfm) #common tests ADD_EXECUTABLE(test_arg_parse test_arg_parse.c) add_minc_test(test_arg_parse test_arg_parse) #MINC2 tests ADD_EXECUTABLE(minc2-convert-test minc2-convert-test.c) ADD_EXECUTABLE(minc2-create-test-images-2 minc2-create-test-images-2.c) ADD_EXECUTABLE(minc2-create-test-images minc2-create-test-images.c) ADD_EXECUTABLE(minc2-datatype-test minc2-datatype-test.c) ADD_EXECUTABLE(minc2-large-attribute minc2-large-attribute.c) ADD_EXECUTABLE(minc2-dimension-test minc2-dimension-test.c) ADD_EXECUTABLE(minc2-full-test minc2-full-test.c) ADD_EXECUTABLE(minc2-grpattr-test minc2-grpattr-test.c) ADD_EXECUTABLE(minc2-hyper-test-2 minc2-hyper-test-2.c) ADD_EXECUTABLE(minc2-hyper-test minc2-hyper-test.c) ADD_EXECUTABLE(minc2-label-test minc2-label-test.c) #ADD_EXECUTABLE(minc2-m2stats minc2-m2stats.c) ADD_EXECUTABLE(minc2-multires-test minc2-multires-test.c) ADD_EXECUTABLE(minc2-record-test minc2-record-test.c) ADD_EXECUTABLE(minc2-slice-test minc2-slice-test.c) ADD_EXECUTABLE(minc2-valid-test minc2-valid-test.c) ADD_EXECUTABLE(minc2-vector_dimension-test minc2-vector_dimension-test.c) ADD_EXECUTABLE(minc2-volprops-test minc2-volprops-test.c) ADD_EXECUTABLE(minc2-read-rgb minc2-read-rgb.c) ADD_EXECUTABLE(minc2-read-metadata minc2-read-metadata.c) add_minc_test(minc2-convert-test minc2-convert-test) add_minc_test(minc2-create-test-images-2 minc2-create-test-images-2) add_minc_test(minc2-create-test-images minc2-create-test-images) add_minc_test(minc2-large-attribute-10k minc2-large-attribute 10000) add_minc_test(minc2-large-attribute-100k minc2-large-attribute 100000) add_minc_test(minc2-large-attribute-1m minc2-large-attribute 1000000) add_minc_test(minc2-datatype-test minc2-datatype-test) add_minc_test(minc2-dimension-test minc2-dimension-test) add_minc_test(minc2-full-test minc2-full-test) add_minc_test(minc2-grpattr-test minc2-grpattr-test) add_minc_test(minc2-hyper-test-2 minc2-hyper-test-2) add_minc_test(minc2-hyper-test minc2-hyper-test) add_minc_test(minc2-label-test minc2-label-test) #add_minc_test(minc2-m2stats minc2-m2stats) add_minc_test(minc2-multires-test minc2-multires-test) add_minc_test(minc2-record-test minc2-record-test) add_minc_test(minc2-slice-test minc2-slice-test) add_minc_test(minc2-valid-test minc2-valid-test) add_minc_test(minc2-vector_dimension-test minc2-vector_dimension-test) add_minc_test(minc2-volprops-test minc2-volprops-test) libminc-libminc-2-3-00/testdir/create_grid_xfm.c000066400000000000000000000022471257462267400216120ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif /* Create a grid transform from an existing MINC volume. */ #include #include #include "time_stamp.h" int main( int ac, char* av[] ) { VIO_Volume v; minc_input_options mio; VIO_General_transform t; VIO_Status st; if ( ac != 3 ) { fprintf( stderr, "usage: %s in_grid.mnc out.xfm\n", av[0] ); return 1; } /* The displacement volume must retain the vector dimension, * so we turn off "vector -> scalar" conversion. */ set_default_minc_input_options( &mio ); set_minc_input_vector_to_scalar_flag( &mio, 0 ); st = input_volume( av[1], 0, NULL, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &v, &mio ); if ( st != VIO_OK ) { fprintf( stderr, "failed to read grid volume \"%s\"\n", av[1] ); return 1; } create_grid_transform( &t, v, NULL ); st = output_transform_file( av[2], time_stamp(ac,av), &t ); if ( st != VIO_OK ) { fprintf( stderr, "error writing to xfm file \"%s\"\n", av[2] ); return 1; } return 0; } libminc-libminc-2-3-00/testdir/icv.c000066400000000000000000000053051257462267400172470ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_STRING_H #include #endif #define TRUE 1 #define FALSE 0 int main(int argc, char **argv) { int icv, cdfid, img, max, min, dimvar; static int dim[MAX_VAR_DIMS]; static struct { long len; char *name;} diminfo[] = { { 3, MIzspace }, { 4, MIyspace }, { 5, MIxspace } }; static int numdims=sizeof(diminfo)/sizeof(diminfo[0]); static long coord[]={0,0,0}; static long count[]={3,4,5}; double dvalue; short int ivalue[]={ 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349 }; long i, j, k; int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ icv=miicv_create(); miicv_setint(icv, MI_ICV_DO_NORM, TRUE); snprintf(filename, sizeof(filename), "test_icv-%d.mnc", getpid()); cdfid=micreate(filename, NC_CLOBBER | cflag); for (i=0; i #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_STRING_H #include #endif #define TRUE 1 #define FALSE 0 int main(int argc, char **argv) { int icv, cdfid, img, max, min, dimvar; static int dim[MAX_VAR_DIMS]; static struct { long len; char *name;} diminfo[] = { { 3, MIzspace }, { 9, MIyspace }, { 2, MIxspace } }; /* static struct { long len; char *name;} diminfo[]= {3, MIzspace, 4, MIyspace, 5, MIxspace}; */ static const int numdims=sizeof(diminfo)/sizeof(diminfo[0]); static long coord[]={0,0,0}; static long count[]={3,4,5}; double dvalue; short int ivalue[]={ 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349 }; long i, j, k; int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ icv=miicv_create(); miicv_setint(icv, MI_ICV_XDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_YDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_ADIM_SIZE, 5); miicv_setint(icv, MI_ICV_BDIM_SIZE, 4); miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE); miicv_setint(icv, MI_ICV_KEEP_ASPECT, FALSE); miicv_setint(icv, MI_ICV_DO_NORM, TRUE); snprintf(filename, sizeof(filename), "test_icv_dim-%d.mnc", getpid()); cdfid=micreate(filename, NC_CLOBBER | cflag); for (i=0; i #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_STRING_H #include #endif #define TRUE 1 #define FALSE 0 int main(int argc, char **argv) { int icv, cdfid, img, max, min, dimvar; static int dim[MAX_VAR_DIMS]; static struct { long len; char *name;} diminfo[] = { { 7, MIzspace }, { 9, MIyspace }, { 2, MIxspace } }; /* static struct { long len; char *name;} diminfo[]= {3, MIzspace, 4, MIyspace, 5, MIxspace}; */ static int numdims=sizeof(diminfo)/sizeof(diminfo[0]); static long coord[]={0,0,0}; static long count[]={3,4,5}; double dvalue; short int ivalue[]={ 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 211, 213, 215, 217, 219, 221, 223, 225, 227, 229, 231, 233, 235, 237, 239, 241, 243, 245, 247, 249, 311, 313, 315, 317, 319, 321, 323, 325, 327, 329, 331, 333, 335, 337, 339, 341, 343, 345, 347, 349 }; int i, j, k; int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ icv=miicv_create(); miicv_setint(icv, MI_ICV_XDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_YDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_ICV_NEGATIVE); miicv_setint(icv, MI_ICV_NUM_IMGDIMS, 3); miicv_setint(icv, MI_ICV_DIM_SIZE+0, 5); miicv_setint(icv, MI_ICV_DIM_SIZE+1, 4); miicv_setint(icv, MI_ICV_DIM_SIZE+2, 3); miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE); miicv_setint(icv, MI_ICV_KEEP_ASPECT, FALSE); miicv_setint(icv, MI_ICV_DO_NORM, TRUE); snprintf(filename, sizeof(filename), "test_icv_dim1-%d.mnc", getpid()); cdfid=micreate(filename, NC_CLOBBER | cflag); for (i=0; i #include #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STRING_H #include #endif #define TRUE 1 #define FALSE 0 int main(int argc, char **argv) { int icv, mincid, img, i; static int dim[MAX_VAR_DIMS]; static struct { long len; char *name;} diminfo[] = { { 4, MIyspace }, { 5, MIxspace} }; static int numdims=sizeof(diminfo)/sizeof(diminfo[0]); static long coord[]={0,0}; static long count[]={4,5}; static short int ivalue[]={ 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, -5 }; static int ivallen = sizeof(ivalue)/sizeof(ivalue[0]); int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ icv = miicv_create(); (void) miicv_setint(icv, MI_ICV_VALID_MAX, 200); (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0); (void) miicv_setint(icv, MI_ICV_DO_FILLVALUE, TRUE); snprintf(filename, sizeof(filename), "test_icv_fillvalue-%d.mnc", getpid()); mincid=micreate(filename, NC_CLOBBER | cflag); for (i=0; i<2; i++) { dim[i]=ncdimdef(mincid, diminfo[i].name, diminfo[i].len); } img=micreate_std_variable(mincid, MIimage, NC_SHORT, numdims, dim); (void) miattputint(mincid, img, MIvalid_max, 200); (void) miattputint(mincid, img, MIvalid_min, 0); (void) ncendef(mincid); (void) miicv_attach(icv, mincid, img); (void) miicv_put(icv, coord, count, ivalue); for (i=0; i #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STRING_H #include #endif #define TRUE 1 #define FALSE 0 #define MAX_IN_TYPES 2 #define MAX_NORM 2 #define MAX_OUT_TYPES 2 #define MAX_MAX 2 #define MAX_VAL 2 int main(int argc, char **argv) { int icv, cdfid, img, max, min; static char *typenm[]={"short", "double"}; static char *boolnm[] = {"true", "false"}; static nc_type intypes[] = {NC_SHORT, NC_DOUBLE}; static int norms[] = {TRUE, FALSE}; static nc_type outtypes[] = {NC_SHORT, NC_DOUBLE}; static int maxpresent[] = {TRUE, FALSE}; static int valpresent[] = {TRUE, FALSE}; static int dim[MAX_VAR_DIMS]; static struct { long len; char *name;} diminfo[] = { { 3, MIzspace }, { 1, MIyspace }, { 1, MIxspace } }; static int numdims=sizeof(diminfo)/sizeof(diminfo[0]); static long coord[]={0,0,0}; static long count[]={1,1,1}; static double max_values[] = {0.4, 0.6, 0.8}; double dvalue; short int ivalue; int i, intype, inorm, outtype, imax, ival; int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ snprintf(filename, sizeof(filename), "test_icv_range-%d.mnc", getpid()); for (intype=0; intype #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #include #include #ifdef HAVE_STRING_H #include #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static const int NZ = 9; /* NZ,NY, and NX should be <= 10! */ static const int NY = 8; static const int NX = 10; static const int NV = 6; /* define the dimension ordering we use - z,y,x,vector_dimension */ enum dimension_index { IZ, IY, IX, IV, N_DIM }; #define TVAL(z,y,x,v) ((z)*1000+(y)*100+(x)*10+(v)) #define TINDV(z,y,x,v) ((v) + ((x)*NV)+((y)*NV*NX)+((z)*NV*NX*NY)) #define TIND(z,y,x) ((x)+((y)*NX)+((z)*NX*NY)) #define TINDVS(z,y,x,v) ((v) + ((x)*NV)+((y)*NV*NX)+((z)*NV*NX*(NY+2))) #define TINDS(z,y,x) ((x)+((y)*NX)+((z)*NX*(NY+2))) #define IMAGE_MIN -1.0 #define IMAGE_MAX 1.0 #define VOXEL_MIN 0.0 #define VOXEL_MAX TVAL(NZ-1,NY-1,NX-1,NV-1) #define N_ELEMENTS (NZ*(NY+2)*NX*NV) /* NY+2 to accomodate stretching test */ static int test_icv_vector(int cflag, nc_type voxel_type) { int icv, cdfid, img, max, min, dimvar; int dim[N_DIM]; struct { long len; const char *name; } diminfo[N_DIM]; int numdims = N_DIM; long coord[N_DIM]; long count[N_DIM]; short int *ivalue = malloc(N_ELEMENTS * sizeof(short int)); double *dvalue = malloc(N_ELEMENTS * sizeof(double)); int i, j, k, v; char filename[256]; int return_val; int error_count = 0; printf("Testing with voxel type=%d\n", voxel_type); if (ivalue == NULL || dvalue == NULL) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); return 1; } diminfo[IZ].len = NZ; diminfo[IZ].name = MIzspace; diminfo[IY].len = NY; diminfo[IY].name = MIyspace; diminfo[IX].len = NX; diminfo[IX].name = MIxspace; diminfo[IV].len = NV; diminfo[IV].name = MIvector_dimension; coord[IZ] = coord[IY] = coord[IX] = coord[IV] = 0; count[IZ] = NZ; count[IY] = NY; count[IX] = NX; count[IV] = NV; snprintf(filename, sizeof(filename), "test_icv_vec-%d.mnc", getpid()); cdfid = micreate(filename, NC_CLOBBER | cflag); for (i = 0; i < numdims; i++) { dim[i] = ncdimdef(cdfid, diminfo[i].name, diminfo[i].len); if (i != IV) { dimvar = micreate_std_variable(cdfid, diminfo[i].name, NC_DOUBLE, 0, &dim[i]); miattputdbl(cdfid, dimvar, MIstep, 0.8); miattputdbl(cdfid, dimvar, MIstart, 22.0); } } img = micreate_std_variable(cdfid, MIimage, voxel_type, numdims, dim); miattputdbl(cdfid, img, MIvalid_max, VOXEL_MAX); miattputdbl(cdfid, img, MIvalid_min, VOXEL_MIN); max = micreate_std_variable(cdfid, MIimagemax, NC_DOUBLE, 1, dim); min = micreate_std_variable(cdfid, MIimagemin, NC_DOUBLE, 1, dim); ncendef(cdfid); for (i = 0; i < diminfo[0].len; i++) { double temp = 1.0; coord[IZ] = i; ncvarput1(cdfid, max, coord, &temp); temp = -temp; ncvarput1(cdfid, min, coord, &temp); } for (i = 0; i < NZ; i++) { for (j = 0; j < NY; j++) { for (k = 0; k < NX; k++) { for (v = 0; v < NV; v++) { int c = TINDV(i, j, k, v); ivalue[c] = TVAL(i, j, k, v); } } } } coord[IZ] = coord[IY] = coord[IX] = coord[IV] = 0; mivarput(cdfid, img, coord, count, NC_SHORT, MI_UNSIGNED, ivalue); icv = miicv_create(); return_val = miicv_setint(icv, MI_ICV_DO_NORM, TRUE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_TYPE, NC_DOUBLE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE); return_val = miicv_attach(icv, cdfid, img); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } for (i = 0; i < N_ELEMENTS; i++) dvalue[i] = 0; return_val = miicv_get(icv, coord, count, dvalue); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } for (i = 0; i < NZ; i++) { for (j = 0; j < NY; j++) { for (k = 0; k < NX; k++) { double iv = 0.0; for (v = 0; v < NV; v++) { iv += TVAL(i,j,k,v); } iv /= NV; if (voxel_type != NC_DOUBLE && voxel_type != NC_FLOAT) { iv = (iv + VOXEL_MIN) / (VOXEL_MAX - VOXEL_MIN); iv = iv * (IMAGE_MAX - IMAGE_MIN) + IMAGE_MIN; } if (fabs(iv - dvalue[TIND(i, j, k)]) > 1e-8) { fprintf(stdout, "Error on line %d, (%d,%d,%d): %8.7f %8.7f\n", __LINE__, i, j, k, dvalue[TIND(i,j,k)], iv); } } } } return_val = miicv_detach(icv); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_DO_SCALAR, FALSE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_attach(icv, cdfid, img); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } for (i=0; i < N_ELEMENTS; i++) dvalue[i] = 0; return_val = miicv_get(icv, coord, count, dvalue); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code %d at line %d!\n", return_val, __LINE__); error_count++; } for (i = 0; i < NZ; i++) { for (j = 0; j < NY; j++) { for (k = 0; k < NX; k++) { for (v = 0; v < NV; v++) { double iv = TVAL(i,j,k,v); if (voxel_type != NC_DOUBLE && voxel_type != NC_FLOAT) { iv = (iv + VOXEL_MIN) / (VOXEL_MAX - VOXEL_MIN); iv = iv * (IMAGE_MAX - IMAGE_MIN) + IMAGE_MIN; } if (fabs(iv - dvalue[TINDV(i,j,k,v)]) > 1e-8) { fprintf(stdout, "ERROR on line %d, (%d,%d,%d,%d): %8.7f %8.7f\n", __LINE__, i, j, k, v, dvalue[TINDV(i,j,k,v)], iv); error_count++; break; } } } } } return_val = miicv_detach(icv); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_DO_SCALAR, TRUE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_KEEP_ASPECT, FALSE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } /* Should increase Y dimension size by one. */ return_val = miicv_setint(icv, MI_ICV_BDIM_SIZE, NY+2); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_DO_RANGE, FALSE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_DO_NORM, FALSE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_TYPE, NC_DOUBLE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } return_val = miicv_attach(icv, cdfid, img); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } for (i = 0; i < N_ELEMENTS; i++) dvalue[i] = 0; count[IY] = NY+2; return_val = miicv_get(icv, coord, count, dvalue); if (return_val != MI_NOERROR) { fprintf(stdout, "ERROR bad return code at line %d!\n", __LINE__); error_count++; } for (i = 0; i < NZ; i++) { for (j = 0; j < NY+2; j++) { for (k = 0; k < NX; k++) { double iv = 0.0; if (j >= 1 && j < NY+1) { for (v = 0; v < NV; v++) { iv += TVAL(i,j-1,k,v); } iv /= NV; } if (fabs(iv - dvalue[TINDS(i,j,k)]) > 1e-8) { fprintf(stdout, "ERROR on line %d, (%d,%d,%d): %8.7f %8.7f\n", __LINE__, i, j, k, dvalue[TINDS(i,j,k)], iv); error_count++; break; } } } } miclose(cdfid); miicv_free(icv); free(ivalue); free(dvalue); unlink(filename); return (error_count); } #define N_TYPES 4 int main(int argc, char **argv) { int output_type[N_TYPES] = {NC_SHORT, NC_INT, NC_FLOAT, NC_DOUBLE}; int i; int error_count = 0; int cflag = 0; ncopts &= ~(NC_FATAL | NC_VERBOSE); printf("Testing ICV with vector dimensions.\n"); #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ for (i = 0; i < N_TYPES; i++) { error_count += test_icv_vector(cflag, output_type[i]); } printf("Exiting with error count %d\n", error_count); return error_count; } libminc-libminc-2-3-00/testdir/minc.c000066400000000000000000000040161257462267400174120ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : test @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif int main() { int cdf, cdf2; int img; int dim[MAX_VAR_DIMS]; int dim2[MAX_VAR_DIMS]; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; double image[256*256]; int i, j, k, ioff; char filename[256]; char filename2[256]; ncopts=NC_VERBOSE|NC_FATAL; snprintf(filename, sizeof(filename), "test_minc-%d.mnc", getpid()); snprintf(filename2, sizeof(filename2), "test_minc2-%d.mnc", getpid()); cdf=micreate(filename, NC_CLOBBER); count[2]=5; count[1]=3; count[0]=7; dim[2]=ncdimdef(cdf, MIzspace, count[2]); dim[1]=ncdimdef(cdf, MIxspace, count[1]); dim[0]=ncdimdef(cdf, MIyspace, count[0]); dim2[0]=ncdimdef(cdf, MItime, NC_UNLIMITED); dim2[1]=dim[0]; dim2[2]=dim[1]; img=ncvardef(cdf, MIimage, NC_SHORT, 3, dim); (void) ncvardef(cdf, "testvar", NC_FLOAT, 2, dim2); (void) miattputstr(cdf, img, MIsigntype, MI_SIGNED); for (j=0; j #include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define NDIMS 3 #define CX 40 #define CY 40 #define CZ 40 /* "Close enough for government work" */ #define EPSILON 1e-13 #define NEARLY_EQUAL(x, y) (fabs(x - y) < EPSILON) #define VALID_MAX 100 #define VALID_MIN 0 #define REAL_MIN -2.0 #define REAL_MAX 2.0 int main(void) { int result; double voxel[NDIMS]; double world[NDIMS]; double new_voxel[NDIMS]; misize_t coords[NDIMS]; misize_t count[NDIMS] = {1,1,1}; midimhandle_t hdims[NDIMS]; int i, j, k; int n; mihandle_t hvol; double v1, v2; double r1, r2, r3; double cosines[3]; result = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdims[0]); result = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdims[1]); result = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdims[2]); /* Set cosines, steps, and starts to reference values. */ cosines[0] = 0.999815712665963; cosines[1] = 0.019197414051189; cosines[2] = 0; miset_dimension_cosines(hdims[0], cosines); miset_dimension_start(hdims[0], -75.4957607345912); miset_dimension_separation(hdims[0], 1.0); cosines[0] = -0.019197414051189; cosines[1] = 0.999815712665963; cosines[2] = 0; miset_dimension_cosines(hdims[1], cosines); miset_dimension_start(hdims[1], 160.392861750822); miset_dimension_separation(hdims[1], -1.0); cosines[0] = 0; cosines[1] = 0; cosines[2] = 1; miset_dimension_cosines(hdims[2], cosines); miset_dimension_start(hdims[2], 121.91304); miset_dimension_separation(hdims[2], -1.0); result = micreate_volume("tst-convert.mnc", NDIMS, hdims, MI_TYPE_UINT, MI_CLASS_REAL, NULL, &hvol); if (result < 0) { TESTRPT("micreate_volume error", result); } result = micreate_volume_image(hvol); if (result < 0) { TESTRPT("micreate_volume_image error", result); } /* Valid values are from 0 to 100 */ miset_volume_valid_range(hvol, VALID_MAX, VALID_MIN); /* "real" values are -2.0 to 2.0 */ miset_volume_range(hvol, REAL_MAX, REAL_MIN); for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; voxel[0] = coords[0]; voxel[1] = coords[1]; voxel[2] = coords[2]; miconvert_voxel_to_world(hvol, voxel, world); miconvert_world_to_voxel(hvol, world, new_voxel); for (n = 0; n < 3; n++) { if (!NEARLY_EQUAL(voxel[n], new_voxel[n])) { fprintf(stderr, "%d %f %f\n", n, voxel[n], new_voxel[n]); TESTRPT("conversion error", 0); } } v1 = (VALID_MAX * (voxel[0] + voxel[1] + voxel[2]) / ((CX-1)+(CY-1)+(CZ-1))); result = miset_voxel_value_hyperslab(hvol, MI_TYPE_DOUBLE, coords, count, &v1); if (result < 0) { TESTRPT("miset_voxel_value_hyperslab error", result); } /* Get a voxel value. */ result = miget_voxel_value_hyperslab(hvol, MI_TYPE_DOUBLE, coords, count, &v1); if (result < 0) { TESTRPT("miget_voxel_value_hyperslab error", result); } /* Convert from voxel to real. */ result = miconvert_voxel_to_real(hvol, coords, 3, v1, &r1); if (result < 0) { TESTRPT("miconvert_voxel_to_real error", result); } /* Double check the conversion. */ r3 = (v1 - VALID_MIN) / (VALID_MAX - VALID_MIN); r3 = (r3 * (REAL_MAX - REAL_MIN)) + REAL_MIN; if (!NEARLY_EQUAL(r3, r1)) { TESTRPT("real value mismatch", 0); } /* Convert it back to voxel. */ result = miconvert_real_to_voxel(hvol, coords, 3, r1, &v2); if (result < 0) { TESTRPT("miconvert_real_to_voxel error", result); } /* Compare to the value read via the ICV */ result = miget_real_value(hvol, coords, 3, &r2); if (result < 0) { TESTRPT("miget_real_value error", result); } if (!NEARLY_EQUAL(v1, v2)) { fprintf(stderr, "v1 %f v2 %f\n", v1, v2); TESTRPT("Voxel value mismatch", 0); } if (!NEARLY_EQUAL(r1, r2)) { fprintf(stderr, "r1 %f r2 %f\n", r1, r2); TESTRPT("Real value mismatch", 0); } result = miget_voxel_value(hvol, coords, 3, &v1); if (result < 0) { TESTRPT("miget_voxel_value error", result); } if (!NEARLY_EQUAL(v1, v2)) { fprintf(stderr, "v1 %f v2 %f\n", v1, v2); TESTRPT("Voxel value mismatch", 0); } } } } world[MI2_X] = -80; world[MI2_Y] = 150; world[MI2_Z] = 120; result = miset_world_origin(hvol, world); if (result != MI_NOERROR) { TESTRPT("miset_world_origin error", result); } voxel[MI2_X] = voxel[MI2_Y] = voxel[MI2_Z] = 0; result = miconvert_voxel_to_world(hvol, voxel, world); if (result != MI_NOERROR) { TESTRPT("miconvert_voxel_to_world error", result); } if (!NEARLY_EQUAL(world[MI2_X], -80.0) || !NEARLY_EQUAL(world[MI2_Y], 150.0) || !NEARLY_EQUAL(world[MI2_Z], 120.0)) { fprintf(stderr, "%f %f %f\n", world[0], world[1], world[2]); TESTRPT("conversion error", 0); } if (error_cnt == 0) { printf("No errors\n"); } else { printf("%d error%s\n", error_cnt, error_cnt == 1 ? "" : "s"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-create-test-images-2.c000066400000000000000000000130271257462267400233160ustar00rootroot00000000000000#include #include #include "minc2.h" /* This test will attempt to create a few different test images * which can be tested with minc2.0 */ #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) #define CZ 142 #define CY 245 #define CX 120 #define NDIMS 3 static int create_real_as_int_image(void) { int r; int error_cnt=0; double start_values[NDIMS]={-6.96, -12.453, -9.48}; double separations[NDIMS]={0.09,0.09,0.09}; midimhandle_t hdim[NDIMS]; mihandle_t hvol; int *buf = ( int *) malloc(CX * CY * CZ * sizeof(int)); int i; misize_t count[NDIMS]; misize_t start[NDIMS]; miboolean_t flag=1; double min = -1.0; double max = 1.0; /*TODO: add error checks in this functions*/ r = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[0]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[1]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = miset_dimension_starts(hdim, NDIMS, start_values); if( r!= MI_NOERROR ) TESTRPT("miset_dimension_starts",r); r = miset_dimension_separations(hdim, NDIMS, separations); if( r!= MI_NOERROR ) TESTRPT("miset_dimension_separations",r); r = micreate_volume("real_as_int_image.mnc", NDIMS, hdim, MI_TYPE_INT, MI_CLASS_REAL, NULL, &hvol); if( r!= MI_NOERROR ) TESTRPT("micreate_volume",r); /* set slice scaling flag to true */ r = miset_slice_scaling_flag(hvol, flag); if( r!= MI_NOERROR ) TESTRPT("miset_slice_scaling_flag",r); r = micreate_volume_image(hvol); if( r!= MI_NOERROR ) TESTRPT("micreate_volume_image",r); for (i = 0; i < CY*CX*CZ; i++) { buf[i] = (int) (i * 0.001); } start[0] = start[1] = start[2] = 0; count[0] = CY; count[1] = CX; count[2] = CZ; r = miset_voxel_value_hyperslab(hvol, MI_TYPE_INT, start, count, buf); if( r!= MI_NOERROR ) TESTRPT("miset_voxel_value_hyperslab",r); /* Set random values to slice min and max for slice scaling*/ start[0] =start[1]=start[2]=0; for (i=0; i < CY; i++) { start[0] = i; min += -0.1; max += 0.1; r = miset_slice_range(hvol,start,NDIMS , max, min); if(r<0) { if( r!= MI_NOERROR ) TESTRPT("miset_slice_range",r); return r; } } r = miclose_volume(hvol); if( r!= MI_NOERROR ) TESTRPT("miclose_volume",r); return error_cnt; } static int create_real_as_float_image(void) { int r; int error_cnt=0; double start_values[NDIMS]={-6.96, -12.453, -9.48}; double separations[NDIMS]={0.09,0.09,0.09}; midimhandle_t hdim[NDIMS]; mihandle_t hvol; float *buf = (float *) malloc(CX * CY * CZ * sizeof(float)); int i; misize_t count[NDIMS]; misize_t start[NDIMS]; miboolean_t flag=1; double min = -1.0; double max = 1.0; r = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[0]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[1]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2]); if( r!= MI_NOERROR ) TESTRPT("micreate_dimension",r); r = miset_dimension_starts(hdim, NDIMS, start_values); if( r!= MI_NOERROR ) TESTRPT("miset_dimension_starts",r); r = miset_dimension_separations(hdim, NDIMS, separations); if( r!= MI_NOERROR ) TESTRPT("miset_dimension_separations",r); r = micreate_volume("real_as_float_image.mnc", NDIMS, hdim, MI_TYPE_FLOAT, MI_CLASS_REAL, NULL, &hvol); if( r!= MI_NOERROR ) TESTRPT("micreate_volume",r); /* set slice scaling flag to true */ r = miset_slice_scaling_flag(hvol, flag); if( r!= MI_NOERROR ) TESTRPT("miset_slice_scaling_flag",r); r = micreate_volume_image(hvol); if( r!= MI_NOERROR ) TESTRPT("micreate_volume_image",r); for (i = 0; i < CY*CX*CZ; i++) { buf[i] = i * 0.001f; } start[0] = start[1] = start[2] = 0; count[0] = CY; count[1] = CX; count[2] = CZ; r = miset_voxel_value_hyperslab(hvol, MI_TYPE_FLOAT, start, count, buf); if( r!= MI_NOERROR ) TESTRPT("miset_voxel_value_hyperslab",r); /* Set random values to slice min and max for slice scaling*/ start[0] =start[1]=start[2]=0; for (i=0; i < CY; i++) { start[0] = i; min += -0.1; max += 0.1; r = miset_slice_range(hvol,start,NDIMS , max, min); if( r!= MI_NOERROR ) TESTRPT("miset_slice_range",r); } r = miclose_volume(hvol); if( r!= MI_NOERROR ) TESTRPT("miclose_volume",r); return error_cnt; } int main(void) { int r = 0; printf("Creating 3D image REAL stored as INT w/ slice scaling!! (real_as_int_image.mnc)\n"); r +=create_real_as_int_image(); printf("Creating 3D image REAL stored as FLOAT w/ slice scaling!! (real_as_float_image.mnc)\n"); r +=create_real_as_float_image(); if (r != 0) { fprintf(stderr, "%d error%s reported\n", r, (r == 1) ? "" : "s"); } else { fprintf(stderr, "\n No errors\n"); } return (r); } libminc-libminc-2-3-00/testdir/minc2-create-test-images.c000066400000000000000000000153211257462267400231560ustar00rootroot00000000000000#include #include #include "minc2.h" /* This test will attempt to create a few different test images * which can be tested with minc2.0 */ static int error_cnt=0; #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) #define CZ 142 #define CY 245 #define CX 10 #define CU 5 #define NDIMS 3 static int create_2D_image ( void ) { int r, i; midimhandle_t hdim[NDIMS - 1]; mihandle_t hvol; short *buf = ( short * ) malloc ( CX * CY * sizeof ( short ) ); double *offsets = ( double * ) malloc ( CX * sizeof ( double ) ); double start_values[NDIMS - 1] = { -1.01, -2.02}; miboolean_t flag = 0; misize_t count[NDIMS - 1]; misize_t start[NDIMS - 1]; r = micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_NOT_REGULARLY_SAMPLED, CX, &hdim[0] ); if(r<0) return r; r = micreate_dimension ( "yspace", MI_DIMCLASS_USER, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1] ); if(r<0) return r; for ( i = 0; i < CX; i++ ) { offsets[i] = ( i * i ) + 0.1; } r = miset_dimension_offsets ( hdim[0], CX, 0, offsets ); if(r<0) return r; r = miset_dimension_separation ( hdim[1], 0.06 ); if(r<0) return r; r = miset_dimension_starts ( hdim, NDIMS - 1, start_values ); if(r<0) return r; r = micreate_volume ( "2D_image.mnc", NDIMS - 1 , hdim, MI_TYPE_SHORT, MI_CLASS_REAL, NULL, &hvol ); if(r<0) return r; /* set slice scaling flag to true */ r = miset_slice_scaling_flag ( hvol, flag ); if(r<0) return r; r = micreate_volume_image ( hvol ); if(r<0) return r; for ( i = 0; i < CX * CY; i++ ) { buf[i] = ( short ) (i * 0.1); } start[0] = start[1] = 0; count[0] = CX; count[1] = CY; r = miset_voxel_value_hyperslab ( hvol, MI_TYPE_SHORT, start, count, buf ); if(r<0) return r; r = miclose_volume ( hvol ); if(r<0) return r; return r; } static int create_3D_image ( void ) { int r; double start_values[NDIMS] = { -6.96, -12.453, -9.48}; double separations[NDIMS] = {0.09, 0.09, 0.09}; midimhandle_t hdim[NDIMS]; mihandle_t hvol; unsigned short *buf = ( unsigned short * ) malloc ( CX * CY * CZ * sizeof ( unsigned short ) ); int i; misize_t count[NDIMS]; misize_t start[NDIMS]; miboolean_t flag = 1; double min = -1.0; double max = 1.0; r = micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[0] ); if(r<0) return r; r = micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[1] ); if(r<0) return r; r = micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2] ); if(r<0) return r; r = miset_dimension_starts ( hdim, NDIMS, start_values ); if(r<0) return r; r = miset_dimension_separations ( hdim, NDIMS, separations ); if(r<0) return r; r = micreate_volume ( "3D_image.mnc", NDIMS, hdim, MI_TYPE_USHORT, MI_CLASS_REAL, NULL, &hvol ); if(r<0) return r; /* set slice scaling flag to true */ r = miset_slice_scaling_flag ( hvol, flag ); if(r<0) return r; r = micreate_volume_image ( hvol ); if(r<0) return r; for ( i = 0; i < CY * CX * CZ; i++ ) { buf[i] = ( unsigned short ) (i * 0.001); } start[0] = start[1] = start[2] = 0; count[0] = CY; count[1] = CX; count[2] = CZ; r = miset_voxel_value_hyperslab ( hvol, MI_TYPE_USHORT, start, count, buf ); if(r<0) return r; /* Set random values to slice min and max for slice scaling*/ start[0] = start[1] = start[2] = 0; for ( i = 0; i < CY; i++ ) { start[0] = i; min += 0.1; max += 0.1; r = miset_slice_range ( hvol, start, NDIMS , max, min ); if(r<0) return r; } r = miclose_volume ( hvol ); return r; } static int create_4D_image ( void ) { int r; double start_values[NDIMS + 1] = { -6.96, -12.453, -9.48, 20.002}; double separations[NDIMS + 1] = {0.09, 0.09, 0.09, 1}; midimhandle_t hdim[NDIMS + 1]; mihandle_t hvol; unsigned char *buf = ( unsigned char * ) malloc ( CX * CU * CZ * CY * sizeof ( unsigned char ) ); int i, j; misize_t count[NDIMS + 1]; misize_t start[NDIMS + 1]; miboolean_t flag = 1; double min = -1.0; double max = 1.0; r = micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[0] ); if(r<0) return r; r = micreate_dimension ( "user", MI_DIMCLASS_USER, MI_DIMATTR_REGULARLY_SAMPLED, CU, &hdim[1] ); if(r<0) return r; r = micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2] ); if(r<0) return r; r = micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[3] ); if(r<0) return r; r = miset_dimension_starts ( hdim, NDIMS + 1, start_values ); if(r<0) return r; r = miset_dimension_separations ( hdim, NDIMS + 1, separations ); if(r<0) return r; r = micreate_volume ( "4D_image.mnc", NDIMS + 1, hdim, MI_TYPE_UBYTE, MI_CLASS_REAL, NULL, &hvol ); if(r<0) return r; /* set slice scaling flag to true */ r = miset_slice_scaling_flag ( hvol, flag ); if(r<0) return r; r = micreate_volume_image ( hvol ); if(r<0) return r; for ( i = 0; i < CX * CU * CZ * CY; i++ ) { buf[i] = ( unsigned char ) i; } start[0] = start[1] = start[2] = start[3] = 0; count[0] = CX; count[1] = CU; count[2] = CZ; count[3] = CY; r = miset_voxel_value_hyperslab ( hvol, MI_TYPE_UBYTE, start, count, buf ); if(r<0) return r; /* Set random values to slice min and max for slice scaling*/ start[0] = start[1] = start[2] = start[3] = 0; for ( i = 0; i < CX; i++ ) { start[0] = i; for ( j = 0; j < CU; j++ ) { start[1] = j; min += -0.1; max += 0.1; r = miset_slice_range ( hvol, start, NDIMS + 1 , max, min ); if(r<0) return r; } } r = miclose_volume ( hvol ); return r; } int main ( void ) { printf ( "Creating 2D image with IRREGULAR sample dimension!! (2D_image.mnc)\n" ); if(create_2D_image()<0) TESTRPT("create_2D_image",0); printf ( "Creating 3D image with slice scaling!! (3D_image.mnc)\n" ); if( create_3D_image()<0) TESTRPT("create_3D_image",0); printf ( "Creating 4D image with slice scaling!! (4D_image.mnc)\n" ); if(create_4D_image()<0) TESTRPT("create_4D_image",0); if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "\n No errors\n" ); } return ( error_cnt ); } libminc-libminc-2-3-00/testdir/minc2-datatype-test.c000066400000000000000000000020571257462267400222650ustar00rootroot00000000000000#include #include #include "minc2.h" int main(int argc, char **argv) { miclass_t myclass = MI_CLASS_REAL; mitype_t mytype = MI_TYPE_UNKNOWN; misize_t mysize = 0; char *myname = NULL; mihandle_t volume = NULL; /* Turn off automatic error reporting. */ H5Eset_auto2(H5E_DEFAULT, NULL, NULL); /* Check each file. */ while (--argc > 0) { ++argv; if (micreate_volume(*argv, 0, NULL, MI_TYPE_INT, 0, NULL, &volume) < 0) { /*type 0 is equivalent to H5T_INTEGER */ fprintf(stderr, "Error opening %s\n", *argv); } else { int i; /* Repeat many times to expose resource leakage problems, etc. */ for (i = 0; i < 25000; i++) { miget_data_type(volume, &mytype); miget_data_type_size(volume, &mysize); miget_data_class(volume, &myclass); miget_space_name(volume, &myname); mifree_name(myname); } miclose_volume(volume); printf("file: %s type %d size %lld class %d name %s\n", *argv, mytype, mysize, myclass, myname); } } return (0); } libminc-libminc-2-3-00/testdir/minc2-dimension-test.c000066400000000000000000000177161257462267400224470ustar00rootroot00000000000000#include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CX 10 #define CY 10 #define CZ 6 #define CT 8 #define NDIMS 4 #define XSTART (-24) #define XSTEP (1) #define YSTART (-26) #define YSTEP (2) #define ZSTART (22) #define ZSTEP (-1.5) static int check_dims(mihandle_t vol, midimhandle_t dim[]) { int i; int r; int n; mihandle_t vol_tmp; midimhandle_t dim_tmp[NDIMS]; double offsets[100]; for (i = 0; i < CT; i++) { double tmp = -1; r = miget_dimension_offsets(dim[0], 1, i, &tmp); if (r < 0) { TESTRPT("failed", r); } if ((i * i) + 100.0 != tmp) { TESTRPT("bad value", i); } } r = miget_dimension_offsets(dim[1], CX, 0, offsets); if (r < 0) { TESTRPT("failed", r); } for (i = 0; i < CX; i++) { if (offsets[i] != XSTART + (i * XSTEP)) { TESTRPT("bad value", i); } } r = miget_dimension_offsets(dim[2], CY, 0, offsets); if (r < 0) { TESTRPT("failed", r); } for (i = 0; i < CY; i++) { if (offsets[i] != YSTART + (i * YSTEP)) { TESTRPT("bad value", i); } } r = miget_dimension_offsets(dim[3], CZ, 0, offsets); if (r < 0) { TESTRPT("failed", r); } for (i = 0; i < CZ; i++) { if (offsets[i] != ZSTART + (i * ZSTEP)) { TESTRPT("bad value", i); } } r = miget_volume_dimension_count(vol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, &n); if (r < 0) { TESTRPT("failed", r); } if (n != NDIMS-1) { TESTRPT("wrong number of spatial dimensions", n); } r = miget_volume_dimension_count(vol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &n); if (r < 0) { TESTRPT("failed", r); } if (n != NDIMS) { TESTRPT("wrong number of dimensions", n); } r = miget_volume_dimension_count(vol, MI_DIMCLASS_TIME, MI_DIMATTR_ALL, &n); if (r < 0) { TESTRPT("failed", r); } if (n != 1) { TESTRPT("wrong number of time dimensions", n); } for (i = 0; i < NDIMS; i++) { miboolean_t flag_value; r = miget_dimension_sampling_flag(dim[i], &flag_value); if (r < 0) { TESTRPT("error getting sampling flag", r); } else if (flag_value != (i == 0)) { TESTRPT("wrong value for sampling flag", i); } } r = miget_volume_dimensions(vol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_FILE, NDIMS, dim_tmp); if (r < 0) { TESTRPT("failed to get dimensions", r); } for (i = 0; i < NDIMS; i++) { vol_tmp = NULL; r = miget_volume_from_dimension(dim_tmp[i], &vol_tmp); if (r < 0) { TESTRPT("failed to get volume from dimension", r); } else if (vol_tmp != vol) { TESTRPT("wrong volume returned", i); } } return (error_cnt); } int main(void) { mihandle_t vol; int r; midimhandle_t dim[NDIMS]; int n; misize_t coords[NDIMS]; misize_t count[NDIMS]; int i,j,k; double offset; unsigned int voxel; /* Write data one voxel at a time. */ for (i = 0; i < NDIMS; i++) { count[i] = 1; } r = micreate_dimension("time", MI_DIMCLASS_TIME, MI_DIMATTR_NOT_REGULARLY_SAMPLED, CT, &dim[0]); if (r < 0) { TESTRPT("failed", r); } for (i = 0; i < CT; i++) { offset = (i * i) + 100.0; r = miset_dimension_offsets(dim[0], 1, i, &offset); if (r < 0) { TESTRPT("failed", r); } } r = micreate_dimension("xspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &dim[1]); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_start(dim[1], XSTART); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_separation(dim[1], XSTEP); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("yspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &dim[2]); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_start(dim[2], YSTART); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_separation(dim[2], YSTEP); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("zspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &dim[3]); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_start(dim[3], ZSTART); if (r < 0) { TESTRPT("failed", r); } r = miset_dimension_separation(dim[3], ZSTEP); if (r < 0) { TESTRPT("failed", r); } r = micreate_volume("tst-dim.mnc", NDIMS, dim, MI_TYPE_UINT, MI_CLASS_REAL, NULL, &vol); if (r < 0) { TESTRPT("failed", r); } r = micreate_volume_image(vol); if (r < 0) { TESTRPT("failed", r); } check_dims(vol, dim); for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = 0; coords[1] = i; coords[2] = j; coords[3] = k; voxel = (i*10000)+(j*100)+k; r = miset_voxel_value_hyperslab(vol, MI_TYPE_UINT, coords, count, &voxel); if (r < 0) { TESTRPT("Error writing voxel", r); } } } } r = miclose_volume(vol); if (r < 0) { TESTRPT("failed", r); } /***** 03-Aug-2004: Added two tests for bugs reported by Leila */ r = miopen_volume("tst-dim.mnc", MI2_OPEN_RDWR, &vol); if (r < 0) { TESTRPT("failed", r); } r = miget_volume_dimension_count(vol, MI_DIMCLASS_ANY, MI_DIMATTR_REGULARLY_SAMPLED, &n); if (r < 0) { TESTRPT("failed", r); } if (n != NDIMS - 1) { TESTRPT("wrong result", n); } r = miget_volume_dimension_count(vol, MI_DIMCLASS_ANY, MI_DIMATTR_NOT_REGULARLY_SAMPLED, &n); if (r < 0) { TESTRPT("failed", r); } if (n != 1) { TESTRPT("wrong result", n); } r = miclose_volume(vol); if (r < 0) { TESTRPT("failed", r); } /* Test #2 - verify that we don't print anything scary if a user * closes a volume prematurely. */ r = micreate_dimension("xspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &dim[0]); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("yspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &dim[1]); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("zspace",MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &dim[2]); if (r < 0) { TESTRPT("failed", r); } r = micreate_volume("tst-vol.mnc", 3, dim, MI_TYPE_SHORT, MI_CLASS_LABEL, NULL, &vol); if (r < 0) { TESTRPT("failed", r); } r = miclose_volume(vol); if (r < 0) { TESTRPT("failed", r); } /** End of tests added 03-Aug-2004 **/ if (error_cnt != 0) { fprintf(stderr, "%d error%s reported\n", error_cnt, (error_cnt == 1) ? "" : "s"); } else { fprintf(stderr, "No errors\n"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-full-test.c000066400000000000000000000160271257462267400214160ustar00rootroot00000000000000/* A test program to evaluate some of the MINC2 API's and features. */ #include #include "minc2.h" #define ND 3 #define CX 100 #define CY 100 #define CZ 100 static int test1(int do_real) { int i, j, k; int r; mivolumeprops_t hprops; midimhandle_t hdim[ND]; mihandle_t hvol; double offsets[CX]; misize_t coords[ND]; for (i = 0; i < CX; i++) { offsets[i] = (double) i * (double) i; } r = minew_volume_props(&hprops); if (r != 0) { fprintf(stderr, "unexpected error\n"); return (1); } r = miset_props_compression_type(hprops, MI_COMPRESS_ZLIB); if (r != 0) { fprintf(stderr, "unexpected error\n"); return (1); } r = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_NOT_REGULARLY_SAMPLED, CX, &hdim[0]); if (r != 0) { fprintf(stderr, "unexpected error\n"); return (1); } r = miset_dimension_offsets(hdim[0], 100, 0, offsets); if (r != 0) { fprintf(stderr, "unexpected error\n"); return (1); } r = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1]); if (r < 0) { return (1); } r = miset_dimension_start(hdim[1], -10.0); if (r < 0) { return (1); } r = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2]); if (r < 0) { return (1); } r = miset_dimension_start(hdim[2], -20.0); if (r != 0) { fprintf(stderr, "error setting dimension start\n"); return (1); } r = miset_dimension_separation(hdim[2], 2.0); if (r != 0) { fprintf(stderr, "error setting dimension separation\n"); return (1); } r = micreate_volume("fulltest.mnc", 3, hdim, MI_TYPE_SHORT, MI_CLASS_REAL, hprops, &hvol); if (r != 0) { fprintf(stderr, "error creating volume\n"); return (1); } r = micreate_volume_image(hvol); if (r != 0) { fprintf(stderr, "error creating volume image\n"); return (1); } r = miset_volume_valid_range(hvol, 1000, -1000); if (r != 0) { fprintf(stderr, "error setting valid range\n"); return (1); } r = miset_volume_range(hvol, 5, -5); if (r < 0) { fprintf(stderr, "error setting volume range\n"); return (1); } for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; if (do_real) { r = miset_real_value(hvol, coords, ND, 1.0); if (r < 0) { return (1); } } else { r = miset_voxel_value(hvol, coords, ND, 200.0); if (r < 0) { return (1); } } } } } r = mifree_volume_props(hprops); if (r < 0) { return (1); } r = miclose_volume(hvol); if (r < 0) { return (1); } return (0); } #define CCX 20 #define CCY 20 #define CCZ 20 static int test2() { midimhandle_t hdim[ND]; mihandle_t hvol; int r; int i,j,k; misize_t coords[ND]; misize_t lengths[ND]; mifcomplex_t fcmpl; r = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CCX, &hdim[2]); if (r != 0) { return (1); } r = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CCY, &hdim[1]); if (r < 0) { return (1); } r = miset_dimension_start(hdim[1], -10.0); if (r < 0) { return (1); } r = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CCZ, &hdim[0]); if (r < 0) { return (1); } r = miset_dimension_start(hdim[2], -20.0); if (r < 0) { fprintf(stderr, "error setting dimension start\n"); return (1); } r = miset_dimension_separation(hdim[2], 2.0); if (r < 0) { fprintf(stderr, "error setting dimension separation\n"); return (1); } r = micreate_volume("cmpltest.mnc", 3, hdim, MI_TYPE_FCOMPLEX, MI_CLASS_COMPLEX, NULL, &hvol); if (r != 0) { fprintf(stderr, "error creating volume\n"); return (1); } r = miset_volume_valid_range(hvol, 1919.0, 0.0); r = micreate_volume_image(hvol); if (r != 0) { fprintf(stderr, "error creating volume image\n"); return (1); } lengths[0] = lengths[1] = lengths[2] = 1; for (i = 0; i < CCX; i++) { for (j = 0; j < CCY; j++) { for (k = 0; k < CCZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; fcmpl.real = (i * 100) + j; fcmpl.imag = k; r = miset_voxel_value_hyperslab(hvol, MI_TYPE_FCOMPLEX, coords, lengths, &fcmpl); if (r < 0) { fprintf(stderr, "error writing complex voxel\n"); return (1); } } } } for (i = 0; i < CCX; i++) { for (j = 0; j < CCY; j++) { for (k = 0; k < CCZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; r = miget_voxel_value_hyperslab(hvol, MI_TYPE_FCOMPLEX, coords, lengths, &fcmpl); if (r < 0) { fprintf(stderr, "error writing complex voxel\n"); return (1); } if (fcmpl.real != (i * 100) + j || fcmpl.imag != k) { fprintf(stderr, "value mismatch for complex voxel\n"); return (1); } } } } r = miclose_volume(hvol); if (r < 0) { return (1); } return (0); } int main(int argc, char **argv) { int errors; int do_real = 0; while (--argc > 0) { char *argp = *++argv; if (*argp == '-') { argp++; switch (*argp) { case 'r': do_real = 1; break; } } } errors = 0; errors += test1(do_real); errors += test2(); if (errors == 0) { printf("No errors\n"); } else { printf("%d error%s found\n", errors, (errors == 1) ? "" : "s"); } return (errors); } libminc-libminc-2-3-00/testdir/minc2-grpattr-test.c000066400000000000000000000214071257462267400221350ustar00rootroot00000000000000#include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) #define TESTARRAYSIZE 11 static int error_cnt = 0; int main(void) { mihandle_t hvol; mihandle_t hvol1; int r; mitype_t data_type; size_t length; static double tstarr[TESTARRAYSIZE] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9, 10.10, 11.11 }; double dblarr[TESTARRAYSIZE]; float fltarr[TESTARRAYSIZE]; int intarr[TESTARRAYSIZE]; char valstr[128]=""; float val1=12.5; float val2=34.5; milisthandle_t hlist, h1list; char pathbuf[256]=""; char namebuf[256]=""; char pathbuf1[1024]=""; int count=0; r = micreate_volume("tst-grpa.mnc", 0, NULL, MI_TYPE_UINT, MI_CLASS_REAL, NULL, &hvol); if (r < 0) { TESTRPT("Unable to create test file", r); return (-1); } r = micreate_volume("tst-grpb.mnc", 0, NULL, MI_TYPE_UINT, MI_CLASS_REAL, NULL, &hvol1); if (r < 0) { TESTRPT("Unable to create test file", r); return (-1); } r = micreate_group(hvol, "/", "test1"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/", "test2"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/", "test3"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/", "test4"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test2", "stuff2"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1", "stuff"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1", "otherstuff"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1", "theotherstuff"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1/theotherstuff", "thisstuff"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1/stuff", "hello"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = micreate_group(hvol, "/test1/stuff", "helloleila"); if (r < 0) { TESTRPT("micreate_group failed", r); } r = miset_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff/hello", "animal", 8, "fruitbat"); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 10, "automobile"); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_STRING, "/test3", "objtype", 10, "automobile"); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objname", 10, "automobile"); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_DOUBLE, "/test2", "maxvals", TESTARRAYSIZE, tstarr); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miget_attr_type(hvol, "/test1/stuff/hello", "animal", &data_type); if (r < 0) { TESTRPT("miget_attr_type failed", r); } if (data_type != MI_TYPE_STRING) { TESTRPT("miget_attr_type failed", data_type); } r = miget_attr_length(hvol, "/test1/stuff/hello", "animal", &length); if (r < 0) { TESTRPT("miget_attr_length failed", r); } if (length != 8) { TESTRPT("miget_attr_length failed", (int)length); } r = midelete_group(hvol, "/test1/stuff", "goodbye"); if (r >= 0) { TESTRPT("midelete_group failed", r); } r = midelete_group(hvol, "/test1/stuff", "hello"); /* This should succeed. */ if (r < 0) { TESTRPT("midelete_group failed", r); } r = miget_attr_length(hvol, "/test1/stuff/hello", "animal", &length); /* This should fail since we deleted the group. */ if (r >= 0) { TESTRPT("miget_attr_length not failed", r); } r = miget_attr_values(hvol, MI_TYPE_DOUBLE, "/test2", "maxvals", TESTARRAYSIZE, dblarr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } for (r = 0; r < TESTARRAYSIZE; r++) { if (dblarr[r] != tstarr[r]) { TESTRPT("miget_attr_values mismatch", r); } } /* Get the values again in float rather than double format. */ r = miget_attr_values(hvol, MI_TYPE_FLOAT, "/test2", "maxvals", TESTARRAYSIZE, fltarr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } for (r = 0; r < TESTARRAYSIZE; r++) { if (fltarr[r] != (float) tstarr[r]) { TESTRPT("miget_attr_values mismatch", r); fprintf(stderr, "fltarr[%d] = %f, tstarr[%d] = %f\n", r, fltarr[r], r, tstarr[r]); } } /* Get the values again in int rather than double format. */ r = miget_attr_values(hvol, MI_TYPE_INT, "/test2", "maxvals", TESTARRAYSIZE, intarr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } for (r = 0; r < TESTARRAYSIZE; r++) { if (intarr[r] != (int) tstarr[r]) { TESTRPT("miget_attr_values mismatch", r); fprintf(stderr, "intarr[%d] = %d, tstarr[%d] = %d\n", r, intarr[r], r, (int) tstarr[r]); } } r = miget_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 128, valstr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } if (strcmp(valstr, "automobile") != 0) { TESTRPT("miget_attr_values failed", 0); fprintf(stderr,"Expected :\"%s\" read \"%s\"\n","automobile",valstr); } /* Get the values again but this time with only enough space for the result with null termination. */ memset(valstr, 0x55, sizeof(valstr)); r = miget_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 11, valstr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } if (strcmp(valstr, "automobile") != 0) { TESTRPT("miget_attr_values failed", 0); } /* Get the values again but this time with only enough space for the result and without null termination. */ memset(valstr, 0x55, sizeof(valstr)); r = miget_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 10, valstr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } if (valstr[9] != 'e') { TESTRPT("miget_attr_values failed", 0); } if (valstr[10] != 0x55) { TESTRPT("miget_attr_values failed", 0); } r = miset_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 8, "bicycle"); if (r < 0) { TESTRPT("miset_attr_values failed on rewrite", r); } r = miget_attr_values(hvol, MI_TYPE_STRING, "/test1/stuff", "objtype", 128, valstr); if (r < 0) { TESTRPT("miget_attr_values failed", r); } if (strcmp(valstr, "bicycle") != 0) { TESTRPT("miget_attr_values failed", 0); } r = miset_attr_values(hvol, MI_TYPE_FLOAT, "/OPT", "zoom",1, &val1); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_FLOAT, "/OPT", "binning",1, &val2); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = miset_attr_values(hvol, MI_TYPE_FLOAT, "/OPT", "gain",1, &val1); if (r < 0) { TESTRPT("miset_attr_values failed", r); } r = milist_start(hvol, "/", 0, &hlist); if (r == MI_NOERROR) { count++; while (milist_attr_next(hvol, hlist, pathbuf, sizeof(pathbuf), namebuf, sizeof(namebuf)) == MI_NOERROR) { printf(" %s %s\n", pathbuf, namebuf); } } else { TESTRPT("milist_start failed", r); } milist_finish(hlist); printf("copy all attributes in the provided path in the new volume\n"); if((r = micopy_attr(hvol,"/OPT",hvol1))<0) TESTRPT("micopy_attr failed", r); printf("***************** \n"); r = milist_start(hvol1, "/", 1, &h1list); if (r == MI_NOERROR) { while( milist_grp_next(h1list, pathbuf1, sizeof(pathbuf1)-1) == MI_NOERROR) { printf("%s \n", pathbuf1); } } else { TESTRPT("milist_start failed", r); } r = milist_finish(h1list); if(r<0) { TESTRPT("milist_finish failed", r); } r = miclose_volume(hvol1); if(r<0) { TESTRPT("miclose_volume failed", r); } r = miclose_volume(hvol); if(r<0) { TESTRPT("miclose_volume failed", r); } if (error_cnt != 0) { fprintf(stderr, "%d error%s reported\n", error_cnt, (error_cnt == 1) ? "" : "s"); } else { fprintf(stderr, "No errors\n"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-hyper-test-2.c000066400000000000000000000146271257462267400217460ustar00rootroot00000000000000#include #include #include "minc2.h" /* Courtesy of Dr Jason Lerch * A test of the dimensions ordering/hyperslab functions in minc2. The * basic flow is the following: * open an existing minc volume * set the apparent dimension order to be different from the file order * get the volume dimensions * load the entire image into a buffer using the hyperslab function * compare with single voxel value returned from single voxel functions. */ #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CZ 142 #define CY 245 #define CX 210 #define NDIMS 3 static void create_test_file ( void ) { double start_values[3] = { -6.96, -12.453, -9.48}; double separations[3] = {0.09, 0.09, 0.09}; midimhandle_t hdim[NDIMS]; mihandle_t hvol; unsigned short *buf = ( unsigned short * ) malloc ( CX * CY * CZ * sizeof ( unsigned short ) ); int i; misize_t count[NDIMS]; misize_t start[NDIMS]; miboolean_t flag = 1; double min = -1.0; double max = 1.0; micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[0] ); micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1] ); micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[2] ); miset_dimension_starts ( hdim, NDIMS, start_values ); miset_dimension_separations ( hdim, NDIMS, separations ); micreate_volume ( "hyperslab-test2.mnc", NDIMS, hdim, MI_TYPE_USHORT, MI_CLASS_REAL, NULL, &hvol ); /* set slice scaling flag to true */ miset_slice_scaling_flag ( hvol, flag ); micreate_volume_image ( hvol ); for ( i = 0; i < CZ * CY * CX; i++ ) { buf[i] = ( unsigned short ) (i * 0.001); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CY; count[2] = CX; miset_voxel_value_hyperslab ( hvol, MI_TYPE_USHORT, start, count, buf ); /* Set random values to slice min and max for slice scaling*/ start[0] = start[1] = start[2] = 0; for ( i = 0; i < CZ; i++ ) { start[0] = i; min += 0.1; max += 0.1; miset_slice_range ( hvol, start, 3, max, min ); } miclose_volume ( hvol ); free(buf); } int main ( void ) { mihandle_t vol; midimhandle_t dim[NDIMS]; misize_t sizes[NDIMS]; misize_t start[NDIMS]; misize_t count[NDIMS]; misize_t howfar[NDIMS]; misize_t location[NDIMS]; #ifdef APPARENTORDER char *dimorder[] = {MIxspace,MIyspace,MIzspace}; #endif double *buffer, value; int r = 0; printf ( "Creating image with slice scaling!! \n" ); create_test_file(); printf ( "Opening hyperslab-test2.mnc! \n" ); r = miopen_volume ( "hyperslab-test2.mnc", MI2_OPEN_READ, &vol ); if ( r < 0 ) { TESTRPT ( "failed to open image", r ); } #ifdef APPARENTORDER /* set the apparent dimension order to be xyz */ r = miset_apparent_dimension_order_by_name ( vol, 3, dimorder ); /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, MI_DIMORDER_APPARENT, 3, dim ); r = miget_dimension_sizes ( dim, 3, sizes ); #else /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, MI_DIMORDER_FILE, 3, dim ); r = miget_dimension_sizes ( dim, 3, sizes ); #endif if ( r == MI_NOERROR ) { printf ( "Sizes: %d, %d, %d\n", (int)sizes[0], (int)sizes[1], (int)sizes[2] ); } else { fprintf ( stderr, "Error getting dimension sizes\n" ); } /* try to play with hyperslab functions!! */ start[0] = 4; start[1] = 3; start[2] = 5; howfar[0] = 120; howfar[1] = 180; howfar[2] = 110; count[0] = howfar[0] - start[0]; count[1] = howfar[1] - start[1]; count[2] = howfar[2] - start[2]; /* Alocate memory for the hyperslab*/ buffer = ( double * ) malloc ( count[0] * count[1] * count[2] * sizeof ( double ) ); if ( buffer == NULL ) { fprintf ( stderr, "Error allocation memory.\n" ); exit ( -1 ); } /* Get real value hyperslab*/ printf ( "\n" ); printf ( "Getting a real value hyperslab \n" ); printf ( "Starting at %d, %d, %d \n", (int)start[0], (int)start[1], (int)start[2] ); printf ( "Extending to %d, %d, %d \n", (int)howfar[0], (int)howfar[1], (int)howfar[2] ); printf ( "\n" ); if ( miget_real_value_hyperslab ( vol, MI_TYPE_DOUBLE, start, count, buffer ) < 0 ) { fprintf ( stderr, "Could not get hyperslab.\n" ); exit ( -1 ); } /* set an arbitrary location to print values from */ location[0] = 70; location[1] = 100; location[2] = 104; printf ( "Test arbitrary location %d, %d, %d \n", (int)location[0], (int)location[1], (int)location[2] ); miget_real_value ( vol, location, 3, &value ); printf ( "Test from hyperslab: %f \n", * ( buffer + ( location[0] - start[0] ) *count[1]*count[2] + ( location[1] - start[1] ) * count[2] + ( location[2] - start[2] ) ) ); printf ( "Test from voxel scaled: %f\n", value ); miget_voxel_value ( vol, location, 3, &value ); printf ( "Test voxel value itself: %f\n", value ); printf ( "\n" ); printf ( "HMMMMMMMMMM! let's try something else \n" ); printf ( "\n" ); /* set another arbitrary location to print values from */ location[0] = 104; location[1] = 100; location[2] = 70; printf ( "Test arbitrary location %d, %d, %d \n", (int)location[0], (int)location[1], (int)location[2] ); miget_real_value ( vol, location, 3, &value ); printf ( "Test from hyperslab: %f \n", * ( buffer + ( location[0] - start[0] ) *count[1]*count[2] + ( location[1] - start[1] ) * count[2] + ( location[2] - start[2] ) ) ); printf ( "Test from voxel scaled: %f\n", value ); miget_voxel_value ( vol, location, 3, &value ); printf ( "Test voxel value itself: %f\n", value ); /* close volume*/ miclose_volume ( vol ); free(buffer); if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "\n No errors\n" ); } return ( error_cnt ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/testdir/minc2-hyper-test.c000066400000000000000000000722731257462267400216100ustar00rootroot00000000000000#include #include #include #include "minc2.h" #include "config.h" #define NDIMS 3 #define CX 11 #define CY 12 #define CZ 9 #ifdef _MSC_VER double rint(double v); /*hack: defined in m2util.c*/ #endif #define TESTRPT(msg, val) (error_cnt++, printf(\ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) #define XP 101 #define YP 17 #define ZP 1 #define VALID_MAX (((CX-1)*XP)+((CY-1)*YP)+((CZ-1)*ZP)) #define VALID_MIN (0.0) #define REAL_MAX (1.0) #define REAL_MIN (-1.0) #define NORM_MAX (1.0) #define NORM_MIN (-1.0) static int create_and_test_image(const char *name, mihandle_t *hvol_ptr, midimhandle_t hdims[], unsigned short stemp[CZ][CX][CY], unsigned short stmp2[CX][CY][CZ]) { mihandle_t hvol; int result; misize_t start[NDIMS]; misize_t count[NDIMS]; double dtemp[CX][CY][CZ]; int i,j,k; char *dimnames[] = {"zspace", "xspace", "yspace"}; int error_cnt = 0; result = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdims[0]); if (result < 0) { TESTRPT("Unable to create test volume", result); return error_cnt; } result = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdims[1]); if (result < 0) { TESTRPT("Unable to create test volume", result); return error_cnt; } result = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdims[2]); if (result < 0) { TESTRPT("Unable to create test volume", result); return error_cnt; } result = micreate_volume(name, NDIMS, hdims, MI_TYPE_UINT, MI_CLASS_REAL, NULL, &hvol); if (result < 0) { TESTRPT("Unable to create test volume", result); return error_cnt; } result = miget_volume_dimensions(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_FILE, NDIMS, hdims); if (result < 0) { TESTRPT("Unable to get volume dimensions", result); return error_cnt; } micreate_volume_image(hvol); *hvol_ptr = hvol; for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { stmp2[i][j][k] = (short)((i*XP)+(j*YP)+(k*ZP)); } } } result = miset_volume_valid_range(hvol, VALID_MAX, VALID_MIN); if (result < 0) { TESTRPT("error setting valid range", result); return error_cnt; } result = miset_volume_range(hvol, REAL_MAX, REAL_MIN); if (result < 0) { TESTRPT("error setting real range", result); return error_cnt; } start[0] = start[1] = start[2] = 0; count[0] = CX; count[1] = CY; count[2] = CZ; result = miset_voxel_value_hyperslab(hvol, MI_TYPE_SHORT, start, count, stmp2); if (result < 0) { TESTRPT("unable to set hyperslab", result); return error_cnt; } start[0] = start[1] = start[2] = 0; count[0] = CX; count[1] = CY; count[2] = CZ; result = miget_real_value_hyperslab(hvol, MI_TYPE_DOUBLE, start, count, dtemp); printf("miget_real_value_hyperslab()\n"); if (result < 0) { TESTRPT("Unable to read real value hyperslab", result); return error_cnt; } else { for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { double r = stmp2[i][j][k]; /* Use fixed known values for valid range, etc. */ r -= VALID_MIN; r /= (VALID_MAX - VALID_MIN); r *= (REAL_MAX - REAL_MIN); r += REAL_MIN; /* Have to do approximate comparison, since conversion may * not be exact. */ if (fabs(r - dtemp[i][j][k]) > 1.0e-15) { printf("%f != %f at %d,%d,%d ",r,dtemp[i][j][k],i,j,k); TESTRPT("Value error!", 0); break; } } } } } printf("miget_voxel_value_hyperslab, default dimensions\n"); memset(stmp2, 0, sizeof(short)*CX*CY*CZ); /* Clear the array. */ result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stmp2); if (result < 0) { TESTRPT("Unable to get raw hyperslab", result); } else { /* Verify all of the values. */ for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { if (stmp2[i][j][k] != (i*XP)+(j*YP)+(k*ZP)) { TESTRPT("Value error", 0); break; } } } } } result = miset_apparent_dimension_order_by_name(hvol, NDIMS, dimnames); if (result < 0) { TESTRPT("unable to set dimension order", result); } printf("miget_voxel_value_hyperslab(), swapped dimensions\n"); start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { if (stemp[i][j][k] != (j*XP)+(k*YP)+(i*ZP)) { printf("%d != %d: ", stemp[i][j][k], (j*XP)+(k*YP)+(i*ZP)); TESTRPT("Value error", 0); break; } } } } } /******************************************************************** * Read and validate the entire dataset. * This is done with: * - Axes in (z,y,x) order * - Z axis reversed */ printf("miget_voxel_hyperslab, reversed Z axis\n"); result = miset_dimension_apparent_voxel_order(hdims[2], MI_COUNTER_FILE_ORDER); if (result < 0) { TESTRPT("unable to set voxel order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { short t = (short)((j*XP)+(k*YP)+(((CZ-1)-i)*ZP)); if (stemp[i][j][k] != t) { printf("%d != %d: @ %d,%d,%d", stemp[i][j][k], t,i,j,k); TESTRPT("Value error", 0); break; } } } } } /******************************************************************** * Attempt to get a series of 3x2x2 matrices from the file. * This is done with: * - Axes in (z,x,y) order * - Z axis reversed */ printf("Read 3x2x2 matrices of voxel values:\n"); count[0] = 3; count[1] = 2; count[2] = 2; for (i = 0; (i + 3) < CZ; i += 3) { for (j = 0; (j + 2) < CX; j += 2) { for (k = 0; (k + 2) < CY; k += 2) { unsigned short stmp3[3][2][2]; start[0] = i; start[1] = j; start[2] = k; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stmp3); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { int q,r,s; for (q = 0; q < 3; q++) { for (r = 0; r < 2; r++) { for (s = 0; s < 2; s++) { int x,y,z; short t; x = r + j; y = s + k; z = q + i; t = (short)((x*XP)+(y*YP)+(((CZ-1)-z)*ZP)); if (stmp3[q][r][s] != t) { printf("%d != %d: ", stmp3[q][r][s], t); TESTRPT("Value error", 0); break; } } } } } } } } return error_cnt; } static int test1() { mihandle_t hvol; int result; misize_t start[NDIMS]; misize_t count[NDIMS]; unsigned short stmp2[CX][CY][CZ]; unsigned short stemp[CZ][CX][CY]; unsigned char btemp[CZ][CX][CY]; int i,j,k; midimhandle_t hdims[NDIMS]; int error_cnt = 0; error_cnt = create_and_test_image("tst-hyper.mnc", &hvol, hdims, stemp, stmp2); /********************************************************************* * Read the entire dataset, normalized. * * This is done with the axes in (z,y,x) order, but with the Z axis * restored to normal (file) order. */ printf("miget_hyperslab_normalized()\n"); result = miset_dimension_apparent_voxel_order(hdims[2], MI_FILE_ORDER); if (result < 0) { TESTRPT("unable to set voxel order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_hyperslab_normalized(hvol, MI_TYPE_UBYTE, start, count, NORM_MIN, NORM_MAX, btemp); if (result < 0) { TESTRPT("Can't read normalized hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { double r = stmp2[j][k][i]; /* Calculate what the normalized value ought to be * in this case. Use fixed known values for valid * range, etc. */ r -= VALID_MIN; r /= (VALID_MAX - VALID_MIN); r *= (REAL_MAX - REAL_MIN); r += REAL_MIN; /* r now contains the alleged "real" value for this * voxel. Now we have to map it to 0-255 for the * unsigned byte type. */ if (r > NORM_MAX) { r = 255; } else if (r < NORM_MIN) { r = 0; } else { r = ((r - REAL_MIN) / (REAL_MAX - REAL_MIN)) * 255; } if (btemp[i][j][k] != (unsigned char)rint(r)) { printf("%d != %d: @ %d,%d,%d ", (int)btemp[i][j][k], (int)rint(r), i,j,k); TESTRPT("Value error!", 0); break; } } } } } /******************************************************************** * Now read, modify, write, and repeat, performing an exclusive OR * operation on each voxel value. * Axes are still in (z,y,x) order. */ printf("read and write entire hyperslab:\n"); result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("Can't read hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { short t = stemp[i][j][k]; if (t != (j*XP)+(k*YP)+(i*ZP)) { printf("%d != %d: ", t, (j*XP)+(k*YP)+(i*ZP)); TESTRPT("Value error!", 0); break; } stemp[i][j][k] ^= 0x5555; } } } } result = miset_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); /* stemp has now been modified by the operation! */ result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("oops", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { short t = stemp[i][j][k] ^ 0x5555; if (t != (j*XP)+(k*YP)+(i*ZP)) { printf("%d != %d: ", t, (j*XP)+(k*YP)+(i*ZP)); TESTRPT("Value error!", 0); break; } stemp[i][j][k] = t; } } } } result = miset_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("miset_voxel_value_hyperslab failed\n", result); } /* This call should have the effect of resetting the dimension order * to the "raw" file order. */ miset_apparent_dimension_order_by_name(hvol, 0, NULL); /* Verify this. */ printf("Read and verify hyperslab in original dimension order:\n"); start[0] = start[1] = start[2]; count[0] = CX; count[1] = CY; count[2] = CZ; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stmp2); if (result < 0) { TESTRPT("Unable to read real value hyperslab", result); } else { for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { short t = stmp2[i][j][k]; if (t != (i*XP)+(j*YP)+(k*ZP)) { printf("%d != %d: ", t, (i*XP)+(j*YP)+(k*ZP)); TESTRPT("Value error!", 0); break; } } } } } miclose_volume(hvol); if (error_cnt == 0) { printf("No errors\n"); } else { printf("**** %d error%s\n", error_cnt, (error_cnt == 1) ? "" : "s"); } return (error_cnt); } static int test2() { mihandle_t hvol; int result; misize_t start[NDIMS]; misize_t count[NDIMS]; unsigned short stmp2[CX][CY][CZ]; unsigned short stemp[CZ][CX][CY]; int itemp[CZ][CX][CY]; int i,j,k; midimhandle_t hdims[NDIMS]; int error_cnt = 0; error_cnt = create_and_test_image("tst-hyper-s.mnc", &hvol, hdims, stemp, stmp2); /********************************************************************* * Read the entire dataset, normalized. * * This is done with the axes in (z,y,x) order, but with the Z axis * restored to normal (file) order. */ printf("miget_hyperslab_normalized()\n"); result = miset_dimension_apparent_voxel_order(hdims[2], MI_FILE_ORDER); if (result < 0) { TESTRPT("unable to set voxel order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_hyperslab_normalized(hvol, MI_TYPE_INT, start, count, REAL_MIN, REAL_MAX, itemp); if (result < 0) { TESTRPT("Can't read normalized hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { double r = stmp2[j][k][i]; /* Calculate what the normalized value ought to be * in this case. Use fixed known values for valid * range, etc. */ r -= VALID_MIN; r /= (VALID_MAX - VALID_MIN); r *= (REAL_MAX - REAL_MIN); r += REAL_MIN; /* r now contains the alleged "real" value for this * voxel. Now we have to map it back into the full * range of the buffer type. */ if (r > NORM_MAX) { printf("clipping %g @ %d,%d,%d\n", r, i, j, k); r = INT_MAX; } else if (r < NORM_MIN) { printf("clipping %g @ %d,%d,%d\n", r, i, j, k); r = INT_MIN; } else { r = ((r - REAL_MIN) / (REAL_MAX - REAL_MIN)) * ((double)INT_MAX-(double)INT_MIN)+(double)INT_MIN; } if (itemp[i][j][k] != (int)rint(r)) { printf("%d != %d: @ %d,%d,%d ", itemp[i][j][k], (int)rint(r), i,j,k); TESTRPT("Value error!", 0); break; } } } } } printf("miset_hyperslab_normalized()\n"); result = miset_hyperslab_normalized(hvol, MI_TYPE_INT, start, count, REAL_MIN, REAL_MAX, itemp); if (result < 0) { TESTRPT("Can't write hyperslab", result); } printf("miget_voxel_value_hyperslab()\n"); start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); /* Verify that we haven't screwed up the values. */ for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { unsigned short r = stmp2[j][k][i]; if (r != stemp[i][j][k]) { printf("%d != %d, @ %d,%d,%d\n", r, stemp[i][j][k], i, j, k); TESTRPT("Value error: ", 0); break; } } } } miclose_volume(hvol); if (error_cnt == 0) { printf("No errors\n"); } else { printf("**** %d error%s\n", error_cnt, (error_cnt == 1) ? "" : "s"); } return (error_cnt); } static int test3() { mihandle_t hvol; mivolumeprops_t hprops; int result; misize_t start[NDIMS]; misize_t count[NDIMS]; double dtemp[CX][CY][CZ]; unsigned short stmp2[CX][CY][CZ]; unsigned short stemp[CZ][CX][CY]; int itemp[CZ][CX][CY]; int i,j,k; midimhandle_t hdims[NDIMS]; char *dimnames[] = {"zspace", "xspace", "yspace"}; int error_cnt = 0; printf("Testing hyperslab operations with multi-resolution image.\n"); result = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdims[0]); result = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdims[1]); result = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdims[2]); result = minew_volume_props(&hprops); miset_props_multi_resolution(hprops, TRUE, 2); result = micreate_volume("tst-hyper-m.mnc", NDIMS, hdims, MI_TYPE_INT, MI_CLASS_REAL, hprops, &hvol); mifree_volume_props(hprops); if (result < 0) { TESTRPT("Unable to create test volume", result); } result = miget_volume_dimensions(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_FILE, NDIMS, hdims); if (result < 0) { TESTRPT("Unable to get volume dimensions", result); } micreate_volume_image(hvol); for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { stmp2[i][j][k] = (unsigned short)((i*XP)+(j*YP)+(k*ZP)); } } } result = miset_volume_valid_range(hvol, VALID_MAX, VALID_MIN); if (result < 0) { TESTRPT("error setting valid range", result); } result = miset_volume_range(hvol, REAL_MAX, REAL_MIN); if (result < 0) { TESTRPT("error setting real range", result); } start[0] = start[1] = start[2] = 0; count[0] = CX; count[1] = CY; count[2] = CZ; result = miset_voxel_value_hyperslab(hvol, MI_TYPE_SHORT, start, count, stmp2); if (result < 0) { TESTRPT("unable to set hyperslab", result); } start[0] = start[1] = start[2] = 0; count[0] = CX; count[1] = CY; count[2] = CZ; result = miget_real_value_hyperslab(hvol, MI_TYPE_DOUBLE, start, count, dtemp); if (result < 0) { TESTRPT("Unable to read real value hyperslab", result); } else { for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { double r = stmp2[i][j][k]; /* Use fixed known values for valid range, etc. */ r -= VALID_MIN; r /= (VALID_MAX - VALID_MIN); r *= (REAL_MAX - REAL_MIN); r += REAL_MIN; /* Have to do approximate comparison, since conversion may * not be exact. */ if (fabs(r - dtemp[i][j][k]) > 1.0e-15) { printf("%f != %f at %d,%d,%d ",r,dtemp[i][j][k],i,j,k); TESTRPT("Value error!", 0); break; } } } } } memset(stmp2, 0, sizeof(short)*CX*CY*CZ); /* Clear the array. */ result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stmp2); if (result < 0) { TESTRPT("Unable to get raw hyperslab", result); } else { /* Verify all of the values. */ for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { if (stmp2[i][j][k] != (i*XP)+(j*YP)+(k*ZP)) { TESTRPT("Value error", 0); break; } } } } } result = miset_apparent_dimension_order_by_name(hvol, NDIMS, dimnames); if (result < 0) { TESTRPT("unable to set dimension order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { if (stemp[i][j][k] != (j*XP)+(k*YP)+(i*ZP)) { printf("%d != %d: ", stemp[i][j][k], (j*XP)+(k*YP)+(i*ZP)); TESTRPT("Value error", 0); break; } } } } } /******************************************************************** * Read and validate the entire dataset. * This is done with: * - Axes in (z,y,x) order * - Z axis reversed */ result = miset_dimension_apparent_voxel_order(hdims[2], MI_COUNTER_FILE_ORDER); if (result < 0) { TESTRPT("unable to set voxel order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { short t = (short)((j*XP)+(k*YP)+(((CZ-1)-i)*ZP)); if (stemp[i][j][k] != t) { printf("%d != %d: @ %d,%d,%d", stemp[i][j][k], t,i,j,k); TESTRPT("Value error", 0); break; } } } } } /******************************************************************** * Attempt to get a series of 3x2x2 matrices from the file. * This is done with: * - Axes in (z,x,y) order * - Z axis reversed */ count[0] = 3; count[1] = 2; count[2] = 2; for (i = 0; (i + 3) < CZ; i += 3) { for (j = 0; (j + 2) < CX; j += 2) { for (k = 0; (k + 2) < CY; k += 2) { unsigned short stmp3[3][2][2]; start[0] = i; start[1] = j; start[2] = k; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stmp3); if (result < 0) { TESTRPT("Error reading swapped hyperslab", result); } else { int q,r,s; for (q = 0; q < 3; q++) { for (r = 0; r < 2; r++) { for (s = 0; s < 2; s++) { int x,y,z; short t; x = r + j; y = s + k; z = q + i; t = (short)((x*XP)+(y*YP)+(((CZ-1)-z)*ZP)); if (stmp3[q][r][s] != t) { printf("%d != %d: ", stmp3[q][r][s], t); TESTRPT("Value error", 0); break; } } } } } } } } /********************************************************************* * Read the entire dataset, normalized. * * This is done with the axes in (z,y,x) order, but with the Z axis * restored to normal (file) order. */ result = miset_dimension_apparent_voxel_order(hdims[2], MI_FILE_ORDER); if (result < 0) { TESTRPT("unable to set voxel order", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_hyperslab_normalized(hvol, MI_TYPE_INT, start, count, REAL_MIN, REAL_MAX, itemp); if (result < 0) { TESTRPT("Can't read normalized hyperslab", result); } else { for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { double r = stmp2[j][k][i]; /* Calculate what the normalized value ought to be * in this case. Use fixed known values for valid * range, etc. */ r -= VALID_MIN; r /= (VALID_MAX - VALID_MIN); r *= (REAL_MAX - REAL_MIN); r += REAL_MIN; /* r now contains the alleged "real" value for this * voxel. Now we have to map it back into the full * range of the buffer type. */ if (r > NORM_MAX) { printf("clipping %g @ %d,%d,%d\n", r, i, j, k); r = INT_MAX; } else if (r < NORM_MIN) { printf("clipping %g @ %d,%d,%d\n", r, i, j, k); r = INT_MIN; } else { r = ((r - REAL_MIN) / (REAL_MAX - REAL_MIN)) * ((double)INT_MAX-(double)INT_MIN)+(double)INT_MIN; } if (itemp[i][j][k] != (int)rint(r)) { printf("%d != %d: @ %d,%d,%d ", itemp[i][j][k], (int)rint(r), i,j,k); TESTRPT("Value error!", 0); break; } } } } } result = miset_hyperslab_normalized(hvol, MI_TYPE_INT, start, count, REAL_MIN, REAL_MAX, itemp); if (result < 0) { TESTRPT("Can't write hyperslab", result); } start[0] = start[1] = start[2] = 0; count[0] = CZ; count[1] = CX; count[2] = CY; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_USHORT, start, count, stemp); /* Verify that we haven't screwed up the values. */ for (i = 0; i < CZ; i++) { for (j = 0; j < CX; j++) { for (k = 0; k < CY; k++) { unsigned short r = stmp2[j][k][i]; if (r != stemp[i][j][k]) { printf("%d != %d, @ %d,%d,%d\n", r, stemp[i][j][k], i, j, k); TESTRPT("Value error: ", 0); break; } } } } result = miselect_resolution(hvol, 2); if (result != MI_NOERROR) { TESTRPT("Can't select resolution depth 2.", result); } result = miselect_resolution(hvol, 1); if (result != MI_NOERROR) { TESTRPT("Can't select resolution depth 1.", result); } result = miselect_resolution(hvol, 0); if (result != MI_NOERROR) { TESTRPT("Can't select resolution depth 0.", result); } result = miselect_resolution(hvol, 21); if (result != MI_ERROR) { TESTRPT("Selected resolution depth 21.", result); } miclose_volume(hvol); if (error_cnt == 0) { printf("No errors\n"); } else { printf("**** %d error%s\n", error_cnt, (error_cnt == 1) ? "" : "s"); } return (error_cnt); } int main(void) { int error_count = 0; error_count += test1(); error_count += test2(); error_count += test3(); return (error_count); } libminc-libminc-2-3-00/testdir/minc2-label-test.c000066400000000000000000000171451257462267400215350ustar00rootroot00000000000000#include #include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CX 3 #define CY 4 #define CZ 2 #define NDIMS 3 static int create_label_image ( void ) { mihandle_t hvol; char *name; int result; int value; midimhandle_t hdim[3]; misize_t coords[3]; int i, j, k; int counter = 0; result = micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[0] ); result = micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1] ); result = micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2] ); result = micreate_volume ( "tst-label.mnc", 3, hdim, MI_TYPE_UINT, MI_CLASS_LABEL, NULL, &hvol ); if ( result < 0 ) { fprintf ( stderr, "Unable to create test file %x\n", result ); return ( -1 ); } result = miget_number_of_defined_labels ( hvol, &value ); if ( result != MI_NOERROR ) { TESTRPT ( "Invalid return from miget_label_name", result ); } else { printf ( " %d \n", value ); } /* Now test some stuff... */ midefine_label ( hvol, 0, "Black" ); midefine_label ( hvol, 0xffffff, "White" ); midefine_label ( hvol, 0x808080, "Grey" ); midefine_label ( hvol, 0xff0000, "Red" ); midefine_label ( hvol, 0x00ff00, "Blue" ); midefine_label ( hvol, 0x0000ff, "Green" ); result = miget_number_of_defined_labels ( hvol, &value ); if ( result != MI_NOERROR ) { TESTRPT ( "Invalid return from miget_label_name", result ); } else { printf ( " %d \n", value ); } result = miget_label_name ( hvol, 0, &name ); if ( result != MI_NOERROR ) { TESTRPT ( "Invalid return from miget_label_name", result ); } if ( strcmp ( name, "Black" ) != 0 ) { TESTRPT ( "Unexpected label for value 0", 0 ); } mifree_name ( name ); result = miget_label_name ( hvol, 0x00ff00, &name ); if ( result != MI_NOERROR ) { TESTRPT ( "Invalid return from miget_label_name", result ); } if ( strcmp ( name, "Blue" ) != 0 ) { TESTRPT ( "Unexpected label for value 0", 0 ); } mifree_name ( name ); result = miget_label_name ( hvol, 1, &name ); if ( result != MI_ERROR ) { TESTRPT ( "Invalid return from miget_label_name", result ); } result = miget_label_value ( hvol, "White", &value ); if ( result != MI_NOERROR ) { TESTRPT ( "Invalid return from miget_label_value", result ); } if ( value != 0xffffff ) { TESTRPT ( "Unexpected value for label 'White'", 0 ); } result = miget_label_value ( hvol, "Mauve", &value ); if ( result != MI_ERROR ) { TESTRPT ( "Invalid return from miget_label_value", result ); } micreate_volume_image ( hvol ); for ( i = 0; i < CX; i++ ) { for ( j = 0; j < CY ; j++ ) { for ( k = 0; k < CZ ; k++ ) { coords[0] = i; coords[1] = j; coords[2] = k; if ( counter == 1 ) { miset_voxel_value ( hvol, coords, 3, 0xffffff ); } else if ( counter == 2 ) { miset_voxel_value ( hvol, coords, 3, 0x808080 ); } else if ( counter == 3 ) { miset_voxel_value ( hvol, coords, 3, 0xff0000 ); } else if ( counter == 4 ) { miset_voxel_value ( hvol, coords, 3, 0x00ff00 ); } else if ( counter == 5 ) { miset_voxel_value ( hvol, coords, 3, 0x0000ff ); } else { miset_voxel_value ( hvol, coords, 3, 0 ); } counter++; if ( counter >= 6 ) { counter = 0; } } } } miclose_volume ( hvol ); return 0; } int main ( void ) { int i, j, k; mihandle_t vol; midimhandle_t hdims[NDIMS], cp_hdims[NDIMS]; misize_t coords[NDIMS]; misize_t count[NDIMS]; int value; int white_value; int blue_value; int counter = 0; int id; int *buf = ( int * ) malloc ( CX * CY * CZ * sizeof ( int ) ); double *dbuf = ( double * ) malloc ( CX * CY * CZ * sizeof ( double ) ); int result; printf ( "Creating label image !! \n" ); error_cnt += create_label_image(); result = miopen_volume ( "tst-label.mnc", MI2_OPEN_RDWR, &vol ); if (result != MI_NOERROR) { TESTRPT("miopen_volume:", result); } miget_number_of_defined_labels ( vol, &value ); printf ( "Number of defined labels %d \n", value ); miget_label_value ( vol, "White", &white_value ); miget_label_value ( vol, "Blue", &blue_value ); if (white_value != 0xffffff) { TESTRPT("miget_label_value, White", white_value); } if (blue_value != 0x00ff00) { TESTRPT("miget_label_value, Blue", white_value); } coords[0] = coords[1] = coords[2] = 0; count[0] = CX; count[1] = CY; count[2] = CZ; result = miget_voxel_value_hyperslab ( vol, MI_TYPE_INT, coords, count, buf ); if (result != MI_NOERROR) { TESTRPT("miget_voxel_value_hyperslab:", result); } printf ( "Print label file with file order x,y,z \n" ); for ( i = 0; i < CX; i++ ) { for ( j = 0; j < CY; j++ ) { for ( k = 0; k < CZ; k++ ) { id = i * CY * CZ + j * CZ + k; printf ( "%8x ", buf[id] ); counter++; if ( counter == 6 ) { counter = 0; printf ( " \n" ); } } } } miget_volume_dimensions ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_REGULARLY_SAMPLED, MI_DIMORDER_FILE, NDIMS, hdims ); cp_hdims[0] = hdims[2]; cp_hdims[1] = hdims[1]; cp_hdims[2] = hdims[0]; miset_apparent_dimension_order ( vol, NDIMS, cp_hdims ); coords[0] = coords[1] = coords[2] = 0; count[0] = CZ; count[1] = CY; count[2] = CX; result = miget_voxel_value_hyperslab ( vol, MI_TYPE_UINT, coords, count, buf ); if (result != MI_NOERROR) { TESTRPT("miget_voxel_value_hyperslab:", result); } printf ( "Print label file with apparent order z,y,x \n" ); counter = 0; for ( i = 0; i < CZ; i++ ) { for ( j = 0; j < CY; j++ ) { for ( k = 0; k < CX; k++ ) { id = i * CY * CX + j * CX + k; printf ( "%8x ", buf[id] ); counter++; if ( counter == 6 ) { counter = 0; printf ( " \n" ); } } } } #if 1 counter = 0; for ( i = 0; i < CZ; i++ ) { for ( j = 0; j < CY; j++ ) { for ( k = 0; k < CX; k++ ) { dbuf[counter] = (counter & 1) ? white_value : blue_value; counter++; } } } result = miset_voxel_value_hyperslab ( vol, MI_TYPE_DOUBLE, coords, count, dbuf ); if (result != MI_NOERROR) { error_cnt++; } result = miget_voxel_value_hyperslab ( vol, MI_TYPE_INT, coords, count, buf ); if (result != MI_NOERROR) { error_cnt++; } /* We should read back exactly what we wrote. */ counter = 0; for ( i = 0; i < CZ; i++ ) { for ( j = 0; j < CY; j++ ) { for ( k = 0; k < CX; k++ ) { if (counter & 1) { if (buf[counter] != white_value) { TESTRPT("Voxel is not white", buf[counter]); } } else { if (buf[counter] != blue_value) { TESTRPT("Voxel is not blue", buf[counter]); } } counter++; } } } #endif result = miclose_volume ( vol ); if (result != MI_NOERROR) { error_cnt++; } if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "No errors\n" ); } return ( error_cnt ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/testdir/minc2-large-attribute.c000066400000000000000000000063201257462267400225650ustar00rootroot00000000000000#include #include #include #include "minc2.h" /* This test will attempt to create a few different test images * which can be tested with minc2.0 */ static int error_cnt=0; #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) #define CZ 142 #define CY 245 #define CX 10 #define CU 5 #define NDIMS 3 static int create_3D_image ( int attribute_size ) { int r; double start_values[NDIMS] = { -6.96, -12.453, -9.48}; double separations[NDIMS] = {0.09, 0.09, 0.09}; midimhandle_t hdim[NDIMS]; mihandle_t hvol; unsigned short *buf = NULL; int i; misize_t count[NDIMS]; misize_t start[NDIMS]; miboolean_t flag = 1; char *attribute; double min = -1.0; double max = 1.0; r = micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[0] ); if(r<0) return r; r = micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[1] ); if(r<0) return r; r = micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2] ); if(r<0) return r; r = miset_dimension_starts ( hdim, NDIMS, start_values ); if(r<0) return r; r = miset_dimension_separations ( hdim, NDIMS, separations ); if(r<0) return r; r = micreate_volume ( "3D_image_a.mnc", NDIMS, hdim, MI_TYPE_USHORT, MI_CLASS_REAL, NULL, &hvol ); if(r<0) return r; /* set slice scaling flag to true */ r = miset_slice_scaling_flag ( hvol, flag ); if(r<0) return r; r = micreate_volume_image ( hvol ); if(r<0) return r; buf = ( unsigned short * ) malloc ( CX * CY * CZ * sizeof ( unsigned short ) ); for ( i = 0; i < CY * CX * CZ; i++ ) { buf[i] = ( unsigned short ) (i * 0.001); } start[0] = start[1] = start[2] = 0; count[0] = CY; count[1] = CX; count[2] = CZ; r = miset_voxel_value_hyperslab ( hvol, MI_TYPE_USHORT, start, count, buf ); if(r<0) return r; /* Set random values to slice min and max for slice scaling*/ start[0] = start[1] = start[2] = 0; for ( i = 0; i < CY; i++ ) { start[0] = i; min += 0.1; max += 0.1; r = miset_slice_range ( hvol, start, NDIMS , max, min ); if(r<0) return r; } attribute=malloc(attribute_size); memset(attribute,'Z',attribute_size-1); attribute[attribute_size-1]=0; miset_attr_values(hvol,MI_TYPE_STRING,"test","test",attribute_size,attribute); miset_attr_values(hvol,MI_TYPE_STRING,"test","test2",6,"test2"); r = miclose_volume ( hvol ); free(buf); free(attribute); return r; } int main ( int argc, char **argv ) { int attribute_size=100000; if(argc>1) attribute_size=atoi(argv[1]); printf ( "Creating 3D image with attribute %d ! (3D_image_a.mnc)\n", attribute_size ); if( create_3D_image(attribute_size)<0) TESTRPT("create_3D_image",0); if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "\n No errors\n" ); } return ( error_cnt ); } libminc-libminc-2-3-00/testdir/minc2-m2stats.c000066400000000000000000001275211257462267400210760ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "minc2.h" #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define SQR(x) ((x)*(x)) #define WORLD_NDIMS 3 #define DEFAULT_VIO_BOOL (-1) #define BINS_DEFAULT 2000 /* Double_Array structure */ typedef struct { int numvalues; double *values; } Double_Array; /* Stats structure */ typedef struct { double vol_range[2]; double mask_range[2]; float *histogram; double hvoxels; double vvoxels; double volume; double vol_per; double hist_per; double min; double max; double sum; double sum2; double mean; double variance; double stddev; double voxel_com_sum[WORLD_NDIMS]; double voxel_com[WORLD_NDIMS]; double world_com[WORLD_NDIMS]; double median; double majority; double biModalT; double pct_T; double entropy; } Stats_Info; /* Function prototypes */ void do_math(long coords[], long num_voxels, int input_vector_length, double *input_data[]); void do_stats(double value, long index[], Stats_Info * stats); void print_result(char *title, double result); long get_minc_nvoxels(mihandle_t hvol); double get_minc_voxel_volume(mihandle_t hvol); int get_minc_ndims(mihandle_t hvol); int get_minc_lengths(mihandle_t hvol, int *cx, int *cy, int *cz); void find_minc_spatial_dims(mihandle_t hvol, int space_to_dim[], int dim_to_space[]); void get_minc_voxel_to_world(mihandle_t hvol, double voxel_to_world[WORLD_NDIMS + 1][WORLD_NDIMS + 1]); void normalize_vector(double vector[]); void transform_coord(double out_coord[], double transform[WORLD_NDIMS][WORLD_NDIMS + 1], double in_coord[]); void print_com(Stats_Info * stats); int get_double_list(char *dst, char *key, char *nextarg); void verify_range_options(Double_Array * min, Double_Array * max, Double_Array * range, Double_Array * binvalue); void init_stats(Stats_Info * stats, int hist_bins); void free_stats(Stats_Info * stats); /* Argument variables */ int max_buffer_size_in_kb = 4 * 1024; static int verbose = FALSE; static int quiet = FALSE; static int clobber = FALSE; static int ignoreNaN = DEFAULT_VIO_BOOL; static double fillvalue = -DBL_MAX; static int All = FALSE; static int Vol_Count = FALSE; static int Vol_Per = FALSE; static int Vol = FALSE; static int Min = FALSE; static int Max = FALSE; static int Sum = FALSE; static int Sum2 = FALSE; static int Mean = FALSE; static int Variance = FALSE; static int Stddev = FALSE; static int CoM = FALSE; static int World_Only = FALSE; static int Hist = FALSE; static int Hist_Count = FALSE; static int Hist_Per = FALSE; static int Median = FALSE; static int Majority = FALSE; static int BiModalT = FALSE; static int PctT = FALSE; static double pctT = 0.0; static int Entropy = FALSE; static Double_Array vol_min = { 0, NULL }; static Double_Array vol_max = { 0, NULL }; static Double_Array vol_range = { 0, NULL }; static Double_Array vol_binvalue = { 0, NULL }; static int num_ranges; char *mask_file; static Double_Array mask_min = { 0, NULL }; static Double_Array mask_max = { 0, NULL }; static Double_Array mask_range = { 0, NULL }; static Double_Array mask_binvalue = { 0, NULL }; static int num_masks; char *hist_file; static int hist_bins = BINS_DEFAULT; static double hist_sep; static double hist_range[2] = { -DBL_MAX, DBL_MAX }; static int discrete_histogram = FALSE; static int integer_histogram = FALSE; static int max_bins = 10000; /* Global Variables to store info for stats */ Stats_Info **stats_info = NULL; double voxel_volume; double nvoxels; int space_to_dim[WORLD_NDIMS] = { -1, -1, -1 }; int dim_to_space[MI2_MAX_VAR_DIMS]; int file_ndims = 0; /* Argument table */ ArgvInfo argTable[] = { {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "General options:"}, {"-verbose", ARGV_CONSTANT, (char *)TRUE, (char *)&verbose, "Print out extra information."}, {"-quiet", ARGV_CONSTANT, (char *)TRUE, (char *)&quiet, "Print requested values only."}, {"-clobber", ARGV_CONSTANT, (char *)TRUE, (char *)&clobber, "Clobber existing files."}, {"-noclobber", ARGV_CONSTANT, (char *)FALSE, (char *)&clobber, "Do not clobber existing files (default)."}, {"-max_buffer_size_in_kb", ARGV_INT, (char *)1, (char *)&max_buffer_size_in_kb, "maximum size of internal buffers."}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nVoxel selection options:"}, {"-floor", ARGV_FUNC, (char *)get_double_list, (char *)&vol_min, "Ignore voxels below this value (list)"}, {"-ceil", ARGV_FUNC, (char *)get_double_list, (char *)&vol_max, "Ignore voxels above this value (list)"}, {"-range", ARGV_FUNC, (char *)get_double_list, (char *)&vol_range, "Ignore voxels outside this range (list)"}, {"-binvalue", ARGV_FUNC, (char *)get_double_list, (char *)&vol_binvalue, "Include voxels within 0.5 of this value (list)"}, {"-mask", ARGV_STRING, (char *)1, (char *)&mask_file, " Use mask file for calculations."}, {"-mask_floor", ARGV_FUNC, (char *)get_double_list, (char *)&mask_min, "Exclude mask voxels below this value (list)"}, {"-mask_ceil", ARGV_FUNC, (char *)get_double_list, (char *)&mask_max, "Exclude mask voxels above this value (list)"}, {"-mask_range", ARGV_FUNC, (char *)get_double_list, (char *)&mask_range, "Exclude voxels outside this range (list)"}, {"-mask_binvalue", ARGV_FUNC, (char *)get_double_list, (char *)&mask_binvalue, "Include mask voxels within 0.5 of this value (list)"}, {"-ignore_nan", ARGV_CONSTANT, (char *)TRUE, (char *)&ignoreNaN, "Exclude NaN values from stats (default)."}, {"-include_nan", ARGV_CONSTANT, (char *)FALSE, (char *)&ignoreNaN, "Treat NaN values as zero."}, {"-replace_nan", ARGV_FLOAT, (char *)1, (char *)&fillvalue, "Replace NaNs with specified value."}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nHistogram Options:"}, {"-histogram", ARGV_STRING, (char *)1, (char *)&hist_file, " Compute histogram."}, {"-hist_bins", ARGV_INT, (char *)1, (char *)&hist_bins, " of bins in each histogram."}, {"-bins", ARGV_INT, (char *)1, (char *)&hist_bins, "synonym for -hist_bins."}, {"-hist_floor", ARGV_FLOAT, (char *)1, (char *)&hist_range[0], "Histogram floor value. (incl)"}, {"-hist_ceil", ARGV_FLOAT, (char *)1, (char *)&hist_range[1], "Histogram ceiling value. (incl)"}, {"-hist_range", ARGV_FLOAT, (char *)2, (char *)&hist_range, "Histogram floor and ceiling. (incl)"}, {"-discrete_histogram", ARGV_CONSTANT, (char *)TRUE, (char *)&discrete_histogram, "Match histogram bins to data discretization"}, {"-integer_histogram", ARGV_CONSTANT, (char *)TRUE, (char *)&integer_histogram, "Set histogram bins to unit width"}, {"-int_max_bins", ARGV_INT, (char *)1, (char *)&max_bins, "Set maximum number of histogram bins for integer histograms"}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nStatistics (Printed in this order)"}, {"-all", ARGV_CONSTANT, (char *)TRUE, (char *)&All, "all statistics (default)."}, {"-none", ARGV_CONSTANT, (char *)TRUE, (char *)&Vol_Count, "synonym for -count. (from volume_stats)"}, {"-count", ARGV_CONSTANT, (char *)TRUE, (char *)&Vol_Count, "# of voxels."}, {"-percent", ARGV_CONSTANT, (char *)TRUE, (char *)&Vol_Per, "percentage of valid voxels."}, {"-volume", ARGV_CONSTANT, (char *)TRUE, (char *)&Vol, "volume (in mm3)."}, {"-min", ARGV_CONSTANT, (char *)TRUE, (char *)&Min, "minimum value."}, {"-max", ARGV_CONSTANT, (char *)TRUE, (char *)&Max, "maximum value."}, {"-sum", ARGV_CONSTANT, (char *)TRUE, (char *)&Sum, "sum."}, {"-sum2", ARGV_CONSTANT, (char *)TRUE, (char *)&Sum2, "sum of squares."}, {"-mean", ARGV_CONSTANT, (char *)TRUE, (char *)&Mean, "mean value."}, {"-variance", ARGV_CONSTANT, (char *)TRUE, (char *)&Variance, "variance."}, {"-stddev", ARGV_CONSTANT, (char *)TRUE, (char *)&Stddev, "standard deviation."}, {"-CoM", ARGV_CONSTANT, (char *)TRUE, (char *)&CoM, "centre of mass of the volume."}, {"-com", ARGV_CONSTANT, (char *)TRUE, (char *)&CoM, "centre of mass of the volume."}, {"-world_only", ARGV_CONSTANT, (char *)TRUE, (char *)&World_Only, "print CoM in world coords only."}, {NULL, ARGV_HELP, (char *)NULL, (char *)NULL, "\nHistogram Dependant Statistics:"}, {"-hist_count", ARGV_CONSTANT, (char *)TRUE, (char *)&Hist_Count, "# of voxels portrayed in Histogram."}, {"-hist_percent", ARGV_CONSTANT, (char *)TRUE, (char *)&Hist_Per, "percentage of histogram voxels."}, {"-median", ARGV_CONSTANT, (char *)TRUE, (char *)&Median, "median value."}, {"-majority", ARGV_CONSTANT, (char *)TRUE, (char *)&Majority, "most frequently occurring histogram bin."}, {"-biModalT", ARGV_CONSTANT, (char *)TRUE, (char *)&BiModalT, "value separating a volume into 2 classes."}, {"-pctT", ARGV_FLOAT, (char *)1, (char *)&pctT, "<%> threshold at the supplied % of data."}, {"-entropy", ARGV_CONSTANT, (char *)TRUE, (char *)&Entropy, "entropy of the volume."}, {NULL, ARGV_HELP, NULL, NULL, ""}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main(int argc, char *argv[]) { char **infiles; int nfiles; mihandle_t hvol; int idim; int irange, imask; double real_range[2], valid_range[2]; mitype_t mitype; double voxel_to_world[WORLD_NDIMS + 1][WORLD_NDIMS + 1]; Stats_Info *stats; FILE *FP; double scale, voxmin, voxmax; int x, y, z; int cx, cy, cz; double * buffer; /* Get arguments */ if(ParseArgv(&argc, argv, argTable, 0) || (argc != 2)) { (void)fprintf(stderr, "\nUsage: %s [options] \n", argv[0]); (void)fprintf(stderr, " %s -help\n\n", argv[0]); exit(EXIT_FAILURE); } nfiles = argc - 1; infiles = &argv[1]; infiles[1] = &mask_file[0]; if(infiles[1] != NULL) { nfiles++; } /* Check for NaN options */ if(ignoreNaN == DEFAULT_VIO_BOOL) { ignoreNaN = (fillvalue != -DBL_MAX); } if(ignoreNaN && fillvalue == -DBL_MAX) { fillvalue = 0.0; } /* Check range options: not over-specified and put values in vol_min/vol_max */ verify_range_options(&vol_min, &vol_max, &vol_range, &vol_binvalue); num_ranges = vol_min.numvalues; /* Check mask range options: not over-specified and put values in mask_min/mask_max */ verify_range_options(&mask_min, &mask_max, &mask_range, &mask_binvalue); num_masks = mask_min.numvalues; /* Check histogramming options */ if((discrete_histogram && integer_histogram) || ((discrete_histogram || integer_histogram) && (hist_bins != BINS_DEFAULT))) { (void)fprintf(stderr, "Please specify only -discrete_histogram, -integer_histogram or -bins\n"); exit(EXIT_FAILURE); } /* init PctT boolean before checking */ if(pctT > 0.0) { PctT = TRUE; pctT /= 100; } /* if nothing selected, do everything */ if(!Vol_Count && !Vol_Per && !Vol && !Min && !Max && !Sum && !Sum2 && !Mean && !Variance && !Stddev && !Hist_Count && !Hist_Per && !Median && !Majority && !BiModalT && !PctT && !Entropy && !CoM) { All = TRUE; Hist = TRUE; } if((hist_file != NULL) || Hist_Count || Hist_Per || Median || Majority || BiModalT || PctT || Entropy) { Hist = TRUE; } if(hist_bins <= 0) Hist = FALSE; /* do checking on arguments */ if(hist_bins < 1) { (void)fprintf(stderr, "%s: Must have one or more bins for a histogram\n", argv[0]); exit(EXIT_FAILURE); } if(access(infiles[0], F_OK) != 0) { (void)fprintf(stderr, "%s: Couldn't find %s\n", argv[0], infiles[0]); exit(EXIT_FAILURE); } if(infiles[1] != NULL && access(infiles[1], F_OK) != 0) { (void)fprintf(stderr, "%s: Couldn't find mask file: %s\n", argv[0], infiles[1]); exit(EXIT_FAILURE); } if(hist_file != NULL && !clobber && access(hist_file, F_OK) != -1) { (void)fprintf(stderr, "%s: Histogram %s exists! (use -clobber to overwrite)\n", argv[0], hist_file); exit(EXIT_FAILURE); } /* Open the file to get some information */ if (miopen_volume(infiles[0], MI2_OPEN_READ, &hvol) < 0) { fprintf(stderr, "Unable to open the input file\n"); exit(EXIT_FAILURE); } nvoxels = get_minc_nvoxels(hvol); voxel_volume = get_minc_voxel_volume(hvol); miget_data_type(hvol, &mitype); miget_volume_real_range(hvol, real_range); miget_volume_valid_range(hvol, &valid_range[1], &valid_range[0]); file_ndims = get_minc_ndims(hvol); find_minc_spatial_dims(hvol, space_to_dim, dim_to_space); get_minc_voxel_to_world(hvol, voxel_to_world); /* Check whether discrete histogramming makes sense - i.e. not floating-point. Silently ignore the option if it does not make sense. */ if(mitype == MI_TYPE_FLOAT || mitype == MI_TYPE_DOUBLE) { discrete_histogram = FALSE; } /* set up the histogram definition, if needed */ if(Hist) { if(hist_range[0] == -DBL_MAX) { if(vol_min.numvalues == 1 && vol_min.values[0] != -DBL_MAX) hist_range[0] = vol_min.values[0]; else hist_range[0] = real_range[0]; } if(hist_range[1] == DBL_MAX) { if(vol_max.numvalues == 1 && vol_max.values[0] != DBL_MAX) hist_range[1] = vol_max.values[0]; else hist_range[1] = real_range[1]; } if(discrete_histogram) { /* Convert histogram range to voxel values and round, then convert back. */ scale = (real_range[1] == real_range[0]) ? 0.0 : (valid_range[1] - valid_range[0]) / (real_range[1] - real_range[0]); voxmin = rint((hist_range[0] - real_range[0]) * scale + valid_range[0]); voxmax = rint((hist_range[1] - real_range[0]) * scale + valid_range[0]); if(real_range[1] != real_range[0]) scale = 1.0 / scale; hist_range[0] = (voxmin - valid_range[0]) * scale + real_range[0]; hist_range[1] = (voxmax - valid_range[0]) * scale + real_range[0]; /* Figure out number of bins and bin width */ hist_bins = voxmax - voxmin; if(hist_bins <= 0) { hist_sep = 1.0; hist_bins = 0; } else { hist_sep = (hist_range[1] - hist_range[0]) / hist_bins; } /* Shift the ends of the histogram down and up by half a bin and add one to the number of bins */ hist_range[0] -= hist_sep / 2.0; hist_range[1] += hist_sep / 2.0; hist_bins++; } else if(integer_histogram) { /* Add and subtract the 0.01 in order to ensure that a range that is already properly specified stays that way. Ie. [-0.5,255.5] does not change, regardless of the type of rounding done to .5 */ hist_range[0] = (int)rint(hist_range[0] + 0.01); hist_range[1] = (int)rint(hist_range[1] - 0.01); hist_bins = hist_range[1] - hist_range[0] + 1.0; hist_range[0] -= 0.5; hist_range[1] += 0.5; hist_sep = 1.0; } else { hist_sep = (hist_range[1] - hist_range[0]) / hist_bins; } if((discrete_histogram || integer_histogram) && (hist_bins > max_bins)) { (void)fprintf(stderr, "Too many bins in histogram (%d) - please increase -max_bins if appropriate\n", hist_bins); exit(EXIT_FAILURE); } } /* Initialize the stats structure */ stats_info = malloc(num_ranges * sizeof(*stats_info)); for(irange = 0; irange < num_ranges; irange++) { stats_info[irange] = malloc(num_masks * sizeof(**stats_info)); for(imask = 0; imask < num_masks; imask++) { stats = &stats_info[irange][imask]; init_stats(stats, hist_bins); stats->vol_range[0] = vol_min.values[irange]; stats->vol_range[1] = vol_max.values[irange]; stats->mask_range[0] = mask_min.values[imask]; stats->mask_range[1] = mask_max.values[imask]; } } get_minc_lengths(hvol, &cx, &cy, &cz); buffer = malloc(sizeof(double) * cz * cy); /* Do math */ for (x = 0; x < cx; x++) { long coords[3]; long edges[3]; double *r[2]; double v; coords[0] = x; coords[1] = 0; coords[2] = 0; edges[0] = 1; edges[1] = cy; edges[2] = cz; miget_real_value_hyperslab(hvol, MI_TYPE_DOUBLE, coords, edges, buffer); for (y = 0; y < cy; y++) { for (z = 0; z < cz; z++) { coords[1] = y; coords[2] = z; v = buffer[(y * cy) + z]; r[0] = &v; do_math(coords, 1, 1, r); } } } free(buffer); /* Open the histogram file if it will be needed */ if(hist_file == NULL) { FP = NULL; } else { FP = fopen(hist_file, "w"); if(FP == NULL) { perror("Error opening histogram file"); exit(EXIT_FAILURE); } } /* Loop over ranges and masks, calculating results */ for(irange = 0; irange < num_ranges; irange++) { for(imask = 0; imask < num_masks; imask++) { stats = &stats_info[irange][imask]; stats->vol_per = stats->vvoxels / nvoxels * 100; stats->hist_per = stats->hvoxels / nvoxels * 100; stats->mean = (stats->vvoxels > 0) ? stats->sum / stats->vvoxels : 0.0; stats->variance = (stats->vvoxels > 1) ? (stats->sum2 - SQR(stats->sum) / stats->vvoxels) / (stats->vvoxels - 1) : 0.0; stats->stddev = sqrt(stats->variance); stats->volume = voxel_volume * stats->vvoxels; for(idim = 0; idim < WORLD_NDIMS; idim++) { if(stats->sum != 0.0) stats->voxel_com[idim] = stats->voxel_com_sum[idim] / stats->sum; else stats->voxel_com[idim] = 0.0; } transform_coord(stats->world_com, voxel_to_world, stats->voxel_com); /* Do the histogram calculations */ if(Hist) { int c; float *hist_centre; float *pdf; /* probability density Function */ float *cdf; /* cumulative density Function */ int majority_bin = 0; int median_bin = 0; int pctt_bin = 0; int bimodalt_bin = 0; /* BiModal Threshold variables */ double zero_moment = 0.0; double first_moment = 0.0; double var = 0.0; double max_var = 0.0; /* Allocate space for histograms */ hist_centre = malloc(hist_bins * sizeof(float)); memset(hist_centre, 0, hist_bins * sizeof(float)); pdf = malloc(hist_bins * sizeof(float)); memset(pdf, 0, hist_bins * sizeof(float)); cdf = malloc(hist_bins * sizeof(float)); memset(cdf, 0, hist_bins * sizeof(float)); if(hist_centre == NULL || pdf == NULL || cdf == NULL) { (void)fprintf(stderr, "Memory allocation error\n"); exit(EXIT_FAILURE); } for(c = 0; c < hist_bins; c++) { hist_centre[c] = (c * hist_sep) + hist_range[0] + (hist_sep / 2); /* Probability and Cumulative density functions */ pdf[c] = (stats->hvoxels > 0) ? stats->histogram[c] / stats->hvoxels : 0.0; cdf[c] = (c == 0) ? pdf[c] : cdf[c - 1] + pdf[c]; /* Majority */ if(stats->histogram[c] > stats->histogram[majority_bin]) { majority_bin = c; } /* Entropy */ if(stats->histogram[c] > 0.0) { stats->entropy -= pdf[c] * (log(pdf[c]) / log(2.0)); } /* Histogram Median */ if(cdf[c] < 0.5) { median_bin = c; } /* BiModal Threshold */ if(c > 0) { zero_moment += pdf[c]; first_moment += hist_centre[c] * pdf[c]; var = SQR((stats->mean * zero_moment) - first_moment) / (zero_moment * (1 - zero_moment)); if(var > max_var) { bimodalt_bin = c; max_var = var; } } /* pct Threshold */ if(cdf[c] < pctT) { pctt_bin = c; } } /* median */ if(median_bin == 0) { stats->median = 0.5 * pdf[median_bin] * hist_sep; } else { stats->median = ((double)median_bin + (0.5 - cdf[median_bin]) * pdf[median_bin + 1]) * hist_sep; } stats->median += hist_centre[0]; stats->majority = hist_centre[majority_bin]; stats->biModalT = hist_centre[bimodalt_bin]; /* pct Threshold */ if(pctt_bin == 0) { stats->pct_T = pctT * pdf[pctt_bin] * hist_sep; } else { stats->pct_T = ((double)pctt_bin + (pctT - cdf[pctt_bin]) * pdf[pctt_bin + 1]) * hist_sep; } /* output the histogram */ if(hist_file != NULL) { (void)fprintf(FP, "# histogram for: %s\n", infiles[0]); (void)fprintf(FP, "# mask file: %s\n", (infiles[1] != NULL) ? infiles[1] : "(null)"); if(stats->vol_range[0] != -DBL_MAX || stats->vol_range[1] != DBL_MAX) { (void)fprintf(FP, "# volume range: %g %g\n", stats->vol_range[0], stats->vol_range[1]); } if(stats->mask_range[0] != -DBL_MAX || stats->mask_range[1] != DBL_MAX) { (void)fprintf(FP, "# mask range: %g %g\n", stats->mask_range[0], stats->mask_range[1]); } (void)fprintf(FP, "# domain: %g %g\n", hist_range[0], hist_range[1]); (void)fprintf(FP, "# entropy: %g\n", stats->entropy);; (void)fprintf(FP, "# bin centres counts\n"); for(c = 0; c < hist_bins; c++) (void)fprintf(FP, " %-20.10g %12g\n", hist_centre[c], stats->histogram[c]); (void)fprintf(FP, "\n"); } /* Free the space */ free(hist_centre); free(pdf); free(cdf); } /* end histogram calculations */ /* Print range of data allowed */ if(verbose || (num_ranges > 1 && !quiet)) { (void)fprintf(stdout, "Included Range: %g %g\n", stats->vol_range[0], stats->vol_range[1]); } if(verbose || (num_masks > 1 && !quiet)) { (void)fprintf(stdout, "Mask Range: %g %g\n", stats->mask_range[0], stats->mask_range[1]); } /* Print warnings about ranges */ if(!quiet && real_range[0] != stats->min && stats->vol_range[0] == -DBL_MAX && stats->mask_range[0] == -DBL_MAX) { (void)fprintf(stderr, "*** %s - reported min (%g) doesn't equal header (%g)\n", argv[0], stats->min, real_range[0]); } if(!quiet && real_range[1] != stats->max && stats->vol_range[1] == DBL_MAX && stats->mask_range[1] == DBL_MAX) { (void)fprintf(stderr, "*** %s - reported max (%g) doesn't equal header (%g)\n", argv[0], stats->max, real_range[1]); } /* Output stats */ if(Hist) { if(verbose) { (void)fprintf(stdout, "Histogram Range: %g\t%g\n", hist_range[0], hist_range[1]); (void)fprintf(stdout, "Histogram bins: %i of Width (separation): %f\n", hist_bins, hist_sep); } } if(All && !quiet) { (void)fprintf(stdout, "File: %s\n", infiles[0]); } if(All && !quiet) { (void)fprintf(stdout, "Mask file: %s\n", (infiles[1] != NULL) ? infiles[1] : "(null)"); } if(All && !quiet) { print_result("Total voxels: ", nvoxels); } if(All || Vol_Count) { print_result("# voxels: ", stats->vvoxels); } if(All || Vol_Per) { print_result("% of total: ", stats->vol_per); } if(All || Vol) { print_result("Volume (mm3): ", stats->volume); } if(All || Min) { print_result("Min: ", stats->min); } if(All || Max) { print_result("Max: ", stats->max); } if(All || Sum) { print_result("Sum: ", stats->sum); } if(All || Sum2) { print_result("Sum^2: ", stats->sum2); } if(All || Mean) { print_result("Mean: ", stats->mean); } if(All || Variance) { print_result("Variance: ", stats->variance); } if(All || Stddev) { print_result("Stddev: ", stats->stddev); } if(All || CoM) { print_com(stats); } if(Hist) { if(All && !quiet) { (void)fprintf(stdout, "\nHistogram: %s\n", hist_file); } if(All && !quiet) { print_result("Total voxels: ", nvoxels); } if(All || Hist_Count) { print_result("# voxels: ", stats->hvoxels); } if(All || Hist_Per) { print_result("% of total: ", stats->hist_per); } if(All || Median) { print_result("Median: ", stats->median); } if(All || Majority) { print_result("Majority: ", stats->majority); } if(All || BiModalT) { print_result("BiModalT: ", stats->biModalT); } if(All || PctT) { char str[100]; (void)sprintf(str, "PctT [%3d%%]: ", (int)(pctT * 100)); print_result(str, stats->pct_T); } if(All || Entropy) { print_result("Entropy : ", stats->entropy); } if(!quiet) { (void)fprintf(stdout, "\n"); } } } /* End of loop over masks */ } /* End of loop over ranges */ /* Close the histogram file */ if(FP != NULL) { (void)fclose(FP); } /* Free things up */ for(irange = 0; irange < num_ranges; irange++) { for(imask = 0; imask < num_masks; imask++) { free_stats(&stats_info[irange][imask]); } free(stats_info[irange]); } free(stats_info); return EXIT_SUCCESS; } void do_math(long index[], long num_voxels, int input_vector_length, double *input_data[]) { long ivox; int imask, irange; double mask_min, mask_max; Stats_Info *stats; /* Loop through the voxels - a bit of optimization in case we have a brain-dead compiler */ if(mask_file != NULL) { for(irange = 0; irange < num_ranges; irange++) { for(imask = 0; imask < num_masks; imask++) { stats = &stats_info[irange][imask]; mask_min = stats->mask_range[0]; mask_max = stats->mask_range[1]; if(CoM || All) { for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++) { if((input_data[1][ivox] >= mask_min) && (input_data[1][ivox] <= mask_max)) { do_stats(input_data[0][ivox], index, stats); } } } else { for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++) { if((input_data[1][ivox] >= mask_min) && (input_data[1][ivox] <= mask_max)) { do_stats(input_data[0][ivox], NULL, stats); } } } } } } else { for(irange = 0; irange < num_ranges; irange++) { stats = &stats_info[irange][0]; if(CoM || All) { for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++) { do_stats(input_data[0][ivox], index, stats); } } else { for(ivox = 0; ivox < num_voxels * input_vector_length; ivox++) { do_stats(input_data[0][ivox], NULL, stats); } } } } return; } void do_stats(double value, long index[], Stats_Info * stats) { int idim; int hist_index, dim_index; /* Check for NaNs */ if(value == -DBL_MAX) { if(ignoreNaN) value = fillvalue; else return; } /* Collect stats if we are within range */ if((value >= stats->vol_range[0]) && (value <= stats->vol_range[1])) { stats->vvoxels++; stats->sum += value; stats->sum2 += SQR(value); if(value < stats->min) { stats->min = value; } if(value > stats->max) { stats->max = value; } /* Get voxel index */ if(CoM || All) { for(idim = 0; idim < WORLD_NDIMS; idim++) { dim_index = space_to_dim[idim]; if(dim_index >= 0) { stats->voxel_com_sum[idim] += value * index[dim_index]; } } } if(Hist && (value >= hist_range[0]) && (value <= hist_range[1])) { /*lower limit <= value < upper limit */ hist_index = (int)floor((value - hist_range[0]) / hist_sep); if(hist_index >= hist_bins) { hist_index = hist_bins - 1; } stats->histogram[hist_index]++; stats->hvoxels++; } } } void print_result(char *title, double result) { if(!quiet) { (void)fprintf(stdout, "%s", title); } (void)fprintf(stdout, "%.10g\n", result); } /* Get the number of voxels in the file - this is the total number, not just spatial dimensions */ long get_minc_nvoxels(mihandle_t hvol) { int n; miget_volume_voxel_count(hvol, &n); return n; } int get_minc_lengths(mihandle_t hvol, int *cx, int *cy, int *cz) { midimhandle_t dims[MI2_MAX_VAR_DIMS]; int ndims; int i; char *name; unsigned int length; miget_volume_dimensions(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, 0, MI2_MAX_VAR_DIMS, dims); miget_volume_dimension_count(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &ndims); for (i = 0; i < ndims; i++) { miget_dimension_name(dims[i], &name); miget_dimension_size(dims[i], &length); if (!strcmp(name, "xspace")) { *cx = (int) length; } else if (!strcmp(name, "yspace")) { *cy = (int) length; } else if (!strcmp(name, "zspace")) { *cz = (int) length; } mifree_name(name); } return (MI_NOERROR); } /* Get the volume of a spatial voxel */ double get_minc_voxel_volume(mihandle_t hvol) { int idim, ndims; double volume, step; midimhandle_t *hdim; miget_volume_dimension_count(hvol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, &ndims); hdim = (midimhandle_t *) malloc(sizeof (midimhandle_t) * ndims); if (hdim == NULL) { return (0.0); } miget_volume_dimensions(hvol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, 0, ndims, hdim); /* Loop over them to get the total spatial volume */ volume = 1.0; for (idim = 0; idim < ndims; idim++) { step = 1.0; miget_dimension_separation(hdim[idim], 0, &step); /* Make sure that it is positive and calculate the volume */ if(step < 0.0) step = -step; volume *= step; } free(hdim); return volume; } /* Get the total number of image dimensions in a minc file */ int get_minc_ndims(mihandle_t hvol) { int ndims; miget_volume_dimension_count(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &ndims); return ndims; } /* Get the mapping from spatial dimension - x, y, z - to file dimensions and vice-versa. */ void find_minc_spatial_dims(mihandle_t hvol, int space_to_dim[], int dim_to_space[]) { midimhandle_t dim[MI2_MAX_VAR_DIMS]; int idim, ndims, world_index; char *dimname; /* Set default values */ for(idim = 0; idim < 3; idim++) space_to_dim[idim] = -1; for(idim = 0; idim < MI2_MAX_VAR_DIMS; idim++) dim_to_space[idim] = -1; ndims = miget_volume_dimensions(hvol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, 0, MI2_MAX_VAR_DIMS, dim); /* Loop over them to find the spatial ones */ for(idim = 0; idim < ndims; idim++) { /* Get the name and check that this is a spatial dimension */ miget_dimension_name(dim[idim], &dimname); if((dimname[0] == '\0') || (strcmp(&dimname[1], "space") != 0)) { mifree_name(dimname); continue; } /* Look for the spatial dimensions */ switch (dimname[0]) { case 'x': world_index = 0; break; case 'y': world_index = 1; break; case 'z': world_index = 2; break; default: world_index = 0; break; } space_to_dim[world_index] = idim; dim_to_space[idim] = world_index; mifree_name(dimname); } } /* Get the voxel to world transform (for column vectors) */ void get_minc_voxel_to_world(mihandle_t hvol, double voxel_to_world[WORLD_NDIMS + 1][WORLD_NDIMS + 1]) { extern void miget_voxel_to_world(mihandle_t hvol, double v2w[WORLD_NDIMS+1][WORLD_NDIMS+1]); miget_voxel_to_world(hvol, voxel_to_world); } void normalize_vector(double vector[]) { int idim; double magnitude; magnitude = 0.0; for(idim = 0; idim < WORLD_NDIMS; idim++) { magnitude += (vector[idim] * vector[idim]); } magnitude = sqrt(magnitude); if(magnitude > 0.0) { for(idim = 0; idim < WORLD_NDIMS; idim++) { vector[idim] /= magnitude; } } } /* Prints out centre of mass with correct file order */ void print_com(Stats_Info * stats) { char *spatial_codes[WORLD_NDIMS] = { "x", "y", "z" }; /* In x,y,z order */ int idim; int first; /* Print out voxel coord info */ if(!World_Only) { if(!quiet) { (void)fprintf(stdout, "CoM_voxel("); first = TRUE; for(idim = 0; idim < MI2_MAX_VAR_DIMS; idim++) { if(dim_to_space[idim] >= 0) { if(first) first = FALSE; else (void)fprintf(stdout, ","); (void)fprintf(stdout, "%s", spatial_codes[dim_to_space[idim]]); } } (void)fprintf(stdout, "): "); } first = TRUE; for(idim = 0; idim < MI2_MAX_VAR_DIMS; idim++) { if(dim_to_space[idim] >= 0) { if(first) first = FALSE; else (void)fprintf(stdout, " "); (void)fprintf(stdout, "%.10g", stats->voxel_com[dim_to_space[idim]]); } } (void)fprintf(stdout, "\n"); } /* Print out world coord info */ if(!quiet) { (void)fprintf(stdout, "CoM_real(x,y,z): "); } (void)fprintf(stdout, "%.10g %.10g %.10g\n", stats->world_com[0], stats->world_com[1], stats->world_com[2]); } /* Transforms a coordinate through a linear transform */ void transform_coord(double out_coord[], double transform[WORLD_NDIMS][WORLD_NDIMS + 1], double in_coord[]) { int idim, jdim; double homogeneous_coord[WORLD_NDIMS + 1]; for(idim = 0; idim < WORLD_NDIMS; idim++) { homogeneous_coord[idim] = in_coord[idim]; } homogeneous_coord[WORLD_NDIMS] = 1.0; for(idim = 0; idim < WORLD_NDIMS; idim++) { out_coord[idim] = 0.0; for(jdim = 0; jdim < WORLD_NDIMS + 1; jdim++) { out_coord[idim] += transform[idim][jdim] * homogeneous_coord[jdim]; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_double_list @INPUT : dst - client data passed by ParseArgv key - matching key in argv nextarg - argument following key in argv @OUTPUT : (none) @RETURNS : TRUE since nextarg is used. @DESCRIPTION: Gets a list (array) of double values. @METHOD : @GLOBALS : @CALLS : @CREATED : March 8, 1995 (Peter Neelin) @MODIFIED : ---------------------------------------------------------------------------- */ int get_double_list(char *dst, char *key, char *nextarg) { #define VECTOR_SEPARATOR ',' int num_elements; int num_alloc; double *double_list; double dvalue; char *cur, *end, *prev; Double_Array *double_array; /* Check for a following argument */ if(nextarg == NULL) { (void)fprintf(stderr, "\"%s\" option requires an additional argument\n", key); exit(EXIT_FAILURE); } /* Get pointers to array variables */ double_array = (Double_Array *) dst; /* Set up pointers to end of string and first non-space character */ end = nextarg + strlen(nextarg); cur = nextarg; while(isspace(*cur)) cur++; num_elements = 0; num_alloc = 0; double_list = NULL; /* Loop through string looking for doubles */ while(cur != end) { /* Get double */ prev = cur; dvalue = strtod(prev, &cur); if(cur == prev) { (void)fprintf(stderr, "expected vector of doubles for \"%s\", but got \"%s\"\n", key, nextarg); exit(EXIT_FAILURE); } /* Add the value to the list */ num_elements++; if(num_elements > num_alloc) { num_alloc += 20; if(double_list == NULL) { double_list = malloc(num_alloc * sizeof(*double_list)); } else { double_list = realloc(double_list, num_alloc * sizeof(*double_list)); } } double_list[num_elements - 1] = dvalue; /* Skip any spaces */ while(isspace(*cur)) cur++; /* Skip an optional comma */ if(*cur == VECTOR_SEPARATOR) cur++; } /* Update the global variables */ double_array->numvalues = num_elements; if(double_array->values != NULL) { free(double_array->values); } double_array->values = double_list; return TRUE; } /* Check range options and set appropriate values. At least one value will be set up for min and max. */ void verify_range_options(Double_Array * min, Double_Array * max, Double_Array * range, Double_Array * binvalue) { int overspecified = FALSE; int min_defaults, max_defaults; int num_values; int ivalue; /* Check the min and max */ if(min->numvalues != 0 && max->numvalues != 0 && min->numvalues != max->numvalues) { (void)fprintf(stderr, "Number of floor ceil values differs\n"); exit(EXIT_FAILURE); } num_values = min->numvalues; if(num_values == 0) num_values = max->numvalues; /* Check for range */ if(range->numvalues > 0) { if(num_values == 0) num_values = range->numvalues / 2; else overspecified = TRUE; } /* Check for binvalue */ if(binvalue->numvalues > 0) { if(num_values == 0) num_values = binvalue->numvalues; else overspecified = TRUE; } /* Print an error if too many options have been given */ if(overspecified) { (void)fprintf(stderr, "Set only one of floor/ceil, range or binvalue\n"); exit(EXIT_FAILURE); } /* Double-check that we got the sizes right */ if((min->numvalues > 0 && min->numvalues != num_values) || (max->numvalues > 0 && max->numvalues != num_values)) { (void)fprintf(stderr, "Problem with range specification\n"); exit(EXIT_FAILURE); } /* Check if we are setting default values. Make sure that at least one value is set */ if(num_values <= 0) { num_values = 1; min_defaults = max_defaults = TRUE; } else { min_defaults = (min->numvalues == 0 && max->numvalues > 0); max_defaults = (max->numvalues == 0 && min->numvalues > 0); } /* Allocate the space */ if(min->numvalues <= 0) { min->numvalues = num_values; min->values = malloc(num_values * sizeof(double)); } if(max->numvalues <= 0) { max->numvalues = num_values; max->values = malloc(num_values * sizeof(double)); } if(min->values == NULL || max->values == NULL) { (void)fprintf(stderr, "Memory allocation error\n"); exit(EXIT_FAILURE); } /* Set defaults, if needed */ if(min_defaults) { for(ivalue = 0; ivalue < num_values; ivalue++) min->values[ivalue] = -DBL_MAX; } if(max_defaults) { for(ivalue = 0; ivalue < num_values; ivalue++) max->values[ivalue] = DBL_MAX; } /* Set min and max from range, if needed */ if(range->numvalues > 0) { /* Check for odd number of values - they should be in pairs */ if((vol_range.numvalues % 2) != 0) { (void)fprintf(stderr, "Specify range values in pairs (even number)\n"); exit(EXIT_FAILURE); } /* Loop over values */ for(ivalue = 0; ivalue * 2 + 1 < range->numvalues; ivalue++) { min->values[ivalue] = range->values[ivalue * 2]; max->values[ivalue] = range->values[ivalue * 2 + 1]; } } /* Set min and max from binvalue, if needed */ if(binvalue->numvalues > 0) { for(ivalue = 0; ivalue < binvalue->numvalues; ivalue++) { min->values[ivalue] = binvalue->values[ivalue] - 0.5; max->values[ivalue] = binvalue->values[ivalue] + 0.5; } } } /* Initialiaze a Stats_Info structure */ void init_stats(Stats_Info * stats, int hist_bins) { stats->vol_range[0] = -DBL_MAX; stats->vol_range[1] = DBL_MAX; stats->mask_range[0] = -DBL_MAX; stats->mask_range[1] = DBL_MAX; if(Hist && hist_bins > 0) { stats->histogram = malloc(hist_bins * sizeof(float)); memset(stats->histogram, 0, hist_bins * sizeof(float)); if(stats->histogram == NULL) { (void)fprintf(stderr, "Memory allocation error\n"); exit(EXIT_FAILURE); } } else { stats->histogram = NULL; } stats->hvoxels = 0.0; /* number of voxels in histogram */ stats->vvoxels = 0.0; /* number of valid voxels */ stats->volume = 0.0; stats->vol_per = 0.0; stats->hist_per = 0.0; stats->min = DBL_MAX; stats->max = -DBL_MAX; stats->sum = 0.0; stats->sum2 = 0.0; stats->mean = 0.0; stats->variance = 0.0; stats->stddev = 0.0; stats->voxel_com_sum[0] = 0.0; stats->voxel_com_sum[1] = 0.0; stats->voxel_com_sum[2] = 0.0; stats->voxel_com[0] = 0.0; stats->voxel_com[1] = 0.0; stats->voxel_com[2] = 0.0; stats->world_com[0] = 0.0; stats->world_com[1] = 0.0; stats->world_com[2] = 0.0; stats->median = 0.0; stats->majority = 0.0; stats->biModalT = 0.0; stats->pct_T = 0.0; stats->entropy = 0.0; } /* Free things from a Stats_Info structure */ void free_stats(Stats_Info * stats) { if(stats->histogram != NULL) free(stats->histogram); } libminc-libminc-2-3-00/testdir/minc2-multires-test.c000066400000000000000000000102011257462267400223040ustar00rootroot00000000000000#include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CX 60 #define CY 55 #define CZ 50 #define NDIMS 3 int main(void) { mihandle_t vol; int r; midimhandle_t dim[NDIMS]; mivolumeprops_t props; int n; misize_t coords[NDIMS]; misize_t count[NDIMS]; int i,j,k; unsigned int voxel; printf("Creating volume...\n"); /* Write data one voxel at a time. */ for (i = 0; i < NDIMS; i++) { count[i] = 1; } r = minew_volume_props(&props); r = miset_props_compression_type(props, MI_COMPRESS_ZLIB); r = miset_props_zlib_compression(props, 3); r = miset_props_multi_resolution(props, 1, 3); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("xspace",MI_DIMCLASS_SPATIAL,MI_DIMATTR_REGULARLY_SAMPLED, CX,&dim[0]); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("yspace",MI_DIMCLASS_SPATIAL,MI_DIMATTR_REGULARLY_SAMPLED, CY, &dim[1]); if (r < 0) { TESTRPT("failed", r); } r = micreate_dimension("zspace",MI_DIMCLASS_SPATIAL,MI_DIMATTR_REGULARLY_SAMPLED, CZ,&dim[2]); if (r < 0) { TESTRPT("failed", r); } r = micreate_volume("tst-multi.mnc", NDIMS, dim, MI_TYPE_UINT, MI_CLASS_REAL,props,&vol); if (r < 0) { TESTRPT("failed", r); } r = miset_volume_valid_range(vol, CX*10000.0 + CY*100 + CZ, 0.0); r = micreate_volume_image(vol); if (r < 0) { TESTRPT("failed", r); } r = miget_volume_dimension_count(vol, MI_DIMCLASS_SPATIAL, MI_DIMATTR_ALL, &n); if (r < 0) { TESTRPT("failed", r); } printf("Writing data...\n"); for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; voxel = i*10000 + j*100 + k; r = miset_voxel_value_hyperslab(vol, MI_TYPE_UINT, coords, count, &voxel); if (r < 0) { TESTRPT("Error writing voxel", r); } } } } printf("Selecting half-size image\n"); r = miselect_resolution(vol, 1); if (r < 0) { TESTRPT("miselect_resolution failed", r); } /* OK, now try to read the lower-resolution hyperslab */ coords[0] = 0; coords[1] = 0; coords[2] = 0; count[0] = CX/2; count[1] = CY/2; count[2] = CZ/2; { unsigned int buffer[CX/2][CY/2][CZ/2]; r = miget_voxel_value_hyperslab(vol, MI_TYPE_UINT, coords, count, buffer); if (r < 0) { TESTRPT("failed", r); } } printf("Selecting quarter-size image\n"); r = miselect_resolution(vol, 2); if (r < 0) { TESTRPT("miselect_resolution failed", r); } /* OK, now try to read the lower-resolution hyperslab */ coords[0] = 0; coords[1] = 0; coords[2] = 0; count[0] = CX/4; count[1] = CY/4; count[2] = CZ/4; { unsigned int buffer[CX/4][CY/4][CZ/4]; r = miget_voxel_value_hyperslab(vol, MI_TYPE_UINT, coords, count, buffer); if (r < 0) { TESTRPT("failed", r); } } printf("Return to full resolution.\n"); r = miselect_resolution(vol, 0); /* Back to full resolution */ if (r < 0) { TESTRPT("miselect_resolution failed", r); } printf("Flush any remaining thumbnails.\n"); r = miflush_from_resolution(vol, 3); if (r < 0) { TESTRPT("failed", r); } r = miclose_volume(vol); if (r < 0) { TESTRPT("failed", r); } if (error_cnt != 0) { fprintf(stderr, "%d error%s reported\n", error_cnt, (error_cnt == 1) ? "" : "s"); } else { fprintf(stderr, "No errors\n"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-read-metadata.c000066400000000000000000000127701257462267400221710ustar00rootroot00000000000000#include #include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; static const char * get_type_name(mitype_t volume_type) { return volume_type==MI_TYPE_BYTE?"Byte": volume_type==MI_TYPE_SHORT?"Short": volume_type==MI_TYPE_INT?"Int": volume_type==MI_TYPE_FLOAT?"Float": volume_type==MI_TYPE_DOUBLE?"Double": volume_type==MI_TYPE_STRING?"String": volume_type==MI_TYPE_UBYTE?"Unsigned Byte": volume_type==MI_TYPE_USHORT?"Unsigned Short": volume_type==MI_TYPE_UINT?"Unsigned Int":"Other"; } static int print_metadata(mihandle_t vol, const char * path,int ident) { milisthandle_t grplist; char group_name[256]; milisthandle_t attlist; int r=MI_NOERROR; //printf("Printing groups path:%s %d\n",path,ident); if ( (r=milist_start(vol, path, 0, &grplist)) == MI_NOERROR ) { while( milist_grp_next(grplist, group_name, sizeof(group_name)) == MI_NOERROR ) { char add_path[256]; printf("%*s %s:\n",ident,"",group_name); strcpy(add_path,path);strcat(add_path,group_name); print_metadata(vol,add_path,ident+2); } milist_finish(grplist); } else return r; //printf("Printing attributes path:%s %d\n",path,ident); if((r=milist_start(vol, path, 1 , &attlist)) == MI_NOERROR) { char int_path[256]; char attribute[256]; while( milist_attr_next(vol,attlist,int_path,sizeof(int_path),attribute,sizeof(attribute)) == MI_NOERROR ) { mitype_t att_data_type; size_t att_length; if(miget_attr_type(vol,int_path,attribute,&att_data_type) == MI_NOERROR && miget_attr_length(vol,int_path,attribute,&att_length) == MI_NOERROR ) { printf("%*s %s:%s type:%s length:%d\n",ident,"",int_path,attribute,get_type_name(att_data_type),(int)att_length); switch(att_data_type) { case MI_TYPE_STRING: { char *tmp=(char*)malloc(att_length+1); if(miget_attr_values(vol,att_data_type,int_path,attribute,att_length,tmp) == MI_NOERROR ) printf("%*s %s\n",ident,"",tmp); free(tmp); } break; case MI_TYPE_FLOAT: { float *tmp=(float*)malloc(att_length*sizeof(float)); if(miget_attr_values(vol,att_data_type,int_path,attribute,att_length,tmp) == MI_NOERROR ) { size_t i; printf("%*s ",ident,""); for(i=0;i \n",argv[0]); return 1; } r = miopen_volume ( argv[1], MI2_OPEN_READ, &vol ); if ( r < 0 ) { TESTRPT ( "failed to open image", r ); /*nothing else to do here*/ return ( error_cnt ); } printf("Volume %s info: \n",argv[1]); r= miget_volume_dimension_count(vol,MI_DIMCLASS_ANY,MI_DIMATTR_ALL,&ndim); if ( r < 0 ) { TESTRPT ( "failed to get image dimension count", r ); } r=miget_data_class(vol, &volume_class); if ( r < 0 ) { TESTRPT ( "failed to get volume class", r ); } r=miget_data_type(vol, &volume_type); if ( r < 0 ) { TESTRPT ( "failed to get volume type", r ); } printf("\tNumber of dimensions:%d Data class:%s Data Type: %s\n",ndim, volume_class==MI_CLASS_REAL?"Real": volume_class==MI_CLASS_INT?"Int": volume_class==MI_CLASS_LABEL?"Label": volume_class==MI_CLASS_COMPLEX?"Complex":"Other", get_type_name(volume_type) ); /* go over metadata*/ print_metadata(vol,"",0); /* close volume*/ miclose_volume ( vol ); /*free(buffer);*/ if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "\n No errors\n" ); } return ( error_cnt ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/testdir/minc2-read-rgb.c000066400000000000000000000446561257462267400211730ustar00rootroot00000000000000#include #include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; static double calculate_mean_f(float *array,misize_t length) { double avg=0.0; misize_t i; for(i=0;i \n",argv[0]); return 1; } r = miopen_volume ( argv[1], MI2_OPEN_READ, &vol ); if ( r < 0 ) { TESTRPT ( "failed to open image", r ); /*nothing else to do here*/ return ( error_cnt ); } printf("Volume %s info: \n",argv[1]); r= miget_volume_dimension_count(vol,MI_DIMCLASS_ANY,MI_DIMATTR_ALL,&ndim); if ( r < 0 ) { TESTRPT ( "failed to get image dimension count", r ); } r=miget_data_class(vol, &volume_class); if ( r < 0 ) { TESTRPT ( "failed to get volume class", r ); } r=miget_data_type(vol, &volume_type); if ( r < 0 ) { TESTRPT ( "failed to get volume type", r ); } printf("\tNumber of dimensions:%d Data class:%s Data Type: %s\n",ndim, volume_class==MI_CLASS_REAL?"Real": volume_class==MI_CLASS_INT?"Int": volume_class==MI_CLASS_LABEL?"Label": volume_class==MI_CLASS_COMPLEX?"Complex":"Other", volume_type==MI_TYPE_BYTE?"Byte": volume_type==MI_TYPE_SHORT?"Short": volume_type==MI_TYPE_INT?"Int": volume_type==MI_TYPE_FLOAT?"Float": volume_type==MI_TYPE_DOUBLE?"Double": volume_type==MI_TYPE_STRING?"String": volume_type==MI_TYPE_UBYTE?"Unsigned Byte": volume_type==MI_TYPE_USHORT?"Unsigned Short": volume_type==MI_TYPE_UINT?"Unsigned Int":"Other" ); /*Allocate memory buffers*/ dim= malloc(sizeof(midimhandle_t)*ndim); sizes=malloc(sizeof(misize_t)*ndim); start=malloc(sizeof(misize_t)*ndim); count=malloc(sizeof(misize_t)*ndim); howfar=malloc(sizeof(misize_t)*ndim); origin=malloc(sizeof(double)*ndim); step=malloc(sizeof(double)*ndim); /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_ANY , MI_DIMATTR_ALL, MI_DIMORDER_FILE, ndim, dim ); if ( r <0 ) { TESTRPT("Error getting number of dimensions\n" ,r ); } r = miget_dimension_sizes ( dim, ndim, sizes ); if ( r <0 ) { TESTRPT("Error getting dimension sizes\n" ,r ); } r = miget_dimension_separations(dim,MI_ORDER_FILE,ndim,step); if ( r <0 ) { TESTRPT("Error getting dimension steps\n" ,r ); } printf("\tDimensions:\n"); for(i=0;i=0 && ndim==4 ) /*We have got a vector data*/ { /*Now we are going to work with the volume using apparent dimension order*/ midimhandle_t my_dim[4]; static char *my_dimorder[] = {MIvector_dimension,MIxspace,MIyspace,MIzspace}; misize_t my_sizes[4]; misize_t my_start[4]; misize_t my_count[4]; float *f_coronal,*f_sagittal,*f_axial;/*floating point info*/ float *f_full; double *d_full; short *s_coronal,*s_sagittal,*s_axial;/*short info*/ short *s_full; /**/ printf("Going to set apparent dimension order\n"); r = miset_apparent_dimension_order_by_name ( vol, 4, my_dimorder ); if ( r <0 ) { TESTRPT("Error setting apparent dimension order\n" ,r ); } /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_APPARENT, 4, my_dim ); if ( r <0 ) { TESTRPT("Error in miget_volume_dimensions\n" ,r ); } r = miget_dimension_sizes ( my_dim, 4, my_sizes ); if ( r <0 ) { TESTRPT("Error in miget_dimension_sizes\n" ,r ); } f_full=malloc(sizeof(float)*my_sizes[0]*my_sizes[1]*my_sizes[2]*my_sizes[3]); my_start[0]=my_start[1]=my_start[2]=my_start[3]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; my_count[3]=my_sizes[3]; printf("Reading full volume %dx%dx%dx%d float ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_full )) < 0 ) { TESTRPT ( "Could not get float full volume.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_full,my_count[0]*my_count[1]*my_count[2]*my_count[3])); free(f_full); /* let's extract coronal, axial and sagittal slices*/ printf("Going to read coronal, axial and sagittal slices\n"); /*always extract all three vector components*/ my_start[0]=0; my_count[0]=my_sizes[0]; /*axial, z=const slice*/ my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=0;my_count[2]=my_sizes[2]; my_start[3]=0;my_count[3]=1;/*my_sizes[3]/2*/ f_axial=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Axial slice:%dx%dx%d float... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_axial )) < 0 ) { TESTRPT ( "Could not get float axial hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_axial,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*sagittal, x=const slice*/ my_start[1]=my_sizes[1]/2;my_count[1]=1; my_start[2]=0;my_count[2]=my_sizes[2]; my_start[3]=0;my_count[3]=my_sizes[3]; f_sagittal=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Sagittal slice:%dx%dx%d float... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_sagittal )) < 0 ) { TESTRPT ( "Could not get float sagittal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_sagittal,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*coronal, y=const slice*/ my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=my_sizes[2]/2;my_count[2]=1; my_start[3]=0;my_count[3]=my_sizes[3]; f_coronal=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Coronal slice:%dx%dx%d float... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_coronal )) < 0 ) { TESTRPT ( "Could not get float coronal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_coronal,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*TODO: do something with volumes*/ free(f_axial);free(f_coronal);free(f_sagittal); s_full=malloc(sizeof(short)*my_sizes[0]*my_sizes[1]*my_sizes[2]*my_sizes[3]); my_start[0]=my_start[1]=my_start[2]=my_start[3]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; my_count[3]=my_sizes[3]; printf("Reading full volume %dx%dx%dx%d short ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_full )) < 0 ) { TESTRPT ( "Could not get float full volume.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_full,my_sizes[0]*my_sizes[1]*my_sizes[2]*my_sizes[3])); free(s_full); /*axial, z=const slice*/ my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=0;my_count[2]=my_sizes[2]; my_start[3]=my_sizes[3]/2;my_count[3]=1; s_axial=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Axial slice:%dx%dx%d short... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_axial )) < 0 ) { TESTRPT ( "Could not get short axial hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_axial,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*sagittal, x=const slice*/ my_start[1]=my_sizes[1]/2;my_count[1]=1; my_start[2]=0;my_count[2]=my_sizes[2]; my_start[3]=0;my_count[3]=my_sizes[3]; s_sagittal=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Sagittal slice:%dx%dx%d short... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_sagittal )) < 0 ) { TESTRPT ( "Could not get short sagittal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_sagittal,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*coronal, y=const slice*/ my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=my_sizes[2]/2;my_count[2]=1; my_start[3]=0;my_count[3]=my_sizes[3]; s_coronal=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]*my_count[3]); printf("Reading Coronal slice:%dx%dx%d short... ",(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_coronal )) < 0 ) { TESTRPT ( "Could not get short coronal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_coronal,my_count[0]*my_count[1]*my_count[2]*my_count[3])); /*TODO: do something with volumes*/ free(s_axial);free(s_coronal);free(s_sagittal); d_full=malloc(sizeof(double)*my_sizes[0]*my_sizes[1]*my_sizes[2]*my_sizes[3]); my_start[0]=my_start[1]=my_start[2]=my_start[3]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; my_count[3]=my_sizes[3]; printf("Reading full volume %dx%dx%dx%d double ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2],(int)my_count[3]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_DOUBLE, my_start, my_count, d_full )) < 0 ) { TESTRPT ( "Could not get double full volume.\n",r ); } printf("mean=%f\n",calculate_mean_d(d_full,my_sizes[0]*my_sizes[1]*my_sizes[2]*my_sizes[3])); free(d_full); } else if(ndim==3) { /*Now we are going to work with the volume using apparent dimension order*/ midimhandle_t my_dim[3]; static char *my_dimorder[] = {MIxspace,MIyspace,MIzspace}; misize_t my_sizes[3]; misize_t my_start[3]; misize_t my_count[3]; float *f_coronal,*f_sagittal,*f_axial; float *f_full; short *s_coronal,*s_sagittal,*s_axial; short *s_full; double *d_full; /**/ printf("Going to read coronal, axial and sagittal slices\n"); r = miset_apparent_dimension_order_by_name ( vol, 3, my_dimorder ); if ( r <0 ) { TESTRPT("Error setting apparent dimension order\n" ,r ); } /* let's extract coronal, axial and sagittal slices*/ /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_APPARENT, 3, my_dim ); if ( r <0 ) { TESTRPT("Error in miget_volume_dimensions\n" ,r ); } r = miget_dimension_sizes ( my_dim, 3, my_sizes ); if ( r <0 ) { TESTRPT("Error in miget_dimension_sizes\n" ,r ); } f_full=malloc(sizeof(float)*my_sizes[0]*my_sizes[1]*my_sizes[2]); my_start[0]=my_start[1]=my_start[2]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; printf("Reading full volume %dx%dx%d float ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_full )) < 0 ) { TESTRPT ( "Could not get float full volume.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_full,my_sizes[0]*my_sizes[1]*my_sizes[2])); free(f_full); /*axial, z=const slice*/ my_start[0]=0;my_count[0]=my_sizes[0]; my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=0;my_count[2]=1; f_axial=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]); printf("Reading Axial slice:%dx%dx%d float... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_axial )) < 0 ) { TESTRPT ( "Could not get float axial hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_axial,my_count[0]*my_count[1]*my_count[2])); /*sagittal, x=const slice*/ my_start[0]=my_sizes[0]/2;my_count[0]=1; my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=0;my_count[2]=my_sizes[2]; f_sagittal=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]); printf("Reading Sagittal slice:%dx%dx%d float... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_sagittal )) < 0 ) { TESTRPT ( "Could not get float sagittal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_sagittal,my_count[0]*my_count[1]*my_count[2])); /*coronal, y=const slice*/ my_start[0]=0;my_count[0]=my_sizes[0]; my_start[1]=my_sizes[1]/2;my_count[1]=1; my_start[2]=0;my_count[2]=my_sizes[2]; f_coronal=malloc(sizeof(float)*my_count[0]*my_count[1]*my_count[2]); printf("Reading Coronal slice:%dx%dx%d float... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, f_coronal )) < 0 ) { TESTRPT ( "Could not get float coronal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_f(f_coronal,my_count[0]*my_count[1]*my_count[2])); /*TODO: do something with volumes*/ free(f_axial);free(f_coronal);free(f_sagittal); /*axial, z=const slice*/ my_start[0]=0;my_count[0]=my_sizes[0]; my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=my_sizes[2]/2;my_count[2]=1; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; s_axial=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]); printf("Reading full volume %dx%dx%d short ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); s_full=malloc(sizeof(short)*my_sizes[0]*my_sizes[1]*my_sizes[2]); my_start[0]=my_start[1]=my_start[2]=0; if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_full )) < 0 ) { TESTRPT ( "Could not get float short volume.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_full,my_sizes[0]*my_sizes[1]*my_sizes[2])); free(s_full); printf("Reading Axial slice:%dx%dx%d short... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_axial )) < 0 ) { TESTRPT ( "Could not get short axial hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_axial,my_count[0]*my_count[1]*my_count[2])); /*sagittal, x=const slice*/ my_start[1]=my_sizes[0]/2;my_count[0]=1; my_start[1]=0;my_count[1]=my_sizes[1]; my_start[2]=0;my_count[2]=my_sizes[2]; s_sagittal=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]); printf("Reading Sagittal slice:%dx%dx%d short... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_sagittal )) < 0 ) { TESTRPT ( "Could not get short sagittal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_sagittal,my_count[0]*my_count[1]*my_count[2])); /*coronal, y=const slice*/ my_start[0]=0;my_count[0]=my_sizes[0]; my_start[1]=my_sizes[1]/2;my_count[1]=1; my_start[2]=0;my_count[2]=my_sizes[2]; s_coronal=malloc(sizeof(short)*my_count[0]*my_count[1]*my_count[2]); printf("Reading Coronal slice:%dx%dx%d short... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_SHORT, my_start, my_count, s_coronal )) < 0 ) { TESTRPT ( "Could not get short coronal hyperslab.\n",r ); } printf("mean=%f\n",calculate_mean_s(s_coronal,my_count[0]*my_count[1]*my_count[2])); /*TODO: do something with volumes*/ free(s_axial);free(s_coronal);free(s_sagittal); d_full=malloc(sizeof(double)*my_sizes[0]*my_sizes[1]*my_sizes[2]); my_start[0]=my_start[1]=my_start[2]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; printf("Reading full volume %dx%dx%d double ... ",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_DOUBLE, my_start, my_count, d_full )) < 0 ) { TESTRPT ( "Could not get float full volume.\n",r ); } printf("mean=%f\n",calculate_mean_d(d_full,my_sizes[0]*my_sizes[1]*my_sizes[2])); free(d_full); } else { fprintf(stderr,"Sorry, currently I can only process 4D or 3D volumes\n"); } /* close volume*/ miclose_volume ( vol ); /*free(buffer);*/ free(count);free(howfar);free(start);free(sizes);free(origin);free(step); if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "\n No errors\n" ); } return ( error_cnt ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/testdir/minc2-record-test.c000066400000000000000000000070111257462267400217230ustar00rootroot00000000000000#include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CX 10 #define CY 10 #define CZ 6 #define NDIMS 3 int main(void) { mihandle_t hvol; char *name; int result; midimhandle_t hdim[NDIMS]; misize_t coords[NDIMS]; misize_t count[NDIMS]; int i,j,k; struct test { int r; int g; int b; } voxel; /* Write data one voxel at a time. */ for (i = 0; i < NDIMS; i++) { count[i] = 1; } result = micreate_dimension("xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[0]); result = micreate_dimension("yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1]); result = micreate_dimension("zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[2]); result = micreate_volume("tst-rec.mnc", NDIMS, hdim, MI_TYPE_UINT, MI_CLASS_UNIFORM_RECORD, NULL, &hvol); if (result < 0) { TESTRPT("Unable to create test file", result); } result = miset_record_field_name(hvol, 0, "Red"); if (result < 0) { TESTRPT("miset_record_field_name", result); } miset_record_field_name(hvol, 1, "Green"); miset_record_field_name(hvol, 2, "Blue"); miget_record_field_name(hvol, 1, &name); if (strcmp(name, "Green") != 0) { TESTRPT("Unexpected label for value 1", 0); } mifree_name(name); miget_record_field_name(hvol, 0, &name); if (strcmp(name, "Red") != 0) { TESTRPT("Unexpected label for value 0", 0); } mifree_name(name); miget_record_field_name(hvol, 2, &name); if (strcmp(name, "Blue") != 0) { TESTRPT("Unexpected label for value 2", 0); } mifree_name(name); result = micreate_volume_image(hvol); if (result < 0) { TESTRPT("micreate_volume_image failed", result); } for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; voxel.r = i; voxel.g = j; voxel.b = k; result = miset_voxel_value_hyperslab(hvol, MI_TYPE_UNKNOWN, coords, count, &voxel); if (result < 0) { TESTRPT("Error writing voxel", result); } } } } for (i = 0; i < CX; i++) { for (j = 0; j < CY; j++) { for (k = 0; k < CZ; k++) { coords[0] = i; coords[1] = j; coords[2] = k; result = miget_voxel_value_hyperslab(hvol, MI_TYPE_UNKNOWN, coords, count, &voxel); if (result < 0) { TESTRPT("Error reading voxel", result); } if (voxel.r != i || voxel.g != j || voxel.b != k) { TESTRPT("Data mismatch", 0); } } } } miclose_volume(hvol); if (error_cnt != 0) { fprintf(stderr, "%d error%s reported\n", error_cnt, (error_cnt == 1) ? "" : "s"); } else { fprintf(stderr, "No errors\n"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-slice-test.c000066400000000000000000000016131257462267400215460ustar00rootroot00000000000000#include #include #include "minc2.h" int main ( int argc, char **argv ) { mihandle_t hvol; int r; misize_t coords[3]; double min, max; int i; while ( --argc > 0 ) { r = miopen_volume ( *++argv, MI2_OPEN_READ, &hvol ); if ( r < 0 ) { fprintf ( stderr, "can't open %s, error %d\n", *argv, r ); } else { for ( i = 0; i < 10; i++ ) { coords[0] = i; coords[1] = rand(); coords[2] = rand(); r = miget_slice_min ( hvol, coords, 3, &min ); if ( r < 0 ) { fprintf ( stderr, "error %d getting slice minimum\n", r ); } r = miget_slice_max ( hvol, coords, 3, &max ); if ( r < 0 ) { fprintf ( stderr, "error %d getting slice maximum\n", r ); } printf ( "%d. min %f max %f\n", i, min, max ); } miclose_volume ( hvol ); } } return ( 0 ); } libminc-libminc-2-3-00/testdir/minc2-testminctools.sh000077500000000000000000000015371257462267400226010ustar00rootroot00000000000000#! /bin/sh # # Test minc tools on minc2 file. set -e ../../mincconvert ../../volume_io/Testing/t3_grid_0.mnc t3_grid_0_2.mnc -2 -clobber && echo "Converted file to MINC2.0 format." || exit 1 ../../mincinfo -minc_version -image_info t3_grid_0_2.mnc ../../mincmath -const 1 -mult t3_grid_0_2.mnc t32.mnc -clobber || exit 1 echo "Statistics on image" ../../mincstats -mean -std t32.mnc || exit 1 echo "Adding const 2 to all three channels." ../../mincmath -const 2 -add t32.mnc t32_added.mnc -clobber || exit 1 echo "Statistics on image with added constant" ../../mincstats -mean -std t32_added.mnc || exit 1 echo "Resampling file with t1.xfm transform" ../../mincresample -transform ../../volume_io/Testing/t1.xfm -tfm_input_sampling t32_added.mnc t32_transformed.mnc -clobber || exit 1 echo "Transformed file" ../../mincinfo t32_transformed.mnc exit 0 libminc-libminc-2-3-00/testdir/minc2-valid-test.c000066400000000000000000000035731257462267400215550ustar00rootroot00000000000000#include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; int main(int argc, char **argv) { mihandle_t hvol; int r; double min, max; double orig_min, orig_max; while (--argc > 0) { r = miopen_volume(*++argv, MI2_OPEN_RDWR, &hvol); if (r < 0) { TESTRPT("can't open input", r); continue; } r = miget_volume_valid_min(hvol, &min); if (r < 0) { TESTRPT("error getting valid minimum", r); } r = miget_volume_valid_max(hvol, &max); if (r < 0) { TESTRPT("error getting valid maximum", r); } r = miget_volume_valid_range(hvol, &max, &min); if (r < 0) { TESTRPT("error getting valid range", r); } printf("min %f max %f\n", min, max); if (min > max) { TESTRPT("error - min exceeds max!", 0); } orig_min = min; orig_max = max; /* Try some arbitrary manipulations */ max = orig_max + 100; min = orig_min - 100; r = miset_volume_valid_range(hvol, max, min); if (r < 0) { TESTRPT("error setting new volume range", 0); } r = miget_volume_valid_min(hvol, &min); if (r != 0 || min != orig_min - 100) { TESTRPT("error changing volume minimum", 0); } r = miget_volume_valid_max(hvol, &max); if (max != orig_max + 100) { TESTRPT("error changing volume maximum", 0); } r = miset_volume_valid_range(hvol, orig_max, orig_min); if (r < 0) { TESTRPT("error restoring volume range", r); } miclose_volume(hvol); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc2-vector_dimension-test.c000066400000000000000000000115201257462267400240140ustar00rootroot00000000000000#include #include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; #define CZ 41 #define CY 410 #define CX 530 #define NDIMS 4 static void create_test_file ( void ) { midimhandle_t hdim[NDIMS]; mihandle_t hvol; unsigned char *buf = malloc ( CZ * CX * CY * 3 ); int i; misize_t count[NDIMS]; misize_t start[NDIMS]; micreate_dimension ( "zspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CZ, &hdim[0] ); micreate_dimension ( "yspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CY, &hdim[1] ); micreate_dimension ( "xspace", MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, CX, &hdim[2] ); micreate_dimension ( "vector_dimension", MI_DIMCLASS_RECORD, MI_DIMATTR_REGULARLY_SAMPLED, 3, &hdim[3] ); micreate_volume ( "example_vector2.mnc", NDIMS, hdim, MI_TYPE_BYTE, MI_CLASS_INT, NULL, &hvol ); micreate_volume_image ( hvol ); for ( i = 0; i < CZ * CY * CX * 3; i++ ) { buf[i] = ( unsigned char ) i; } start[0] = start[1] = start[2] = start[3] = 0; count[0] = CZ; count[1] = CY; count[2] = CX; count[3] = 3; miset_voxel_value_hyperslab ( hvol, MI_TYPE_BYTE, start, count, buf ); miclose_volume ( hvol ); } int main ( void ) { mihandle_t vol; int r = 0; midimhandle_t dim[NDIMS]; misize_t lengths[NDIMS]; midimhandle_t copy_dim[NDIMS]; misize_t coords[NDIMS]; misize_t count[NDIMS]; int i, j; unsigned char * Atmp; midimclass_t dimension_class; int ndims; Atmp = ( unsigned char * ) malloc ( CX * CY * CZ * sizeof ( unsigned char ) ); create_test_file(); printf ( " \n" ); printf ( "Opening vector-dimension file!\n" ); printf ( " \n" ); r = miopen_volume ( "example_vector2.mnc", MI2_OPEN_READ, &vol ); if ( r < 0 ) { TESTRPT ( "failed to open vector_dimension volume", r ); } r = miget_volume_dimension_count ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_REGULARLY_SAMPLED, &ndims ); if ( r < 0 ) { TESTRPT ( "failed to get number of dimensions", r ); } printf ( "Total number of dimensions : %d \n", ndims ); r = miget_volume_dimensions ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_REGULARLY_SAMPLED, MI_DIMORDER_FILE, NDIMS, dim ); if ( r < 0 ) { TESTRPT ( "Could not get dimension handles from volume", r ); } r = miget_dimension_sizes ( dim, NDIMS, lengths ); if ( r < 0 ) { TESTRPT ( " more trouble", r ); } printf ( "Dimension Size in file order : " ); for ( i = 0; i < NDIMS; i++ ) { printf ( " %lld ", lengths[i] ); } printf ( " \n" ); for ( i = 0; i < NDIMS; i++ ) { r = miget_dimension_class ( dim[i], &dimension_class ); if ( r < 0 ) { TESTRPT ( "failed to get dimension class", r ); } if ( dimension_class == MI_DIMCLASS_RECORD ) { printf ( "Dim class RECORD present check dim name for *vector_dimension*\n" ); } } printf ( "Let's get the first 10 data values of each vector component (file order) \n" ); coords[0] = coords[1] = coords[2] = 0; count[0] = CZ; count[1] = CY; count[2] = CX; count[3] = 1; printf ( " FILE ORDER --> zspace, yspace, xspace, vector_dimension \n" ); for ( i = 0; i < 3; i++ ) { printf ( "Vector Componenet %d \n", i + 1 ); coords[3] = i; r = miget_voxel_value_hyperslab ( vol, MI_TYPE_UBYTE, coords, count, Atmp ); if ( r < 0 ) { TESTRPT ( "Failed to operate hyperslab function", r ); } for ( j = 0; j < 10; j++ ) { printf ( " %u ", Atmp[j] ); } printf ( " \n" ); } printf ( "APPARENT ORDER --> vector_dimension, zspace, yspace, xspace\n" ); // Set the apparent dimension order copy_dim[0] = dim[3]; copy_dim[1] = dim[0]; copy_dim[2] = dim[1]; copy_dim[3] = dim[2]; r = miset_apparent_dimension_order ( vol, NDIMS, copy_dim ); if ( r < 0 ) { TESTRPT ( "failed to set apparent order", r ); } coords[1] = coords[2] = coords[3] = 0; count[0] = 1; //must always be one count[1] = CZ; count[2] = CY; count[3] = CZ; printf ( "APPARENT ORDER SET \n" ); for ( i = 0; i < 3; i++ ) { printf ( "Vector Componenet %d \n", i + 1 ); coords[0] = i; r = miget_voxel_value_hyperslab ( vol, MI_TYPE_UBYTE, coords, count, Atmp ); if ( r < 0 ) { TESTRPT ( "Failed to operate hyperslab function", r ); } for ( j = 0; j < 10; j++ ) { printf ( " %u ", Atmp[j] ); } printf ( " \n" ); } if ( error_cnt != 0 ) { fprintf ( stderr, "%d error%s reported\n", error_cnt, ( error_cnt == 1 ) ? "" : "s" ); } else { fprintf ( stderr, "No errors\n" ); } return ( error_cnt ); } // kate: indent-mode cstyle; indent-width 2; replace-tabs on; libminc-libminc-2-3-00/testdir/minc2-volprops-test.c000066400000000000000000000045741257462267400223440ustar00rootroot00000000000000#include #include "minc2.h" #define TESTRPT(msg, val) (error_cnt++, fprintf(stderr, \ "Error reported on line #%d, %s: %d\n", \ __LINE__, msg, val)) static int error_cnt = 0; int main(int argc, char **argv) { mihandle_t vol; mivolumeprops_t props; int r; micompression_t compression_type; miboolean_t enable_flag; int zlib_level; int depth; int edge_lengths[MI2_MAX_VAR_DIMS]; int edge_count; int i; r = minew_volume_props(&props); if (r < 0) { TESTRPT("failed", r); } r = miset_props_multi_resolution(props, 1 , 2); if (r < 0) { TESTRPT("failed", r); } r = miget_props_multi_resolution(props, &enable_flag, &depth); if (r < 0) { TESTRPT("failed", r); } else { printf("Multiresolution enabled: %d depth: %d \n", enable_flag, depth); } r = miset_props_compression_type(props, MI_COMPRESS_NONE); if (r < 0) { TESTRPT("failed", r); } else { printf("Set compression type to %d\n", MI_COMPRESS_NONE); } r = miget_props_compression_type(props,&compression_type); if (r < 0 || compression_type != MI_COMPRESS_NONE) { TESTRPT("failed", r); } else { printf("Got compression type %d \n", compression_type); } r = miset_props_zlib_compression(props,4); if (r < 0) { TESTRPT("failed", r); } else { printf("Set zlib level to %d\n", 4); } r = miget_props_zlib_compression(props,&zlib_level); if (r < 0 || zlib_level != 4) { TESTRPT("failed", r); } else { printf("Got zlib level %d \n", zlib_level); } mifree_volume_props(props); while (--argc > 0) { r = miopen_volume(*++argv, MI2_OPEN_RDWR, &vol); if (r < 0) { TESTRPT("failed", r); } r = miget_volume_props(vol, &props); if (r < 0) { TESTRPT("failed", r); } r = miget_props_blocking(props, &edge_count, edge_lengths, MI2_MAX_VAR_DIMS); if (r < 0) { TESTRPT("failed", r); } printf("edge_count %d\n", edge_count); for (i = 0; i < edge_count; i++) { printf(" %d", edge_lengths[i]); } printf("\n"); mifree_volume_props(props); miclose_volume(vol); } if (error_cnt != 0) { fprintf(stderr, "%d error%s reported\n", error_cnt, (error_cnt == 1) ? "" : "s"); } else { fprintf(stderr, "No errors\n"); } return (error_cnt); } libminc-libminc-2-3-00/testdir/minc_conversion.c000066400000000000000000000221031257462267400216540ustar00rootroot00000000000000#define _GNU_SOURCE 1 #include "config.h" #include #include #include #include #include #include #if HAVE_UNISTD_H #include #endif #include #define FUNC_ERROR(x) (fprintf(stderr, "On line %d, function %s failed unexpectedly\n", __LINE__, x), ++errors) #define TST_X 0 #define TST_Y 1 #define TST_Z 2 static long errors = 0; extern void icv_tests(void); #define XSIZE 20 #define YSIZE 30 #define ZSIZE 40 #define YBOOST 10 #define ZBOOST 20 static struct dimdef { char * name; int length; } dimtab1[3] = { { MIxspace, XSIZE }, { MIyspace, YSIZE }, { MIzspace, ZSIZE } }; struct testinfo { char *name; int fd; int maxid; int minid; int imgid; int dim[3]; int test_group; int test_attribute; }; /* Test case 1 - file creation & definition. */ static int test1(struct testinfo *ip, struct dimdef *dims, int ndims) { int varid; int stat; int i; /* Test case #1 - file creation */ ip->name = micreate_tempfile(); if (ip->name == NULL) { FUNC_ERROR("micreate_tempfile\n"); } ip->fd = micreate(ip->name, NC_CLOBBER|MI2_CREATE_V1); /*Create MINC1 format*/ if (ip->fd < 0) { FUNC_ERROR("micreate"); } /* Have to use ncdimdef() here since there is no MINC equivalent. Sigh. */ for (i = 0; i < ndims; i++) { /* Define the dimension */ ip->dim[i] = ncdimdef(ip->fd, dims[i].name, dims[i].length); if (ip->dim[i] < 0) { FUNC_ERROR("ncdimdef"); } /* Create the dimension variable. */ varid = micreate_std_variable(ip->fd, dims[i].name, NC_DOUBLE, 0, &ip->dim[i]); if (varid < 0) { FUNC_ERROR("micreate_std_variable"); } stat = miattputdbl(ip->fd, varid, MIstep, 0.8); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, varid, MIstart, 22.0); if (stat < 0) { FUNC_ERROR("miattputdbl"); } } /* Create the image-max variable. */ ip->maxid = micreate_std_variable(ip->fd, (char*)MIimagemax, NC_DOUBLE, 0, NULL); if (ip->maxid < 0) { FUNC_ERROR("micreate_std_variable"); } /* Create the image-min variable. */ ip->minid = micreate_std_variable(ip->fd, (char*)MIimagemin, NC_DOUBLE, 0, NULL); if (ip->minid < 0) { FUNC_ERROR("micreate_std_variable"); } ip->imgid = micreate_std_variable(ip->fd, (char*)MIimage, NC_FLOAT, ndims, ip->dim); if (ip->imgid < 0) { FUNC_ERROR("micreate_std_variable"); } return (0); } static int test2(struct testinfo *ip, struct dimdef *dims, int ndims) { int i, j, k; int stat; long coords[3]; double flt; stat = miattputdbl(ip->fd, ip->imgid, MIvalid_max, (XSIZE * 10000.0)); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, ip->imgid, MIvalid_min, 0.0); if (stat < 0) { FUNC_ERROR("miattputdbl"); } ncendef(ip->fd); /* End definition mode. */ coords[0] = 0; flt = 0.0; stat = mivarput1(ip->fd, ip->minid, coords, NC_DOUBLE, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } flt = XSIZE * 10000.0; stat = mivarput1(ip->fd, ip->maxid, coords, NC_DOUBLE, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { float tmp = (i * 10000.0f) + (j * 100.0f) + k; coords[TST_X] = i; coords[TST_Y] = j; coords[TST_Z] = k; stat = mivarput1(ip->fd, ip->imgid, coords, NC_FLOAT, MI_SIGNED, &tmp); if (stat < 0) { fprintf(stderr, "At (%d,%d,%d), status %d: ", i,j,k,stat); FUNC_ERROR("mivarput1"); } } } } return (0); } static int test3(struct testinfo *ip, struct dimdef *dims, int ndims) { /* Get the same variable again, but this time use an ICV to scale it. */ size_t total; long coords[3]; long lengths[3]; double range[2]; void *buf_ptr; float *flt_ptr; int i, j, k; int stat; int icv; total = 1; for (i = 0; i < ndims; i++) { total *= dims[i].length; } buf_ptr = malloc(total * sizeof (float)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length; lengths[TST_Z] = dims[TST_Z].length; icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } stat = miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_NORM, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_USER_NORM, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_attach(icv, ip->fd, ip->imgid); if (stat < 0) { FUNC_ERROR("miicv_attach"); } stat = miicv_get(icv, coords, lengths, buf_ptr); if (stat < 0) { FUNC_ERROR("miicv_get"); } stat = miget_image_range(ip->fd, range); if (stat < 0) { FUNC_ERROR("miget_image_range"); } if (range[0] != 0 || range[1] != (XSIZE * 10000.0)) { fprintf(stderr, "miget_image_range: bad result\n"); errors++; } stat = miget_valid_range(ip->fd, ip->imgid, range); if (stat < 0) { FUNC_ERROR("miget_valid_range"); } if (range[0] != 0 || range[1] != (XSIZE * 10000.0)) { fprintf(stderr, "miget_valid_range: bad result\n"); errors++; } flt_ptr = (float *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { float tmp = (i * 10000.0f) + (j * 100.0f) + k; if (*flt_ptr != tmp ) { fprintf(stderr, "1. Data error at (%d,%d,%d) %f != %f\n", i,j,k, *flt_ptr, tmp); errors++; } flt_ptr++; } } } stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } stat = miicv_free(icv); if (stat < 0) { FUNC_ERROR("miicv_free"); } free(buf_ptr); return (0); } static void test4(struct testinfo *ip, struct dimdef *dims, int ndims) { mihandle_t vol; int ndim; int r; /*Now we are going to work with the volume using apparent dimension order*/ midimhandle_t my_dim[3]; static char *my_dimorder[] = {MIxspace,MIyspace,MIzspace}; misize_t my_sizes[3]; misize_t my_start[3]; misize_t my_count[3]; miclass_t volume_class; mitype_t volume_type; misize_t i,j,k; float *buffer; float *flt_ptr; /**/ if( miopen_volume ( ip->name, MI2_OPEN_READ, &vol )<0) FUNC_ERROR("miopen_volume"); if(miget_volume_dimension_count(vol,MI_DIMCLASS_ANY,MI_DIMATTR_ALL,&ndim)<0) FUNC_ERROR("miget_volume_dimension_count"); if(ndim!=3) FUNC_ERROR("incorrect dimenions count"); r=miget_data_class(vol, &volume_class); if ( r < 0 ) { FUNC_ERROR ( "failed to get volume class" ); } r=miget_data_type(vol, &volume_type); if ( r < 0 ) { FUNC_ERROR ( "failed to get volume type" ); } if(miset_apparent_dimension_order_by_name ( vol, 3, my_dimorder )<0) FUNC_ERROR("miset_apparent_dimension_order_by_name"); /* get the apparent dimensions and their sizes */ r = miget_volume_dimensions ( vol, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_APPARENT, 3, my_dim ); if ( r <0 ) { FUNC_ERROR("Error in miget_volume_dimensions\n" ); } r = miget_dimension_sizes ( my_dim, 3, my_sizes ); if ( r <0 ) { FUNC_ERROR("Error in miget_dimension_sizes\n" ); } buffer=flt_ptr=malloc(sizeof(float)*my_sizes[0]*my_sizes[1]*my_sizes[2]); my_start[0]=my_start[1]=my_start[2]=0; my_count[0]=my_sizes[0]; my_count[1]=my_sizes[1]; my_count[2]=my_sizes[2]; printf("Reading full volume %dx%dx%d float ... \n",(int)my_count[0],(int)my_count[1],(int)my_count[2]); if ( (r=miget_real_value_hyperslab ( vol, MI_TYPE_FLOAT, my_start, my_count, flt_ptr )) < 0 ) { FUNC_ERROR ( "Could not get float full volume." ); } for (i = 0; i < my_count[0]; i++) { for (j = 0; j < my_count[1]; j++) { for (k = 0; k < my_count[2]; k++) { float tmp = (i * 10000) + (j * 100) + k; if (*flt_ptr != (float) tmp ) { fprintf(stderr, "2. Data error at (%llu,%llu,%llu) %f != %f\n", i,j,k, *flt_ptr, tmp); errors++; } flt_ptr++; } } } free(buffer); /* close volume*/ miclose_volume ( vol ); } /* Test MINC API's */ int main(int argc, char **argv) { struct testinfo info; test1(&info, dimtab1, 3); test2(&info, dimtab1, 3); test3(&info, dimtab1, 3); if (miclose(info.fd) != MI_NOERROR) { FUNC_ERROR("miclose"); } /*now let's use MINC2 API*/ test4(&info, dimtab1, 3); /*unlink(info.name);*/ /* Delete the temporary file. */ free(info.name); /* Free the temporary filename */ if(!errors) printf("No errors detected!\n"); else printf("Errors: %ld!\n",errors); return (errors); } libminc-libminc-2-3-00/testdir/minc_long_attr.c000066400000000000000000000146341257462267400214720ustar00rootroot00000000000000#define _GNU_SOURCE 1 #include "config.h" #include #include #include #include #include #if HAVE_UNISTD_H #include #endif #include #define FUNC_ERROR(x) (fprintf(stderr, "On line %d, function %s failed unexpectedly\n", __LINE__, x), ++errors) #define TST_X 0 #define TST_Y 1 #define TST_Z 2 static long errors = 0; extern void icv_tests(void); #define XSIZE 20 #define YSIZE 30 #define ZSIZE 40 #define YBOOST 10 #define ZBOOST 20 static struct dimdef { char * name; int length; } dimtab1[3] = { { MIxspace, XSIZE }, { MIyspace, YSIZE }, { MIzspace, ZSIZE } }; struct testinfo { char *name; int fd; int maxid; int minid; int imgid; int dim[3]; int test_group; int test_attribute; char *large_attribute; int attribute_size; }; /* Test case 1 - file creation & definition. */ static int test1(struct testinfo *ip, struct dimdef *dims, int ndims) { int varid; int stat; int i; /* Test case #1 - file creation */ ip->name = micreate_tempfile(); if (ip->name == NULL) { FUNC_ERROR("micreate_tempfile\n"); } ip->fd = micreate(ip->name, NC_CLOBBER); if (ip->fd < 0) { FUNC_ERROR("micreate"); } /* Have to use ncdimdef() here since there is no MINC equivalent. Sigh. */ for (i = 0; i < ndims; i++) { /* Define the dimension */ ip->dim[i] = ncdimdef(ip->fd, dims[i].name, dims[i].length); if (ip->dim[i] < 0) { FUNC_ERROR("ncdimdef"); } /* Create the dimension variable. */ varid = micreate_std_variable(ip->fd, dims[i].name, NC_DOUBLE, 0, &ip->dim[i]); if (varid < 0) { FUNC_ERROR("micreate_std_variable"); } stat = miattputdbl(ip->fd, varid, MIstep, 0.8); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, varid, MIstart, 22.0); if (stat < 0) { FUNC_ERROR("miattputdbl"); } } /* Create the image-max variable. */ ip->maxid = micreate_std_variable(ip->fd, MIimagemax, NC_FLOAT, 0, NULL); if (ip->maxid < 0) { FUNC_ERROR("micreate_std_variable"); } /* Create the image-min variable. */ ip->minid = micreate_std_variable(ip->fd, MIimagemin, NC_FLOAT, 0, NULL); if (ip->minid < 0) { FUNC_ERROR("micreate_std_variable"); } ip->imgid = micreate_std_variable(ip->fd, MIimage, NC_INT, ndims, ip->dim); if (ip->imgid < 0) { FUNC_ERROR("micreate_std_variable"); } ip->test_group = ncvardef(ip->fd,(char*)"test",NC_INT,0,0);/* micreate_group_variable(ip->fd,(char*)"test");*/ if(ip->test_group<0) { FUNC_ERROR("micreate_group_variable"); } ip->large_attribute=calloc(ip->attribute_size,sizeof(char)); memset(ip->large_attribute,'X',ip->attribute_size-1); ip->test_attribute = ncattput(ip->fd, ip->test_group, "test", NC_CHAR, ip->attribute_size, ip->large_attribute); return (0); } static int test2(struct testinfo *ip, struct dimdef *dims, int ndims) { int i, j, k; int stat; long coords[3]; float flt; stat = miattputdbl(ip->fd, ip->imgid, MIvalid_max, (XSIZE * 10000.0)); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, ip->imgid, MIvalid_min, -(XSIZE * 10000.0)); if (stat < 0) { FUNC_ERROR("miattputdbl"); } ncendef(ip->fd); /* End definition mode. */ coords[0] = 0; flt = -(XSIZE * 100000.0); stat = mivarput1(ip->fd, ip->minid, coords, NC_FLOAT, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } flt = XSIZE * 100000.0; stat = mivarput1(ip->fd, ip->maxid, coords, NC_FLOAT, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { int tmp = (i * 10000) + (j * 100) + k; coords[TST_X] = i; coords[TST_Y] = j; coords[TST_Z] = k; stat = mivarput1(ip->fd, ip->imgid, coords, NC_INT, MI_SIGNED, &tmp); if (stat < 0) { fprintf(stderr, "At (%d,%d,%d), status %d: ", i,j,k,stat); FUNC_ERROR("mivarput1"); } } } } return (0); } static int test3(struct testinfo *ip, struct dimdef *dims, int ndims) { /* Try to read the data back. */ size_t total; long coords[3]; long lengths[3]; void *buf_ptr; int *int_ptr; int i, j, k; int stat; int varid; int att_length; nc_type att_datatype; char *att; total = 1; for (i = 0; i < ndims; i++) { total *= dims[i].length; } buf_ptr = malloc(total * sizeof (int)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length; lengths[TST_Z] = dims[TST_Z].length; stat = mivarget(ip->fd, ip->imgid, coords, lengths, NC_INT, MI_SIGNED, buf_ptr); if (stat < 0) { FUNC_ERROR("mivarget"); } int_ptr = (int *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { int tmp = (i * 10000) + (j * 100) + k; if (*int_ptr != tmp) { fprintf(stderr, "1. Data error at (%d,%d,%d)\n", i,j,k); errors++; } int_ptr++; } } } free(buf_ptr); varid = ncvarid(ip->fd, "test"); if(varid<0) FUNC_ERROR("ncvarid"); if ((ncattinq(ip->fd, varid, "test", &att_datatype, &att_length) == MI_ERROR) || (att_datatype != NC_CHAR)) FUNC_ERROR("ncattinq"); if(att_length!=ip->attribute_size) FUNC_ERROR("Wrong attribute length!"); att=malloc(att_length); if(miattgetstr(ip->fd, varid, "test", att_length, att)==NULL) FUNC_ERROR("miattgetstr"); if(memcmp(att,ip->large_attribute,att_length)!=0) FUNC_ERROR("Attribute contents mismath!"); free(att); return (0); } /* Test MINC API's */ int main(int argc, char **argv) { struct testinfo info; if(argc>1) info.attribute_size=atoi(argv[1]); else info.attribute_size=100000; printf("Running test with attribute size=%d\n",info.attribute_size); test1(&info, dimtab1, 3); test2(&info, dimtab1, 3); test3(&info, dimtab1, 3); if (miclose(info.fd) != MI_NOERROR) { FUNC_ERROR("miclose"); } unlink(info.name); /* Delete the temporary file. */ free(info.name); /* Free the temporary filename */ return (errors); } libminc-libminc-2-3-00/testdir/minc_types.c000066400000000000000000000065371257462267400206500ustar00rootroot00000000000000/* ----------------------------- MNI Header ----------------------------------- @NAME : test @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : @MODIFIED : ---------------------------------------------------------------------------- */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STRING_H #include #endif static struct { nc_type type; char *sign; char *ctype; } types[]= { { NC_BYTE, MI_UNSIGNED, "byte" }, { NC_BYTE, MI_SIGNED, "byte" }, { NC_SHORT, MI_UNSIGNED, "short" }, { NC_SHORT, MI_SIGNED, "short" }, { NC_INT, MI_UNSIGNED, "int" }, { NC_INT, MI_SIGNED, "int" }, { NC_FLOAT, MI_SIGNED, "float" }, { NC_DOUBLE, MI_SIGNED, "double" } }; static const int ntypes = sizeof(types)/sizeof(types[0]); int main(int argc, char **argv) { int cdf; int img, img2; int dim[MAX_VAR_DIMS]; long start[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; double image[256*256]; int i, itype, jtype; int cflag = 0; char filename[256]; #if MINC2 if (argc == 2 && !strcmp(argv[1], "-2")) { cflag = MI2_CREATE_V2; } #endif /* MINC2 */ snprintf(filename, sizeof(filename), "test_minc_types-%d.mnc", getpid()); ncopts=NC_VERBOSE|NC_FATAL; for (itype=0; itype #include #include #include #include #if HAVE_UNISTD_H #include #endif #define FUNC_ERROR(x) (fprintf(stderr, "On line %d, function %s failed unexpectedly\n", __LINE__, x), ++errors) #define TST_X 0 #define TST_Y 1 #define TST_Z 2 static long errors = 0; extern void icv_tests(void); #define XSIZE 20 #define YSIZE 30 #define ZSIZE 40 #define YBOOST 10 #define ZBOOST 20 static struct dimdef { char * name; int length; } dimtab1[3] = { { MIxspace, XSIZE }, { MIyspace, YSIZE }, { MIzspace, ZSIZE } }; struct testinfo { char *name; int fd; int maxid; int minid; int imgid; int dim[3]; }; /* Test case 1 - file creation & definition. */ static int test1(struct testinfo *ip, struct dimdef *dims, int ndims) { int fd2; int varid; int stat; int i; /* Test case #1 - file creation */ ip->name = micreate_tempfile(); if (ip->name == NULL) { FUNC_ERROR("micreate_tempfile\n"); } ip->fd = micreate(ip->name, NC_CLOBBER); if (ip->fd < 0) { FUNC_ERROR("micreate"); } /* Try to create another file of the same name - should fail. */ fd2 = micreate(ip->name, NC_NOCLOBBER); if (fd2 >= 0) { FUNC_ERROR("micreate"); } /* Try to open the file for write - should fail. */ /* VF: it doesn't fail! fd2 = miopen(ip->name, NC_WRITE); if (fd2 >= 0) { FUNC_ERROR("miopen"); } */ /* Have to use ncdimdef() here since there is no MINC equivalent. Sigh. */ for (i = 0; i < ndims; i++) { /* Define the dimension */ ip->dim[i] = ncdimdef(ip->fd, dims[i].name, dims[i].length); if (ip->dim[i] < 0) { FUNC_ERROR("ncdimdef"); } /* Create the dimension variable. */ varid = micreate_std_variable(ip->fd, dims[i].name, NC_DOUBLE, 0, &ip->dim[i]); if (varid < 0) { FUNC_ERROR("micreate_std_variable"); } stat = miattputdbl(ip->fd, varid, MIstep, 0.8); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, varid, MIstart, 22.0); if (stat < 0) { FUNC_ERROR("miattputdbl"); } } /* Try to create a bogus variable. This should trigger an error. */ varid = micreate_std_variable(ip->fd, "xyzzy", NC_DOUBLE, 0, NULL); if (varid >= 0) { FUNC_ERROR("micreate_std_variable"); } /* Create the image-max variable. */ ip->maxid = micreate_std_variable(ip->fd, MIimagemax, NC_FLOAT, 0, NULL); if (ip->maxid < 0) { FUNC_ERROR("micreate_std_variable"); } /* Create the image-min variable. */ ip->minid = micreate_std_variable(ip->fd, MIimagemin, NC_FLOAT, 0, NULL); if (ip->minid < 0) { FUNC_ERROR("micreate_std_variable"); } ip->imgid = micreate_std_variable(ip->fd, MIimage, NC_INT, ndims, ip->dim); if (ip->imgid < 0) { FUNC_ERROR("micreate_std_variable"); } return (0); } static int test2(struct testinfo *ip, struct dimdef *dims, int ndims) { int i, j, k; int stat; long coords[3]; float flt; stat = miattputdbl(ip->fd, ip->imgid, MIvalid_max, (XSIZE * 10000.0)); if (stat < 0) { FUNC_ERROR("miattputdbl"); } stat = miattputdbl(ip->fd, ip->imgid, MIvalid_min, -(XSIZE * 10000.0)); if (stat < 0) { FUNC_ERROR("miattputdbl"); } ncendef(ip->fd); /* End definition mode. */ coords[0] = 0; flt = -(XSIZE * 100000.0); stat = mivarput1(ip->fd, ip->minid, coords, NC_FLOAT, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } flt = XSIZE * 100000.0; stat = mivarput1(ip->fd, ip->maxid, coords, NC_FLOAT, MI_SIGNED, &flt); if (stat < 0) { FUNC_ERROR("mivarput1"); } for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { int tmp = (i * 10000) + (j * 100) + k; coords[TST_X] = i; coords[TST_Y] = j; coords[TST_Z] = k; stat = mivarput1(ip->fd, ip->imgid, coords, NC_INT, MI_SIGNED, &tmp); if (stat < 0) { fprintf(stderr, "At (%d,%d,%d), status %d: ", i,j,k,stat); FUNC_ERROR("mivarput1"); } } } } return (0); } static int test3(struct testinfo *ip, struct dimdef *dims, int ndims) { /* Try to read the data back. */ size_t total; long coords[3]; long lengths[3]; void *buf_ptr; int *int_ptr; int i, j, k; int stat; total = 1; for (i = 0; i < ndims; i++) { total *= dims[i].length; } buf_ptr = malloc(total * sizeof (int)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length; lengths[TST_Z] = dims[TST_Z].length; stat = mivarget(ip->fd, ip->imgid, coords, lengths, NC_INT, MI_SIGNED, buf_ptr); if (stat < 0) { FUNC_ERROR("mivarget"); } int_ptr = (int *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { int tmp = (i * 10000) + (j * 100) + k; if (*int_ptr != tmp) { fprintf(stderr, "1. Data error at (%d,%d,%d)\n", i,j,k); errors++; } int_ptr++; } } } free(buf_ptr); return (0); } static int test4(struct testinfo *ip, struct dimdef *dims, int ndims) { /* Get the same variable again, but this time use an ICV to scale it. */ size_t total; long coords[3]; long lengths[3]; double range[2]; void *buf_ptr; float *flt_ptr; int i, j, k; int stat; int icv; double dbl; total = 1; for (i = 0; i < ndims; i++) { total *= dims[i].length; } buf_ptr = malloc(total * sizeof (float)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length; lengths[TST_Z] = dims[TST_Z].length; icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } stat = miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_NORM, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_attach(icv, ip->fd, ip->imgid); if (stat < 0) { FUNC_ERROR("miicv_attach"); } /* This next call _should_ fail, since the ICV has been attached. */ stat = miicv_setint(icv, MI_ICV_DO_NORM, 0); if (stat >= 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_inqdbl(icv, MI_ICV_DO_NORM, &dbl); if (stat < 0) { FUNC_ERROR("miicv_inqdbl"); } if (dbl != 1.0) { fprintf(stderr, "miicv_inqdbl: Bad value returned\n"); errors++; } stat = miicv_get(icv, coords, lengths, buf_ptr); if (stat < 0) { FUNC_ERROR("miicv_get"); } stat = miget_image_range(ip->fd, range); if (stat < 0) { FUNC_ERROR("miget_image_range"); } if (range[0] != -(XSIZE * 100000.0) || range[1] != (XSIZE * 100000.00)) { fprintf(stderr, "miget_image_range: bad result\n"); errors++; } stat = miget_valid_range(ip->fd, ip->imgid, range); if (stat < 0) { FUNC_ERROR("miget_valid_range"); } if (range[0] != -(XSIZE * 10000.0) || range[1] != (XSIZE * 10000.0)) { fprintf(stderr, "miget_valid_range: bad result\n"); errors++; } flt_ptr = (float *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++) { float tmp = (i * 10000) + (j * 100) + k; if (*flt_ptr != (float) tmp * 10.0) { fprintf(stderr, "2. Data error at (%d,%d,%d) %f != %f\n", i,j,k, *flt_ptr, tmp); errors++; } flt_ptr++; } } } stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } /* Try it again, to make certain we fail gracefully. */ stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } /* Try to detach a completely random number. */ stat = miicv_detach(rand()); if (stat >= 0) { FUNC_ERROR("miicv_detach"); } stat = miicv_free(icv); if (stat < 0) { FUNC_ERROR("miicv_free"); } free(buf_ptr); return (0); } static int test5(struct testinfo *ip, struct dimdef *dims, int ndims) { /* Get the same variable again, but this time use an ICV to scale it. */ size_t total; long coords[3]; long lengths[3]; void *buf_ptr; int *int_ptr; int i, j, k; int stat; int icv; total = 1; total *= dims[TST_X].length; total *= dims[TST_Y].length + YBOOST; total *= dims[TST_Z].length + ZBOOST; buf_ptr = malloc(total * sizeof (int)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } /* Now set up a dimension conversion. */ stat = miicv_setint(icv, MI_ICV_DO_NORM, 0); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_RANGE, 0); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_TYPE, NC_INT); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_YDIM_DIR, MI_ICV_NEGATIVE); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_ICV_NEGATIVE); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_BDIM_SIZE, dims[TST_Y].length + YBOOST); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_ADIM_SIZE, dims[TST_Z].length + ZBOOST); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_DIM_CONV, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_attach(icv, ip->fd, ip->imgid); if (stat < 0) { FUNC_ERROR("miicv_attach"); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length + YBOOST; lengths[TST_Z] = dims[TST_Z].length + ZBOOST; stat = miicv_get(icv, coords, lengths, buf_ptr); if (stat < 0) { FUNC_ERROR("miicv_get"); } int_ptr = (int *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length + YBOOST; j++) { for (k = 0; k < dims[TST_Z].length + ZBOOST; k++, int_ptr++) { int x; int y; int z; int tmp; if (j < YBOOST/2 || j >= dims[TST_Y].length + YBOOST/2) continue; if (k < ZBOOST/2 || k >= dims[TST_Z].length + ZBOOST/2) continue; x = i; y = (YSIZE + YBOOST - 1) - j; z = (ZSIZE + ZBOOST - 1) - k; y -= YBOOST / 2; z -= ZBOOST / 2; tmp = (x * 10000) + (y * 100) + (z); if (*int_ptr != (int) tmp) { fprintf(stderr, "3. Data error at (%d,%d,%d) %d != %d\n", i,j,k, *int_ptr, tmp); errors++; } } } } stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } stat = miicv_free(icv); if (stat < 0) { FUNC_ERROR("miicv_free"); } free(buf_ptr); return (0); } #if 0 static int test6(struct testinfo *ip, struct dimdef *dims, int ndims) { size_t total; long coords[3]; long lengths[3]; void *buf_ptr; int *int_ptr; int i, j, k; int stat; int icv; total = 1; total *= dims[TST_X].length; total *= dims[TST_Y].length - YBOOST; total *= dims[TST_Z].length - ZBOOST; buf_ptr = malloc(total * sizeof (int)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } /* Now try reading the image with reduced size. */ stat = miicv_setint(icv, MI_ICV_BDIM_SIZE, dims[TST_Y].length - YBOOST); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_ADIM_SIZE, dims[TST_Z].length - ZBOOST); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_DIM_CONV, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_attach(icv, ip->fd, ip->imgid); if (stat < 0) { FUNC_ERROR("miicv_attach"); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length - YBOOST; lengths[TST_Z] = dims[TST_Z].length - ZBOOST; stat = miicv_get(icv, coords, lengths, buf_ptr); if (stat < 0) { FUNC_ERROR("miicv_get"); } int_ptr = (int *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length - YBOOST; j++) { for (k = 0; k < dims[TST_Z].length - ZBOOST; k++, int_ptr++) { int tmp; tmp = (i * 10000) + (j * 100) + (k); if (*int_ptr != (int) tmp) { fprintf(stderr, "4. Data error at (%d,%d,%d) %d != %d\n", i,j,k, *int_ptr, tmp); errors++; } } } } stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } stat = miicv_free(icv); if (stat < 0) { FUNC_ERROR("miicv_free"); } free(buf_ptr); return (0); } #endif static int test7(struct testinfo *ip, struct dimdef *dims, int ndims) { size_t total; long coords[3]; long lengths[3]; void *buf_ptr; int *int_ptr; int i, j, k; int stat; int icv; total = 1; for (i = 0; i < ndims; i++) { total *= dims[i].length; } buf_ptr = malloc(total * sizeof (float)); if (buf_ptr == NULL) { fprintf(stderr, "Oops, malloc failed\n"); return (-1); } icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } /* Test range conversion. */ stat = miicv_setint(icv, MI_ICV_DO_DIM_CONV, 0); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_DO_RANGE, 1); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED); if (stat < 0) { FUNC_ERROR("miicv_setstr"); } stat = miicv_setint(icv, MI_ICV_TYPE, NC_INT); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_VALID_MAX, 1000); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_VALID_MIN, -1000); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_attach(icv, ip->fd, ip->imgid); if (stat < 0) { FUNC_ERROR("miicv_attach"); } coords[TST_X] = 0; coords[TST_Y] = 0; coords[TST_Z] = 0; lengths[TST_X] = dims[TST_X].length; lengths[TST_Y] = dims[TST_Y].length; lengths[TST_Z] = dims[TST_Z].length; stat = miicv_get(icv, coords, lengths, buf_ptr); if (stat < 0) { FUNC_ERROR("miicv_get"); } int_ptr = (int *) buf_ptr; for (i = 0; i < dims[TST_X].length; i++) { for (j = 0; j < dims[TST_Y].length; j++) { for (k = 0; k < dims[TST_Z].length; k++, int_ptr++) { int tmp = (i * 10000) + (j * 100) + (k); int rng = (XSIZE * 10000) / 1000; /* Round tmp properly. */ tmp = (tmp + (rng / 2)) / rng; if (*int_ptr != tmp) { fprintf(stderr, "5. Data error at (%d,%d,%d) %d != %d\n", i,j,k, *int_ptr, tmp); errors++; } } } } stat = miicv_detach(icv); if (stat < 0) { FUNC_ERROR("miicv_detach"); } /* Free the ICV */ stat = miicv_free(icv); if (stat < 0) { FUNC_ERROR("miicv_free"); } free(buf_ptr); return (0); } /* Test MINC API's */ int main(int argc, char **argv) { int stat; struct testinfo info; milog_init("mincapi"); /* Disable error messages completely. */ milog_set_verbosity(0); ncopts &= ~(NC_FATAL | NC_VERBOSE); fprintf(stderr, "Part 1...\n"); test1(&info, dimtab1, 3); fprintf(stderr, "Part 2...\n"); test2(&info, dimtab1, 3); fprintf(stderr, "Part 3...\n"); test3(&info, dimtab1, 3); fprintf(stderr, "Part 4...\n"); test4(&info, dimtab1, 3); fprintf(stderr, "Part 5...\n"); test5(&info, dimtab1, 3); #if 0 test6(&info, dimtab1, 3); #endif fprintf(stderr, "Part 7...\n"); test7(&info, dimtab1, 3); stat = miicv_free(rand()); if (stat >= 0) { FUNC_ERROR("miicv_free"); } if (miclose(info.fd) != MI_NOERROR) { FUNC_ERROR("miclose"); } if (miclose(info.fd) != MI_ERROR) { FUNC_ERROR("miclose"); } if (miclose(42) != MI_ERROR) { FUNC_ERROR("miclose"); } unlink(info.name); /* Delete the temporary file. */ free(info.name); /* Free the temporary filename */ icv_tests(); fprintf(stderr, "**** Tests completed with "); if (errors == 0) { fprintf(stderr, "no errors\n"); } else { fprintf(stderr, "%ld error%s\n", errors, (errors == 1) ? "" : "s"); } return (errors); } void icv_tests(void) { /* Some random ICV tests */ int icv; int stat; int i; double min, max; icv = miicv_create(); if (icv < 0) { FUNC_ERROR("miicv_create"); } for (i = NC_BYTE; i <= NC_DOUBLE; i++) { stat = miicv_setint(icv, MI_ICV_TYPE, i); if (stat < 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED); stat = miicv_inqdbl(icv, MI_ICV_VALID_MAX, &max); if (stat < 0) { FUNC_ERROR("miicv_inqdbl"); } stat = miicv_inqdbl(icv, MI_ICV_VALID_MIN, &min); if (stat < 0) { FUNC_ERROR("miicv_inqdbl"); } switch (i) { case NC_BYTE: if (min != 0 || max != UCHAR_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_SHORT: if (min != 0 || max != USHRT_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_INT: if (min != 0 || max != UINT_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_FLOAT: if (min != -FLT_MAX || max != FLT_MAX) { fprintf(stderr, "Type %d min %g max %g, header min %g max %g\n", i, min, max, FLT_MIN, FLT_MAX); errors++; } break; case NC_DOUBLE: if (min != -DBL_MAX || max != DBL_MAX) { fprintf(stderr, "Type %d min %g max %g, header min %g max %g\n", i, min, max, DBL_MIN, DBL_MAX); errors++; } break; } stat = miicv_setstr(icv, MI_ICV_SIGN, MI_SIGNED); if (stat < 0) { FUNC_ERROR("miicv_setstr"); } stat = miicv_inqdbl(icv, MI_ICV_VALID_MAX, &max); if (stat < 0) { FUNC_ERROR("miicv_inqdbl"); } stat = miicv_inqdbl(icv, MI_ICV_VALID_MIN, &min); if (stat < 0) { FUNC_ERROR("miicv_inqdbl"); } switch (i) { case NC_BYTE: if (min != SCHAR_MIN || max != SCHAR_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_SHORT: if (min != SHRT_MIN || max != SHRT_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_INT: if (min != INT_MIN || max != INT_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_FLOAT: if (min != -FLT_MAX || max != FLT_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; case NC_DOUBLE: if (min != -DBL_MAX || max != DBL_MAX) { fprintf(stderr, "Type %d min %g max %g\n", i, min, max); errors++; } break; } } #if 0 /* For some reason we're allowed to set MI_ICV_TYPE to an illegal value. */ stat = miicv_setint(icv, MI_ICV_TYPE, 1000); if (stat < 0) { FUNC_ERROR("miicv_setint"); } #endif stat = miicv_setint(icv, MI_ICV_NUM_IMGDIMS, MI_MAX_IMGDIMS + 1); if (stat >= 0) { FUNC_ERROR("miicv_setint"); } stat = miicv_setint(icv, MI_ICV_NUM_IMGDIMS, MI_MAX_IMGDIMS); if (stat < 0) { FUNC_ERROR("miicv_setint"); } miicv_free(icv); } libminc-libminc-2-3-00/testdir/nifti_test.c000066400000000000000000000712501257462267400206400ustar00rootroot00000000000000/* * test program for NIFTI lib. */ #if HAVE_CONFIG_H #include "config.h" #endif #include enum NIFTITEST_BOOL { NIFTITEST_TRUE=1, NIFTITEST_FALSE=0 }; static enum NIFTITEST_BOOL g_verbose = NIFTITEST_FALSE; static void _PrintTest(const int line,const char * message,const int FailureOccured, const enum NIFTITEST_BOOL isFatal,int *ErrorAccum) { if(g_verbose || FailureOccured > 0) { char const * const PREFIX= (FailureOccured)?"==========ERROR":"..........SUCCESS"; char const * const ISFATALPREFIX= (isFatal && FailureOccured)?" FATAL":""; printf("%s%s (LINE %d): %s\n",PREFIX,ISFATALPREFIX,line,message); fflush(stdout); *ErrorAccum+=FailureOccured; if(isFatal && FailureOccured > 0) { printf("\n\nTOTAL ERRORS=%d\n",*ErrorAccum); exit( *ErrorAccum); } } return; } #define PrintTest(message,failure,isfailure,errorcount) \ _PrintTest(__LINE__,message,failure,isfailure,errorcount) static nifti_image * generate_reference_image( const char * write_image_filename , int * const Errors) { nifti_1_header reference_header; memset(&reference_header,0,sizeof(reference_header)); reference_header.sizeof_hdr=sizeof(reference_header); reference_header.regular='r'; reference_header.extents=16384; reference_header.dim[0]=5; reference_header.dim[1]=23; reference_header.dim[2]=17; reference_header.dim[3]=11; reference_header.dim[4]=7; reference_header.dim[5]=3; reference_header.dim[6]=1; //This MUST be 1 anything else is invalid due to code that usees huristics to fix other possible problems; reference_header.dim[7]=1; //This MUST be 1 anything else is invalid due to code that usees huristics to fix other possible problems; reference_header.intent_p1=10101010.101F; reference_header.intent_p2=987654321.0F; reference_header.intent_p3=-1234.0F; reference_header.intent_code=NIFTI_INTENT_ESTIMATE; reference_header.datatype=DT_INT32; reference_header.pixdim[0]=-1.0F; /* this is really qfac */ reference_header.pixdim[1]=0.25F; reference_header.pixdim[2]=0.5F; reference_header.pixdim[3]=1.0F; reference_header.pixdim[4]=2.0F; reference_header.pixdim[5]=4.0F; reference_header.pixdim[6]=-2.0e10F; reference_header.pixdim[7]=-2.0e10F; reference_header.vox_offset=0; reference_header.scl_slope=0.25; reference_header.scl_inter=128; reference_header.qform_code=NIFTI_XFORM_SCANNER_ANAT; reference_header.quatern_b=-0.5F; reference_header.quatern_c= 0.5F; reference_header.quatern_d=-0.5F; reference_header.qoffset_x=reference_header.dim[1]/2.0F; reference_header.qoffset_y=reference_header.dim[2]/2.0F; reference_header.qoffset_z=reference_header.dim[3]/2.0F; reference_header.sform_code=NIFTI_XFORM_SCANNER_ANAT; reference_header.srow_x[0]=0.5; reference_header.srow_x[1]=0.0; reference_header.srow_x[2]=0.0; reference_header.srow_x[3]=0.0; reference_header.srow_y[0]=0.0; reference_header.srow_y[1]=1.0; reference_header.srow_y[2]=0.0; reference_header.srow_y[3]=0.0; reference_header.srow_z[0]=0.0; reference_header.srow_z[1]=0.0; reference_header.srow_z[2]=2.0; reference_header.srow_z[3]=0.0; reference_header.magic[0]='n'; reference_header.magic[1]='+'; reference_header.magic[2]='1'; reference_header.magic[3]='\0'; /* String is purposfully too long */ memcpy(reference_header.intent_name,"TEST_DATA_STRING",16); memcpy(reference_header.descrip,"01234567890123456789012345678901234567890123456789012345678901234567890123456789",80); { int nbyper; int swapsize; nifti_datatype_sizes(reference_header.datatype ,&nbyper,&swapsize); reference_header.bitpix=nbyper*8; } nifti_image * reference_image=nifti_convert_nhdr2nim(reference_header,write_image_filename); { const unsigned int NumVoxels=reference_image->nx*reference_image->ny*reference_image->nz*reference_image->nt*reference_image->nu; reference_image->data=(signed int *)calloc(NumVoxels,sizeof(signed int)) ; /*!< pointer to data: nbyper*nvox bytes */ PrintTest("Checking memory allocation",reference_image->data ==0 ,NIFTITEST_TRUE,Errors); { signed int i=0; for(; i < (signed int)NumVoxels ; i++) { ((signed int *)(reference_image->data))[i]=i; } } } PrintTest("Setting filenames",nifti_set_filenames( reference_image,write_image_filename, 0, 0 ) != 0, NIFTITEST_TRUE,Errors); PrintTest("Setting type from names",nifti_set_type_from_names( reference_image ) != 0, NIFTITEST_TRUE,Errors); /* PrintTest("Checking type and names",nifti_type_and_names_match( reference_image , 1 ) != 1, NIFTITEST_TRUE,Errors); */ PrintTest("Check reference_image data is non null",(reference_image->data==0),NIFTITEST_TRUE,Errors); return reference_image; } static void compare_reference_image_values(nifti_image const * const reference_image, nifti_image const * const reloaded_image, int * const Errors) { PrintTest("Checking nifti_type",(reference_image->nifti_type!=reloaded_image->nifti_type),NIFTITEST_FALSE,Errors); PrintTest("Checking fname",(strcmp(reference_image->fname,reloaded_image->fname)),NIFTITEST_FALSE,Errors); PrintTest("Checking iname",(strcmp(reference_image->iname,reloaded_image->iname)),NIFTITEST_FALSE,Errors); PrintTest("Checking ndim",(reference_image->ndim!=reloaded_image->ndim),NIFTITEST_FALSE,Errors); PrintTest("Checking nx",(reference_image->nx!=reloaded_image->nx),NIFTITEST_FALSE,Errors); PrintTest("Checking ny",(reference_image->ny!=reloaded_image->ny),NIFTITEST_FALSE,Errors); PrintTest("Checking nz",(reference_image->nz!=reloaded_image->nz),NIFTITEST_FALSE,Errors); PrintTest("Checking nt",(reference_image->nt!=reloaded_image->nt),NIFTITEST_FALSE,Errors); PrintTest("Checking nu",(reference_image->nu!=reloaded_image->nu),NIFTITEST_FALSE,Errors); PrintTest("Checking dx",(reference_image->dx!=reloaded_image->dx),NIFTITEST_FALSE,Errors); PrintTest("Checking dy",(reference_image->dy!=reloaded_image->dy),NIFTITEST_FALSE,Errors); PrintTest("Checking dz",(reference_image->dz!=reloaded_image->dz),NIFTITEST_FALSE,Errors); PrintTest("Checking dt",(reference_image->dt!=reloaded_image->dt),NIFTITEST_FALSE,Errors); PrintTest("Checking du",(reference_image->du!=reloaded_image->du),NIFTITEST_FALSE,Errors); PrintTest("Checking datatype",(reference_image->datatype!=reloaded_image->datatype),NIFTITEST_FALSE,Errors); { const unsigned int NumVoxels=reference_image->nx*reference_image->ny*reference_image->nz*reference_image->nt*reference_image->nu; PrintTest("Check loaded data is non null",(reloaded_image->data==0),NIFTITEST_TRUE,Errors); PrintTest("Check reference_image data is non null",(reference_image->data==0),NIFTITEST_TRUE,Errors); { unsigned int CurrVoxel=0; for(; CurrVoxel < NumVoxels ; CurrVoxel++) { /*printf("%d ",CurrVoxel); fflush(stdout);*/ if( ((int *)(reference_image->data))[CurrVoxel] != ((int *)(reloaded_image->data))[CurrVoxel]) { PrintTest("Incorrect Pixel Value Found",0,NIFTITEST_FALSE,Errors); } } } } PrintTest("Checking xyz_units",(reference_image->xyz_units!=reloaded_image->xyz_units),NIFTITEST_FALSE,Errors); PrintTest("Checking time_units",(reference_image->time_units!=reloaded_image->time_units),NIFTITEST_FALSE,Errors); PrintTest("Checking intent_code",(reference_image->intent_code!=reloaded_image->intent_code),NIFTITEST_FALSE,Errors); PrintTest("Checking intent_name",(strncmp(reference_image->intent_name,reloaded_image->intent_name,16) )!=0,NIFTITEST_FALSE,Errors); PrintTest("Checking description",(strncmp(reference_image->descrip,reloaded_image->descrip,80))!=0,NIFTITEST_FALSE,Errors); return ; } int main (int argc, char *argv[]) { char TEMP_STR[256]; int level = 0; while (--argc > 0) { level = atoi(*++argv); } nifti_set_debug_level(level); int Errors=0; { PrintTest("NOT REALLY AN ERROR, JUST TESTING THE ERROR TEST REPORTING MECHANISM",1,NIFTITEST_FALSE,&Errors); PrintTest("NOT REALLY AN ERROR, JUST TESTING THE ERROR COUNTING MECHANISM",Errors==1,NIFTITEST_FALSE,&Errors); Errors=0; } #if !defined(HAVE_ZLIB) PrintTest("This test will not run properly if HAVE_ZLIB is not set.", 1, NIFTITEST_TRUE, &Errors); #endif { const char write_image_filename[6][64]={ "ATestReferenceImageForReadingAndWriting.nii", "ATestReferenceImageForReadingAndWriting.hdr", "ATestReferenceImageForReadingAndWriting.img", "ATestReferenceImageForReadingAndWriting.nii.gz", "ATestReferenceImageForReadingAndWriting.hdr.gz", "ATestReferenceImageForReadingAndWriting.img.gz" }; printf("======= Testing All Nifti Valid Names ======\n"); fflush(stdout); unsigned int filenameindex; for(filenameindex=0;filenameindex<6; filenameindex++) { char buf[512]; int CompressedTwoFile = strstr(write_image_filename[filenameindex],".img.gz") != 0 || strstr(write_image_filename[filenameindex],".hdr.gz") != 0; printf("======= Testing with filename: %s ======\n",write_image_filename[filenameindex]); fflush(stdout); nifti_image * reference_image = generate_reference_image(write_image_filename[filenameindex],&Errors); /* * Add an extension to test extension reading */ { static char ext[] = "THIS IS A TEST"; sprintf(buf,"nifti_add_extension %s",write_image_filename[filenameindex]); PrintTest(buf, nifti_add_extension(reference_image, ext,sizeof(ext), NIFTI_ECODE_COMMENT) == -1, NIFTITEST_FALSE,&Errors); sprintf(buf,"valid_nifti_extension %s",write_image_filename[filenameindex]); PrintTest("valid_nifti_extensions", valid_nifti_extensions(reference_image) == 0, NIFTITEST_FALSE,&Errors); } PrintTest("Create reference image",reference_image==0,NIFTITEST_TRUE,&Errors); nifti_image_write ( reference_image ) ; /* * test nifti_copy_extension */ { nifti_image *nim = nifti_simple_init_nim(); PrintTest("nifti_copy_extension", nifti_copy_extensions(nim,reference_image), NIFTITEST_FALSE,&Errors); nifti_image_free(nim); nim = nifti_copy_nim_info(reference_image); PrintTest("nifti_copy_nim_info", nim == 0, NIFTITEST_FALSE,&Errors); PrintTest("nifti_nim_is_valid", nifti_nim_is_valid(nim,0) == 0, NIFTITEST_FALSE,&Errors); nifti_image_free(nim); } { nifti_image * reloaded_image = nifti_image_read(reference_image->fname,1); PrintTest("Reload of image ",reloaded_image==0,NIFTITEST_TRUE,&Errors); { /* * if the file is named '.img', '.hdr', '.img.gz', or '.hdr.gz', then * the header extensions won't be saved with the file. * The test will fail if it finds an extension in a 2-file NIfTI, or * fails to find one in a '.nii' or '.nii.gz' file. */ int result = valid_nifti_extensions(reloaded_image); sprintf(buf,"reload valid_nifti_extensions %s",write_image_filename[filenameindex]); PrintTest(buf, CompressedTwoFile ? result != 0 : result == 0, NIFTITEST_FALSE,&Errors); } nifti_image_infodump(reloaded_image); compare_reference_image_values(reference_image,reloaded_image,&Errors); nifti_image_free(reloaded_image); } { nifti_brick_list NB_orig, NB_select; nifti_image * nim_orig, * nim_select; int blist[5] = { 7, 0, 5, 5, 9 }; /* * test some error paths in the nifti_image_read_bricks */ nim_orig = nifti_image_read_bricks(reference_image->fname,0,blist, &NB_orig); PrintTest("invalid arg bricked image read 1",nim_orig != 0,NIFTITEST_FALSE,&Errors); nim_orig = nifti_image_read_bricks(reference_image->fname, 0, NULL, &NB_orig); PrintTest("Reload of bricked image",nim_orig == 0,NIFTITEST_FALSE,&Errors); nifti_free_NBL(&NB_orig); nifti_image_free(nim_orig); nim_select = nifti_image_read_bricks(reference_image->fname, 5, blist, &NB_select); PrintTest("Reload of bricked image with blist",nim_orig == 0,NIFTITEST_FALSE,&Errors); nifti_free_NBL(&NB_select); nifti_image_free(nim_select); } /* * test nifti_update_dims_from_array */ PrintTest("nifti_update_dims_from_array -- valid dims", nifti_update_dims_from_array(reference_image) != 0, NIFTITEST_FALSE,&Errors); reference_image->dim[0] = 8; PrintTest("nifti_update_dims_from_array -- invalid dims", nifti_update_dims_from_array(reference_image) == 0, NIFTITEST_FALSE,&Errors); { nifti_1_header x = nifti_convert_nim2nhdr(reference_image); sprintf(buf,"nifti_hdr_looks_good %s",reference_image->fname); PrintTest(buf, !nifti_hdr_looks_good(&x), NIFTITEST_FALSE,&Errors); } nifti_image_free(reference_image); } /* * check nifti_findimgname */ { char *imgname = nifti_findimgname("ATestReferenceImageForReadingAndWriting.hdr",2); PrintTest("nifti_findimgname", imgname == 0 || strcmp(imgname,"ATestReferenceImageForReadingAndWriting.img") != 0, NIFTITEST_FALSE,&Errors); free(imgname); } { int IsNiftiFile; IsNiftiFile = is_nifti_file(write_image_filename[0]); PrintTest("is_nifti_file0", IsNiftiFile != 1,NIFTITEST_FALSE,&Errors); IsNiftiFile = is_nifti_file(write_image_filename[1]); PrintTest("is_nifti_file1", IsNiftiFile != 2,NIFTITEST_FALSE,&Errors); IsNiftiFile = is_nifti_file(write_image_filename[3]); PrintTest("is_nifti_file2", IsNiftiFile != 1,NIFTITEST_FALSE,&Errors); IsNiftiFile = is_nifti_file(write_image_filename[4]); PrintTest("is_nifti_file2", IsNiftiFile != 2,NIFTITEST_FALSE,&Errors); } } { /* * test writing and reading an ascii file */ nifti_image * reference_image = generate_reference_image("TestAsciiImage.nia",&Errors); reference_image->nifti_type = 3; nifti_image_write(reference_image); nifti_image * reloaded_image = nifti_image_read("TestAsciiImage.nia",1); PrintTest("Read/Write Ascii image", reloaded_image == 0,NIFTITEST_FALSE,&Errors); nifti_image_free(reference_image); nifti_image_free(reloaded_image); } { enum { NUM_FILE_NAMES=8 }; const char * FILE_NAMES[NUM_FILE_NAMES]={ "myimage", "myimage.tif", "myimage.tif.gz", "myimage.nii", "myimage.img.gz", ".nii", ".myhiddenimage", ".myhiddenimage.nii" }; const char * KNOWN_FILE_BASENAMES[NUM_FILE_NAMES]={ "myimage", "myimage.tif", "myimage.tif.gz", "myimage", "myimage", "", ".myhiddenimage", ".myhiddenimage" }; const int KNOWN_nifti_validfilename[NUM_FILE_NAMES]={ 1, 1, 1, 1, 1, 0, 1, 1 }; const int KNOWN_nifti_is_complete_filename[NUM_FILE_NAMES]={ 0, 0, 0, 1, 1, 0, 0, 1 }; unsigned int fni; for(fni=0;fnidata != 0, NIFTITEST_FALSE,&Errors); znzclose(f); nifti_image_free(nim); } /* * call various functions from nifti_stats */ printf("\n\nTOTAL ERRORS=%d\n",Errors); return Errors; } libminc-libminc-2-3-00/testdir/run_test2_cmake.sh000077500000000000000000000000331257462267400217370ustar00rootroot00000000000000# /bin/sh $1 -2 |diff - $2 libminc-libminc-2-3-00/testdir/run_test_arg_parse.sh000077500000000000000000000035761257462267400225570ustar00rootroot00000000000000#! /bin/sh #set -e fail=no check() { #args expected out=`./test_arg_parse $1` leftovers=`echo $out | sed "/^$2\$/d"` test -z "$leftovers" && return echo "Args : $1" echo "Output : $out" echo "Expected: $2" fail=yes } check '' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_a' 'const_a:1 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_b' 'const_a:0 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_a -const_b' 'const_a:1 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_b -const_a' 'const_a:1 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-int_a 33' 'const_a:0 const_b:0 int_a:33 int_b:0 long_a:0 long_b:0' check '-int_a -3' 'const_a:0 const_b:0 int_a:-3 int_b:0 long_a:0 long_b:0' check '-int_b 22' 'const_a:0 const_b:0 int_a:0 int_b:22 long_a:0 long_b:0' check '-int_b -2' 'const_a:0 const_b:0 int_a:0 int_b:-2 long_a:0 long_b:0' check '-int_a -1 -int_b 3' 'const_a:0 const_b:0 int_a:-1 int_b:3 long_a:0 long_b:0' check '-int_b -1 -int_a 3' 'const_a:0 const_b:0 int_a:3 int_b:-1 long_a:0 long_b:0' check '-long_a 12' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:12 long_b:0' check '-long_a -99' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:-99 long_b:0' check '-long_b -12' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:-12' check '-long_b 99' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:99' check '-long_a 3 -long_b -9' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:3 long_b:-9' check '-long_b 3 -long_a -9' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:-9 long_b:3' check '-long_a -99 -int_b 3 -const_b' 'const_a:0 const_b:1 int_a:0 int_b:3 long_a:-99 long_b:0' check '-nonsense' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' test $fail = yes && exit 1 exit 0 libminc-libminc-2-3-00/testdir/run_test_arg_parse_cmake.sh000077500000000000000000000034561257462267400237140ustar00rootroot00000000000000#! /bin/sh #set -e CMD=$1 fail=no check() { #args expected out=`$CMD $1` leftovers=`echo $out | sed "/^$2\$/d"` test -z "$leftovers" && return fail=yes } check '' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_a' 'const_a:1 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_b' 'const_a:0 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_a -const_b' 'const_a:1 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-const_b -const_a' 'const_a:1 const_b:1 int_a:0 int_b:0 long_a:0 long_b:0' check '-int_a 33' 'const_a:0 const_b:0 int_a:33 int_b:0 long_a:0 long_b:0' check '-int_a -3' 'const_a:0 const_b:0 int_a:-3 int_b:0 long_a:0 long_b:0' check '-int_b 22' 'const_a:0 const_b:0 int_a:0 int_b:22 long_a:0 long_b:0' check '-int_b -2' 'const_a:0 const_b:0 int_a:0 int_b:-2 long_a:0 long_b:0' check '-int_a -1 -int_b 3' 'const_a:0 const_b:0 int_a:-1 int_b:3 long_a:0 long_b:0' check '-int_b -1 -int_a 3' 'const_a:0 const_b:0 int_a:3 int_b:-1 long_a:0 long_b:0' check '-long_a 12' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:12 long_b:0' check '-long_a -99' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:-99 long_b:0' check '-long_b -12' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:-12' check '-long_b 99' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:99' check '-long_a 3 -long_b -9' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:3 long_b:-9' check '-long_b 3 -long_a -9' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:-9 long_b:3' check '-long_a -99 -int_b 3 -const_b' 'const_a:0 const_b:1 int_a:0 int_b:3 long_a:-99 long_b:0' check '-nonsense' 'const_a:0 const_b:0 int_a:0 int_b:0 long_a:0 long_b:0' test $fail = yes && exit 1 exit 0 libminc-libminc-2-3-00/testdir/run_test_cmake.sh000077500000000000000000000000301257462267400216520ustar00rootroot00000000000000# /bin/sh $1 |diff - $2 libminc-libminc-2-3-00/testdir/run_test_progs.sh000077500000000000000000000003741257462267400217370ustar00rootroot00000000000000#! /bin/sh set -e root=`pwd`/.. progs=${root}/progs PATH=${root}:${progs}/mincdiff::${progs}/mincpik:${progs}/mincheader:${progs}/minchistory:${progs}/mincview:${PATH} export PATH mincheader icv.mnc > /dev/null mincdiff icv.mnc icv.mnc > /dev/null libminc-libminc-2-3-00/testdir/run_tests.sh000077500000000000000000000003211257462267400207000ustar00rootroot00000000000000#! /bin/sh set -e tests="minc_types icv_range icv icv_dim icv_dim1 icv_fillvalue" for test in $tests; do echo Testing $test ./$test > junk.out diff $srcdir/$test.out junk.out && rm junk.out done libminc-libminc-2-3-00/testdir/t1.xfm000066400000000000000000000001771257462267400173640ustar00rootroot00000000000000MNI Transform File % Single linear transformation. Transform_Type = Linear; Linear_Transform = 1 0 0 10 0 1 0 20 0 0 1 30; libminc-libminc-2-3-00/testdir/test_arg_parse.c000066400000000000000000000016521257462267400214710ustar00rootroot00000000000000#include #include static int const_a = 0; static int const_b = 0; static int int_a = 0; static int int_b = 0; static long long_a = 0; static long long_b = 0; static ArgvInfo argTable[] = { {"-const_a", ARGV_CONSTANT, (char *)1, (char *)&const_a, "const_a option"}, {"-const_b", ARGV_CONSTANT, (char *)1, (char *)&const_b, "const_b option"}, {"-int_a", ARGV_INT, (char *)1, (char *)&int_a, "int_a option"}, {"-int_b", ARGV_INT, (char *)1, (char *)&int_b, "int_b option"}, {"-long_a", ARGV_LONG, (char *)1, (char *)&long_a, "long_a option"}, {"-long_b", ARGV_LONG, (char *)1, (char *)&long_b, "long_b option"}, {NULL, ARGV_END, NULL, NULL, NULL} }; int main (int argc, char *argv[]) { ParseArgv(&argc, argv, argTable, 0); printf( "const_a:%d const_b:%d int_a:%d int_b:%d long_a:%ld long_b:%ld\n", const_a, const_b, int_a, int_b, long_a, long_b ); return(0); } libminc-libminc-2-3-00/testdir/test_mconv.c000066400000000000000000000020741257462267400206470ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UNISTD_H #include #endif int main() { char filename[256]; int cdfid; int dim[MAX_VAR_DIMS]; snprintf(filename, sizeof(filename), "test_mconv-%d.mnc", getpid()); cdfid=nccreate(filename, NC_CLOBBER); dim[0]=ncdimdef(cdfid, MIzspace, 3L); dim[1]=ncdimdef(cdfid, MIyspace, 5L); dim[2]=ncdimdef(cdfid, MIxspace, 6L); (void) micreate_std_variable(cdfid, MIimage, NC_SHORT, 3, dim); (void) micreate_std_variable(cdfid, MIimagemax, NC_DOUBLE, 1, dim); (void) micreate_std_variable(cdfid, MIimagemin, NC_DOUBLE, 1, dim); (void) micreate_std_variable(cdfid, MIzspace, NC_DOUBLE, 1, NULL); (void) micreate_std_variable(cdfid, MIzspace_width, NC_DOUBLE, 1, &dim[0]); (void) micreate_group_variable(cdfid, MIpatient); (void) micreate_group_variable(cdfid, MIstudy); (void) micreate_group_variable(cdfid, MIacquisition); (void) ncclose(cdfid); unlink(filename); return 0; } libminc-libminc-2-3-00/testdir/test_speed.c000066400000000000000000000152311257462267400206240ustar00rootroot00000000000000#if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #define TRUE 1 #define FALSE 0 #define NORMAL_STATUS 0 #define ERROR_STATUS 1 char *nctypename(nc_type datatype) { switch (datatype) { case NC_BYTE: return "byte"; case NC_CHAR: return "char"; case NC_SHORT: return "short"; case NC_INT: return "int"; case NC_FLOAT: return "float"; case NC_DOUBLE: return "double"; } return "unknown"; } int test_icv_read(char *filename, int xsize, int ysize, double image_min, double image_max, nc_type datatype, char *signtype) { int icv, cdfid, img, ndims; static long coord[MAX_VAR_DIMS]; static long count[MAX_VAR_DIMS]; int dim[MAX_VAR_DIMS]; long dim_size; unsigned char *image; int i; double min, max; int n; min = DBL_MAX; max = -DBL_MAX; image = malloc(xsize * ysize * nctypelen(datatype)); if (image == NULL) { return (ERROR_STATUS); } /* Create the icv */ icv=miicv_create(); (void) miicv_setint(icv, MI_ICV_XDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icv, MI_ICV_YDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_ICV_POSITIVE); (void) miicv_setint(icv, MI_ICV_ADIM_SIZE, xsize); (void) miicv_setint(icv, MI_ICV_BDIM_SIZE, ysize); (void) miicv_setint(icv, MI_ICV_KEEP_ASPECT, FALSE); (void) miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE); (void) miicv_setint(icv, MI_ICV_TYPE, datatype); (void) miicv_setstr(icv, MI_ICV_SIGN, signtype); (void) miicv_setdbl(icv, MI_ICV_VALID_MAX, image_max); (void) miicv_setdbl(icv, MI_ICV_VALID_MIN, image_min); /* Open the file, attach the image variable */ cdfid=miopen(filename, NC_NOWRITE); /* Attach image variable */ img=ncvarid(cdfid, MIimage); (void) miicv_attach(icv, cdfid, img); /* Get the number of dimensions and modify count and coord */ (void) ncvarinq(cdfid, img, NULL, NULL, &ndims, dim, NULL); if (ndims!=3) { (void) fprintf(stderr, "File must have 3 dimensions\n"); free(image); return ERROR_STATUS; } (void) ncdiminq(cdfid, dim[0], NULL, &dim_size); count[0]=1; count[1]=ysize; count[2]=xsize; coord[1]=0; coord[2]=0; /* Get the data */ for (i=0; i max) { max = uc; } if (uc < min) { min = uc; } } } else { for (n = 0; n < xsize*ysize; n++) { signed char sc = *(((signed char *)image) + n); if (sc > max) { max = sc; } if (sc < min) { min = sc; } } } break; case NC_SHORT: if (!strcmp(signtype, MI_UNSIGNED)) { for (n = 0; n < xsize*ysize; n++) { unsigned short uc = *(((unsigned short *)image) + n); if (uc > max) { max = uc; } if (uc < min) { min = uc; } } } else { for (n = 0; n < xsize*ysize; n++) { signed short sc = *(((signed short *)image) + n); if (sc > max) { max = sc; } if (sc < min) { min = sc; } } } break; case NC_INT: if (!strcmp(signtype, MI_UNSIGNED)) { for (n = 0; n < xsize*ysize; n++) { unsigned int uc = *(((unsigned int *)image) + n); if (uc > max) { max = uc; } if (uc < min) { min = uc; } } } else { for (n = 0; n < xsize*ysize; n++) { signed int sc = *(((signed int *)image) + n); if (sc > max) { max = sc; } if (sc < min) { min = sc; } } } break; case NC_FLOAT: for (n = 0; n < xsize*ysize; n++) { float sc = *(((float *)image) + n); if (sc > max) { max = sc; } if (sc < min) { min = sc; } } break; case NC_DOUBLE: for (n = 0; n < xsize*ysize; n++) { double sc = *(((double *)image) + n); if (sc > max) { max = sc; } if (sc < min) { min = sc; } } break; } printf("%d %s %s %f %f\n", i, signtype, nctypename(datatype), min, max); } /* Close the file and free the icv */ (void) miclose(cdfid); (void) miicv_free(icv); free(image); return (NORMAL_STATUS); } main(int argc, char *argv[]) { int xsize, ysize; double image_max, image_min; char *pname, *filename; /* Parse the command line */ pname=argv[0]; if (argc!=6) { (void) fprintf(stderr, "Usage: %s \n", pname); return ERROR_STATUS; } filename=argv[1]; xsize=atoi(argv[2]); ysize=atoi(argv[3]); image_min=atof(argv[4]); image_max=atof(argv[5]); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_BYTE, MI_UNSIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_SHORT, MI_UNSIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_INT, MI_UNSIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_BYTE, MI_SIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_SHORT, MI_SIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_INT, MI_SIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_FLOAT, MI_SIGNED); test_icv_read(filename, xsize, ysize, image_min, image_max, NC_DOUBLE, MI_SIGNED); return NORMAL_STATUS; } libminc-libminc-2-3-00/testdir/test_xfm.c000066400000000000000000000037051257462267400203210ustar00rootroot00000000000000#define _GNU_SOURCE 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include VIO_Real tolerance = 1e-8; static int is_equal_real( VIO_Real e, VIO_Real a ) { return fabs(e-a) < tolerance; } /* Args: expected, actual. */ static void assert_equal_point( VIO_Real ex, VIO_Real ey, VIO_Real ez, VIO_Real ax, VIO_Real ay, VIO_Real az, const char* msg ) { if ( is_equal_real(ex,ax) && is_equal_real(ey,ay) && is_equal_real(ez,az) ) return; printf( "%s failure.\n" "Expected: %f %f %f\n" " Actual: %f %f %f\n", msg, ex,ey,ez, ax,ay,az ); exit(3); } int main( int ac, char* av[] ) { int N; VIO_General_transform xfm; if ( ac != 3 && ac != 4 ) { fprintf( stderr, "usage: %s N transform.xfm [tolerance]\n", av[0] ); return 1; } N = atoi( av[1] ); if ( input_transform_file( av[2], &xfm ) != VIO_OK ) { fprintf( stderr, "Failed to load transform '%s'\n", av[2] ); return 2; } if ( ac == 4 ) { tolerance = atof( av[3] ); printf( "Setting tolerance to %f.\n", tolerance ); } while (N-- > 0) { VIO_Real x = 500.0 * ( drand48() - 0.5 ); VIO_Real y = 500.0 * ( drand48() - 0.5 ); VIO_Real z = 500.0 * ( drand48() - 0.5 ); VIO_Real tx,ty,tz; VIO_Real a,b,c; general_transform_point( &xfm, x,y,z, &tx,&ty,&tz ); /* Check that general_inverse_transform_point() and invert_general_transform() behave sensibly. */ general_inverse_transform_point( &xfm, tx,ty,tz, &a,&b,&c ); assert_equal_point( x,y,z, a,b,c, "general_inverse_transform_point()" ); invert_general_transform( &xfm ); general_transform_point( &xfm, tx,ty,tz, &a,&b,&c ); assert_equal_point( x,y,z, a,b,c, "general_transform_point() / inverted xfm" ); general_inverse_transform_point( &xfm, x,y,z, &a,&b,&c ); assert_equal_point( tx,ty,tz, a,b,c, "general_inverse_transform_point() / inverted xfm" ); } return 0; } libminc-libminc-2-3-00/testdir/vio_xfm_test/000077500000000000000000000000001257462267400210255ustar00rootroot00000000000000libminc-libminc-2-3-00/testdir/vio_xfm_test/check_xfm.sh000077500000000000000000000003421257462267400233120ustar00rootroot00000000000000#! /bin/sh # Exit if error. set -e ./test-xfm 10000 $srcdir/t1.xfm ./test-xfm 10000 $srcdir/t2.xfm # Transformation t3 is non-injective, so we have to set the tolerance # fairly large. ./test-xfm 10000 $srcdir/t3.xfm 0.9 libminc-libminc-2-3-00/testdir/vio_xfm_test/copy-xfm.c000066400000000000000000000012411257462267400227310ustar00rootroot00000000000000#define _GNU_SOURCE 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include int main( int ac, char* av[] ) { VIO_General_transform xfm; if ( ac != 3 ) { fprintf( stderr, "usage: %s in.xfm out.xfm %d\n", av[0],ac ); return 1; } if ( input_transform_file( av[1], &xfm ) != VIO_OK ) { fprintf( stderr, "Failed to load transform '%s'\n", av[1] ); return 2; } if ( output_transform_file( av[2], "created by copy-xfm",&xfm ) != VIO_OK ) { fprintf( stderr, "Failed to save transform '%s'\n", av[2] ); return 2; } return 0; } libminc-libminc-2-3-00/testdir/vio_xfm_test/example_modify.c000066400000000000000000000037541257462267400242040ustar00rootroot00000000000000#include /* ------------------------------------------------------------------ @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ------------------------------------------------------------------ */ int main( int argc, char *argv[] ) { int v1, v2, v3, sizes[MAX_DIMENSIONS]; Real value; Volume volume; /*--- input the volume */ if( input_volume( "volume.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &volume, (minc_input_options *) NULL ) != OK ) return( 1 ); get_volume_sizes( volume, sizes ); /*--- change all values over 100 to 100 */ for( v1 = 0; v1 < sizes[0]; ++v1 ) { for( v2 = 0; v2 < sizes[1]; ++v2 ) { for( v3 = 0; v3 < sizes[2]; ++v3 ) { value = get_volume_real_value( volume, v1, v2, v3, 0, 0 ); if( value > 100.0 ) { set_volume_real_value( volume, v1, v2, v3, 0, 0, 100.0 ); } } } } /*--- output the modified volume */ if( output_modified_volume( "output.mnc", MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, volume, "volume.mnc", "Modified by clamping to 100", (minc_output_options *) NULL ) != OK ) return( 1 ); return( 0 ); } libminc-libminc-2-3-00/testdir/vio_xfm_test/example_tags.c000066400000000000000000000067771257462267400236630ustar00rootroot00000000000000#include /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ int main( int argc, char *argv[] ) { int i, n_volumes, n_tag_points, *structure_ids, *patient_ids; Real **tags1, **tags2, *weights; STRING *labels; int new_n_tag_points, *new_structure_ids, *new_patient_ids; Real **new_tags1, *new_weights; STRING *new_labels; /*--- input the tag file */ if( input_tag_file( "input_tags.tag", &n_volumes, &n_tag_points, &tags1, &tags2, &weights, &structure_ids, &patient_ids, &labels ) != OK ) return( 1 ); /*--- create a new tag point list of only those tag points whose x coordinate is nonnegative */ new_n_tag_points = 0; for_less( i, 0, n_tag_points ) { if( tags1[i][0] >= 0.0 ) { /*--- increase the memory allocation of the tag points */ SET_ARRAY_SIZE( new_tags1, new_n_tag_points, new_n_tag_points+1, 10 ); ALLOC( new_tags1[new_n_tag_points], 3 ); SET_ARRAY_SIZE( new_weights, new_n_tag_points, new_n_tag_points+1, 10 ); SET_ARRAY_SIZE( new_structure_ids, new_n_tag_points, new_n_tag_points+1, 10 ); SET_ARRAY_SIZE( new_patient_ids, new_n_tag_points, new_n_tag_points+1, 10 ); SET_ARRAY_SIZE( new_labels, new_n_tag_points, new_n_tag_points+1, 10 ); new_labels[new_n_tag_points] = create_string( labels[i] ); /*--- copy from the input tags to the new tags */ new_tags1[new_n_tag_points][0] = tags1[i][0]; new_tags1[new_n_tag_points][1] = tags1[i][1]; new_tags1[new_n_tag_points][2] = tags1[i][2]; new_weights[new_n_tag_points] = weights[i]; new_structure_ids[new_n_tag_points] = structure_ids[i]; new_patient_ids[new_n_tag_points] = patient_ids[i]; /*--- increment the number of new tags */ ++new_n_tag_points; } } /*--- output the new tags, the subset of the input tags */ if( output_tag_file( "output.tag", "Removed negative X's", 1, new_n_tag_points, new_tags1, NULL, new_weights, new_structure_ids, new_patient_ids, new_labels ) != OK ) return( 1 ); free_tag_points( n_volumes, n_tag_points, tags1, tags2, weights, structure_ids, patient_ids, labels ); free_tag_points( 1, new_n_tag_points, new_tags1, NULL, new_weights, new_structure_ids, new_patient_ids, new_labels ); return( 0 ); } libminc-libminc-2-3-00/testdir/vio_xfm_test/example_volume_io.c000066400000000000000000000070751257462267400247130ustar00rootroot00000000000000#include /* ------------------------------------------------------------------ @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ------------------------------------------------------------------ */ int main() { int v1, v2, v3, sizes[MAX_DIMENSIONS]; Real x_world2, y_world2, z_world2; Real voxel2[MAX_DIMENSIONS]; Real voxel_value; Volume volume1, volume2; int i, n_volumes, n_tag_points; int *structure_ids, *patient_ids; Real **tags1, **tags2, *weights; STRING *labels; General_transform transform; /*--- input the two volumes */ if( input_volume( "volume1.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &volume1, (minc_input_options *) NULL ) != OK ) return( 1 ); if( input_volume( "volume2.mnc", 3, NULL, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &volume2, (minc_input_options *) NULL ) != OK ) return( 1 ); /*--- input the tag points */ if( input_tag_file( "tags_volume1.tag", &n_volumes, &n_tag_points, &tags1, &tags2, &weights, &structure_ids, &patient_ids, &labels ) != OK ) return( 1 ); /*--- input the general transform */ if( input_transform_file( "vol1_to_vol2.xfm", &transform ) != OK ) return( 1 ); /*--- convert each tag point */ get_volume_sizes( volume2, sizes ); for_less( i, 0, n_tag_points ) { /*--- transform the tag points from volume 1 to volume 2 world space */ general_transform_point( &transform, tags1[i][X], tags1[i][Y], tags1[i][Z], &x_world2, &y_world2, &z_world2 ); /*--- transform from volume 2 world space to volume 2 voxel space */ convert_world_to_voxel( volume2, x_world2, y_world2, z_world2, voxel2 ); /*--- convert voxel coordinates to voxel indices */ v1 = ROUND( voxel2[0] ); v2 = ROUND( voxel2[1] ); v3 = ROUND( voxel2[2] ); /*--- check if voxel indices inside volume */ if( v1 >= 0 && v1 < sizes[0] && v2 >= 0 && v2 < sizes[1] && v3 >= 0 && v3 < sizes[2] ) { voxel_value = get_volume_real_value( volume2, v1, v2, v3, 0, 0 ); print( "The value for tag point %d (%s) is: %g\n", i, labels[i], voxel_value ); } else print( "The tag point %d (%s) is outside.\n" ); } /*--- free up memory */ delete_volume( volume1 ); delete_volume( volume2 ); free_tag_points( n_volumes, n_tag_points, tags1, tags2, weights, structure_ids, patient_ids, labels ); delete_general_transform( &transform ); return( 0 ); } libminc-libminc-2-3-00/testdir/vio_xfm_test/random2000066400000000000000000000037521257462267400223210ustar00rootroot000000000000006.2389604048803404268,7.3921469412744000849,5.6087192520499202786,-17.761039595119655132,-0.60785305872560257967,5.6087192520499229431,30.238960404880337762,15.392146941274400973,5.6087192520499229431 -8.3347081393003499272,0.53905580192804303241,2.5124699715524898558,-32.334708139300346375,-7.4609441980719566345,2.512469971552491188,15.665291860699653625,8.5390558019280433655,2.512469971552491188 -8.6099674226716196301,-3.6081556696444700982,-9.0650388319045305252,-32.609967422671616077,-11.608155669644467878,-9.0650388319045305252,15.390032577328383923,4.3918443303555321222,-9.0650388319045305252 -6.0415686480700996341,3.8466235948726499316,-8.2606054982170498135,-30.04156864807009697,-4.1533764051273465157,-8.2606054982170462608,17.95843135192990303,11.846623594872653484,-8.2606054982170462608 8.7594228982925397275,3.4794605430215601594,-6.5526262670755404116,-15.240577101707458496,-4.5205394569784402847,-6.5526262670755386353,32.759422898292541504,11.479460543021559715,-6.5526262670755386353 -4.4880708120763301849,-9.4592844881117308375,4.4645565701648601831,-28.488070812076330185,-17.45928448811173439,4.4645565701648592949,19.511929187923669815,-1.4592844881117343903,4.4645565701648592949 -2.5828403746709200917,4.4556724280118897852,8.5753612639382499339,-26.582840374670922756,-3.5443275719881128794,8.5753612639382481575,21.417159625329077244,12.455672428011887121,8.5753612639382481575 7.6529856910929101588,0.72133641224354505539,-4.5738626644015303313,-16.347014308907091618,-7.2786635877564549446,-4.5738626644015312195,31.652985691092908382,8.7213364122435450554,-4.5738626644015312195 -3.2040871959179599848,-5.0321907689794898033,0.15087676234543301312,-26.964447750090926093,-12.841734983407860682,0.15087676234543323517,21.7464299294964718,0.22187234302118952201,0.15087676234543323517 7.2239409713074600461,-1.7689023399725600783,-2.7574423979967801479,-16.776059028692543507,-9.7689023399725627428,-2.7574423979967832565,31.223940971307456493,6.2310976600274372572,-2.7574423979967832565 libminc-libminc-2-3-00/testdir/vio_xfm_test/t1.xfm000066400000000000000000000001771257462267400220720ustar00rootroot00000000000000MNI Transform File % Single linear transformation. Transform_Type = Linear; Linear_Transform = 1 0 0 10 0 1 0 20 0 0 1 30; libminc-libminc-2-3-00/testdir/vio_xfm_test/t2.xfm000066400000000000000000000003211257462267400220620ustar00rootroot00000000000000MNI Transform File % Concatenation of two linear transforms. Transform_Type = Linear; Linear_Transform = 1 0 0 10 0 1 0 20 0 0 1 30; Transform_Type = Linear; Linear_Transform = 1 0 0 3 0 1 0 2 0 0 1 1; libminc-libminc-2-3-00/testdir/vio_xfm_test/t3.xfm000066400000000000000000000004371257462267400220730ustar00rootroot00000000000000MNI Transform File % Concatenation including a grid transform. Transform_Type = Linear; Linear_Transform = 1 0 0 -87 0 1 0 -45 0 0 1 55; Transform_Type = Grid_Transform; Displacement_Volume = t3_grid_0.mnc; Transform_Type = Linear; Linear_Transform = 1 0 0 63 0 1 0 37 0 0 1 -55; libminc-libminc-2-3-00/testdir/vio_xfm_test/t3_grid_0.mnc000066400000000000000000006005421257462267400233050ustar00rootroot00000000000000‰HDF  ’’’’’’’’b’’’’’’’’`ˆØˆØTREE’’’’’’’’’’’’’’’’ąHEAPXČminc-2.0@øØTREE’’’’’’’’’’’’’’’’č HEAPX(ˆdimensionsinfoimage0SNOD HhPp TREE’’’’’’’’’’’’’’’’xHEAPX8 zspaceyspacexspacevector_dimension SNOD(Pp š 80 X x X x TREE’’’’’’’’’’’’’’’’xHEAPX˜ P8TREE’’’’’’’’’’’’’’’’pHEAPXX0HŲųTREE’’’’’’’’’’’’’’’’˜ HEAPX0image-minimage-maximage(SNOD°ŲųŠ&Ą Pident)vfonov:viola:2013.08.13.18.38.03:29476:1 0 minc_version2.2.00 ?@4 4’P#PKµ R 8varidMINC standard variable 0vartypedimension____ĄųSNOD Ų5 ø` 8versionMINC Version 1.0 X comments.Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’š? 8start ?@4 4’€K@ (unitsmm 0 spacetypeŲ ?@4 4’ #PKµ R 8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 X comments/Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’š? 8start ?@4 4’0bĄ (unitsmm 0 spacetypeŠ ?@4 4’š#PKµ R 8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 P comments'X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4’š? 8start ?@4 4’ą[Ą (unitsmm 0 spacetype  ?@4 4’IĄKµ R 8varidMINC standard variable 0vartypevar_attributeą!@SNOD(@$ "€ 8versionMINC Version 1.0  ?@4 4’I@Kµ R 8varidMINC standard variable 0vartypevar_attribute 8versionMINC Version 1.0 0 length  0 length  0 length ą €Hąą deflate 'ąKµ R P dimorder&vector_dimension,zspace,yspace,xspace 8varidMINC standard variable 0vartypegroup________ 8versionMINC Version 1.0 0 completetrue_ X valid_range ?@4 4’€’ߥ€’ß@Hh  history{Tue Aug 13 18:38:03 2013>>> mincconvert -2 /home/vfonov/src/libminc/testdir/vio_xfm_test/t3_grid_0.mnc /tmp/t3_grid_0.mnc TREE’’’’’’’’’’’’’’’’rŹš6ą Lµ R 0 length px^ģ»u˜×¾ZŪŹ[ĘŻ}pwwww'ŲąīīÜ5‚Ćąīīīww¼UŻ3“{ι÷żū¾G}$ĆLwWķ½~ĖŖ{$é’’ó’ķ?䧃¦,åą®Cąq(8T‡!™8l8ģ’Crāš<%/Ž’ä+łIžRŽ@)H –B¤P) Gø!EJQ8¢„)VŠĆ/%H‰8ŅHiq¤“ŅKpd”2įČ,e‘²āČ&eĒ‘Cʉ#—”G)/Ž|R~püuĄ³ņįłyšJ¹šš9šźŁpž,8c&œ;®"®& ®+W‡kĮ5Gįź#°Š0¬'ė Āś°R?¬Ų+÷Āx`/8ģŲūd`·¬]S]{h턵§ÖŽŗ÷ŁŚóT žļ~Ę,-ėu-“~`•ŠT*JnŒ¼’Ę'Š……‹…Š…ˆ…F¬=ƒk÷­ĻŹćŚé‚R!©ˆTL*.•”JIe¤rRy©¢TIŖ"U•ŖK5¤šRm©ŽTOŖ/5”I„¦R3©…ŌRj%µ–ŚHķ¤$©½ŌQź$u–ŗJݤīRO©—Ō[ź#õ•śIż„ŅĄ’Ó1ķ‡ēōĘó»ć•:ć5ŪćÕŪą<-„ę8kcœæ¾TWS×UUŖŒ«,/••Jćŗ‹KE±ŠXO¬+֗+Ķ€§ĮŹć°Q.„-|ƒ\Ųŗ‘u㚊©…h*ž©Xŗqüæżqc÷77ĆRŁõOÄ,FYxY\²˜d”ķb…UzĢ«…T*N„ĀĄØ$V[NŖ€µW2µ± €ISģĻ/@£­ ‰.@”ö±/vt 4X* —FIc¤qŅi¢4Yš*͐fIs¤yŅi‘“XZ"ż!-—VJ«„µR²“AŚ$m‘¶I;¤Ņni“OŚ/ų_żxÜižµ ĻŽˆ×YƒW\†×ž g™‹óM—¦ąģćq#„aøŖø¾^øŅĪR ÜÕųÖÚj`m•€l¬¶V]ŒĪ&gu!š܍OĮ3,Mææ±“ųéFŅĀэ¢…į’†`*ļ,ä,¶¹¹ö?QūĮ2‹c?0‹OaW*byqÕ§Jbå± ­:˜ŽĘąNKĢtųŅ“Ž Ę~Œ’ʝIĄf&™\~*+€É:i=يŻ=>(–ŽI'„3Ņyé’tEŗ.Ż’īH÷¤‡Ņé™ōRz#½“>J_¤ÆŅw‰FQˆFtbóæ”—ČWé³ōAz‹×zŠW½‹×æ†3“Nἇp»€š&ą» č.¶³ģd\ūh¬b0ķ‰uu¢­0“±āŚąjeģAi©8Z“œ;da™{öÉ`ģØÅJ G‹‘©(ŗ1üļ¦j¦…^*ć~ ÷3Ū\¾•Лœ“£–ŪŲ¢øĪŅøŽŹøīZ˜G‹]bķ1©=€×iˆ4hżźĀj®“H-NÉŲ•mŲ}Ų„cŲ­sŲµkŅMé¶t_zl^aO-d¾K;­qoāG‚H(‰$1$ž¤%Hf’ä"yIR˜#%HiR–”'IeR…TżGü“"U†”$EIA<;'ÉJ2’4$–D`œĆgSī7é®ā…ōøŽ”.Kg„ćøŚ=Ņv0v-V±“7śc§½\\“ xˆ’ŹĮŸ™÷3vēÜČYŒ³³<-óbń-ēĶ•ĻÜņCū‹‚keįUįu”$Ķ0ķ0‰Ż „żŁHčß$Ģź ¶ĢZ¼¶cķÖié"°śH=Įī¼Å.}J xį ^Ųæ`N¢IIG2‘l$7ÉtŠ›ņŲū¤i@š¤5I"H7Ņ‹ō#ƒČP2‚Œ!ćÉD2™L#3Č,2›Ģł7Ēlüd™J&‘ xü2„ōĒ+t%HҜ4$µpy [øfŖ‰$Š„_\™Jł Ę?ĆußÄ“‘Žbś,,×@;a„Ó0„£”’żĮŹ.˜ąÖąd#ģN Lw9x}1ģ[ޟPŒ'"ĄŽT Ż}FߊĻX Ģ ęĮ|Y aį,ŠÅ°Xē:bY4‹`”,ˆł1Ofā‘ßéGś’>Äó/Šct]IŅit4XŚ3RĢ. $ć”Ģvpņ p¼ uŻ>.B ķhļB± pp1J#r^ ć\…ž@¾qc8ĶÅĆž.-m%5ĮnVs±°2EŽæ ~. µh)čæā—šZ,åLEĻVž©šVV),Gśµü®xļÖĢ)¼sc·ży'T’4XwWzŽdBąqŽą\ĀßČÕ'æ.Hy£ČųÉ ² īr.vØ} jåƒI‡/å¦EhYbĶ ƒŻh?čŽxģē<ŗ„®qįu„ž”Wé]ś”¾£ßØĢĄ" Ųd`9X~VŒ•eUXm֐5c­X;ցufŻXwÖĆutg]Y'֞µe-YVUgåYq–eei€“Ӂē[ yŻM×ŃÅ8óH°²8Y W–ŖķÆżŸ¼6īŠ įŅĆąŁ­±ŹJP˜h!aŠS…ü…ts»baø źäÖұšĆŽ`@[øa}p¢"ŗ—„£nćAØKC-ŖĄź~?k§;µX¾gqĻž•WRŪÕ¬“āf^M“ŗ&˜™öč}qžŃŠĢYČ)Ė]¼;ˆ+tc÷Ib`?ņvzdµ¢šø:ą\G䑑˜Õ…d|ä 2ß-ų٤;“1†fjÅhEųPSģTw:{6‘ĪĀī­JAģ,½FļŃēō 1/ &e`¹XaV†UcõY `҃ d#ŁÆl›Ļ–²Ul=ŪŹv²½ģ ;ĀŽ±ć®ć¾>Čö±]ųŁz<ęw6—MacŲ Ū˜We%Y^–Xz2ĘŽāœ Ć1;Óčp\YKZƒ–@ ŠŠ üń!fš0rĪČY£±Ź$䛪č9¹¦ĆÓdšš¹tŖtβ ³¾^3,ģ‡L‚–5ĄŽVCŠŗ8˜ »ėŅP‹6häü~x_Ŗv:žÆK9Żč¹uÓź –nZžWü® ·µ˜×Öåx ā ęó‘Æ,æŪ•æ„+|ŽYŲ µe„Ó•ė¢Oõ€ZžJęĀį6»ū“<'_ˆ ¾EB!ó`7ŖŠšœ®@m4\l]N7Š]Ąģ½&¼¦QްH––egYiģr}š§#ėƆ± l[ĢV³-@å8»Čn±‡ģū“Pø{r?ÄCyäQ8"y8žČ}¹“ėœń/ģ5ƒŖ[ŁJ6MdCĄŃ¬+³Å‚“œ½”·įš;”¬³0Y=€buĢ[¬ĄƒJō¹ &ī@÷˜CĘ¢‡¶†UPs@‚Ń)yG¹TpJŗVśŖ5 ‰ot“=’DC‚„]*š»Ÿ$,z¹Tž æTļs³ĻŅNx„½Ø’€ž„›p–$©+ŚŻ`óÜŖ¹ ėˆt=öś·Ķōļ2ø°«Š5“ƒÓ€^.B ߅^ƒę|" ‹‚N€Jֆætncąg‹įf;€Śyz‹>”ļ)‡łĀ«Ņ°l.ĢŖ±ģha?pl2öyŪĄvƒOŁöxqī.Ń<=ĻÉ ńҼ2ÆĶń¼-ļĄ»šī¼'ļ…£'ļĮ»ņŽųnKü“ÆÄKš|<įžÜą_ńJ7Ł)°v›ĆF³ž˜’j¬ĖÕŁhöi\ć2pqn8snh¾eō5ęņ$0\Af“1pĶŠŅ H5™1;Dƒ’¾ĄŒ_D¶ŪÅśCš'MFŪ'ꀽµ8X>Xūž<‚\ j-öõü™}–óY©Åņ½ŸŃ+ō“ė5FĒsėęPh÷Thų’ꝄjރĘ‘č}84#'ę®2x×օŻT$īd8ŻųÜKœŽKLR©ąxŪP: |[E··‹ōrÄ_TeŽPÆ“š²"š¦Z¬)KĀ>uńģw¶Žķ€^`·±ÓŸ™ ~…ņ4</Ā+š:@«ļˇó_ł,¾˜Æāł¾į'ų~Ž_ĄqžŸå§ų1~ļę[ųZ¾„Ļįł0 Ś†×ćåy~žŽƒ™Ł}v8.cS”Źķą§Å€b0SĄÅō0]? =m ÕĻKø8}…Nz ž°Łl8éߨ–” ł-ŗDČt©+P«=˜üe@p ø`!h©h}©:øRŽ•źf ?ŌZ?ćēĪ.nöyaĖłĀ\÷X¬Ō’š8 ¦ WŻ•7ĮŒtw%–qHPó1=Éš¼CЃė)Ģ3”š±Č^Ńlė ©tGGš Ž­vgŻk8X—éæ*ÖÜ™"[†Ļėł`u…ßć/ł.„]ųŠP-Ez‘Idq™šu"¾ŠŸŁų~˜īękų|>Ž÷į­y ^”g›É®ĄAWŬ5®+?‹‡3~/ž„3/¤ca$ŌÜČĖžō;\ż2ŗb2Y@ĘCIŪ!q—C‚Ė€ŁöDĖżŗ†©ß›‚ ÅĮ`F;$™ŗš©²pĮ¼ČĆĮ,/°Lż~–zŗŁgåĪĪ—čŹœīŌRŹł½ö.ō†»\oĪ»iÓŅĶ[ŅSx'hÆi0g%H5äčŽd®}Y Ķ<UyE(°‹F/Ak‚u=é(øČ pī8{ ĘČłńą[1äĘF`[o6™b!Ļå+ų60ģĄßs.øÜ}dI•ł¹œ`\ Ö{3éa¼g;; ¶=`oįvčY"<­ÆĒJ?Fó|)ß ¼¦½jŽ" Ģ*"ŖˆĘ@¬æ'ęˆeb“Ų'NˆKāOńP<ÆÅ;ńĒ;ńJ<ÄMqA»Ä:ń›˜"†ˆĪ¢‘($£ĮŚü?ĢWó©P榚ŅL`üWųģa\Ūd֋5ʧG²ł š-c&LŪb…Ń÷}hž čł«Č,xH7Ņi (šT4ڽ…ą#“ĀŅnØŲ$‰_įJ½ŃÉ,¬Ž¬_ Ģ4bąk¾PI-æīēVO·÷¹Ł÷C;­¾WÆRӐšZ,ß³”s ½Ż@ļ"¼ų…ō—¤Į›£1[…Š ;÷C/Ÿ ęķ†jŽC?Šh0ü®4³x7–ΧÉō½ Ö}£NäÉ,¬(ņ…ÜP(ŌļH$Ą·;Ȅi$”§å¹yIØY*ŪęĮÓvšćüŹ’ā¦×ņa×ė‹$ ö«X(’Å~ąņ@|²ģ#GŹéå\r¹Œ\Y®!בėŹõšßšr¹œ\LĪ#g”£e_Y–ß‹»ā“Ų.–ą5z‰¦¢ŒČ,üÅ7°ńų=ł§RNōō;ĮÖBz"Me ČÅļŠ÷Š„tri#°0 Ö,Ć Æ!mÆĆnŒĘL·€2s!čf’^z(]–ŽĮƒÖ"N—ĘĄ»CC›"ēWB_+ŅCĆą€N õ?ńs«§•ł€|÷Ź;äņJy‘ēńZóÅPhky č¼ EĶūżES`xMc<ė„öŸ›…”_<‚® Ā*aĶ‘ŠŃwhG°óGŻEBˆ„2ņ©ļÄV¤ųłpĮąHgh]Ø^Yč_n ‘f@%u`öO÷³#»ųŗ’ēĻģĖ öYŚiu†ŗhė­Ń÷zIƒ’&C©—‚ļnß³”3½løŖŖp½ĪŠś©d šģ1\÷k"Ó0Æ0zRkōƒ)ČÜ» ™éwźÅĢĖ*²&¬šĄ,(Ņn“Æ{@NåPŹ<š·ŗHƒ}I깕|'ÉĀĄŪ"> ĆŁš@ķÜømn·›&‡’ąWGyø*ߑ?ĖN%VÉ­”W(핾ŹHe’2[YØü®,Įń›2W™¦ŒU*]”fJU„šōU¾É÷šÜUņD¹ “6Pü%N†¢¶åD¤›{H·ó0Wup„žü=;‡63“²W ūŠŽŗ.A—ķDkĮ 橖>[ä0TtIŗbŹ+"‹¦%AhŹ_¤'Č1ĒĮˆ5Ņ"ØŪHd ‹õį\V†ÉL¢Šś?«ū™)źie«µ[ģs'—ŌÜYyÖ­–óMGnY)mFß;-Ż@~ś(ÉŠń(ų^aĢT“ō–”½ž†ė}"v4…œ“z-ł_š³ÜĻ=­Žnõ¾“’¾*pŃĘR+$¢ž.ķœ‚ž² <߃Ģt ōÄŠĀ”1OpU–rž‘‚ŽgäĶš.Š’öƒn®D^¹‰¤é€ćå‡_“EƜ…p˜ŻdļŠäĀxV^Š×G‡†T¹~œūν‘čóqĶ‘’ĒBɒÅAqE<߄©$«\žÕN(O•WČūåėŠI°Ø$ų4ģŚŖ\RŽ(v5VͧVV›ŖÕźu²:S£ĪĘ'ć_Õ.j3µŠZ@MP=ŌŹ5e‡2OéĘęS”wņ)y™5Q‡«ĖŌźKÕ©„ÓJj µ.ŚPm²6O[¢-×Vh˓ߓYŚxm€–¤Õъh šM{ŽĒ/ĒóšØyUoõ©²ÆŲJģ„ÜGvf”™|ĪŪ J…†x3׃W„oKü Kfć°Ā’,– Øč~ś;’LZ) €~'Č ²]bś¼„”9<‘b^ĄNĄW£›M‚WõŚ@A«GłI"ņż ?«=¤¦O+½Xī—Ŗžy”¹Vņ“¼Ļb_·”ä2łČj Ē””œxĮł²ørK äāŃHWÉšč›ä-1›sC9-īYč¦OØ _ȋvŽś;Üā*ĻŽ5ä5y{>n·½ą!—„ŸH/ŠĆéŗ#1,Ä-ńYxĮŠ¢±Y™r¶¼ ą¢üXžK¶+H’„•†J7e¼²\9¬ż©ī42e&Fgc€1ĀcŒ2†½Œ6F-£°očĘ#ā.ŅņPhIZØč¶é¬>’{ł]…Ón’ ś½ź0Yœ> ¬†ģ—>$Čšā4ōm5züÆąL7xW=¤¢ąT:ų›?ŠśŸ»żYķĮJŸÖĖż²A=ݽ½†Ōų·‡’Ęø¼o•“{^ŗćJ.F2B¤2i†F:šĢ'Š×ļ ·xŅDZ„Ö£ŻéD\÷z¾ŽÄi”7 Śr–½d6dīāhĄ}Į¼d~9Sˆp‘GT‡ß†³ģw“£€]St¹ÅņAtk”D*ł•šhqƔ™ŹJä“ÓʟŹk…©¾ČԚj'õWu½z¼ĖŖµŠfkē55Dß©ŠÓõŃĘzćŠńÉš2ćĢģf³ˆYŲĢkf2#LÓ|k\46ŒVFĆĆø­ÆÖ{éÅt›~N›&Fkį‰Ijõ=m?„(ĶI4‹:øĘ'b½č'JCõļņu|ÆŹcłgĢč"Ö}0ӑcö”ĻĀD£ńŌ¤/ɤ<‹I}0 öŅ$Ÿa.@A×Kæ!ƒGęo *#‹ä³¢€ÓĀ/ŌÕż,÷Ėõ“īšYźŁXj ÷‘†I‰–HÉ.ļ» ö}”āēJ.„‘;Ū“A˜„.ķ|¾…ŌYÓ6}o?ż“~§Į,«…ž>™å<{Ć4?™Ü¦ŁTµ½3ļšĒ͵ęÆf[³˜`>16ƒ• _ćŖ>Go¤Gź·µłŠį08ā,µŽØ^ĮüŌUB‘I"I„•_#_ ‚>āOōž>X[(ŚÄ>6µGމDŸæB7¢AuAĶAé7Ģł!xąŅÉ”É%ó$ߑ Æ"Ūo†Ņ͐Faē“ą`U”†yŠ£“ó_šKmV{°ŅK:t‡\?©g3Wv€Wš FÆĄėĪJĀūÜģĖöYÉ„'‡Ü¹ļ!“3f”åŠųÓyt;½J?¢1dĆvf“ĢĪ=Ož Y­šĀü 惼,r‰j¢#2łrqާ!eV;ÉÓå]ņ$”lJxÜ"eærøµbj (egu€:{ŗȝUŸŖŖ–ØUŅśikµ'Z=Iߤ3£¢1Ūxdd6»™›Ģ7f¼­†­Ÿmžm«ķ„ķŠķ–ķ¦ķ’ķˆm£m6¾[ǖŦŲ.˜ Ķ6fóPģiä4ŽékōÖH87µiČ5†¶Oķ ‡}§¬…¦SžŹĖŃ=3ŹoŠ(ś!s9Äž<”÷D£ßĄF”)åb^ģ9th J›Ńā` A_³d3Rč0“®šČ0é\ ś¹ž„“{m9`tĄ&H‘„ ‰_ šSSš³īžżĄ/Ģ•^,÷³š{Ŗz¶Äó{¦d—ߊ,wĄ]/ƒćļ]Ž—¾čģ3 ę‘«Ž£±'@;ėĆ­§Ņdz†¾D[ĻĄŹ³$$²Õģ4{ ō2óJțć0©Ēł®ŠQY³·˜.6Š āš•syCŠĘ/ ¤QŖ£ƒż”œE6 ÕA×­ĪUW©ŪŌCźõŗśHżä‚µlZ­˜rV3õ²ś$żO=«1Ņøcä³ī›Łl}m»mŁ2ŚėŁŚēŚ×Ū÷ŚŪŁwŁ×ŲgŚūŚėڳŁUūEŪ|[K[ZŪ3søa^¢–4¾Ćfŗæ~³‘E{€Ž_EÕÕ½J%§ņYø=|%’‘—ó Yœ„'4C2£ü,[ ĻØČāeבb¦ŠĪ“*<Š~Ęnķ%K1łŻŠ"JAAC‰ŽxŻl7öz>4o š[3$Š2ŠÄLČ'@Ė_źŻwū rŻ{IGŻī—š=[„ØēÆ)ŁeÆtJŗ Ž’%™Hžéį»UŠA-öż5?KžŽÜ™“V”IŠĪ?Š]ļSĮb£[°įl);Źž1ŗ^%ށOą«Ńó^p»H Żl!‹b—ø!¾‰px^+łWä̲7{Ge¾rJł¦$ŗĮLu‹zN}¬~bžZ©„×DŹh¦õ…ćķÓ^k‰H+ėtnŌ3¶aęó‰YĮ¶Ņ¦ŲkŪŁļŪĆ=Ók»ū[8&8:8Ź8ĀĻģģ½ģyķŸmɶ6¶Ūys4÷1Å(j¼Ņēb*>iækÕŠ÷7Ø- '•”pä@°­œ(?ĖD[‘}pĮ«ńHhčn6«Ļ‡ó \ lD ”[qśia ZD_ģa’ -ŠA¾ —Y˜Œ¬? Ģé‚R ¢zA,pśŸųY÷®ƒ]÷^Ņüķ~e”žuR²gØē4Ź)Łå®ōF¢Šź(’ ¾[ģL¦»ŲwŻ §m‚Ö0‡nƒv~”Ap¾z¬›7øĻ4d³ņ<‰ēkųž®— ø­‰Är@Üi³ŒÜAžVžFŽRŖ"©lU^*ŃjuuŚŲUõ»¢åŠJkµ“Ęčtmµöhk½µaŚT4ńƒŚ#Ķ Éqˆ~\0śwŹę~3Æm-Ź>ĪžÖ^ѱĄńŲļ¬ēī\ęÜļ<ļ¼ę¼ģ<įÜāœķģį,ē rŽq,v4Žģ#ģ¹ķm“llį˜9Ģ›ĘP#­qVļ”G臵Ž`ū!č@“z^ߏĖäfh97ąŽõE„xĄWš®ŠPŗąr֗U%vMjf»MGķō²ŗ„ C‘Ž«“$x£Å?C·>$m”cׇ½~åįiYO‚’-~īö‹ōbuæH;唹Ö}3wöt«§u×ģ^ŪŹ.* ‰$/¦¦ ’ēäØ-䌋}ačģÕiG:¹ó$}ķĢČ*Įł¦²-ģ:#<†—ä­ŃV”§æįŽ"«Ø‚¼9 üŗ‚†./wAS8"’Ó ‘OCĀ4ąużŌą\ V©rX¶RŪ¬m×vhŪŠģ¶h;µ#ŚUšĪ©ēŠ[čóõzncŗĮĶ^ęs Ķ“O²ū:Ę;$g3ē§įQĪcØG²Ē%7ÜÓšTJߣ £š±Ī7g™q¶ķ¶ŗöÆöŽ ĪMĪ,s=¾yTšœģyŅó›g“WAÆJ^5½ŖxńŠó’¼NzNń¬ęióÜåŃĪĆßc‡³‘“8ē8r;ĪŁŪŲ¹}†-“ķ Yß|gģŅėźĮųŚed™Xõ|0­r]/—æ!ĒtYÄ;¾ ŻØw‚ĖX/V)ō#zątm  §„ށj-#Ą†Š²Ģ$˜Øä-Ģqi›“ t$ŅGk4Ą hšŁ€_Č?š³ī¾¤¶‡ų”ōbŻ9«”ŅŗH}ŃÜ'Ió¤åȞīę÷źéEbŠŪK£¹t$ĆÉō¾ä’g0¼ÆķXCOÓ×Ō›eGkčķ<Ąž¢3äąuł¾˜įĻ”œŁŠõŗ‰©bƒ8'^ $€J@ošŽ9-Š[¤ÜWŅ”5ļRu䒩Ś9 ”IÆØ7×»č}ōŽząU\ϬĒéiõųz؞¬?ŅŒ®Ę#æ¹Õ,c{n›oÆē„Bvõ°yNšä^ͽ¶xļ<Ž­½'xÆōŽķ}Č{ž?Ś»žw“÷Ÿ^S¼Šz=Ćć2yžšhįńÕ9Įå\ē(ģ8…ÄóŲÖĶĘmĮĀuF1撮³3GĖ„]‚'#É“S•Ćr/9ƒ|O̵…æøÄgņF<æa;ŁX4łLLF†Y߆–FUécrĪ3™ō&MIYųQĢ{tų“H Ė„™p®^h€õĮ§"Ą/įæāg„ĻŌōR%Åż¬ī0ZšźjīŪŃK.Ć]? łłC=ó‘Š8k2ž,&;ČEņŠ˜ø¦b˜­.öݦœÅ²Ņ¬ü{»Į8OƒÜŅĻę{ł#¤–,@Ƈ˜!¶Š+ā“š“³ÉÕ䮚½½ņ+9Fi ĢUī)™ŠĪ«‰Č|g“p½‰>MߦŸŃoč÷ō‡śż„žp¬Ž]^oÆ/ŠÆéF-c‘ńÅhfŽ6;Ū‚ģ×ģėS=ņy¾óœźį=Łū“wyŸI>'|¾łÄśó­ćŪŲ·–oA__ß>3|Źų¼Į#²xójäõŚs€§é9Ł#Äc3Ö¹Ō‘Ę±ŅžÉžlĖeہ†xŅØm<Š»éŖ>Łé„ŚZ5ÕÕJM…)«å†ŠŠC¢æČƒ³™÷†jH”ó°Ń"Ņt:2h%š:Š!ΐõČīżŠĮ* ĶG Ć[ ō4¼j÷hč_;ō€J@%ūĄĻzļĻŻ¬ō™/%½X÷­“RÜo¼tšū (óSé‹+{f@c©īRĻ©h”ɟä ńCļ«„Ö>Žw ģóAļ©Ļ!wždļY“¤ ŸČ·šŪ\ œ=1§»ÅmAå¹€\³; mﱄž>Q¹Ŗ¤U¢ēGąąÜ.Żf1ź ht•pä2žŅ— ]ÕkŁgz1c2ŗB.s®émŪdkbw¼wŲ=²y¦ćVy7ņ¹éSŃwÆźWĮo„ßVæ‡~’’ŒžQžÄ’˜ß0æ¬~ē}“|™ļDŸ0Ÿ%Žé½×xeņZ陱¹Ą#Šc‚Sqöu¼·'!Ń4±Ż2š·Ęs ČõIZœ¶]ō2Uɧܓ'`-ÆÄo¢xžOį5Ńäļ³5Č0åX{CÓł.ĢF½é[r f6H~A‡ĻLh#Ÿ¤{h;] p4Ņ;8Ye©č’ĄĻ}÷Śłwū³ŚCv¤ĻāčīVz±ŗ_ÆæŻoÜ¹KÄI"IVču]4æa.õĘU#}ģ«qŠX–2ĪŒ½ŠN»žG_©—5®Ķ.ęs³‹ķ»mŒ}ž£ ³žĒXĻtŽ |6ūlņ-ė·Ļ/£’R3 r@÷€Ew‚  L|°6 nĄ;’žš’?‡ßX_Ćw°Ļgļ¶Ž×½Źxmš ÷įńĘY×yБձĄīadūh¶7Ÿæõ$ż½6PóŠ~SóŖ—”nJ²Gn#ŹGŃåsˆWČiIh»Ń"F³Ś,‘}…Æ,A>؇œ@?’+č]óП[“Ź$«A|–īKgĮ–•Ų÷1®;0’?ėī™…Ÿ»żeD{( •@śt§—Nxöpi¢4^ŗI:ąźo%N|ŠÜs“r¤1šēXä§äyƒŽžŖŽŠŽ¤Ėčqś ģĖͲaH.™Ø -YŒĘ÷™G‹ņč¹ ÄQø^€œŠÓOž-o’ĻB9=•Ō čZ`…Ą™~ó÷÷į÷Ž·”ļ~Ÿ8Ÿ!Ž·¼r{Mņ|īQŹc±“;›9ŁÓŪ§Į ;›FÆėõō[č1oÕĮj€ŗN©Ø¼‘§ĖEä—bĘł^ z sܜee‚]¢+é0øL~¤…/H ;Č4ˆ6hŅy‘*œ)ųķNĮÆļ’?w{·Ś_NōŒ’H;5Sŗ»õ¾ƒuēl„“U:,]”Ąż¬īm„ 4»™D–“żäŌӟfGvéJ§Ņ-ō:’g+Ļŗ°ŁH./Y /Š9œĮ÷ó—›/Ž‹Ļ"F® w“gŹ[åĖh{†­Bn™¬œEß„ŹŚ8-D_”W0¾SĶ4¶=¶ńhÖÕm9Ķ^Ęa£ØYĻÜflŌi»5}‘¾ĄH6ēŚjŲ«9:9–8ź;'y”ńŠó)ģ»ŽÆ‹ū€īƒBƒ§‡‡T )²*D„® ķ–5ģ[蜩L”C§ ō,°Ząö€Ø€!žwüņųö½ģéÓŹ{­×ĻbžæzüéĢāįøm/`Ÿ Ū™× Ę~½ ¾K+¬«¾@[VČ­eoy·č(bÅ > Ąo°ßX{“x“Ż ėč(Ś 4”~Ej߉ö5 š_ńūĮæ’¦Ÿ~ÖŻ—ŌögŻū,…“c„Ļ6)ŻŻJ/«‘^Žŗŗßgɀūetu‡6d¼w9†ģÉi$-HA~£‡č3źö5FoHFrŃx6hēøł}ī% ‰$ųŽ ńMdČcå-ņYV¢”¼˜Ż&P _•ĶčėyÕ%jiķ…6_O4–ULĶÖĮVÉ~Ȟl_m+jÖ2JóŒĒF?ģŽ/čSōāĘNć¾YŅ>Ņń‹3ɹŹQÑǹĒc¾W%Ÿ­¾·üņymƒŚē Łz04(¬Ų©°ŃįšuĢ}F&c!ŗĢT-@›§¦Uw(Ք×ņD9‡ü§+ ˆ·čJ-y<ĢV³¬(ódwčF:–¶ …Ń•ągń/šsė§å+’—üāĘĻśģK*~Y¤Ü®{Ÿ•¤ŚRWśˆ pw÷č$א^¬ī÷+įræįŠīĶäyģ™†–A.K×Ņ‹ō+[wEo8Ę>²häĪ~|æŹu‘Kü‚Ģy\9»ÜŽwE¶A1›(•Ź2%YŁ¢ģGrł®äR§æjśY}ر֘a$˜Ķ­¶Föö×¶µęƒĄ}ŗĄ’r÷IļéĘ3ŚŽÓ±Ė¹Ģ¹Ė‘äąĪĻ$ļƒ>éüśś ˜ø-čEš«äŠņaĒĆź…æ ÆQ.¢rä¹Čł‘“"Ć#“#¶„o ˶8ōsH͐Įjpõ ‰[.śæņ³ū„õ-ėÓĪ{’×NĻ—q‹ķŁķĆl7Ģ<ę\C5ŗźO“fŚ]µRĢP%DŁ,×–æ‹„¢†PÅŽóūŽmaXęĒ@ŸĘӖ“H ~;Čü’sē—ēĻ’Ž~ągŻ=s·w«ż•[­öŠ.%}Ī”~—’„=®;Ÿ/¤ļ’\6)ƒĘŁ•ŒAwŲE®’ÄyŖķNg¢9ܧv–ƒ5‚cobw™'t{>ż;Ļ(‹ÉāŠĖ®,敳(”%ŹEå‹ā‹£śØß›jW}Õ|źu™–-ᦾ]/j0ó!L°o¶żaīD30üw‘QÕ4d#·±Åči.°9žĪ·Ž/Ž÷ĪdωŽM}oūр3?Ķ ń +~8¼tÄżˆN‘K#/G&FõˆŚÕ2Ŗz”Õ!ryDڈžį¶š²asBåŠz!“‚Ēõ ģ0ĄøßßY>˼÷xŻņž=9g8.ڃģĶmM‡ŁĪ8‡ž¹FKŠ–€•’Ź dé`yh+‚Å 8`Až f«Ą‚Ųcŗ żø%ų ’»Šü2:ö ژÕŒæūß’ÖßĘĻ}÷%ėßķĻzē(ÉõĪĆx°ŲJŸū\éóŗ»7ŅK“•¦¤ł•,#Čmņ„ĀŅAČV'č{ĘJĮū 7|céy>žļįoyœØ#&ˆƒ@/ŸÜ[Ž Õ¬¤ĢV¢£·T'Ŗ«ÕÓź%u²ZFĶ N‚•lźµ–Ø”Ż%9̃ff[~Ū#³¼¹ŌjÕĻėłŒ18r¢IōFʙa>°æw\u\v q>ō(ė•ĶēŖo ’ž/¶¦ Ž ®GīŽ¼¹1*)ŗXtĶčžŃ[£·Go‰ī}/ŠG÷ˆŚY:ņXD‰ˆ©į×ĀʇM ­ģ ’?ū?ņ»ģ{Ņē”÷ō{Å3Ī£‚³Ÿ#ŁžŹ–Ż6м`d6&ėß“vŚ=µ‰ś@éˆ8SĪ)_D:q’ŹKs…‚›Ta!ģ šO›Ćm‚?/Aæf¢’5sõ÷P¢’7®ū/[„? |©÷ĻŹ’›ūg’ÄĻŻŽ‹žŻžŚ’ŻÜéóœtGzƒōéKŠŻ+“–8ėT²ī÷ˆ(4––¤­ŃüÖ#»Č,#«ĒF²Ķģ’KYhē:ž€‡ˆ*b”Ų/$ō£ņ!ŁKi¦lSüŃŽ©žZE­‰ÖE P“Ō×Ŗ¢%©ÕIjc- ų ZŻK3·M·-1K›÷ĘĘ.]ÓŪkŽś\=ŹŲaxбNŸ£'˜µķ•ŻEœ!]=·z÷Łģ×Õ’™’“€YķƒG‡fŲy"jAtƘ­1bÖĘ‹ķ›/6w,½3)&oĢźčųčQ/"«EˆHŒHv!dUš“ Į5"üæł>ņ¹å}Óė¾ē;ų`geĒū&ø`As‚ńD/£ÆÓ¢“骟:S‰ƒ‚VF$ņ‰Ē|ÆĀmüĖŖ²Pą·…Ž”Mh^ź‡žwŽ$c{‘Fčc™H ®ūŸÖ;øK<¬ū×-įfåžĶżėTü¬w"]wĻÜķ½œT ŠŪķÆ·ė£¹®{g‡¤ č•ļ$Ł•> ’j®ō2 Żļ ÜĻN3 ¹wzī”OØ/+Œ¬5f ­Æ _€Īē!ʈ”bøWMtRNa T_mØvUK‡īÆŸŌś«²ŚXķ£]Ōfi“£ZŻ×øl 2żlKlŻm;Ķ»Ęd—eś-§6RmŖBa»éżōd<6æŽĘØd+dßbßąųīŒš*€Ö·ĖoŽ’’źY½‚w‡E|޼Õ*¦LģöX)nYÜĮø‡qćތ»·8®p܆ا1ž1Õ¢wG…EµÜ±4|x˜wh\HpšįĄŽYż~Æ|®zŸš:āyŅćŗó­ĆŪ‘ßŽŽ¶Ü|m2¦éµH”ÅŌ}J)子„Å;¾ˆ×āüü‚ŁC䗑“ĶA=é r‚¬"ąDu‘Ó¢™Iä©tY:Åū-åż£ę’įż£’Ž_K©cJū›'µŚĆ„”öDŅć\5IRÓ|Ø÷%ņžųājjÓžōwz’~¦±øĪ!l{ĢĀxu>–āLDŸŻ)Ø\B'_•Ó"ÆÜTŠ©›ŌŚ>­›žB5śӍ“ś'5'¾ŪVĖ…Tn”3:‡€^Ø­”­ø-É|®wŌ…^^ćŚ5‡Z@9,'+·”üj²zCm”}Ń^źUĢi¶¶VöŒN»W;ŸC¾ĖŠųń_P.Ø@赚ß#ƒĮ¾Ö±X§ųÆń=ś$ĢH8ž %TKŲ?*®W찘żŃĪč¼Q³"—Ft ļV*“fˆgšśĄåü~·|¶y/ņšę9Łc¦s™ć€ż‰-ĄVŜŠ•WŸ©1­“śXi„¼‚ĘųÉÉ¢ŗųʗńz܋Ÿ’*³vża0­E3Qƒ>$É2Š“'Õ”i±®ōłJ·‰ŃżžmG$‘Č$łRŽæM}’żāē¾ūbįW‰Õjļī{×VūŪęŗ÷łHś$é®öPŒŌ!qŽß^®“æH-@›bŖÖŅkTCviĮf°Œó|¼“óĻ,ŗˆĶą^iyŖüPĪ‹†÷F©©īWӃgŻõrFA³¤ŁŅ¬hv5Źź³µŚY­—~Ҙd.„61'š—ĶęM㚾JkƖWlЁ„PEž :ˆ[⹈ɗ•j1­ŗ~×xh¶łŁĻ9–zŽņēū‹_M’7žßż•Ąß‚’B‡o‰L5fxģ@/&įiBBāĀě‰iÓD„i’ędāŠ„#ńƒāZĒN޹]2Ŗvd…ˆįåĆ„œž$‚ęōā»Āg°w Æ*ž%=Š;+:šŪ‡Ū’ͧFF£Æ~^˦ĶQź\įd9NŽ%.VņśĄļ8üÆ<óF’[Nū@­ā)„7‘>ēAĒZ’ņ$; #y‡”a„÷ē'śĮÅĆĻJ¦|~āĒē_ž~ī÷ž¦“÷‘hļ \÷®IW„'®{Ÿa$3ŚC}Ņ…Œ%KŃŻļJ£i Ś–N¢;čcźĒJ²žl{ĄĀy“×+¶ĀfOc§nӛiÕĢźb%Rł]Ī*ĘŹU±üsņĆl(+ž~.¤°_”H/ēį~SHOŅ€‡ŖłJž»>æ“ ī7»ß -ĪśüY±’ńł³’†Ÿu÷¬”·Ÿėw«½ļųGū+…öŠés9LŻÆÜo=ŒģĖjA)ö3‰ēå½ų.D1,) ō>Ėu”]J<ŅęWµŽ¶L{®ŠK~ęY“»o¦aSmː.ĢŽfœYČlmÖ¦YmK”[ ‡^_ķ!æć³Y6¶ĶaŸhšDó°:ģ+KÅe9=ćY}¶`Ūn»iW=|¼vzšļ÷ł³fPŐg”" Fż½)flhü«ų^ {š%¾MŒKó!q{āęÄr‰łŠĘ—Œ»;3¶lģ˜œ1Ÿ¢.!ɬ _¶/4]hpȋ ņ'ż'śUńõō¹åµŅsØGgCG{WŪdsÆńU/¢OŌž©•Õ]Je#rŚ)“]M¬āµ¹Į÷°¾,?cģ(†ö—zŃgŲ½„P±$¤ ÷½—/PĻóŅ^Øē8××»ļÖ»·EžĒē?’3~Ö'—¹Žūsß}Y$­A{?!ݐžKß$ėŽuv“æĘČL“Éjrœ<%6hyuŚŻįeŲŻVčķ×X\{æĒ3‰ā’Č"O’?ÉMY  )ÄjÓµ×HÕõzdĖ&³EŲ†Ś>ŁŽŚ.™9lĆmmž¶cfS3 ~Vß¶Ęü¦ļWK+›…'æM‹Ņ|ō:iBŚAy>“é“2ĖĀżD «¼›zÅy~OŁĻŚī›Ŗ™Óč¢oלZ;õ:ą^xż9ŃJ(ā^•s¾™ubYŲ{ŗ“ŽĀäe ź¹ °/VR šDdņūøė½æYŅš§£ėÓK©ŸŸ’łó×’?ėīµu÷ÓżŽmi“4Ķu÷e4Łjļ±Ś{NRķÆrļ:rš¼"ž4;­K‡ŠÕōu°"PĻõģ5ö“+ßÉmp€MĀOī#?–ė+”Jź)µˆ¶]ˬ÷ŌéGŠė^£Ģ?”Ÿ…mlõģm——·}Ž-“ķ¼¹Įhe\4|ĢVĘvķ±ŅN£Ųd¤¶Ī˜ÕSpåR}øļ:ÕāUDE¹•ņ‹ŚŽ:Ķøo¶±§uŹžw½ÖųtöĖō*xqh‡p9$jzō͘ŠqéćÆÅ‡&Jh’˜”ų$aZĀš„“ ācāžˆ;(WŽ+ÕŌėjķ’–€®¶M梷4œę3—­·ĶiĖiūĖÖĻvŚÜbęAWŲ`Koó6+ õJz]ż’ÖQ}-/xa–“FĒOσ”|FJ"éé7z­å3Äy°Ņ[ķ„MŠOÅlļķWW6nNlƒŲ~±3bGžE—Č=7jC¤É#ņ…]2,xDPæĄzĖĄĄ±ŽE¼˜ēēlōĄ¶Įę|ć˜ĪõŅŚ,õ“ŅH9#——ĻŠĘā2@žŠ_éå"K[ŃÜŌNļ‘d6v±Ņ„å}Œ¼@ó;–ņŪ桞Nhqu¤Š.÷ĖčśżĶæ?ößšK½{=Ąu÷Ózļvtś§»/¹IŅķ}ŁH.$鳝exIcX}dĻ+,”7ēÉ\ū¶‹Hy‚lS¦+ÅÕŪjkķ¾–CÆ _ÖsµŒæŒćę[&»—=}Œm˜-Övųe²ķ¶ °õ6 sõ„ZZķ¢jŖšrNlä²™t¹„4vüūčļ}¾Æü‹¹ŚN~§#XcžCŲå×ņ eÆŗVŪ­{šėm;ß=B¼3CAk– v†N ;~:"9rnŌĢčŃ1 bcā¶ĒåŠļOĪĒæß!¾m\·Ųŗ±µb‡Ę6Ž=s :.śtŌšØį‘ė#„ļ Ėś!ųLŠŚĄźWüŠł~õŽėÕĻ3»ĒGĒ1ūFŪ&óØń\×jĖU®¶P΁'D-ńœāłU6†e„ķƒzV§±ō3zórx_R‘äHIžw°ĖÖ'?S{ĢśżŪźR©±~:ŲõūÓ’?ėÓ©ų„¾ūz÷z±”ģzļö¶ōZbh˜ń$ĪŁ‚ō'3É&r‘|"Į“0fj ŻO?Ņōģ¶„=eYł~–§ĆÅSQģ+¦\U« “ š®7׏źŠŃĀøit6GŲ²Ū©ƒ8 A1ĆĮB'šž·M³2ĒĆōÉŚ^ōłZŹ8y Rܶī!ū„wß+æżmį·ßf}+ż½’4’ £=X9®‰cbŠÜ]i£^SWk;õ,fAūwg?ÆŖ>Üæx`ąL”ėĆv„WŒ8ņ’pöąm$ĖÖCŻĆ#°%3C˜7L›dĆĢ̇™™™™™™6œ 33KCż—œdw/½wߟłģXŽ­H}ŗN3SS×?¾MBēÄ¢Iƒ:&_JĪž¦7ąx%˜6WŚ ^F'/IīŸ\&ycŅŁÄ°Äc€ßSp‚b÷Ē,Š®%G®/ö(d‘·ƒ§h°Ļ=\„äx©>!ʹ¤ŽāAZCü•ĀĒPt—ėÅEq'Ųžlö³ŽéĘaœĢcČ}3Ą·×ߗDk©W®RGa­—ę•z÷m ŖNj’‚@˜ōżžŽæą;~źėˆĖ~?ÆŽšūyõ!P;±"µņģ2ą÷óģYAŗ28–A{w{÷ƒū+īaØO‹É Yzk²e¹9ÜG®Ś‚"š(ŠŻłoü\AF‰Ni8ómrf„æ’YŻ .ÓĪi¢/B=„\SŽ(O”>ŹipS„Óā,a5»q”ĀĶa72;éŌ~ņĄĪeŸ²¦YĆ­ĪVv»052ą6¶(Ż„ØNĖÆåÓ G„gāRi‘²Ū1Ü飒?<92_ōż˜‘±FlD\·øĖń—–'Ę&H:ŸÄ%ēH®|y<łIr…4ZšÉó“W&{’ė&Hĉä„<ńõćfĘNй}(*SäŗšāatčsļQĻŅąśAw\½œ¹(œ!“zŠ{YhĮŸÅ…šNTåšq˜ŪČ6b½ģ%šXõ˜4ąĪ‚w*¾8¬§‹6’Ģ|Ė!cåŅrCČ}å@»äI½ūżg’—æšæĄ½›’æž?®>¬„g= ųĪ~":äĒŁĻÖō``ļŻ€Ÿų•üf3gŠĶźå+qõ€=]ąŚļ” ų ĪČOę]B a½ŠA,,…ČÆä%JcŠ›—ŌŚ`Ķ©¹ŌłąŅk*Õd‘īņ*)^..ńsń^ōšK˵b—0ĒčćŌ:2ƞbŻ2›ŻĶQęS3Ų&„ŠMŁī'£éØ0ØĀ?āÄÖRv%ŅńŌ52ų©— +1(ź 81֊]70žKģ–x=18)1) >²&•Nj™“:éaŅ×$üßąäbÉ“–&¾N(ģŁ!nc,[6fōᨑ«Ć³…łBnxy¶— zēšä¬čH«yT·’ —–śˆ{^hČĀ™ńr”m%ššģ©3Ū™ž} ØēĶĄ­č2tFPō~ĘŽOō½'~vÉї,éł[’„æš TžÄ/põ½DmąźQæ ©ųm‡8{ż9æōtaŗ*Ż“ū¼ńąĻvgš-.ē8ė_}šĖŠę Įßp+ˆ §ŠSŲ- 'J‹åĻŹaõ£ZUkÆŃ®i­µ’źB„ƒR\ńĖyäŅv)‡ŌD#Ģā§į©h9wM`‡0÷gxj·]ßŖbV5Ŗ‹7āĶüBa¼8DŖ('*éՓšÓ59h’g}HJX鈳‘õ¢ūĘ܌y× !]b•Äa‰²&“Žß÷4®FüŠųCń㹄¤„¼ éüń'ćÅŒŸ·?ö·˜%ŃC£³E7Š2"ź„Ÿ ݲšĖüŲ=ŁUĀ™ĘAkO”W2+'KuĮ?<ä³ń#ń+T]åźroŲQlfö3…©Ä1·éÕą«ÓŁ ļ}¦nĮŚ~?Ū2ü^ĒŌĪ‘US;Gŗļfמļū'’#~źĮÅoXźÕæÕ?jĻžP_)žū_ąźßL`ńßé7“Ęd^Ęle^‚hÅnee®#÷˜ė€\ų®Įē?š5…‘Ā#!8 œūIi”\I¬µusØE» žWŸ*µ”kr1yøt@¼!œį§ā\h›‰‘é‰ŌyŖ}üfŖ„=ˬn>0?ēōķśn£·5Œ“  ²iø³Ü“_ę‡z)R’ŒŁ36vA܋øšų ń9ā3Ę{ć?Ę‹›×1®t\­øö±¹b6DW^µ<2%‡ļ ]rŌŪŁ|ĀŻŅ•č|­5W§)7å»’7NøČGššY”mārsĒA» v hō4Ģ+z=”®MgæžVuä§ļ^½cjĒŗ*?:'z_f@ÄSŃ ;½T ’| ś~öŸ’;~Ń©ų}Æ> TæŌżqõ=€ßüŌ«·'@=żæ_@æ“ē9Ž@Ÿ¦ŸŃ˜IĒTdz0˘?'ų÷Łģ{¶wŽk€ĀńW¼‡?Āoē?óM„ūBqŽhˆł$\Y”ęÓkÓT[®$·7ɒņQž'g–§HÅŻĀ>7^ÉŻfhŗiC†S=é[ą˜ĘS„Č>K²Ö™×Œ{ś½”qÕģc7¤čŗYl<ąw‰o&ī’VĖ>9^”ß•]jMĒD×öąt!(Œ‰(9:Ŗą°+*%*!ź|TéčöŃŁ£ßEõŚu1ŗ`lŁø>qÓā&Ä5Ė÷.vgģšŲZ±…béŲA1Æ¢D{£qŌӈ įʆViį-ė1ƒV»[»ā“ĘźJeƒ¼Iz'‡y–Æ„W#7É) œs±7˜įL!ʤn©±§Š/7·@|Œ†HiK5śŃ-ņƾó‰T`"Ł÷ł+9V’8æćßćØ^ Tæ|Ēoxź}kRļ|ą÷³v°0ą×‚īKO&8Jß§-P E˜ĢDpšŸ˜,ą Ž²Iąņ¢čžÆĒWą‡óŒ°N˜.^lj@Łļ‘›+A³ģ‘ßIÉŅ9©˜DĻnŻ9ڈš™ń1Œ„ö yēĶē}¼6h–{Š+ĪyD[ .WnĖ»%"V ų§8‚^põ¹»l;ˆ¾ULMF…Ģ7…ny§'[ ł©(Ķ@‡‰@ŸĻ\©“s~L]ńRŸ@ö}Š\`žŃ÷?ßń ܽł~{~ā×é~?ƾ’¬ż OåĻŹtsŠ~Üłš‘v0™ŪW Ī‚-Ģgļ³­Ą’ķ@1x ^‚÷,Ćg_,F‰EĮ³R„Lɏ¤åŅˆĒ’Wn%Ļ–—Ź)r˜¼HZ*ŒąĆšXī(ӋCV“ÓŌąęĮt3Ŗ>Ém'Z%@ÔճėÆu‡9Łŗo"ˆZEwēT>Il$* ©HżCy­tR[hćWŻŪ‚‡{”Ā††g‰ˆ‹ųV%“Wˆŗ6“[Ų˰‚į ="KD7Š9Ó3&!öNlŲ‰1/£GOŒžȅDŸ‰šÕ+ŖHŌūˆ¾į½Ćr„v ć}ī”}PKwmW’ó‰vM}­dV\r©·8A˜ĮÄ,®…öp™@»äg/3)L"s C: ĶŠ·€ÓfS©6TMpl³ci©@ņO:(…śūģĘļÓTĪRżłē'~»ĒžæzVæjÆ×R»ąē£ÄTüw®4„{,¢wŠēé'“M‡29™Ŗš*灋7˜<ģ0Vg÷r÷¹Vč2z†8\OĄšE>Ač*¤Ž Ē€G/‹óÄźāqŃ%Ķ”¹£|A¾(÷”H#@½ Įuā>1£éÕTś*ӎ½Ęģ”»PµHū„IŒFU½²¾KonFŁmI;jŹōEGųmā=¹½śVķØfWõ„:H³µ¹ĪłīŠĮc=Ÿ½åBo„ö Āj…fńzū{K„¼ iš;¬GXśˆ§‘§¢vG”čūŃÕbŖÄ܌Nˆõ1ʵ=ŖfŌēș‘"GŲįjx«°i”=Cfyėyŗ værŅNÕ­•V»)ƒäłŅ>q°ŠˆŸˆ/£4h4g°½ śę1EĮ5,ƒ]Ÿ™¶Ø‹°¦c€įj‚ĀĢńöӗӔEL¢?ń„~8t8 ų®EF—Å/p÷Q ś:P½Ø TŸŖ_ś@Vż{õÄóæįč×Ó įz=}<ÄKšŠ&#SŠiΌ`Ö3w™ ¶ ;½Ģīą>pŖŠ”xÜÆÅēqeŽä%ᔐQ¼ d±ųYģ%9åi²¢äR‚åWā&!‰Āć@}®bßĮ3Ödƒaa/ӝ© ŁēĢź†O?ØG[|Ön{ U‘.ĶDs‡šk”TBŽU&(ē•—Ź[õ öĄA;Æ;'¹[/ōōõ>óŽ ł5äCHåļGĻEOœw7SȲŅ”ŁĆІ· ~(¢Vä›ČBQo£r‚×›5.*[ŌśČÄČÅ1ÓĀQų†°±€’Į’^ēMŠm÷×+'vv“Õ©#•-Ą;DÅWĒ‹Šg®wŒ-ʞcš1<³nč}ƒX˜ÜVb%¬½DŁä yG^’gä yüćx^Ą÷^“·ä=łH>“Æ€„‘Šāæāóæü?š«’gõŅ÷ź‰=«žˆ€üWˆ®H7¢»€ƒŸ {jäĄ@Wł÷€”“I` 0u@‹ī`ö3Ļ™Ŗģö#›•ó‚¾?…h|1x.Į·ä€¢‰J óøBl$)ņ9\y#Ÿ–¢Ä—ü+œ7B^“‚ĖÉ­ę CØ<*Źg¦Q<łh%› Œ£‚ŃŹ8`>³ŗ‘éŌŗ#Ūµ†I— —”–ĖU•ēŹš˜/÷œ…ݧ‚÷NšŽóīõ~ńNō~öĢó ÷Ģõ\÷“ōÖņ&…Ōé23dNČśŠa į7ƏA<ˆźU/źB¤7²iÄnȗ£Ā¼aĻBa³BG…ÜöL *ź^é sŻuŌrģŃzŖĶ”6r©š( ÷q6ÜķēbĮ½Šģd&-sņMvśµr^+Ŗš ¢ķ y@®“ ä,9MNĮq¾:/‘«är›Ü#ČS@ņ5 ų‰|ƒX“~ÄąƒßČ?«_µó/)żoųś[÷§Ē£Æ±‡>BŸ&½Łš!虌L{`ŅĢ6š„;˜¶ū޽ĄÕDŠJŠ[ąœø/¾ƒĖóėłHaP\L–¢ä–ņhy¬ü\ü&4ēāāŲV 1hZ‡ųŽ|s,”¬li:3I“®„ŒÖĘxc‘)[nX=‹©ĮõؘIžUŽ)”~u”V=®¦Õśj[µ!ŽÆĪjīcA=įŽeŽwŽ^Ö[ŲSǓߓą™ģiįięqzÓyo{ny¦yŅzzdžä ĶÖ?|GÅFՏ±6üAXRŲĄP:t`<'d¢·“'"ųƒ[v÷r-v>tdvōŅŅŖi•DY’BEāOĀŠ(īųö³L5ę=–.no;5ˆŖŁĪI}lĪ“ƒd;Ył}%«ÉZ²l†ļģ&ūÉar°¼čŽ"÷!"_@$~ųæį÷½śģgõĖš™/)ƒ’éČTü*€nK÷L6“žO/¦—Š ĮŸ§ҽW›¦¤vK< 9r,ķeF1ƒŲ\UäAo8j‰ŗ¢ÆØ¦ła|°°[˜*Ž’6K¼ÜBŗ*ÖVń×ńŠ=Õq)\_ĘMų|.~=rs§čÉd‰•ĒĶX3٬m·;’Ż”Ęfē£1ü±’¼[Ų ¤‰»Å¹ą.^ YĮ?Õ|ļĒ÷pM`Ś‚ĀQ~žĘ)¬F÷ ģ·Vo+Ų~j'3©}tK¶&ŹĒæŠH‘ņUi©4\ś$“(sŌ4ź%eŖRZ½«¹œK\g‚ŚyŽxCŹy'zŖ{^‡wzé~äŽT"xDp™ąČąA‹Ż›]]åŻėݬ§gH‰°“įÕĆӆGz™B*z‡{× ¾48(kTÖ}Āu×9ÄńFūUk©īWX倔"ö¦ókńtŸKäŚ@ō•bĪĆŹ`z+(–œ°ī€ÜpŅ“&H]RŽz¤1iE:’ҐK¦ĀO,#ėČ6Ąš9G®@‚‡" Ķ‹_ąī•?šūYż9꟪—Ģż ĀnŖ Æ©=Ż¢­0{ŗpjVŃ§Øž …›SćAł“z^ic’ż uŸa‚璘Dv[—ćĖįūøœ×„Bń ųūfāG”‹@ų…|5^ä <œ_!o łĪx9wŠ© Š40ck6•ūę]–ÅH屎T\Ī.ŪŅ*iŌ_z/”} £ŗŌiŖ”ŽŃ¼ĪP×G÷•ąļm/ö6šPžfĮsƒī»Ūø«øW»Ū- źtĢ]Ā=ŽÕՙÑąŲē¬é^<Ö[9dvˆ’ōj¼×åIÜ h“;Į½Ū5Ī5ŌuÖÕŃÕŅ™ģx©¶Qµ“²H®!µ[ …ų'č“ˆ»ĀŅlĘ>Š¢PĄŪ½ƒø›Az†¤<) «’™d„# ÉE ‘’ä7R‹4!mŁSČ<²āp79BĪ‚·AÕ¼­cü—ųõ§F~?«—ī¤öP!¢2“—„«„Ī:mOw¢;Š­čŗyéĮ×üNMō>Ą>a)“l!„É»¾=ĘžĶjdž‡ČbržźCg…:­Ķ9Ń.TŸÅY½Ć…Dq²ųŌčŃ!örĻł.ü œŽæ"8¤Żāį2޲™™‘ō:ŗ2#±ćŲ"ģ%փ.į£Ā|é­|Ģ£{nļ Zī>ęņ;Ė89Ę:68(gqēŽUZešī„”C ½JJŻÅv‚ÄoFn4€ĖČĪ_¼ž…¾Ģ™L=^ģMŖ‘Ü$–8G,[· Ū¶9¢’O2‘ü€beR"³;q8Ök=Ł ž½Šō#h˜’æ@õīčŌź³ļÕK߫ϓTüņZU³&t 8šŠµčREś6µŒjM¤^›š’ģ‚żÕŠži­±<Ö³ŽUŽźi属ZHŖ,ųŧĢv —ŻF]ńj܃÷ w…=b iŠ4FZ V³ˆÆ…H”0_œļ 4J‹H(Œ²%˜„tKfŪ„«Ž:£×(‚?'¤HŠŅ\Ķ® Uū(¹AŚb>é¾tE¾ PŚķ¦¶Q;©…:"œć\mƒ†_ īÜXšž»k9W]]\5\Y\·œµœsc™VJŻ©”Ó~ulsüźÜå|éĢā.Ō"Čr}vŗ!:³kƒÕtj3õ†:NĖᨤ„Ss(æÉ„±Riéœ8HłKčÄ}d*1ļa%‘‘ŗ ¹­ÉA4ņѾk_“OŁĒą8e’n’a?²ßŽ 'éHĄ°*pVŅ¢pZ*‚»E/‡>źūń+ŸZ=ؾTĪų³zé^*~Ą/#. °&]b°ü诙n75˜ŖLEQ~Ų/k€)ęŲõ­Yf“6÷Ŗ)˜›wĘV³ž½Œ„šŠ¹…żĢźÜ4'ń3łŽB1pņW„”²)M•8łÄP1T8Ā·ĻKĀ ~źÅ6cÓC˜ƒ,…ŅćѼWÅ©RE%XĶ” Ō¦ŖÅ•̲GŚ/  ‡&Ė3”S*ķx¦•ŅZ«ĮjyĶvÜrĶ ²‚^5 Źā~ķ,ļ|ėüģé¬ēĢå|ģčģHv<×zj…Ō–Źe¶:BŻ„śÕ"Z%mƒ#§ó¹Ćį( >”¹ņB:&ķJÉ픓jnĶ«2Ź}©²ō›TLŗ)>ąłh"7—]Ļb6ÓIō"P÷ČDRxČ ū„½ĪžcO²Ē/M°§Ł ģ5ö.Ąń¦ż¢Q!‘‡…€_ė“ö&#Į%d(Ņ3 zƒ~ūš Ü}T=µz>P}=ęoÕgßļżs¤Ī¦ŹKS ā®]¾*@ĒŃüĢ ųĶ4€Ž1ČĀłÉBū“5Ņ\`t2rięFœńPŸml3ĻZļģd;5Œ.Ė”c·p±Ÿ/"ċ%E]Ü+õ‘’¬8”ĄH_ä\ņzńwį¬POģūz^Į½`Ņ1yĄ—t`“»…ā)­āV·«IZ¢¶JY-OŚ‹Aā5±µ-·P"ŌÆjXó$e»ÜK٧Żr6twĀAgŻeŻI®p§įŲźhķČā(ćģŠ‹µ¶Zzm™Ź©¹•+ņ=ِ§Źk䯚ÕeYRK©Ļ•Q „XŅ#q¦Ą ÅS¼(ē€X,¦øäęRZ)Tź"f:į(#‡ŁĮĢkČ.ė©ŅĄH“HqB‘Sö|»ŸŻĀ® „‚] žnlw€ļŒ·ŪŪĆŪö;›&Į$™ä%eIŅq»Ōč1Č?÷Č+pņ’üÕKź³ū©ÕŸüŅӹ颠`*‚Õ@µFuŠĻ©TŠ+ź= l>Ā~l=6ĻQĘGŻÆsĘ}£¾RŸbĢ2[ķüD¼'Ӆ˜–\(ßPl/5—ZJg¤r=„ŖzA}®ŽT›jWÕ»ŹD9I:!®Ÿ MųVØ/Ū‚)ĢģgjŸ¬ƒŠó»„RØ2M=¦ņZ°V^Ż%”V‰›…ÕB71Ärs9ær\yØTRŹ’L+µœĪÉ® īÉīŚī®WuēbĒEķ«ŗDĶŖŻŃ¦kå5J›š§®2AŽ!Փ’¤kbqĖŋb}iæ”E‘[KAā&Ži¼‚ß/6“+…”,rÉ'v× iłóčw’}Ā`ŠżØÜŌ[2‹” _ģĶv»²ŻŽ²¶l«vmg° Śķ¦v/{¢½ÜŽ ¼ś¢ŠŁ07)ź“é¾b8Ćąoƒżņ’‰_ sˆüC::däŅąƒ’€f4móØF °üą; ’6¶½VØŁÄąŒēśWż¼>Kļ«_ÓūõĶė†5Ń®>t1u™^ŹŗųĒā/r69‹)ē”źͧµ×>©­ŌõŹp‘-Å{Ā,ˆ± ü` µ…ņBWp9•%§üZ\,äęē”±š*–ņSÄ4ņYąüK¢S+täƒ1B¹–ģDęwśIŻg^|¶—B¬e°iū‰uÉ:i·ĪZ׬ĒÖK“cģ<€k{ˆ=׎jŸ±Ś>[#  „Śāt²TĢ)ņyšæĆļ{õõßń Ģzˆ ÓB¶+D—€¬÷kź¼śō“F?£¶ƒŽ) Ļu‡ō"Ļģ¼v «Ž9ŚH0né/õ~śG’}ż€ŽĢ3Ϙå­=Ö»8ų›}Ō%¶ßQ”äõņ~Ł'ŸPˆZɱѹŚÅ»½ī’®ŗĪyŽŚxeŠ”Ux€Ö‚ž+Ź“ż¶X“ ØŃ°¢ć 7ƒ›æJ“O’ŸųņŸJ‡ÓÉt6P E!ņJŃEĖ(Ś1 \z<õ öHN2öŲ'ó†QŲų¤ŸŅkčü‚?ĢÉ?BĻj\6Ś™WĢ›ę9kŻ‹ģ¤¦°łńRįžäVNŹįŠGm”]rD¹Ś«^čĪā®ęšį v¤WßAü½G§¹[ģsv× Ā.Į/t–Ŗ*Œ¶CŪ«%:z9Ni·•<²[²Ą=ÖĀÄā4i“ņX¹«°Ų.œäē æ mųšø A[¹aģj&žÉKƒ==ˆģ±cķĖÖ«–•ÖĀÖx÷ĻĢ÷&ey­¬VEĄp‚µ¢ŠoEŹõ GĪ-sŃ~m# Y°"iNś€žYNö@¼Žś’‚ßßżƒEI©3„3AĢĢ™ŸĪ łŠ ĪQSØßRĻėƒ’oŗm[]­Gę #TKC½æ”/Ņ·Ė7Ɵ¢±Œ±ŹČd5±}ŁfØoTf{Œ«×š•ÄH¹§ņ‹ÖĮńĮQ×I»²ŗŽ9+8Ļ9¾j]ÕÉņń ßūŃrt•Į-š-\‡É‡ž_ rdvdÕ2Ø[åX閱ņŸų|gač“j(]•” R›hšó™³ĖrżįžęĪ44Ø]Šp÷WmW6WŠė– ¹'»Ö8§:jjŗŅ^~'ްĄwĘNœ‡o&8„3üp¾Å×ńz¾¹šA8(äāOq Ų®ģZ΃æįž|ž)¾ ;¬"8÷ŖĢś U˜Z HöJ«£•Éś ¹c9לnĪ6W˜{Ģ«ęgÓ‘Ų¢puŒ²,9<Å>ū†ż84=D`]Ņ4į"Š gČ]p€’wüžż eP"ķ”c!ćeՒ—Ī \Č}›©īTŹ"ČHPYæŁ›¬6V+Ņ\bŌ5~ÕłņłÜ¾¾žlś*čUŒy†jåļŚT£bčēōd¦»†;‚»ˆSåÕź-űÓqŻńŲ±ŃQŻq]‹Ó‚Õuņ/Re”!žµGóAįų%DķUa¼t]6”mj²öQJĒ+Ļ;ƒcĀÓü<”,’)ćÕ źHmžc¹ó7××kПÆÜrŠĀ ABŠ{××AWu÷Z÷~wkwG—āōĆ~ł$÷bEQL+lÄ%ńxüēēŻ|< µFµń:‰GÄ4bA>'*Įf7{”[Š:ąĢ¼Źōœ;ģy“¶©–Ō)KŖĆzŌ·dkŸ9ÖlkV5K›%Į70SĢ©ęN󮉭,Vȉ¬ėĄ¢ vi»µ=Ś^eŸ„IØ˜Źą#†9O€}÷7ü¾wüOžżÆó/ß;ęہŚLCg¦³Ć‘Ō ”.QÓ©ŚT,(Ļg¤Y Yz¢Õ˜·Ž˜ŒŁW’źĖę›é‹÷÷—ÓWėōūśAć±¹ŚŹiļ“«“·¤kź1·čŻÜh~…“L™­ w“wD:hĒ­¤ķWõ½\SźžÆ2Ї£h6‰)a˜ØČ‘JµŒzAĮ¹e8š—`.ŒŸā¬BE)Fį5ŹqŹńÕ¹ĘõÉu͵ŪÕŻµĢµĻ}3hNš­ īī¹®m.É=š{źVŻkæ«źš‘}åkŅ)±µP„/QUwĒ'PyT55Ó\/<ęcķgo0iŁ=l3®ź‚ŸcÆåāŁ1 Ć,§^‘V ÄOĄjˆą„³™‘¦`śĘgƒx\Ķģk.7/“f„G[Ū¬{og²«ŲŻAĒ찯ٟ!3‚hBś‚_ ę&¬ŲOü¤TüŻ’Ół³ŸēÆ×“ž8Ś &@:?ńwˆÄ‘Vöa«³ÕŹjmõ…ׯĪ\£{ż{|¼²ß«Ö怗x£ļ6Ž˜‰V7ąüRąxŅ‘jš ;PłŲiø‰ųB¾¬¾Ņ®kk“"ąĖ6«#ŌÉź µŖ²DŚ#ÄńšznqõFæį~ü1”Žō›²EYīüØ“Rœ"”…“ńØ/ڊrąĪ|Ø8\ž­ÖtÄŁ&>‚†Æ£ĒÜ6'ūs‘ gss«ŃģĀ-øüģĘĖl£ī‚žØa·°N˜ÕĶ­Ęrc­±ĀXģ³ŌŲbœ5ކEĢŽęBó¢ÉZ¹¬ÖTė õŹ ¶ €nšxAœ`aŠ0ŻÉD² Vųņę~é’ææ®żģܘõĒ€Z ”cčD`ĪxŚ Ńw‹ZI5£²Q<ć rvĻH«ŗõšż<ˆĄoĘX=Ü’Å7ŠÆč#ō÷z;ćˆŃĄxm 0—˜óĢ#f„5ŽĀv-x­ūH;ęZ+—¶*ūŌŁźbu°ZDÕT]ɦŽU‰R_ī(īƒ øŻäfr øēÜnT—ļ!—Źe•5ņA©ŲAØĘgĒp5øpī7ī3*‚'įÖĀ,©ØZĶ±Č™ŁżŁżĪ-ŗŪB^ sŻvåŚœ\'(·{€«'Då`Wq×fgG6-TݤdU«Ŗ©ž’H…āü}“ ŅĄ]ųlü<߃L\ ķāl¶$;™IĒ„1v(™«Ø—›Ć¼¦—R Hyi½4Ÿæ‰FVų\Į( Ÿ«­įĘ㚁Ķüfs)čq‡Uvōė‚eX‰vy»3詝żv(hŠŖ AG“ed?¹F^§ā÷}zĒæĒļÆė·?ė'½ŸPAŠ“"0ŽPZ ßQ'©É ]ąĻo¤ Yhæ¶ś[e­œV`„£fCc•™ĻćGĄœ!F`Žŗfs<äšPs˜QŠllņÖ1xŦ½—ö¢uül±¦œC£,PÖ(Ӕ.J=„»²YÉÆ>”7ˆ¢°GW™ūČq>nøˆb?y§¼Oj,Źš5šGī;‡M˦ē,ŌOĒGų{"ÆÄkyåN×dš7õœŗó„ėŒ{\Š-Ē$ēÆĪWŽĀŽĶꄬB©ĻŌŖj7„¶Ü_J/Žą§ą(¼?䣄>üLįā(;JB'ø0®ūS›¦ÆŠ=ŲĄ¢Sø,lA&]‚*NģÖ`³·ńFæ¦æÕM]ž1tĮˆ5ŠmŒŁĘy@š³§¹É|iĘĮ¾nm·YšĻģaĻŚoĮÉgߊ ³‡\!Æž+ü¾×æ|Æ? L] Ģ)~ gC zh…Ö!+n”zS¹(…"Ūɤµ}ÜŖl•·ƒV·F›oõ0^ßX’}¬ń»qĆ(Ś‹˜ńÖ3›±Ģ_×§—5~8¬fv(ĻĄs…o⩇|Ü<­<•·ÉCåÉr+yŌSL#p¼ @„!Ćt`ĻqŃT>XSź% ½ĀGœGæs™¹qlQv$w ø6.ǟ~—śĖķ!ŽŃ8ņ9÷9ņ9Š8f:bgœ\‡\]\‡Ķœ{½¹mĮ…ŌT{)G•õ†ÆT’½ŅJa?_BIø.NeTšŸ¢Rč+÷™³¹ÜtvóÖ Ń¹˜ ģzųŽÉōēw–\²ĻX”VssœjÜŅéūõuś2}¾Oæ:.£ŃȘe\5\fEs“y²`«-°Ö9K·’A¦Ųsķƒö#`Š$R2ą@2ģ$—ČĖƒßĻīƒ?»ŸµųŃ{7Pæ»šŚ‘Śżģ>(˜o Q§ŅZī| ¼:ŸjNEwxvD*Ū£½ĘVw«Łļ¼ž×_Óoė5Ģš š[Œ*ęi³YĖČčžöęė_[}»‘Ķ,f^¶6RĶXŻē¢_Ü!e”»Ė äįr¤įœxX-üŌÕBƹ…\Fsó¹śčäĀ籟Ÿ"œāÄōR^ł„=RWŖ[Ō7jIm…āhęhąø¦ÕÕŖi%“\Ś{µ£ŗK©­œVĘ*5 ÷Ļ ųØ6W˜“ƒĪs“P^œ/E9Ń®·ÓŠn"ūn:š=ՓÉĀEpg™W”›jI&Ś -‡•bīö|¤ÆŠ{čµō*z=½«>M? ŌÓ-!+>5’ĶĄ” ›TÜi=ƒų‹ŻŹž`oµ’° ;Š"õĮĪ"ŪČņā_š‹żæŸõƒßė?õóūW¶„:ų;Ō źeR,ĶӘ¦čŌM@v4UžĮ$™Ml»œ½ŌŖųõ‚ÜcĪ0^€Zyg ¶fŚģ8ū³yŹxe¬6®čåüQ߆|9ņåŁ·/žĪFn™ć¬!T ŪõąW sÄęŅr) ī˽b–1·™™ĢUę »”ĆØ"ŅŠpn{‹ŻĘE£©bOS½€ßŠÓ™)lę)5œ€w<Å* ų­1 ½ž¬ššūżšžQƦÕ÷čßō¼FocæA›%Ģaą‰)+æÕÉZ ŗŽµ³Śu@Ƭ²ĻŪmxˆš¤(ŠĶąąŸ’ųż¼’abj÷Č@ļņĄÄĶ'Ą ßĄÅųųL=†¬øœ_!J¦lĄošg=ū˜U šė ¼‘ÕL<æĢc÷£nŅgAŻŌ±'C†ēü ¾õ_7éüµŒļ?ŌtA3Ü^JU`[£Źü+aØøEģ 9;ßĖ“œSü(¼ ōŁ& IĀz>+’;^€ßąż|?ᤐM|'–ƒµ®(lĮ§PD#DÄN.+Z†ö”5 o£üx=¾‡wĆoģ*ЫŇ¢*å‘KĆ„±Ņ6I¼ŽÉ}Sńؔ’WĪ#=÷ˆUÅ Bø0ö ĘĶ8‹ĮĢZzj Ū>xÕŗÜxž\w¶5ū wbsó;•‡xH85nɔ>u ÜĄ:X‡ņfcž>×éÆģļåŸē?ģéÕ+č£ōSŗbT½g$š­ĢÕęs3ĮŖgM²ŽZŸ­x»¢Ż|ü1ū%dĄ¬ -ŗ’Éd9 .ķ/ü~væž;~?ļÜ?6!µ÷|`ņŹP0©WßĄG|†X¼Fķ|ėĮļŖK?ƒ3© ŒæßjŚ„„e™oŒ2F.óÕ‚Šbϰ ĢÆŌ»¢õöā+’8Ÿč³æuń5ń·ŠĘ4£øõÖ.Hkl]“„Ē‹—!«É÷åģŹi8K —ŖK×EAÜ)Nó=ł:‡Žšķ„÷Ā ±·ō‡”KŚ.Tå+ą©č:7‡kČęŚ¢X|4cäEÓŃ)ü˜Ėwę§ó³ł­ü3>VØ Ō #…B°(Jݤ©ņCe rCŹ(¶Ŗ ł…4Ā ~_9µ’Œ2²Oé1twzĶbKɃsuø…ģ®?7u2ķ)'éd·''ØĒĻSa%&‹ŗ`>1h#Iüƒ|›ĮG•ń÷óÆ÷?ņG.ŠŸź™žĘ!C€8œ “ß@k«õŌņŲÅģöą÷AäIzRčˆ‘ÓäéŸųżģ>’Æųż¼óū䜍Ō^ˆµ«Ō=ź9õ¼öĄń.|ghŌ2”J*j>BnK$rŽŖ Īo‚u×\lœ0žš'ģģō6‰H'3ģ߬Rę 㤾HŸ ĒL}›ž˜Ļ,kcJ§‹r#Tē‹ÄNRˆüXv~”TCrĖu€I½R-ń ų”?ĒI>Q8'ģ]ņų7V̳|.l‚{Š8š»Ī-EŃR“Tāqää!½Š’oˆ÷ čŹ…{ąžø/Näkšæš§ł«BŒ“IöČMÅwü¼ #>’;ųgø>JĖ>¤Y&œq3ŁŲ*Ü=ī Ē”0Ăž\ʵä8v8żœ ±ß[쪁łL3ä,ųąD«‡YŪ˜ÆŸńņÕšÕöm÷!q_’6’'.ąŌ£°{ėB|gä/æß4ͼĄ Ė­–hē±›Śćģmöm›S–“…õ]CN’'?š Lųٽüßćč?ń}źęźu2ąŠ,ƽGŌĄtüTĄ/Xųø“Ä“OY¬~ÖPkšŁŹXld²\$']ƒyBg¤ŹŚU­Ó°ĖښCĶ'fFЧ%Ķ?Œåf_«‹}ŽŌ¦[±5P _Zˆ׉¬ŌYĀņ1ł ÓIbå 2RīČo%Ÿ˜E"hĀkžææĢĻ‚„±ņ²(?‚,™(Lį{>=Ę]e[ræ”k(ŽÄ'P ܘ7 –īĶÕdK±ĒŲ6ÜAī5( å˜ē—b 'š#„:b6ń æGĄć4x ŽĘ3ńœ uaC!’Ug§±•¹Ą“Ÿ” „E~ī8׋› ž/½„\¼FŚßHcZdzŅCH„ŻĆ ³ Bi¦ļń'ųŪśŹłĘś®ūBüÕü“ü—ü^½¾¾R’¢3ʃ27[›kĶ×fZąÆY Am+dĄaözū*(˜8R’“$£ĄĮŸ ’Wü÷ož5{e18ĄŻą .‚W.ž9 x›: ¬:‘j æø«P žżRždµļY£@}†[qfFcƒńҜf£ZÓ«Ø/vA뮱ɘfÖ·hū¾}<¶ļX·­éö/D_™Čžē:ćƒüa ųML”Iפy–|Qz"‘+*»@m –;H£Ä-œä/öj‹½¤>ņky£LɤMbmńwĄpjŹ åŖ wØ)†Gć9ų8ļ™h[ˆyļć#ż–©ÅŽgoq=p?ļFcPEœ†Ÿ…<mD«…Y‡ +šÆ<Ž~6šs”¢(ź ńYO@YŠ!®·ž}ļ5Žž„Ūm­RöbBØ Ģ]ŗ:ÕŌK:+ŃĢųšēšwš•ńõ÷ķõł}¹ż)ž~Ė_JŸ¬ßķLå2k™óĢ» A«[ć¬Ćķ*v?{…ż»ż hQҌ '+ČqņčæĄļ{’ĄÜĶļ“ćß/P7Ø€ŻSų|Æ_J ¾]½H&‘_Čsū›µČJ±ž› Ƒ¬PĮŚd÷'„Čz뵁ĪĘW3Ÿ]œō¢ZшVąY*Ńč½t,{€«†Wó;…5bŠ4DŠ‘‚¤1Rf9'ąŅIĘŹxÅ|śuń½+„1Ā2!ŸØIϤ}rå 2@±å¢²[.,9EėD"jx¼cžćWćD¬”;¬IGPćI rzGeī0¹Ž8=?wG¹Ü¶67‹Ūzrš‹ī"ųw?šo+4“+ĻÕ嚔„ųåói °­ŠpY›™)G—¤ “¼ön+½]„L£NŃi™©Ō ū€•Ę*g&õmž¾J¾Š¾ž¾µ¾'¾S’r’k`ŠĮś=Śhgģ4óWs"d@§õ«5<Ä++Ō|{‘}h)L“”ĄqG’ ~’ܽüńū>7õ;~÷Au>†ĻW©CQ5ą7U*p_įqˆīRD·e{ č—ÆµcŒlF7£æŃÓdģgrĘ 2·ZHGj+ż””Ųī,ĒMęjqs¹¦hö õÅŅ.ɐ J=ÄöbAé”ŌQž#ūå6Ź^囲]¹Ŗ>Ō^üCø!‡ˆ>qŠT4NzÕ©eŃņ«~„‚rEŚ#F *n†"V»/ÆćN8BÜvČJżČ»"iGu¦“… ę. ~'N‹Ö°£ ·d³ˆƱ(+¢qY|MF»Ń Zs$ڊĻó "TŸĮ·Ą“Ńrnx…t7Ų»mĪž ¼7Ć&¤/ĀL¦vŲ§­d+·™l“Ņ·~Ķ|æł:ųśnƒÖņĻõ?ōgGxL2ė ŸQÄnž4±UĢźmm“ƒ‚)awÜ~ ¢iH“%äyųš Ģ+žgžūŸń»B€øģŽ/-<‹DłČŅ;P»hŸ“¦X­«ę=@O0^ėēõFqs¦1D쯩Ļ6gŲ‰Toś!#p„Š<„ŸĀ×ć‹ņ“łfĀIń’T¢ķ˜4R\/|Ŗˆ„P„·2ŠūŃWE$ߕŗJi%—ŌP!„Hż%|^ž­¬Wjó“ćź;å•üBŹ'Z|Y\•BOŃešUq#4’« d05ä% øŚ²4b.03Ų›\Zü½ćбh%Š#©ĀśY7‘[ĘąęŲ‡āŽųDņaÜX˜'¶•¶JHĀā}ž2xų5\v(Żžu» Ųņ¹9Ņ\oõ!æŅf(µĢ>ne°j˜„aśuæĒ?ŠWŲWĻ7ÅwŁēüęųļūӂ“?Ø«FP0ļ¼fó IĢV7kµu×r€‡ogĻ“ƒƒpĆė®O’Åäš?į÷×ō€ļųżćģ±ÄļęŸüyį"på©čŌ~$*µ¢ļ¹½ÕŖŃWÅŚfĪŸ÷R?£ļŅÆB–~ąŸļ»ćØĮ»ŪB1æ³½P>›øLŖ'=gŠQbn‘—rJ „»Ņkń ØūģBœxVŖ\W^*'”NŹS¹†lĄź-–®KĆ䂬}¹¼œO$ēUn+Õ¬ŚyUS9勔Gä…Eėvƒ­‡N ;h4hŽ\4;˜.FM$åĄ]£~”ĻŃŻ˜¬l<[ČqvS‘žA!ś }ŸiĄgO²_øųw<–_Ķļļæļ叉-¤ć’ W•f‹i…UxjĶE‚ņū/Š}žēō€ļs§’Yæ<õņ0¼A]Ś¢5†śBd*„:Kꑦö]p~å­BÖ ³µń@æu•ŽTæģ?ą«ńmŽ·®zk'¹HÅ1ķøMų°ŠDĪÆ”„¬v]Ś%– p%–mpfłvąĪGIs”ŗźn%A©(o•ZJÕ@‹®’rŹõ•źkå…ē`«Óy)1õ*Ų*=š˜²Ģ^ęć`72aĢ`z*uŽLŸdŗ°·Ł.„Ėƒ¦bEh#ØĀ ¾6ŸUØ+ž »g±tL:"ŽN⼨*—Āž¦ļjöyPżGĶ[†©O6&[§Č'XŪöö4kŖUŲlŽ3&č]æ_¢o˜ļ/æ‡+8ˆÜz_pšQ š{id3SĢęW3»Õ4ÄU‹ŃܞdﱟŲ*ÉIj“~d!9ō7ü¾OĢŽü«{ł_³ćž½xE½5øG÷ךN„ܧż’L'iIW{…UĒŹmĶ3›—õ{ś#}­^@āoä+żķŁ·>zf«:I C™ūla<\ä«Ąå¹RWq‘š‰Ÿ ŖRĀ„é|kP §yQŗ"P†+‰ņ-q˜8JÜ+z„iR3٧|T»«ķ”ņ\š;å‹2‘ūɤµĀY\eį&±ŁŁLvŹāņq Üv Ӟīļū0ąwōnz}•Žaš1³‹žL;éĶŌ*˜ę˜i¬Ā¶ 7ŠsćĮü7p*;łs|”<(«ÅŅQi­ä‘õyßäf±ł˜"ŌYū-ø¦uę!ćøŽ[ÆgγB>Éj·“Z3a?0ŻĘ=’ļ¾ÖĄŸ—|I ^fł/ųE½”> ņ‹ĒØo,…ųĖfv7·›ŸĶ,V+k¾uÉāģœv{¼½Ė~hĖ$;©Iś’äą?į÷³ūŁ_³ć¾OųkņķŅžżJŖG}‚żūm†ų,čī°wƒ¾ŗŅ>ž½¬õμm¤1r™;Zź³ż‹}é|œŸ2®˜{ķ\Tz*³sņ«„Žā ńqp’Œ3aĒą#ų-ž‡ßƒ®ÉæšK+ä’ņrq»0Y8(DˆKņŅ9‹ś^ż]õØIJcÉ焃RoéŖøUØÅØøéKĢJfӘIĻNfWOØĖŽuBą½\Ļŗ”ZAķAW¢Ń=éōŖ4¬AYź9O=”_24·…‹EnōŻÅ«xĀ‹Bgx­#„Ū’o#©£XXšįŁ(-W "ČnmŻ1Ÿ£ōÓžĘ«<FŅŲ Õ°Ÿ7™ÅŖś:¬æŠæ.ø÷žćžžD½ž>[æ„ĒĶÕąąs@üm7?™™­Ö\ė‚ÅŲŁķFöX{ø-‘dw݇Ģüī’ æ@÷ŗļŻ žšč>’sī{ąüY`ī{ą ī5Č{/©©ēĻ>–ū]+ū~#_IAŖ&F ķEąž[QÖzÓeī7VŒ`#QĻįæä»źĻe,5ŸYp2‹© 6Z#łP>#F”hw˜ė5W퀕«†Ÿą•ü !£čŪwųC¼É·TМ…åöŠ¢ÖØė”*rG©øtQ,(vŗšqŲ…°s˜:L&…Y ·[‚}É g¦>½r|*ų„6ģĪIŌöŌ3JĒ`Ÿ~%æ’öYŪ‘¹‰īÉ`y4Eāx¼/gßYī řR)y‰|Tī*Ɛŗ‰²0ßęr³•¼…ĻiU5č=ü™nV"xµvM« DŸĻ|d5Öéæźõ ŗīæķä÷ūĆõzоA­g2:›/ąµz™;!žųͱ~·(;«ŻĄmo³ļŚ<ÉBŖƒBœGü~ß»Oü5;®Uėo“‹ēÆē?üyžó3ePå$ĻĄ~®Yó ģ‰.°›ĖPH¬½ŌduµX9¬ķfe3øšZfG£¼ņz'£„ŁĪŖd±&‘‘“ŹŽę6V—P'“ŪŹfb˜łĢ%63W‡ŪĮ…£é(-ކƒų$žįļį§8™ŸĖ—JŠC¤JWu€VHK£n•ĒJoÅ6ba$? 7FOŲĢl¦3„Ēü?ĘĪLn+ŪÖēIÅ]Õ`ʘ™™ŁŽ™™™™™™cffnj1Å1sĢĢĢP éąŪŻIęĪ{wīżŽ×ßı'Ż.iķ½Öś ¤ƒ„“†ŚER‡Üĵšy`#[=VŸTPa˜ŗ’@I=Pw8b¦†ØŠ›ėÉu*ރ›ąŅä”vG?gLtłS`æņÉ­bŠ ŚB8`gĮD%Ėų*ž{ ßO²ü|>Ļ!öŹģhžFlm¦>L ÅiR ŃÜø:„Ö_»«Uя虌Ę*c·ń‡ńĪ(čŲéhēläZčVžM¾Q-ż#£nzyB{Må:ęŲh“Öki擭d™Cę‘_É ²›Ō'pf¼ ¦ōä'Õ(j:£>($G8gT3µGę’me'uŽššŠ“Įl0¦:~†ō›$sϽ×ÓĶ;He¤Ÿ"žŌžõ¾žI®aŽ Z4Ł…śØ l, ‹¬ü-ŪĆmįS]Ō9H4©Äl>yŁkśˆ¾·Ü ³mI{Böķ¦Ļ żj³éģsņJ|<šåDW±āożZČ)rÆ|$ •KՇ9[®Ž’­Ÿž/żāŸ}łļō0Åß»q>Z™šśßIčfwü3ˆhĀėGߊUŠ7R0ÓÕQü Ć× ³‚~]D?ŃAdOųq~“ßįÅ{ŃF6żłpŠa _)™OCÉq,ŁKŚ‘K؃”‹j«JŒ2 qM²ƒÄiƒµ[ZzŲĘqśfż‘žĖŲdŌww¶pķv‡<»|+£^ūžšfńlr q vŽŻśK-Fė­}"kĮ1ē“ud!ł™œĄÉp_tҟĖ+ĄĀń^TLõ„$9ÆŽØģć%5Ś9ViÕtč`» Ł’K› lŸŹ¹ŅYŪUĒ]Ę3ߓĆk{ØVžü7GéµĀŪÕĘ™ĪøHöį/(Ź«~NpŸOb¬¬źŖBr-Lq}±Ø"ĄŽŃĒō żDµ™N§“a÷¶Cw”4kĒ–±;,–×āÓłYąæĀ¢‡X%® œ ßd¹ōÓUNUO VĖ@æ§’¦_ü«ńō÷ļ÷Īł§}ĘÓß“mƒ¤ų#Aægś_Qņ/ Mōźź[ŠŖžØ%NONc M—GÄ`Čæš•KüąWłQžÆGdGUU†ygޟŸąµÄyŠó«œ­R£¶07æ©,j¬$;Ȑ¼¢FĀŌĄ›” ¶!æÆVt\®]֒č“õ,†0\Ī®•nī”ŽL¾a^Ż3܍]og K˧½Ń^jy“¹ ß²˜ &.|}P1Ų.“DC–”ć@Ē$°sóÕiP0žŖ*ŸŌ}u]½ƒo =m,®Ašj3ōĖĘ Ē#ēu×5÷2Ļļjo/߀ØžöŻ#ŗJĄļoä‹óģrę5’ji”ĆĘą(TM]ėä5YP-Q­U:Łų=Ģ»ńų«%~åīĀž}„'éVŠpüž#cåŁ@¶½bixSp¦+Ü%€’֊›Š_žŃļ!č—&bZŖŽż›~~Š/e=üÓ^āļżšO{ōOŪŃAŠļ2¤żćžū_ńĻo†sŸÅ_]¦=Ź€ąöųµŖž9A ķć‹Éoń?8/pŖ jŽl.lČēB¢lčV‚-ģ/GÉŗņ8ƾŸWe„%7©DĄž·Qjh‡ĻqU2‚,G9“¹ yšyæx~ö0×]ēJGE#›ž«v ”.¢%³ČtČĄ(2oBߕG}ÅžŒ§‡>‚Ł9-ÓĄīUŸU` ¶ŠaꡜŽC¹q¼·% µĶśvć“ć¾ó”ė±ŪōŌō¾ņ}ż[ü™k£ Dļ¤ö’éī”Ī҆EJJdŽI:SµJų¹aČ*ņ£ĻĘdz<Œ°oō9½MļĮ×eh§čupSĖ ģ7•cA–ƒwą+ą|yEYčž~ žÓ/žŻ×‰®]ž_×ΊOæų;ßöOh/³””¬Cæ¢C ß%ąæŠ×žĆ×cPļ0Ø[ü7؞Ø@ų»ā[äß|HæQ `QNųÅ+~šsžD$ƒę“v³ŽČ/ŅC&$iEnQTäq†÷ąĻa ׳A<¹ųܧ‰ś¦ŹĄž@sšYĘ_š9č'±½²€–BOgqŌtövåpOr·r7t%sŗ>£“~JŪŖmÖ¼ŚZҟ4">2Ÿ…LجrŹ|ė ”œ_ą]…-†ÉO²žŚ©tTŚS”7Ā£ńJüW$©éæj{õ=ĘMuš®;ī1ž$Ž}ŽŪ¾¾žćžŹ“ŃMüÓ|©½u]ćŒ$)čw’€äaąĘg•„r ¦Ŗ‹xĻ ²lĢžBĄę½ /Cg¹JļÓ³“¬"ėŖ³«Œš¢¼7ßČńQ l³ø ü—śĖ”’Eæ@½Ē·ĻŽ{öļé7ŚĖr ‡ U¼~7ĄA ‡šĻ«Ą«”į”n3œ Ü7)z„ŠØ8Čæu€a†źˆœ"JÜåŸłkQ*žĄņ=ōš "…ˆšŪŠkVšį¼³Å¬Ćģ< ŃS¬9_ĀGalTBe‡Gš õĒ pSģÄK óOg݈ē’yŚr}„1Ī‘élgœ³œ£‹±B’¢õÆm­ķŠBīÕÄŠ •^ՔN±š-”.ŚŅ& ’Ā×ĀꟓŌ•×FšŸø0ĢĘŅO­’Ę“wŠ_²9[¹źBwiģ¹ęéä­ė+UÖ?Łß"Š1ŗ_ æ_‹ŹīMī¾cdÖü¤:Ł ķkP“Īt*§čÅnŠó°uēAÆW4L5ö€žź=j@,+̚²±l{Ā¢yE>ŒļäÆxRQM ŪÄ}įžkż3¾æü'żāß=O™ ½ü“~ń÷}’‹ŻĒŻ-@«Ą%w£ßŠ)Šļ:xŚ]ųß5ų]üõ×ZĆ÷!ō\™šx㯉n ¹j—Ģ-SČb4˜& ļ",(bDŁ ¶ō7ł ”­ś ’•ä%9ē/ČĪ@+OēŅ?h]–Šą'y7šŠ *)jžĘįo~>ŃO½’Ē ·˜Ņq"’B čÆõiĘ'9~7Z'õTz[ķ<éJœä^ˆēą{ą cÕNé”D ^™Õ¦ķ ö8ŗ‚ĶćķD"¹K–†ü«¹ŽÄÉOĄł›µ¬ZNmæ–T;ćØęüāźķözŹ{Vx>{z{ū®śFŻńæńēģń/Zļźn똪µ'qQü{Ā«ŁI®öxIM–‡xI&؛Ą×”½|„Øx2š•|9YUփĶc'Ųg˜Ŗŗ|"ĢōGžFŌ£ÅńPødAŁśŌ/žŻó1@i”½üuē‡xöū/vųĖ=×¢­@GŃéżnĆ߄$<[9H? °'_€§lEąQæUT5ET'yśKČ8S¤%vĖ)źžr«(yTĢÅÅŲ½śÜŲfģE“æŻŽžÕnĒv›Uāėų/┼ԾЀ'5Vp’M~ąäRµķĒ;ČD-—¾Læ¤ļÕ»ź5õAŚehUšHīć@ÆsŌ6`ŗ!‹åüs²ö+…6gxyčv[dEuWuGä]GRD j½ō:św->GæŖæŌūtŻŚ®ī“īįžŗžęžqž³žJ į%_ēØÓQŃž1Q}÷ŻĢ1F/JN¬wAÕĄĖtŠ/1LĪ7a³·tĶšŽĻĖ ś=…=|Oƒ žŸe`%įčG²uąž ŅÆ%Ÿ “äEp©Żā‰šČ² šū¾’A?/Š{rhŸŁŅÆüæ=sß=§”y ī¹Rč:žy¶ļ(x Üt+šÖƒ>©ÆŹ‡āƊQ®„«S¦EWŌc©Éy ”wey5 Če—z©žJ”÷D!ųz^¶o+Ė~„ķģ‘Ö|ėøUŽ® ó9ބ稓PŻ„L͈VŖ+ņµøĆo°‰t¶żÅ^ǽ²¶ˆ†ć$ ĶÖīkĻ“ćŚLķ#ÉD6)<†MOĪĀc,*ńŪ¬!ĻĄ·±Ūš½Ó,·’6ż*BŻ#ėƒóOCyń'|“l×f£Öūé!½į7˜ž^ewunpżźęiģ±Ü{ŻyŻķÜoÜ<ż”łīņó]óōĢqīÓ£Ic˜ŻŌądŽ~›:¤^ČF¢9³ķ£v2:ˆo>øēuś'Ÿ,`©X^Ų¾.lŪĶ2¤_¾²™ņģ¢)t¬}ā™šÉ"²œžĄļ’Yæ8hŸŅÆ$øē_÷‹’äŃ4¶o1Z:ķAG¶ļh÷’ž)$ąuąłu°©‰Qüē Ó&Üß,LBr _TQ•OnEiYD€Ž{AķQq@]Õ!+Ē@{9ĢŪóģ_=¶ž°'YM­ŹV[+·KÓ”¬_,²Ŗ×ČDEQičqķya6Ī.`„4_šUčZžJ¶SĆPgœ%©µfZ]č,ć‰ÄYqōŅ8śUż"b&ŪN;0Ź^2»l_±^Y]čB6x4¼$»)ŽšglW䛖MĻ®'Ń£õś$}»¾['Ęrc˜#•s„k…;8č1wÄŝ•œœ+]ĖŻØ}j ņ«q2"&ņeō–µĪjgO°ÆŪ¹i'ŗ š‹Es±4, l_QVŸõc ”{¾e‰xyŽ—ÆęBWĻ“šI¤ƒā„šĖb²½œ!÷ƒ~ĘŠĻś„‚ Ļ•pß±’ŗnyüėFӀÜW Š]»_õż½~’ _žĄ>¶…ī톓ŒļYpÜ"šÓ\ŠhZŖ¦r‡ø,ź‚~{ÕKu"į]‹_T-µTzä,č5wł$^„æesYjV‡6s›Ķ:köµ:ŚÉéz†½ēäu•EĶÕ*ń‰e„­­I‘?ĆG"iģ~,—˜"ˆS§&uµ+¤#ł€ĖāU ÆxMY|.C¼-«}}ؗ\”Iå ‘¼ū,]»7ĮŽfu°¤5Ä~k¤[`æ%|~„kĪF³ ~Ėķe4tŗ‡Ü- ;ĻGÄ#KȎrV‚~Ž’ _ų_Z`÷|Ą•ī:öĻ]f …Ą[Ń>hēA½G°y_A;‚½8 N‡óį*ø;Ž‹ßb¹lĘq)RŽhd®ƒūą8 š-Ɖ¢¶œ#Wʊ²¶H+Ėł²śjč5µD!‚‰‹’ÜC7ސ×e•é «¶UÖŚge±ŁCh}V‘łäMYMå•-łtŹ,ŪfŽ7‹Ųwhuh8{äN5ÕÄßpC2œ "CHIč/^Ņ8 ©FņąSĄ}oł`ŃBŌįüŌē½ų)˜‹s¢U+õH5€\/zcŅ€“%1$?ÉH*’ÉD‘Įזč͌†ŽzĪ®5ĪnŽ«z>ąĖodi„­×O/Č5ڕ҄9›ė˜ŒAOÕdŲæ'²‡üģTZō䣁öRaŸ·rXÓ­»ƒ}Ņ.šŠƒ ÉYh/cŲfvƒ –7āćłnž„ūD1ŃIü"މw"V–”@æƒņéŠĻ ōžżģ^•÷Œæźē?÷œžėYė=@yē@½ĒŠßƒHĄž%Ā?įÜø nˆāµų .J¦Ź@[§ČK’[;Cš‘ ¤ ÉGāp;•XꑓäZčæ$^ ˜Ø'@5ŻEuųj 3VHŌåą„\ņŖ<9[mŸ³Ś[=¬įÖE«’żŅ^¹• Žg€˜+ ń‚-„ķ]V+h9Ēm•ē݁ĄūØźČ»ęēÜ ¤ ©GAL"KČzRžÖ +d µ}ō “ŠļŠ?󰆩>7~Śgȓ½ü9÷ƒóvóÅ ń^ÄÉR ßģ’QæŠCz”ŅÆŖŻ„Żß÷¬Šē†ų×lć_sæ9÷…Ę~œ<³0xe+<ÆĄ—`÷:‚ļL&‹‰G[¤ŻÓžkßÉ&ņüŁWœ—ŸdR§Ėņ3lŻP1LxÅ%~‰gE[ń’wgUh76„eµhk™ŪŹ`µ[Ϭ.¶YQ‹Żgłß²Šl=k'µ[uģŹšē­y7ŃYÖSYŠ 4ßÅE@µedAŚ‘JąķČ*ŲČEx:Z„4µ4Ōä]q˜Ź/›ĖĆņ'5%>”]’@łq' Åßšiü+ąiųÖHr Rõ¶Xļeōq 7Źkõp.4zåµ !’C“Śp}­žMÆ„u%ÉpA“QeT[Įńˆep¤Nq‡­ )i4}n?¶ŗ[­­_¬V.ŪMGƒ>‚ČĄj°Įl=»śåäMłd¾Ÿæą˜³®bųÜ+Ń’ŖŸ č=ōÜĄĮ=›·’uĒšŠŹā?ówzęH=)6/.k'Ü7u>‰æą,¤'Y@–“Ǥ–öY»£oÕ/hX{KŚV’æV«TU4Z„U·åŁPzå/ā ’ĮVį“ÉÅ Ų©TōMĮ"ōū„ŁŠ,d0?šł!óżöR µŻ4kĮŚAÓ¾AŪŃļöH;›}ß>BׁO}d-ą„kŠBŸƒ{$kĄŁ–“~$/į8©O ’–Š«ØTź9V6•5¦ęČ;2§š mŖ0Lģ‡N½_åĆÅÉ’QS¤3™ŠæBāŌO=…I½'‹Ü"„˜Ēė±żö«²X%¬FVk¢•Ü~`7„ čIśų½,ėĶV²Ė@Ł’Öļ%„D7±PœLųõ’®_ ŠCfHæ’ īŁõ€ķ‹æćъūÄŲGH='“•Œø i#H½qx%>Ž_į(R¦~&ŁK’k£µ|ĄŃ©Œś%­°vXŪÆU ³a2āŠ;µF-QT"µC¾~8®½<'jO^ōŪ@Ńyt¦żŁšnęŽģ‹Ō7÷›«³õČjm沛Ó}ō%}KĻŠ™“Ģņ'ŪEūŅ<Œ³Ū|Ƙ%ŪØō0aį±T&³`ļ/@™)ĀiĄÕMÜŅx ¤ü\•O}'€£.JŌ¾Xżē]¾’×Oø‚icČō;h&Šoąß_`*’ĆŸ_„9XNLH‡xøźĶ¢‹Ø#€ Ō]tō-B|¤¾ŽUØ„‚š)æA.b'ÆĮ1ßM[ūĶ"V7«€•ĘJg•²ŚX™ķÆvuڟ®¦W©¢łX{¶€e&Ėł7‘ļƒżū’×Ļ ōžŚKčč5P ‡¾°}]1$žõö+ W,°!õ’oĆÕqKÜOĮė`6_b7Ģy}Hœéä&I¢ķŃŹėź%Œ”F7=™¾G£×Š2ą ż('“¢_ćß„&ĖÕĀ'°Ø&‰Æ¬ģ]r–”ͦ-ģJÖ ³KäP¤¶¹Ż”fkėŽÕĢ~ }{%č»īIėŅ*“9] ĶmkĪ3ˆoā˜§J€Ą_q}č·É'Hį#d0‰æ›dŠń)L› ST3Uqe(K&SÕE@§}HĒÕšhHńżų ž‰Óā‘0k‹Ō ø*+|×>hŚ•ĮQwCā÷P;…bmx±A,•ØÄš}Į…źā,ø2lĄOčd_9B<ēy>š=¢WmĶzłÕä¦e>4ߚ^˜Ėœ6³³Į‘ ”;čš–5bÓŁqö„‡n5žļįĻ ’ž“ž§žé†ķHķ„0°wm8¢®Š<'Ąģ­žūóīŻļ!ž~9ŁqIšĶöxž…7ć³Š:}¤i =o™F>BW3µ‘ Y!Ći\Ņ󟌽Fvż'2Uف<ø7Ł24I½Æłe^FŒ7ų@š’ś ß$zĘž`9­™fKs©yŪLm²˜5ĆNG7Ó Ņ£ķhk:š›?ŽŸąCEqi˽Ŗ ’h –øšĆW’^K„}$ I/Po!p@z²ęī$Ŗ†ī©¾*“JŖŖŖMŹ‹ŗA§NIwvOC’’8?UŸäd¹E*YVVĒ•„éčF£¼0yODm¾‰uēuE!§ę n8ˆēA¢œNx÷ĮVŠ=ź#W ŹŪń{¬»Gm{žu-ņ1²Öc¶1˜³”9’ t4þŁ^™N”©›Ub£Ų>öŽ„āµų(¾ƒ?†žY\tóÄńõ—æųįæóŸčļ'ŲŽbØ2j$Žšēt ¾Ķ’ķnq„`Ī:AźĶĒ;šeüĒ’R¤;t¼?Č3ņš“‡ŽŅF’¤W0JCŒõ†2@/f(¢ }BYČž‚ŪĒ1(¹œĀƬ¤X( ÉE|7+Ķ8ŻE«ŅUö7«‹•ÖņZé­ÖQ«²żÅŽHė±u`‹igPo ŻģŻfu"Ļ.īƒ{–‡2e?ˆ†ŚB’üZķ%Y =f6ųč\R–¼Į`žÖ¢ hŽJ”|Ŗ»bŖ7ŗ Į±|rģAʀĪI œ•SoÄ@ĮEC¹22NÕTćÕvŲĘīj¾¼¹Wr-¹Ü-ū©Éh|^ü ŌŖ¾ĖVŅ'SČ@D½ųgÖZēŚÓ^dę‰tŽX‘ē‘Vą,YĢ fč0§”]ļµ£ąˆ¶A -G“™=e‰`g‡ņ­üš_ŃAĢGÅŪ~蘚 ²’ōü‹Ü-dTI˜Ļ&Š]ĀöĶCkŃn öų{…Ē'yü½nćļ•ś×½āāļõž'ƒfŽężłI«­]×¾hUõ¤Ęć…‘Ū‘ŻŃÅńȑ̹Ķ9Ą‘AąĪš}Ėš38SkH 2 M{ų@¾FT€iz#–ņc,Lė3Z‰®¶Ū¬ÉÖ$k·eƱGÓ,+/Ē3²Š4ĶHWPŽõ ģ€äĖEč¶óTŲ£ZųĪ=j%Ł_oÉ!22yüsüi.šŠ±Šœū”°}†:„ŗB¦—÷s’¾äO’N‹Óž@R¦Ą?” tŌb¤8,¾‹¼²—\'oK§*ŖĘØŹVÕ4•^5PMU!•:ĀhčmеM¾Aޜęxžˆ<;{CkŃvŒł8l‡Ž8"WĀ…"w"CMĆjlU“.Yķķ[v!:Ž£iXk¶œŻb^^†÷ēėłm®‹ŠĶg‰ßž~ž%žł³’üüµ%¹Ģ‡Ź šąž=Šš„ķۊ~C!ł>!…bqf\üļ;MĒß©ń攩Ļ“ä;)”ĶŠ^kEu·ŃŃČįįxęųÉŁÜ9ĪyĪY×ÕÄ5Ō™ĮČØu&ˆDAĶ$ė`Gjąjźä’yŻüĶņ˜p‹±<8Ósš®±Kؚ±ŁķķˆżŠÖāo õdZĘ.H?Š!,ĀśńŸ R€£Ä?÷<šÕpóād ųåL˜ŖįШڽuķVĆNĪ.w8 ×DŁŠGÕ”1p‚ ŌūF¦h/5.Io2 BõT¹@Ä Éó‰%@‰Rd“]`O(7ī§žŒö‚O„ģ§~VYŌUŁLĖųbö•–`ćŁDčŹXŽ„Mčaėldoųrųcų|xuų{øw$©łĆD–f4“½ŽöÓīō88h-6“gˆ꯳J~ķļē?§’żügQŁNN“{åC©©ģ’Mæd(“—ņØ.øg_4²o-<ĀÓą/o”·ų “Ę5xa1¤ü- ŪTŠė‘pģ„µÅĄ·=ōD5&8ž8Š9·:‡»vøę¹^»Źø¹;¹Lc½¶ ‡Śē3œM a¾:¬!9”ZĶ‘q@ūÅpPi;MNŚcģ>ö8ūˆ—&fåų1DŌäóiŗ”¶c)ł2ž’=¹\+ó©ßU čžÕąqHC2Ü|ģ\WR‘†ßõƒ{HRh>ķŠh#ņ/‚ŽÅü•ųŽ#­Ąūėj©õz}ż–SėDrįŒč¼|ąšŌ¢ œÅb›Ø"G©$@‡ąo…_CKXØĪ'\s‰U<oĄVŅ““ ż²ZA[¾IŸÓ‚tŖµ8r(|)|7|$Æ£32”eöR 5„Ī<MjߊŽ¢«ąAH¾”ąš•”sĘæą]ųOHģpvāó=)»Öśź†qĪź0eœ;m\n÷,÷Z÷w2ĻSŌĘYSO®F2h哼Z¬6Īć,¼~ź dŖ ąGßåg9XzÄ}ęb#hVŠiÄ&4Ž6†v–˜—€¤FB¦,fmł^¶āgyDT{T~“Ś’\ Šāš˜‘°įæa¤6lÜ0Ų=·6M;®ķŠ~Ńŗhy“Æ@Ó -W#UH]Šv1“®~ZCŻolÖśc˜Å’$T5äw>Ļäg”eį«ą×Nƒēę&äŠ .AÖÄ*§|Ē;pŹZ±hv––¢“ģ%öA»AOŠ?éśåv k@ä`ųjųIųhų÷šÉšÆį·įģ‘Rę%³õŃźdß“ ÓYō)ĶĪz±_Ł{h Młl˜œĻ$&¼ž÷XøeAČÖÉr—¼/±Źś ü7ż¢€Ž³”¢Ø*j lDĀż6ö@s¹̧P0CiÜ÷ĀSńF|šZ|kéAÖ·fŅk“ŚśC}“QŲńĶQŅŁÅétI×Yw.O~OOOO O=WXo§UŅĘjæĮ½K«ÆMÕŽC>}#šT]Q£K©²øˆāÅŲI:›NV˜K”ÕčB:ˆ]į‹ÅyAŖŹ‰¢ŸpÉĶŅ­:Ŗ Ŗ0Nō¢ū8kĀ}“w’Ɛ~ėa;=,…Żz¤×ēė]ō†zi=­njgµ9ZC-„ö†‡ ÜKž“JÜ ō”z =«žN;O&aa¦xÉ×@—čĒóóÆlŪΫȟ`V&ĀY*†Ž«Śj€l&źńŅ,3=kŸ·÷Ųuģ+V?+£}ĒnH×ÓŪ°Gčū™rļYųģŽÕš±šÄšĄpūHó3ģß «}ĢN śM S°’=bIym> fę O Œ9Zü*‡Ģ'[ȉr§¼+‘Ź ÷@`é’Ņ/Źē±j…ś qh4Ļ#°}/P¹qœ’ŒŪąaą<‡šC¬‘<¤ “ŗ?IœÖɤÆÓ‹BgYģ(å\ąœīlīZč¾ļQž)§'­§)hxĘUŠĮ““šKļ£ßŅ÷éÕt®-ŅrhćÉ2ÜĪÄ5[UUN™]|c¹ŁKśŠfbX vīF²éž‹x.®Źj2ĢŻD™FĶS×U4j‰ö£Dø?¾ŠÓBŚ-„™ø@.eļ%› «’Ód€V_ht6ņ?™ō†×x”Æ×k鯵!Z2ķOšņĶą Yµ¾Ś ķ«—u‘o蚦(w “ą9‡hĆ*°9¬¹(—p'7ZŖØģ /ŠüėDÓŁ3¬‹Öė7+›õĀÜo³Š-ō<īł“ø]×<N …‡7…—„§‡ļ…wD–™qÖ`ė”UŃŽb{hzZYM6›]…S‰ä{ł[žųx˜Ų"īMę‘Måxł+¤°”YT-5ō;ś·~~ ÷\Ø4Ŗ|=MAĖ”ķŸ†ķūæ›Ė8¼ŸĘŸpršž±ąB’ü¬-ӘÖS’”Ļ4J8¾;–:?:±ė¾ĖéIāć}ęi剸+zvxęŗ„ć“žW_­4ĀFĒ:`ƀ޲¦®„z©÷ņ,|€ӂ×b—č3š‘͇”«Ī¾Į7a°ēü®˜#‹)KÅßŃ~ō½ų†¼½A9`÷®āŌ¤Yŗ=ŽyH®‘“{Č* ĮAZw}•g7FVGqGAGRĒ#£“±6±‘v—Œ„Ļ_U`_7@K]‰æ ZĄ§  Įą)ł@Õóa–QĀZņ°č¬¦Ć™«¢AB6 ]t†•ßŖeM³ŗ[ܤéi;+kdNˆ†.Cž  Wż¢#éĢ;f-køõ§•ß^`‡!×Cf–dcŲI¦Xqh [łS'*Ǭ‡¬@2§l,ĒŹmņ–2Mµō{’ _č=*ģ× ÷\‡”+č9 %Ü£ųÆ{ÜvÅは.bgOxNųÉ® ÓniÅõŻzIć”1ÓQœs¦ė–«Š»°§Š÷“÷™—{jxb½­½æxb\§ lä2ž!Ē!ēmG+ć€>M[F&ą(“Z.ÕEFńˆ^Že„Ó³²Š„[±™ eWV™W d"u^F `†®ąåø.«9žm* éŽy“|! ’7ä6t’}ƒ-…VMū¤×%Ó†sØóggYg-gKG£ŗŽW;Ź‡\/Š ~ŒÖ v0É hrŠZ)ėŠ”¼‹c1Ģ`étš8Ūā'’īŹ—ź›¼/"|!æÉ¼ō…ÕĪoM“Y-eZ–ĒŠÖ¤č`{žY!¼3Ų2T%\&|&“#“%T!\+’Ö¬ożlõ¾Me¶ŸŚÅé8Ҭ/ŪĖ‚,/ļĘ×ņ{@šeD±Z\ĶMi“Ü"oH.3ŖŠ{©#’Ņļ' ‡ŠØ1°ĆpĻ­Ą}·Ń{đ§Å¹ž«nGĄY;‹m\œé7b!oÓ¼ś`Ų¾1FzĒ}ĒZē@WwG÷zwOĻhļß~ß7ļļO¾E¾zŽh÷Ē/Ęv£®£‘³‚k†ó«1Y?«UÖ2“¶čœØĀ_±tüĖÅjѰ-ģaŌÅŹ1'ƒ\·»Įl.cŁEOŁQUB³ń RR‹žENįĶx~Ó“zž V"Mł]OH¶ fRčJrOkbsü€ čŖļŹēśÓyŁ1ÕčØwŅF‘‹Š¬÷¢āč4ĢōhPė$•ĖÕmuVJ^—•‚>ِNB2?)ö«3ض†nŹķĀ+ņņkt‚M­ˆõČZkµL«Æ²“ŃūŠ ›Ū… »t µ ~ nņ҆ܔ¢”·”=aŸyÜLnU±VZŲīŁ™~žUš‚µe€l3ń6| ’“¢˜č&–‰‹Ā™’¾Åux|TuÕ÷ßō‹z/€*£ę~Ń2ąösĄ¶ńŌž§‡¾UWLøKų æļø³žY ]¤,l_^ć¤ŃĮ‘ŅłĘyĀ5ŽżŻĶÜū=½×}5¢²G%Žšµ=Ŗ²Æ¦gŠ3ƒĆķ˜āØć¼ļ\ėģąČ®×Ó]z:ĶĄcåCŽĀ{֐u¦ĻįXRŠŗ@ylfÅZsĢ VCz•‘…Ō4Ԉ$љžA/Ŗ ±ä~ƒ}¤:dń}’Ek¬5×ri‚<ŻĶeüwų’6Ś{½·£™ó’³‹ė‡«—«‡³Ÿ#·‘C÷h©Im¼}ģ#oˆtāßĒkŹĖ²ŗ(_ó'“ŸŻŌ¾jg _Nk°Žüh¦!‚ĖĮ×@”Z“ųMŗ:绣]4;`3;żb÷°/Z„¬Źf·šĮ"Į­ĮĆĮQĮņĮ ĮoĮN”ŠįQ‘āęF3Ęj½“ŖŪ»ģPĒ˜Śzl{Ą’óF|.„äDG±Pœa‘^֖ĆäyMŚ2½Ŗś-ü—~1(=*ķ³€¦‹B׊ė„;¤Ę_a>ŠC^\—fšį|d:ĢxEm=ō‘ś{½»!5ŽĘĪ4®Æ®Cī:žŽ·×鋍Jä•ŪæĒ?Ā_1*Ö»ŚuÜŃŅ1ĖįqŽv¶vn0²źūõ£šV÷D`ØAā:æ%3$ĶU˜Ü&š5Ą<¹™g¦„Ėł±^A­IWųū郵$ y]8 i ­ÅŠJhŠ?NA'™ $^†p ¼'89Oīh[ŒkŽŪĪή箜®³Ž%FE=‹ÖŸ,ǧUU;9Tōį5Ł"ZuāsE“Ģ&’ą;č0;³=Ō¾aē¤ėčM¶š’$OŹŖ3Ź M¼ ^Ŗ>‹M¼/ĶH€[¦š„ö‡ŽÜ—ĘŠöP묹6ņghH°vpH°J0M°L°Cpršapwčkų`¤–y׬k¶ņŚ‹mĻfjŅņl4˜ÆÉ§š“Üä9Ek1WœßEZYCwø"Mł“ŖŖś€~æżKæŒØ“Ļvhš6”č.śŒŽ÷̊³ćŒ8 6°DIpc|ĪĒ\ ¹vŚE­ ¾AOk¬7Š:9ę:ė»R»ß»§xøgŖ·½oLT ’(’%’€@…ĄĆØCަīķ°uæĄ™Üi9ŠżµžĆ˜¤U‚¶>uN¹W¼ęMų†Ł+ū©uÖje 7Gņ›¬×ō#;Ä_ˆ*ÕīčmõZņXō7`ŃŚŠ2óh5€Hn«{KÉHč¢ä+īƒ÷£Tn_ńf²D‹q¬wnv]qÅø¶9Āz-9YʋګŁr½˜Ė+±ßģžÖė•ķbŸŲŠų+®Slē²ūŚ#l?MÄ*ńµp6‹Č×r³Ś„:Į9y  Y„wbé9z™.”­č)Z“ugIŁHŗĻžlĶ5ÓEFƒ~5ƒ ‚邎`©`Gвx°lhtøuÄgn5óX›¬döXū•]8é-LĻHhk:ÆĄGóĆüĻ i†8&>‹”²Ŗ(WĖK2,ÓBź­€~ō‹E™ j£Nh$¤ßpϧ(œpéĢŠ^²Ār ‰•@ MĆIyŚhŪmōŪz}č.}IĮ›f¹Zø³z{fz©7iŌÓØßüYyŪO{ü{|Õ=I\ĹÕqÕńĄŃÖQĒH«oÓWź“ōƒŚ<2BÕ.łVäĶxOÖ8ųƒ5Ü3ŸÕŽZeĻ„{Yf č‡bŸZ‹ėiY“¹di!põ•8 “VX`8AA—ł™d&ŃälČ%čŅ—Q>püŚw}ŗ3āBīļĪrŽśzmRßAõŃ*õXŗe"ń‰ ¦«,įifU³GŚćķ­ÖVf{®½Żž`¢q¼‡(/:‰äŠ!ĘØRŠŃ?«õ°·ł3ö„½†ō~Eū±z|Ņo?hõI ‡ }@Į$ųZ‹ŖĆ¬nGq$Jmµ–XŸ®ėĘD#ڱRĶåŗęŚčžą©åžlQ÷£öū›Ī:žz°žWŗź8cÜqŪ1Ą±ŪPz}œ~JŸ¢wŌ*’vxśS…äqŸ?`:+D»Ų‡¬!ÖK³7Ųc'v±õ,ŽŸgUe<Ÿvˆæ«ä>š„ø*ĢÓh/I^ €ü$ŽŠ? ĀĄQĄ®É­¹ń³s•«…k‹cž…LĒĖP6“G„V}åVq”Ļc©iY«}äIˆ„džWG ™łĢ9¦e³_ŪSģĄhÕXw>oKxĪ­ź¤C É r…ŲĮAz™Ģg»µČÅĒÓżÖéȝŠVP¬;$ŸöoV‡ņ„Īׄ†‡KDžE†›š5Śśnµ“OƁNÉ:±_Łw–÷‚xÉSˆŚbœŲ'^‹D²¼ģ-—Ésņ‡L„*©ž@Ą’č—eśkˆz”ɐ~GŃōČ!4ĻŠŅ‹€‹~…9v OŖ;źŒ3@»«¬ŅJB‚å7OS]qnŪżĆ#½Ź÷1j‰æW Zt”hū·205šŁĻ}>O×"gsgēWGĒrc»žZ’¬ŅjN­ y>]½‘£E}¾Ÿ½¦kģĶVb« uŪzoÆ¢:4ųĢ@ò="³Źü­ZþS-Ń“÷$ɵ«ä^‹’„ĒOžƒĪ©śŹV“PO< ķ£VŠéhēćé9µ­ų<0Ór•Gķ“å@±œĻ`éX«sÄžņ…ˇŪEŠFF>˜W”_T²}v{:bMŁ֋ß Š‚Šęó«ųUw“PĪåµY –Ÿ/åÕOź(?CŸXs"ĶBكā7.¬|œšZr‡·†kDŽE›Ōģo½¶źŲūģ8ڃžN£Ys¶Ž½cŁxg¾Ž?ꉀąG‰]ā9$rü|–Č3ņ›L}«čwč_śeGeQ˜č~@÷“P Ī’poŌŚø:(…Ļ jh¤Ś‹ćä5é”}Š:čOõ–ĘK£æ#ĘyÜ9ŹÕŠ]Ż3Ü{ĀĒ£Zū£fąUtį˜QŃū³ ­~’ro-wz—īņ»ī; 9ßćõšŚb-•^Mī—C{y”5D^ŒŻ“ŪXKĢ…ęVKŚeŁsŖŃö+}Åīóz"-PQ“œ)™„R«ĪĘońŒš&“ ČZČŌ~Ŗ€š©F¢†ø-0żVm›ŽĢčkģŌ«kšI$Õ•J–eDtūłU¶•–“÷˜U#oĀ×Ā?Ā[Āķ#Æ#!sõŃŗ`m³Z‹v§Ÿhm Ó÷Œ³ĪÜ/ü° SŃe˜ī32—“œ÷q¢œŗ+S‰ėō­Õ.bÓ++sI°o0gčChDøwxhų}øWĄż³Ģ^Öc«Š½ÕvŃvōu°śl{4Õ†Æąwy~ØŲ.‹(YRv• å)łE&WåUwõ č÷(AæÄ(Ŗō0͇<Ż攱œUq3ÜwJ.‡“‚‚w I¾ći$™¶DK¤ĻŌ]ĘdĆļXķ(ėüźÜēšėžē¹åÕĢæĖß"*ęBLŽwōģĄ‹Ą‰ĄSæåėģłčśęķļŽć2œ+õAZ:ż²ŽÕøÆūōŚ:ķ,™?Ø/bļĆŅŅÖs…YÓ:f£ĒčN»%ü¾‡UŒŽgĄ©n±/|²Pb“±»°e&ڈźĄć_‰—“©$iƒg£CjŸņ”'(34˜lŚC­žXO­mĄ°/*¹ŗg£³Ģ©;Q Q‹wauiRŪ’id`¤|dOd,P¶²vX-¬.Ö7ėŻ’ž¦Š™Ę8½Gėń±b‰,ŽŌe–[Ł}č._ŲeŽR<š/”[¬R‘GŠ8ĒB{qķėƒŻC%ĆĆ·Ć·Āi#s"^s‚Éąˆī[åģu6¦Ķč*iu6Żg)y3¾ˆ_ēnQV ›®ZTv’óåļ ļå+§ŗ©¹źąßś%A¹P% ‡”Š^vCś}‚ī’ x” īGāIĄ}cń čq‹·‘[¤ŽvA+£Šó;ĀŽ ŽnĪ”ŠĖ’pŸõx}Ķ¢śū/śq@¢c>Ē,96zPōŖčƁ’8ļ wNOoŸÓŪŽõ8 «ńÉńĀqŻč¤Š>yÄĀ—Ń^#61Ūīg 2˜WMd4@ŲĒĢ™‘RfGŠ3/šÕwj²õ\ńVĀ+oŹ•*jƒzBļjˆ1@АÓ8īd…~†£rāøi¦q2憦TNŅ+ćdR%æC?$ ’Īśut-äī³®Žd3›«­äö k‡yĘfµ±Ņ t0=3=ØųŽyŹ^ÉÄh£*!²*¬ ŪÄ>2ıXę§i¬įĮqĮiĮzAl ÷…Ś†Æ€zįšĻ‘‘4ÆŲźeݳJŁ+ėAX“"›Én°D¼ąe ų’¢ÆX/ī‡,$Ū˹ņøü “زŖ«šó/ż’¢ÜØ j ķs):ˆn£ t—¼ø&ī‚Ēą…x3PūI|“‹#M”Ÿ§Ņ¦j¶ÖU¬74nĶŸ³œe]÷k÷kŪW-Ŗ¾’¾?Eą{ąiōā˜G1“cźĘ”Œéó2:S ½xĒzóG-õĒEņ,rž4œŽŹĪ_½µōū¤‘xž‚ŽŖ ²ÆF7YĢł‘m°g¬Äönėr$žo^°ŚŚ“ģÄtKĆ/°düO~]\“ŪT$ ?b+‘Ę$Y€ŸBł¦NĮĪT/Tō %ĒoUiYØ ‰†ąĮ÷„121h©Äā‰x:vĖ~me·f˜‘Čh³0pĢėt™5ꊄmš¹§öw»+ °Ū ųPvU1j¼ØÉϲYą =X3ę†l/4o†f{ żĮ”Įj”w”öįįća#Ņ6r&’×\nŗ­~°%ķå¶e×ĘüAK³Éģ2`m>ƒŸKø@Oxœ7„&ó˶r¶<*ßĖDŖŒźśų—~yŽ; ±ĄGŃCDQ \7…›wā ų9¦@ %‰w¬µŠNh™õ¹:1acø# håJēęŠ^¾\QUż±+p*zPĢ«˜1c.ۘQ1O“£Šū6ū†ś££c»½Ó]ƒCœó‡ŚHroĮGš2ģĘķTmY{ū²YŽ|aJó‡™ŲjaN<ļŽüi^·2Śul'%,'›M{BgæĖóɞj&Z„;“kä9KžĆW"Ćńt^•VUAŸ-’ĖĪŖ„j\¾%įŁńó¼:4;CV”ƒä& Ŗ|Ŗ¤ óõl ŠĖė¦9Ł\o«æ•ĒŚh®4ĖZǬšv;ū üŻMh%aóy)Q\V/Ž“’—äŪĄ×Óæ>¦éIke¤jØp{`ņ łcV0[ho(SøGų÷pL¤_än¤Œ¹ŁŒŽżöo™±kŠUō -ĪʱóĢĆ«ńÉüē Włē 0­ä ł›|+ćT)ÕYĶż&č— åzļŒ& uŠ^^@ś„Ē!÷&Ć>œƒfą$ŁHm2’ģ%A ­yŚ­¶~PĻh,2āKŁēƒ\…ÜnOČńz¢ūS¾śDæˆs1ę+|”ŲT±ob~‹^ī¾)Q颻˜o£{­³‹s„³¢s€qH3H=<¦¤/y„K£(Y‚Ƥ+ģ/Öx+‡5Śü©`ę2ćĢYęŲŰ'ķ³v6{†µĮrS‹-ÕTŠ“‚\.©ŪŚ!m –Z«F6 Ź©> ʗŠšr“L/³ ĢūAžnbxZń«Č 'ŹĒ²œ‹/jÆģĶ?B÷Ų­o09«Ø7CZ«šÕĶaõ·ĖÓ4KĀNƎ ƒ€3VĶ”żćļļÄŅž¤é5ū‰ĆƒI‚-ƒżŠgćBćB/C•‡Āi"c"o"5Ķ}f k”õĢ* ū¶«Óō#-ĢF±SĢĮ+GžąĻ#:Š%ā²"—l.§ÉCņµŒQ%]f©żė—@µ€ž¦@ū<‡ŽAśe÷ģ ™qČ“, Ž²ƒ¼īė­ż®%Ńč÷ō Ę>#cÆ£¼ó‰sŖ«‚;ŚóÕóŅūĘgFe‹¾]:fiĢł˜PL8Ę[;¶xlƒ˜ih ¦Ą×@¶Ąųاž·®l.äŹķ\eTŌhļń<Œ"‡šVTJ9äF`²k4ŽĘŲ.ė¾9ŃJjW±Ėf”Kܶ XeĶū"÷#o¬r¬·č©ją¤Śe]ĶŒˆ>Uw蓉@­UeYGŌĄ)G ??LĖŅõЂڀ~ēD3yWVV»Tęų«/Ŗq¢€ČČ;ÓX{•µÖŖ īyŃŗavŒM-Ÿ]×NNg³9š}±B²~¼æÅ}2Ś¬ÖČŗ¢.Ę®ÓjŠ#OŁ7¬6#TéĒMh-=‚eƒµ‚KƒVš—ŠåPŠ/sdf$ifžnf“&Yo¬Š»]Fßӂl8ū^žįGxˆēķÄBqAP‘]6•SäłRFƒt„LųGæØ t·ž@ŪŃeōEćüøQ§ÆĪCs‹—Yo² zK@«£-Ņ^iÅōłzDoe\5~v\t4v~w.vÕtĒy>@’¼å GU“‰Īs6ę÷˜Ė1ßbܱŽŲä±Ń±ėbG/ wMXėÆ5Ć{Ę]Äż4,ęéhhŠūh™“óĄßm”;NQ“äbA!LJ±YpĘ÷Ółl »Bڽ­>ęŖČ€póPP’pņHė1­%Ŗ ˜čmM\£\IŸ’F3}Y9Ź2«)²€L&ω+ ^<‘?¦»Ųh^I`¹]–|L ģŌuPŪäY^„Ų:†z`‹v€³ķ¶£hH½svŖčFžSŽ‚Īó”ļea¶œóå½ņ)’ĄR³ötæ}Šje4cĆł~ ūŽźG×`’`!ŠohšF0Mč^ØNųT8dY„˜]ĢĖfnk–õÉśŁ^c›°Ėč;šŸ eǘbeų~ˆēŁDk1Oœ¦Č"ÉIrŸ|!żŖøźśķū—~…Q=ŌĶA»Š BI!żZć‰x;¾É ­e*9 Ž™Gė£Ōt½±¾K6oŒVާޮNģZļjäNāyć¹č½č F5 Šns ęō—1¾X#öȩ˜HĢܘžŃ}QgžvžQ%|å¼<µņ ¢©˜)~߁UźÉ±r—|"½Ŗˆj§¦«½ė— EŠ “BĀÜ{ćE@ ŸqJR²oyKŅim“µŚ{­>Ų”„±Ų}¼ƒ8§żvµw§ō¼š÷īó]‰ņ D'f8Ū)ö÷Ųo± b3ÄfŽÕbūʬ\1~§’÷ØłQ÷}£}e}æyc<™]µ捍FEć“^Lo¦ É7|·Cee!Ų>ZپbĘE…Ź„ŚÓv Ż W±Z°G¢ ZG õ\›=ßÜI]uõō/$Ž\Å>ā#q<ż„Ž«Õņ+OĻ C7|Ž("æČcĄg‘BÕš.| żŚīŌZMR=ń/ÉŅņ’öPn\Y»v±˜Yź–ZĶÜź63ĒÜfŽ™c¦˜™9fffff;f·™ķ˜c“īqOf&É7ßü÷®iف•Ųr=µ÷~ŸŖS§Z‚Ÿ—É™² Ųהčˆč^’Ŗ łGüj”%črO„÷|5žכ‹įŽƒ¬ó€¾Cö ž ^ M ļ ;"Ł‘ŲH;šm³©Ķ”īGŠE™łL„©ĻneQ®%čÓ0’ż|pC+”Š»ÄbœŌTš&¾J>¹>H`;å§²t„öŃiāWj öwz į ½Ōī°üyXpN6G¦#g)Š@¢2Z[Š…°||ī#ę&ÅBEŠņ¬ņ'•š;¢Ł¬Ż£»®kxdœŅg ó s ĖĖR‹Ź2ÓÜŌ\Ć<Ģ5ž4v7ŖŒ« • Q}Cż-Ż0[·TūE½T•ÆÜ¢(§xGŌ&r@A±4t.rŻ• Ņ"”=ļå†0é²ōcj!Cß”–Př "֖ūB3ÓXiE©©ā ŲDäųū'[Ąl££óAʰBõ¢ĶäÕRą~ó€QĶ•‹FļF;Aw”4ø'p„ń° e@JčfōVT’ż„Ø'Z=ś^ž";¢½£žhTžŖļ”ōJØĆ?āņųGŲ! į§p${œŻĪīd—²;ƒīCŽ [Āb(/Ü!ü9Ü,Ņ"Ņ1¢&ļ’]؛T6=žHWg6{’‘=rQ{n?‡ó€?|ē7@8ܤ˜Ų_Ü!¾c„FŅdéØōYŠ•ėŹ#Į¼~"+£Å¢ķ潚‹…Ź@-”įŠ č4ōRwo6vF‘"Hgd r Q”ÕŠ‰čeԈµĀ¶÷k‡ŸĒÄz§X§ČS^SöS%€žyP³B»D·AæĻ°Ż8ĘTĆ<˜bŁo)cŻi-bķn‰µÜ0Ÿ27“¦±&æé€±®ń¢!Åp0|¢“?jݚ)j•ŗ•źå…BQ…hŠWĄRP3²ŗ'£Ņnį˜ƒø»ģMö*ūLޱL°źó<->’7B-]O'¶ŁÄg}Čcč|45Ŗ.–9éˆŌUž,wŒś¢ƒAE” Ķō&<8Ŗ„fCÜ ^ļ„×¹pOØ5HćŃ(uƒäs4ŠF‚™3X~,×ƜŻ%Ćņn1 ˆ•āśrmų¼ œU‹™<¦&0Ž lö“O7"KFŖFn…·„÷…ó#g#÷"Æ#“Č ŌrФźŅiˆiÅ~Ņ=ÅZ¹®ÜNÅ7å׀üłßŒŠˆ}ÅmāŃ#՗&H‡¤’G®-—·Ź@/ śĆTĄļQ!?/Tś­†ĪC!\nÜo/üLæ ĄČ6"ĻŚ]€>Dć°žŲ Ģ‚÷Åļ≽D–b·¢¼ņ¾rØ*Mżš[¤¦›”_d˜olf2šaĖ K}ė-kM[Ą6ßZĮ¶|²<·˜ļ™š˜.}ʙ†¦†*ś£:I»NūR»_»WÓE§ŖÆü xFĒ!ģ ’¢,˜ā ÅNā4” 4YOÖō+7Æ.ĀņŒč7()…²h̉­B“ ²¾ •īp ¤‹ÖāBŃ:Š!ŁĶ^’ūŹCäsr±č±h č&TŽ’GaŲReų<Ō2B_£tōv“et­lēH/Az? ’ź%™•8ńšŠŒ7r!¶ׁĻø.Ģej.¦ś Ӓ=Å”»’×"]#u"p¤[än¤ 9ŸœGš©žŌUŹM÷¤ĻЦs‘‰vy™}÷$ČøĶłµü§B~ūRČ{‹[Ä×¢KŹ—ĘI¤÷’K®)•·Če"Z$Ś&:%ŗē_üŹCm 1Šzč2°+\ī φĮ;Ō޾ł„$”Šuč4€Ą®a ųü7<Ÿ8C”QœPŌR¾RŽWåŖæ€ž¹@;^7N?Ū°Ģ8ÅTĒ<Ń²ŲŹX;ŚnŁTvζĢÖĆVͦµ=“f[×[L–Ńę÷¦J¦9ʗ†÷z»žŌ?Ö_7¬4$ėÓµŌEĮĖSüŽcøˆöGV3=ķm2dČšH™ Qōē‹EĮ‘mz`S8œg‘åˆł¾*m4˜āÕ” |CŅK™boįØĄ‰}„8ł¾l-|'·$÷rѐ^Ą”˜€,IĶ€ōƒ/d¢_¢Ż¢-åŠiė‚üŅSŗ%U”³ä"RM±¦`VšÄ%ńŸø†œŸ­I× ›EF‘~ŗ!Ļ0čT{ņMdddM„Sļ9Y°@ķ¦ĀT9zż˜ĪbĘ2÷™tv4°ĖDn wŽ3ņ­€•žņK’?Ö õ7‰/Ečöc¤}Ņ[É!×gŻ&ą«8čį­’ÄĻU€ŚAćĮ1ŗ…!8{‚ōrĆńHcd r”‘<ą{P-MĒž`yų,<Œ·&n5æ*š*?+g©ŹØiõ)ĶķDŻżbĆ9#jöXŖXÓmKm¼­§ż­½ˆr<·Æ“÷·'ŁļŲŹŪ¦XĻZā-cĢēMĒUŒP‰­L fÄLvź6hī©BŠSÄP¢*1߁ĪCuč dŒĆzčūJķ3 õƈŠŽfAē VšKø"ŅÉA–Ą‡”Š}`k”ß@õģ”NŠĖ;°*Æx[Üśč0IŚŹ{dwtI4ü‰Āv@^BŠ€|‡ŅHwdüšU‚ŗFņ(iŽ4R Š—ÅŻā*ń¼XS\*”Æ|ŠĖā&± ¦15:r&ü ҁZGÆcĘ0+iœ^LM"ßErÉ äK²µ—:E¤^ƒźū‘^O‡čJĢ\ę S‚Ź>f3øaÜeĪŹ·į7ņŸ’ąrÄŸÄ ās}æßÜ#ż&ŁääŸåņ=‹ę~“’Å/Ŗu€&A[”[ ĪĒpx%| ęį ¤52¹‚ hytzø_l ö«ˆÆĄ!¢ń”hŖx čØä”KUÕՐęŠfv†n¦~³įƒ±¹ŁnżjŻe«˜yeœ­¢ć­c‹£¾£ƒĆé m>Ū ėK–eŗ¹ˆłµé¶©ˆ9βÕģ4=ÕoŌNWļUÖV SlSTUų‰ųa|Ä&£}‘s`*wEV#ćrˆ‰Ā&¤4ņ2™‰ EÜH…ļ÷Ē‚~‚¶G—Ė'iŸ\BUa“`k‰ Ä­ mņ*`~¢!-|^Œ“A#č'pvębÉXŖ/|ƒN 8 ’å* g™äńŅqš³0UČ®óĆųÖ<Ėmćfs³Ł &DM u‘åįu3„¦ĒŅæŅŻé«t#ŗ u…Čž7hӎŁÉˆLvū‰-ÉMänsn¾æå~{„ )v׈E“ōƒ4LŚ!½Lrey€¼N.‘hĪ_ųł”*Ph:“Ų{s#x¼ ~ « ļ9lų#½ĢG ±Xl†į-ńCø‡˜H0D?EX1JiUPuPĒhŽjNh7ė6ėĻ$ccó>Ė9«Ļ¾Ō‘ļ¼ē¬ć:źśäJr#nĘtŻtõw]wźœM‡ķ„ģÆl·mkm»­^ėHĖ\ófÓ<£Å0@×VƒŖO«·kšjÓ UßV©Ō5”ØbQtģŹŹŹEŠGD.± OĘ÷c•±h=4ĶB-Ø mÜq\bäß„ņŅ`q€@lššæœm£°U\, KCø­‚Ē#ĆŠŲ`¼Ž Æ2Łr¢1Q‰`q?„žGHø:<äŠé2.­Śš>n*{ž›žÄ%²-AR9y¾ ” Ø“w O®§ōt[:ž~NĶ£†R³ØƒŌ5źWź%PÉ {.ÓĻĻō`38ۈ]Ė~cĖqS¹»\,߉߬ås ł}2Ä.ājń!H]U„!Ņ6é™d+ÉżåµņüZż‰_Ö|P/TQ½TŻQÓÜŃ“Ōü ÖŖr•­ ˆßšųjL@ó±6 bü2š…ō†nŹ ¤ĶāļBžp›ß ŗ^2ļē+ ŻÅ~Rg¹˜~˜ƒļ#ыŲIü#Gl'ZåÓ—µ nĘŖ¢+2ž½ -Öp³łĢźų{ĖžaÖR&ņf8.Ņ%ĀD®‘@¼ Ļ@— Eź45‡Œįu™:@]”"T݆^Aæ¢S™žĢIF˶`7±$[‰›ĮŻē||~’ šė'ģ¾ é kÆļ‹:©²ō³“Ez*éå r?y|[†¢ŁćW L‹ŁŠAč9¤Ōž ‚߃ōYƒD¾!éh7t3ś-‚,Üd~ /J¬ö>]”S.QTŖqźr…öµöš÷×£©¹łˆ%Ž6Ż^É9ÅÕŠ}°[āińlšō$Ę ˆ)SŽÓҽŠ%:—:ާĖ!‚•-%ĶS=S)cĆk=gøl4ŒzYŸ”? k¢«ŖŻ¦„SčśēŗĆŚź‡Z§"”¢qÅ³ĒÓx-b1>녁JČ/ŰPAˆņĒóƒĮœq ÄFRK¹E“T®‚ŌByō ¶W›‰4Å(‚"ź)§ƒ~ŻSQ‚ążĄ<ģęįH>ń­Łłōŗ%«ą^°³Łę&5Ž|9±SČRŌŠ=·Qo@Jł śåjŖ+UŽ*O„šQŻĮ?=£äü|W~ūæų}ŅĎāņ?öĄ(m©K¼µ¼Z¾%GåæņK€~€z€Äuzģ½ÜžŸ€æĄ^¤!2¤Od³~`ŹŠh9l ˜>)ųhü)č4ۈXÅb…[¹IY^õJ5G]WćŅ’ŚWŗ×zʐdźc~likģK¼k„ŪćŁéé“ū)fvL~L4faģŒŲH óŻ÷]c\ÕA?=ź|ė˜j7Ś2­¼e°åˆY ŃaŽiβ,2ē™JWß"ś…ś°>Ł 6lÕWÓ?Ԯ֜RēØH…Oяųļōp» W‰––® %­įļņ'łrüNĆŪæXLŖ-·‹öƒ†ĆćhS¬5>?‰7#N-ˆvDÅZ„OÕW©W8‰yųŒC{ łPš“ėĮ tzӞõ³«™7ōW*‘ZIö"§‘A²õ€Z>aåšłĄžšg‰¢Š]Š"ŹSŹf*Aµ]ŻCSRk×z•!ŽŲŲ“Öl¶n±õsTså»[{ИĒ1kbŪ{xwy§z1ß}Ÿ#nļWļłŲ1åcn{¦yjz¾ø§ø“=9ž—ī×®GNŲŁĄqŁŽÖžfæokb[oõ[s­o¬Ū¬e,mLØq”Įj§_„›„­”¦.®Ÿ ‡šÅ˜“ŃŅhøU4[āxoć›ƒŽ‰æ›÷šfž4Pø'ŖåzŃ5Šļ°åĄōkŽėńeŲIōŗG'”C°įxbų¬ĘÆ£Ēaz"ź²6z¹Ž\I §÷JŖAæ¦FPÉŌr;¹€,E^ŽTu8ĢĄÅŌxŹBu"‡D¼°ųz€ļŖ€J£G€üĒōcĪ€śkĻīf%¶&7{ś~;æd±øX¼łĒ; ÖI÷%Õünü~5”>ŠBč$ō²Ąą^š2ų*,¤3² ¹‹čŃŚč4ōjö°#±źųJ\ÄŪ׈ Š£ŠrŹ_•T õaõ`M5m‚ĪŖwŠ»˜š“¬Ēmćy®=īQž„1Cc»{n_|k}‰qć¾Ä­Š‹3ųģ  Ć1«bZÅŲcŖĒųc bWϏ©ķčŽä‚]ćœ*ē,mogWŪkŲ—ŲŁ)«`®lŠ5v4,×ĻŃUŃĘiFŖG«Ź(͊OųuB_#ݐščWłžų†ÆĻõį¾r#łßx\XĘßāfqŁ|]aœxVņF×BÅhģĘcß÷ūś‚ōBÖĆvąOKąöČĻč8l°“H[ČŻ&ŗłFĢ~rC¤2Ł$”–tc1RS# ŌxņlŒöHY†*A’D7¤{Q^Љ“ō<‘€ß²!u˜²Ņ½čs“äĻ“ŒōĻ]…üęsĻøDž'°žÉ$¶‰7DL*%õ–ÖJ÷$„\Zī)Æü¤’ĮÆŌZ †~‡ģpø/¼ ¾ ĆH1ąÅkĒˆ mˆĪEļ¢.¬=¶±śų&'ŗ÷ˆŠsŠźŹeO•Q}V=VSO›­‹Ó'*˜Īšs­ēmƒ„\/Ü?Ähc3¼y¾@m{ÜĆøOqµü½ü=żvæ?®Øo²Wė=;&¶ģńŲ^ŹšmöŻÅäxfŗĶīV®= Żqģvųœļ5ģc­{ĢFĪšTædT»f©z½j2Cń Ō†~ƒ‹Ćē —Ńqņ}ńļäv±—øĆ|’ š½x˜Ā¶ć®š°XCZ(÷…V m1+ >³ccŠ!HOxŒޘ]}‚/!mŗ0–z /ŁC“‰¼¹N RéZtŚ@ß„fSķØ˜i‘kį…‘_Č#ō¦:»9B›(ˆą‘Ś‘C .ϐE©•D·¦Ó¦3s”ѱmĄü’æļ»$Šmąāu•JJ=„5Ņ]I!—’{ČĖå뒃_ą×Z…¾@.ųx ¼Ų;Ž”ö·y‰xŠęčbō ‡uĒūkR‚ŽčG<#ź+®*ź)Ÿ(؜ź+꩚fŚāŗ}¦įć0Óesqė[G—ß3&feģļKß­øĶ~,žŠ’‚®“Ž’'’oqø…>Æļ¦wųÜó÷ņaqłq²/ßWĒŪ%vLL§œgØ{­+č<äüĶ©vł¤m#˜†›Ż æėh[iĘ«?Øf+k*Äv¬ŗ~ƒG‘MR![ČnāŹ=A’|ĖMbõģk–ā‚Ābq”” ]@N`|!v­‰GžĮ+”¹Ń{r@ī/I"#µŠö™ŽŠ>gˆZ>™]L] ź>՚īK·ü”tµŖCaŌčˆ=2$2‰ģDŪŁW\m>ĢŽ§mTØ=S¤EäØĖ¤˜ü*ŸŽN+˜¶ĢAF ņēŽĀł7ļ?ņk-.ƉˆTš[ ų’…_mØ?“:}ƒ’—Jļ’œĪęūƒĖ]b›±øÉü7a…ŲKņE9ø1ŚŪ‹jŠŗĄņZĄ·£?Ė©•“VŒČ~ó¤€+'JiĀNöWŗ"GĻ¢s‰>MO¤  ś60»įɈœÜ$;ŅLgn 撻Ęč dÆ‘Ā%#+_ĢäūQO؊ō:J·`ö2 ¶»äĻšKā{š»ųŠü> āā|ńŖKÅ„Ņ*©š+ł_ų €–A  ׆‡Į[į'°©TøæŅG$ 툮Gߣ™Ų ģ,fĀ;ć'p1šųD“VzBŽ“>ņ'²•xÆš–ĻāŸrūų"¢Rī*Ūå$ł¹ü)zŹćŌśm./–.Š Ń)<ęWšm€wtZ·ųv\.{€¹ĖLgė±Ł_™*Ģ5ŗ ĶR»Ø1TŐwØw“šżĀęrūYėd¾RūČi‘†į¾”—”ķįŚ‘Ē‘¶äS{ĪPō:L×g¶1č[’/ų+äwGĀ’+恊 č2DB~ø><Ž æ‚­HMd48;CH6Ś݁ŃāŲhģ,ė2žDL!ĀD'ÅsE;ågĄĻśētM_\z§!ĆŲŌ“Ō,YĘŲ’ŸŻWb¶{ć暳‹Ē_”ąJ|Ÿ”ˆ?ķ?ģWÅ/Ÿ›ąHœąOų’9Ž•0"Į—Č&Jjœ43±QĀB?ć;é­ūγĻ]ĻõŃ!ŪßŪŪף>Zޚjƒśš:VÓZ³\½Z5\ŁL‘Eøšģ3¢C¼š@ØF4OŗĢ`\5^#üĘwāėń? VéØl:eNjm˜}оŽĪŠŗ£ dHžžŽļqøiŽĒēń÷yŸpü}K>Čvedz½ŁÉlu–dz1éī4IĶ JRėČĒTmf;{˜Cy}Ė“czÓ%©/‘‚š¤ŠóąÖИpväb¤>y³pķg"=ƒžJ×e¶0šæųĶēžsÉ|Oą7a>ļ~~±„8W¼ņæļļ0žļüA+”+ %Ą įqš^ų ģ@ź ćc…äŽū#ŃŅŲxģ‡ÄÆį©Ät‚"ŗ(^~惞éU’Ŗž ©üA«WĄßG™[šŚ"ö]Ī)īŃ1=½„ćÖųūÄ;'Ä%ÖNō'ŽLč_/žJ|µ"ńMāžÄ²‰x"Ÿ”8;±fR—äń)Ÿ“?' K’*®ŖoGl׍ēŖkšs€£ø]cļj»mqšēfčźÖk—hZ©TĆ KĒ čų3p¢ū öY¢ם—ųüa^ä/ „¤ņ%9YŖ$Õ/|¢ą”| Ģ•°ŌVŗ*ž ¾† fį ߘ/ Žh;į+æœ/Åļćjsٱģ¶[……ŁéĄŽÓ 5™B(?e¢óAjłĄwįÜ]ę1}ŽŚJfG^„¾óBGB#ĀI‘‘źäe²č¶qōTś3]‡Łü?”0žļü¢ā’-æUŠÆ%Įį š~ųģBņ‘‰…WϾÆ\Ś2h9lV€%ąƒń›x1“ æēжŹŹį žSÓTŌŚt¼.¤— >SKó.K‚ķ ½‹³˜ŪSĢ›wĘ’:¾KĀŻ„œÄ-‰tb·Ä± ń gę$’ö'mHj–äI“ғę'ÕOœ²$u\jé”+IY‰ń£ā>xb{&ŗg»“Ī·vŁ–nK³>5ļ0]5²†dCEż/ZI= tŠĶD-ü : I@ū£‰ņ(q9?ƒūĘ-å£ü;Š K ÷€»6uA[u’öHÆĮ±™ņfq¤,|ęI~5ȂSłßøœÄmēŻüZn§å`n7 ؃mĄjŁML1f5m¤WS ؏Tg¦,·š?ĶÆęƳ5™\Z ;DŚ„óCöP»ś%œ9 ų]"+R{)=„ßŠ?›ƒł'²µø…Ü .ŲĶ’?~«”« %ĆMį‰šAųģAź#“‘ӏ”@”Q­ˆMÅīaIųPüž ųQDgÅS_Ž(’;¬ī„ hŻoŗśĮ˜ciyoķc·:øNz bõq[üāļ&('$z“¶& LŖŸäMšœ¤Oī’¼,yer«ä˜d"95yFré”b©xZŁ“S©]Rn$łć[ÅmņZb·yŅŻÆœ„±öī¶©Öi–9ęצ‘¦śĘś4]KĶ0ÕE{ā%V —„÷C£÷„¾bįąńš‡„ü{>F˜$Ċ«Åb71 /‹qRi©Ž„I=ÅAB~7ˆĻ}ó ך{ĮšŲ®Ģ{–bM\}īą·–żdʎ€ßif ćgŖŠ·Ø›Ōjś+qeł5\חiLū©c‘U”߂ƒ†ŠĀ7¼#\.r%R—¼JV¦öS~z蟳ĢVc[Ł:Ü"ī—Ź÷ę÷š¾ˆŠļ?Īæ’Æüņß5ˆƒRąfšäĀż„c‘†ČTä,""„ŠĮčaT@+aÓ°ūX2ąwš›ADˆЇЦŹgŹ^*B½MŻFćÕ~ÖžŖ;¢?føc”MU-ė­qöĆŽŽ®žüŲƾžsńZ&~Hœ›dNžš,%ūS*„ĢM¹—² „XŠ%åkr09!edŠ'5”z#­eŗ-½^ڧ”ĻIæ'ō‹_÷›79¶³§²ėwĒ9ŠAmŒÅi™nęM݌/õŒöƒZ«j§ųŒ÷ĒŽ!éČ98 v@cäRCq£POHLĀ3~=’’ ÉįØ0U˜)ųÄēb‚䐉?IÕÅ;ü.‰ūŹNę~āž³yģX¦ Ż˜ĀĘÉjøjÜ=v[†mĀVc•ģę ³ŒŁKėiĶŠĮ’ӋūĄ~aŽŠ³ØŖäŻPAŠģ“††Ŗ†Æ…GFZdMź(• $D7łSüo7 qłÜRī7.ļĆļµ_Tč_ų„oü’“?ü“„ĀĶį)šųwŲ‡4F¦#ē)A "Z›Ž=(äw ōĻiDˆhüƞņ®²ƒŠW-W×Ö(µw“Ūu õs ėŒ—M˜„„õŠ­±CpžqƍYķ]×+¾ ayb“¤oI‹“»¦TH}’z%5’š˜¶(5-uaŹŠ”;))©#@ķ‰iŖŒóP¦-ćbŚÕWréÄ£ńēāÆ96ŪSÕ„q–qp¶eÖę–n–žęSF£>N[\ \O“ĘėbבkČqĆȩ‰h9Wj'^Z zį&ߗ?ǵäUB!CxśŻiaˆtZz 6‘>Šq+?›[Ķę³£Ų‰ģ &éJ7 ‡Ņ·i #Óۘrl¶)ŪōĻ0’ĘĻ~cŠ2eiŒŽFf†³³YŽA™{”@Ž '†&O«‡^„ŗ…÷Š|Žt#_’Ø³T=o üOƶg÷³׀[Į½ć²ųžüžę‹ „]§BŸ’’Ēõ—’…ßuĄ/ nO…ĀŸį8¤ 29ų•A‡¢GQ©ß}Š?‡ą7@~™B|&š+®*~P^Q6Qż®š¦.®ł¦9؝®ė£ļbčcœ PmķeūhćĢsĖž×±æł ų5 Lāŗ¤ŖÉšEŖ&ķ: J3¤ć鿤MKm”z ՛62-%=+c\ęō¬‘YE3?„?LūœāH®›ø+>×Ö7,¶›ē…«Š3ĘqŲÖÕvĀŗĪŅÕ4OļŠŒS©”ē‰łxEģ ŚMF7 iH7ˆ–ÆJĻÅ8q‚ ņw@&Ćeš„ł`ĪÕęĖń·łŖā$©žtE“Še…Ž|q®'pƒ3—yF»čCōK:ō93óš¦é™ŒųZŠ?‹²,±ŁR¬É¦ļP•čtęØ&ų Šś%Ņ#š,&‡„ŒįŁamdr!‡‘_ żÆ8½œ–éÖĢĘÄva³J®)·†ūČųA _q`BöŸ…¤?]?ė%­łĖõĻæ_æž7?į~Ēą/°iZČ/ZČļõ7 Ģæ|~ųĆxā=Ń@qNQVyBY]õX5XķÓhj»čŖé‹Jė˜™÷[4¶”vȹŚÕܓėõ%ų3–%®LźŸÜ2„mźå“ é{Ó„’ž>5Źč”~8ķZš*}tzɌA™pvłœ½Ł ²Jg¦dtLćS:'O4%¬õ˾-±ż<:÷uēEĒZĒ;ii`Ø„] Ž­Z©ģ«šJ¼$6½‹<3°A4MN—ˆĖ„$į?™oČwą» »Å¾B”+`±Ķy‡øHÜ$|I~÷ˆ­Ļab˜stqŗ<żXözę³€¹ Œmó–)Ü!› °™ģl@ģ  ,S©Tz-sŠž@YØfd0|& ¾šC½BwB•ĀĀ‘u9‹„ØŽŌcŖ2½‘V2™Ó +÷dO²z®%·ūŹ凚Ē@>.# ö _…±ųf7AĘ*-õžĖż‡’y’čĻüŅæļļł7æöOä—) ĘįżńKĄßG/‰šŠ#Šå.eqÕ%U{5®9  -Æsč!ƒ`P˜RĢ­,¬ø}„CćŚēī“ļ­—;”zŅŚäµ)[R÷„]I·dLŅ96³Z꣌ ™ƒ3²2Ģj•Ó>°"gZö°¬z™gŅ—¦Ń©ß’÷&z†ś)ßĶXGĢQ·äšåŠu6±³Ō6™ mu퓳5zuEå/ļAē"3ąŁŠĄhS¹ŽŌš‹¾šæņÓų½|_n 9˜mŤ³µ@ŅlČßąŖrGA2)Ėvgņéu„*B_¢[€£|9 NeV0cd½l[’µƒ^Ł‹yJ÷¢_Qå©(9ŗDU¦:“įHXƒ7Ä5””u!,Ü3üŲūÉH.¹Ž4S£©T=z/mfśśqģ@ökåŚq[¹ĪžHÉ偿‚BšŲA\*މ?ī’=ŌrŁĀū·ßļæ’æńūž_”Z›ˆŻžŽ ?‹»‰ĮÄC¢‚b‡"^¹J™ØŚ£Ŗ”ž žÆ©«µčŽė®€sŹxĻ5—±Ī²ŃöNµū°gČ0¹~m™ø?łyʱŌĶi]Ó»eÜÉ@3d^ĖĢŹź‘õ,³CęąL8ėuVõœ‡-Q9źœkYu2‡d¬M_†„ŌHÜßĆ’Ę÷"vHLOŽū裭lc-ēLŁĘ§ĄßjõrÅ6ü2jEźĮż”1џå¤*ā¾ń×½3<ĻW0a&÷Œ9J_g°8ēānƒ¹†±•›tś‰šIŁčU“žéÄLan0.¶ĖʰHÓĄüKdĖ+Fo§āØ äjr1D†„MįE!!x>  Żå„ē‡åpČćH-ņ(™BĶ„8Ŗ HD>fSĄ¤›¼Īzø®ÜŽåŹócłs<*TF|2ÅĪār±ąO÷ßµry¹Æ¼ś?¬ł;æöĻ&…łEDJ8üÆ 6»Š9šnų1ÜBō&nE«fåL„AµT• ¾æ&IūN»O7[?Ō0Š8Ö“Ź|Óbµõ²?u“s ī1½āŏOœ•|7„ µIš7]H?™aŹ<›‰eMÉzšu<«Eր¬Y‘¬˜œģ€:7čؚS;;5«F&ž™6!ٟń·Sų b€üĮ%~æ–f{d©d®güE?ä˜ ås|JĆļ”jІčn¹š ’ą%~ æ…ļÄOąXVĻ.|Ī3(Ø*™ŁÅŌż°ż™ŖCU„ŽQfzżš¦čĪĢF`’Ųrl ¶4`—ĄZYšļh'Ż‹zB¶%æEęF–ƒĆĆ­B‚‚[‚WƒŖP£ŠŚj>Ž‹L‹0‘Näm²µŅнé›t&3™yĪa'±÷Ų®7w˜“¹*ü$šķB5aœpB`„0œUāżĀõ/ßwłž¾­Šræ’øžģĪæä—ĘČ4ä " ŁæļE#h l$v3ćķńżøščLœ'Rsˆr˜’SŽU™Ō[Õu5H0#u ĮL1¦™Ź™;X–XßŲŖ8:+ø_x–ÄššßH$ON‰KR Ņ6„˘”Y!ėm—%e}Ķb²,ٟ³g?ŹsøĄõÜż¹Å‹³µY2ŗ§ėS{'Å$„ćÖų&zßǤzPwŠ©v¶“_µŽ0_5.ŌwѦ«?)FćyhxdƒĪDKDI?ˆĶ…楎½Ā¾ąCÜG@ĆČ.cš3͘AĢH¦ćeN҉ōŹNįŌpź=Čłõét*ÓxB³‡ńÄRD [‡YC‡©†Ōm!Dꄧ†ē„«‡ ]Ž .ž ~f…….„bĀCĆOĀ•"["Vr4ł‰l Ī =üŖe˜łĢļLöö›É ęNq8_‹ŸĮ_ēµB-`ØgA(*ö׊D½TE\ø~ŠXøžs}įśĻæ®ßżkžü·?4B¦ī‚Zķ‹īDæ”yŲģ4¦Į[ąŪp˜hI&܊±Š ¢³ņµ²‹*¢š¢NŃÜŅLŌž ³čæén™"ęXkKŪV»Ž9Ée󜌙īw1^“1¹ŹüŌéiŻÓ{fōĶ\œ51{fv©ģ˜ģŌģźŁµ²—f'å4 ŒČ›Ź+›W&wQĪ—¬ń™æ¤ĻLm›\6±H|8£oblē’ū³k¶ÓļĄljĖmć&} ­G}FŃ ’ˆąl(½ķ­'ˆ9Ā=ž˜.óų“\MĪÄua'3]ųÄ0iL*£gīÓ}ĮÜ+EU¢VSK÷¤»ŠUétGaÖ0_[‹ķŹÖcO1-éōt0õ*‘ė"ŻĀthehLČz\¼Ō…ņC‹BļCåĆĖĀbø}ä2˜~KIŒźIݦŠŅó€żÕfÖ1S›]Ī~b‹scøĖœŽo¾[o®:]ø(@bI±ÆøQ|&š„źŅ0i§ōR2’±žśžŒžmżüßżļŸžŽ™„œ@h$€öD·¢ŸŠ,¬?vĆšųZœĘó‰m„FŃWńąe-UŖ“:Ŗ^«i Õź t›ōS ƒƒLćĶk-·­{WĒMg]÷[ĻņŲį¾qžq yIO’ćScÓŖ¤7Ėčž¹%ėXv({cvŪģAŁż²×fės؜:¹‘Üy‹ŠT+BåŽÜĖ~Ÿ¹<żRźÓä䤔 ėü|‰ŽĶ1S<£ÜǜÉ`Ö²ųMˆį˜6IķVīĒ+£ ą!:ŗ5Ś&śQ.! Vtp‚ĻąmÜR¦ sŠzF v'ÓvĘų½£§Š"å¦żōtš£=Œ‚™O— [Š(-|×F€mÄNg«²ķ gƒź›JN‹x#%ĀżBĪŠŁą©ą¼ą šćPšK0$ĻĆ!Uø}ųDŲł©O"} ©~¤jћi Ōō~F ld3K³•øiÜmĪĪ·ą—ņy§ŠD˜#\p±œ8HÜ&¾ķyža°¼I¾_ųüʟŸ?śūõ—Iš”ĀėgłČxä(F2Ńnčō-š‚õĄöbV_„’ŽW&–ŃVqYQR¹S™ Lõ=õ@M¬ö¶vŽ®µ¾ø!Ęh4YĢɖšÖ‘¶svsŠKēŁÓĖ[5..žKĀņ¤ž)æ„VIo laz֗ģž9™9ß²ÓsŹä,ĢŁ˜ó,ž[<Ļ\¤l‘ÆyMó“¹'s¶e ĻØžÖ2å`ŅąÄcń§ā^{ÄŖbīŗŪ»:;ŗŚY&›jdķyõe9āŖDnC8Ō7ŠEĒŹu„õ¾ģ÷kŅÜÆĢ@ś5Œ P­(/}œV3„˜& 6a ŠAÆÓnf!póQģ †¢z õ„~:]YŠo«³uŁ;L{P{jźy)²9Ü0|;”:l\\ü5H„~Ķ= e„LJ_‚ī¹!¢&ū÷ɲ…uŻž>QųģŲ`ŻŲƒ,ĀÕåpO8ߞ_Ėæā}ĀĀ"į¶ +‹ĆÄŻā[Ń-åKc„żŅ»Āē†Ź›’x~ģĻĻ’żõśgx"|~÷ÆÕ»_‘“#ŗ}ŽĘa±-X+‡ĻĄŸćEˆ™Ä'¢ŽbÆĀ§œ§Ō©f«œ`VÕ¼×,ŠęƒśFư͸ޓŻ|ĪņŃź·÷p\v–u_ņōŒMõ‘q§ć÷$¾N^—J¤ÆĢų1ó§¬ÅŁēsą€*@ęd́SĒ2¹%óš"óŖäČm”{5Ē–mĻܓ¶/%#łXb’„Ÿü£|Oc;ÅŠīĘ®¾ŽŽ¶Q–!¦ę†­~„œKŌÄś i0Ł£łņx©§˜ |ę–sēŁĘģKś ՄŖM‰ävŖ.żœ®ĀĢ`Žƒ ćc l0…{L[¶&ן;ĮĪ’§Š3„ٹ i ī~iŒŽ’ćČ ‘Ž‘į'”CĻA×ģlģÜ|ō†:„¶…ųP~xS‹tŻ3“œCRdKź8C„ļŅYĢxę“ féÖȵąVsļø4¾æ $¬”£°Rx Åāhń€ųIōI¤IŅį?=æłXVF‹žåłŪ?ßH„Įćą}šŲŽŌ@F {‘H<Ś]‚>@X+l5öĖĆGć·šDb0ˆ’Šå Bł³ņ‹²‡*Ø„¶höhškÕŗĖŗ9śĪ†ŒÅL¹ęr–fÖq¶“v­³ė£{XL¢÷­ļ¤’lĀ›$1„Gڃō6™‰Ye²węTÜĪ060'°1 Ģ}‘Ū>oxž=Æq®1wx CĪŽ¬­«Ó.¤ä'g&-O8źGāŚyÄž’®|G;[1‹ĶōNæJ»G}J¹‰h‰BĄFh©Š ˜Č°ˆī¾*:lžŗ' ]Ł„ųz8=2=ņtĻ}¤J=¢J€é÷œ1‹˜OଘҧŲĆ.ģ}æŸó9Baƒš\pˆłāDń˜„fŅ4éøōEņŹłņ(y‡üTVżķł÷ß’£ ø<¦šž­©† Av o“):½…°ŲBģ– ž,n%z€š ˜Øų¬h”¼¦¬®ŗ¬ŖÆ~„¦ńh/jGčŹéՆ׆‹Ę#¦#ęĖ–wV›½‰c‹Óāžļɉ}ć=·#žjbAņčT{ś“ 9ó]օģė9ēŽÜ“õĆ¹·rĖęń¹”Ą²Ą¤@|@—“–õ{:š¶0„gré¤ Ŗųłq:_Ń؊žJ®b³Ķbyn¢÷jµźĘʟ‰m‹ō…©čV¹§xB8ĻĻę¼ģ5Š æR³Øęą³ŌĀUŽģ¹žRX‚C‚uƒ-ƒ“€=ØB AžłŖ^¦Ā"ū@öü™|@–¢–P Õœ18ӒŁĮČL]v)ų½²¹AÜQ`ųŃüI^äK „Ā;Į+6§‹§EZL“~”fż±’Dy¬¼ė?ģ?ńßæļæ×ūcż„©„ D6#Ļ;Z†^B ¬:6»¹šNų^%š» ­¢§ā¶¢Œr›ŅÆZ©ŠWļTWÖ<ӌÓęź>évéGZ+™J˜ĖXźZūŚÖŁ?:Ŗøø+Ę|ˆŻé[č_•p0© e]Zļ "kMVjvjŽ6p' zG—«rßäzs +½%ķs~Čźž>.õTŹĮäŃIśÄåńõüļ}RģĻ:WKGŗ-b~aÜÆ/„„T*e}ā!V߉bņr±£Šž÷s™rōG2JāŌB2@¦śŠ$ȖG@ ,*` 8Šó¹ś\ ® ׍Ą¹¹&\'n‡šćųŁü.ž8_Ÿæ:ģ:J•£:“„Ażm W / éBėƒż‚UƒmƒĖƒĻƒÉ”ž”³![øGų<0æQ‘g‘rä R"[wpŠżč+ĄŻū’±öz;H/å¹ }jų:ütž O•…ĀA᫐,ž(Ī/‰¢˜#uęI$JJ–›Čžćž/ßłż{żKx8¼~ k Ÿ[‡u5ó|‹Łą78~iāŻä¼4.}Hę“,uö¦ģī9öĄ®ĄžĄĪĄŗĄĒ€5797 fįņĄ‰9ϳFf¬J«–š“ņ:iT¢;įŗM\ŠoML¾[ėlf‹µĄ¦3ś¾ŚŗźkŠ>Dß ž^ō”tDœ*Ōį!n=S†®E­%MäŅųM„»3C™‹Ģ6‡ĖāIī4· ōĢ1Ü"īw‰ū£¹¾#8¦’œ_Åßęńßøß™~ōa*‰ś‰¼Iˆ| ÷  ŻuW䗁ĮA)X3“8ō{Øbxq8Īl‹ØČ®äE2‘G=§JŅæŠļčŅ`Ž>eŅŁŸŁÓ¬’Ėēęq÷9'ߜ_ÄßåB`§€½g‹Ä„ā ‘ŠI]„%ŅÆ…ūg5—'Ėäײįoū/żcżą÷õgA(®7Į÷a)‰ü„¬@ī J“": =€†ĄaV ŸƒæĄÄDāQJ±X!¼«¬©ŗ Ŗ®¾­nÆį5KµÕt¼īˆ~œ”™±”)Ŝhɶ֓ “ļs®nγ"¶½Æ¶æuФO)5ŅÆd|Ė<‘U7ūUö€6g w+ąĖmœ[%76÷aąj u SŽ1{vęšō¬“ó)-“Éę %ćŸĘU÷M‹ŻģÖ8Ūz[Źø®£ž¢ Īīż!{ pæV¹ĘĀ&į…`k{Ų'¾RMi˜“Uz\ørgy^įžÉß÷ĻÆ,‚NA Ÿ_é /…/’±÷Ņ4ä8D’ŠŃčMT‹Õ¦a×1ŽLĄÆx9b6ń–(ÆXŖąm”—”ÅUŪTIźMźĶ M#mH»DWK~5,56µ77·“±“-·?t¤»ę¹1‡c‡śZśŪ%,HĀS7§{3ĻgÖĶzÕ:ۚÓ8p(Š1·BīŲÜ“¼c ƒ29Ł9Dö‘̬ŒSi©MS+¤ É­ŠÆg÷>ö,t5pm÷ĢkŒƒõ­µĶŌ=”ˉX4Yµ”ćÅń¼‡ū¤2łōTj39•źKO «Š]č`(¦ »ŸåيÜt0‰’ų|Óp_X'®+ k@eöf§1ćéŌ32‹\IŽģ Ļéå|øDxe()ƒ†P劄ŠõXř° TēŁˆ‹ģK^Õś3u•ņZ?G[˜ö {²L%ą7X ׌[Ā=ÓÆ)?æÉ«„* ½ģ> q }N‹ßÄ8©°÷=ŅKI/—“’Éēåo’‡®w€’+qŌ}·Ėh+mwlŪ¶m;™ŲĪĞŲv&¶mŪfWÕ¶Ž÷Ļɽļžóī[{U¦éīģoų­5]ū’óśå’õūc ‰ć~¬¹ˆā.rq–”–ū–¢ÖåVĪö—ķ›­»ż«}ƒq.s–t½w-v7ņD{æ{Æ Žr4ō~Q>rq3#GŽøģų‰’ó§õɘœu,g­\r]ĢŻ-OvŒ”·b¾ģ|Eņ³ł3ņYóŚņüČȹ,«v栌nŽŒāé«S›$G&>Ž›’£r4y%lnH« ¼‹tXmé–6ģ^:„ŖA¼Ņ}j+%T.-µƒÄwüh¾ PXt‹ŸB¬¾{ƒ-Ń[АzÉļä(…T "O(;ÕżŚķ_5DŁ*÷”ņ‰*/źĪ 5‚ūöbežČ¾˜=0»TvZv¾ģ†ŁS²/e{|­|Ū}Ęļūt(žŗ 5P_Ļ |5a‰šßwœxU •Z"=?HYr/,ßorN„+Śļ…„ÖW§«§°^rjķ“łŚM=4×§Ž?ü¹’Ćņ’ėżžóū›½‰łÄŃßææYŒģDĪ%OßČŖ5‚ŚN½¤‚éŖō(,˜_t.¦³•łĘäc’bO²v0ÄĪą[v[ÜÖ~ÖÖ ¶m9ķ›ķ¹ŠU/ć\9Ż/Ż+<]¼%‚b‚m!lhPXžšV«"…ØĪ1ßr̊«“')=„`ZzFvę­,W®+¹fåĪgQž{X£æņ–Īw%ļē<)yžäZ’³l–;Ӟq żSzpFhśóԈ”ȤSń½bÓcžDn ļZ(ŲšÜF‚ī°±lĻ£’)•ĒŽ=#Jeø²_ł‰ökÆ.QoØ“Vģ÷Œoa}=ĢÕOž¾N5cŠ’ĻżsžēļO·#g‡Čd(U‘H­§īc–¢ūӛé×t¦9³„yÄİķŲMģ/¶÷76hĖtĖ{K%ė&«Ū6ĀöĆÖÅžĪŽĶįwLrĘ»N»z»“<Æ=[½cƒ:7 ©Ś&lDų¶ˆļ‘•¢wĒŒ½7:”B’;åEźčō²™ć³n圚«Tī…X37ņœĻs$O§<‡soɵ=§™Ł'ƒKo6-­tzõōoiēSÓR¦$õK˜GēxõoݰB!_½›ŻCœmķ­­½ø‰ĢjźqŪ°źƒÕrŹpšx>éµxJ¼+rR–TL*,„K.é bµx],(=ŽÉmÆB+ •¾Ź „«ZNͧō’“¤jbˆŸoŲęÆē÷ūŹłĀ³`­Ģ®‘Ż,»[öŲģ Łą½z¾E¾W¾Üžž«ž˜@ļĄ‰€ku7oņµ…„Ā{!xå“h•źHóš½"åęHĻGŲK ”™ŹE…P‹«żŌź35H«¬ Õ¶hO5‡^JļzøśK4źÌõ’×ūżĻ×/hłēÕĻ\TIÄR,PŹGw¦—ŃwрU™I`@–­†½ĻĘs½ø£œŪŅŃrĢij}j­hŪcK±Æ°Ē8V;r9O8›»4×fwOœ÷³÷TŠŗął!sCW… ’‚ż:¦y웸į QI’›„~IŸ‘–5:gž\7s=ŹU8÷†Ü]r«¹Rr•Īy7³vēÖŌ‡)S«§õN+’Ö6õsr½¤‡ ćŽå£ÅČŪįĆC©ą9ž*®XG°-ܒÄ”“SĶēz­šŚVŁ%gČ/„‹Ņ-齔-ż”^H;„6’$Nsˆ·…ūāéøÜGq(Gå†X›ļåhĀqņ_Ņ~ń‘PHØĒ÷LóĒś_ūŖųjf’Ee§dWĶīš=-{wöK{ßßPC’P_³Ą¦€ØÄĻć_ń¹„”ĀĮ)6—‹oÅ ©—“ ßæ¶ēnł‡œvX­K«tf³‹łÉäg±GXŠ«Å-å¾p„-ó-ß,Õ¬›­NŪ`Ū[[Sū-{]Ē=G;§Ļ9Ó•ßżĀ½ĄÓČ$= ¾r1ōNŲ÷šˆČśQĖ¢ł˜V±7ćj$\J¬–|5„Eڬѐœ sĢ“«{®ž¹ēܓõ6óyĘķō†iīŌ*){S§ęL;zĶžv;edrJҦ„*ń|ģŽ}£ĒE.:*č¢ū°ó™ż•µD±…éšä&óvŪI㈾D+¬ÖRõAŚœÆ0µŠ£ÖTlRį2?FŲ.—K(”mrł“\8A²I•Å}BQa OńiŻ×Ģ÷žkžŻ » ’ó@ö—ģD_kßR$gŒæ­½’‹?`xąLĄĮ7ä—ńoł,,—ƒ‚&”'‰—D»Tī»*ŁåjņDł”¬‚Üū(›”—J˜Zé¹OżØFżFhŪŃ~v½˜ŽėåøžI=t½2^V3ÆŁŲf®0OšÆžč÷ß_æ® Ł—üńƒŒ¢*SØÕŌMŹĆ·£ēӗĄE˜¾Ų ˜T¶3»™żĘäF€"B--‡,k7ė9kŠmšĶgkoægÆķøęØē|źģķ²ŗ·ŗ{¬ŽóŽA­ƒ‹‡Ä…zĀįĮI‘%£ZGO‰9”ćglīųA gc“¦¤¤Iæ•Ad†vQ™o3ęeČéOR_&»“G„“Ls¤ĻK‹Im¼)ńDüØø¹±cÄØr‘ĆÓCĖĶv‡:gŚ·XC,پō-2‹ØbN2{˜‚ńKŸŖ/Ņ=F=F›¤ŽVBÅżŹ\i‰*\ā-ā9驼A -ž—FK„°ńu±ŲOp gų8~«’»Æ±ļköęģ©Šnuöłlv¦Æƒo•ļ…/Ž[ē’čĻ8 ųŖüLž)“Öb¹dŠ=ŝāO1ÆŌWŚ!}“2å.ņł™®ŌU&+'^ÉRŪŖó‘ž’š©µŠ¦kG“OhæŠz?}9ÖK6Öge£±Č8a|0\fA³™9Ņ\ež2_’Öļ¾~d9²;ā(łžōbĮt„PgĄš t}z}€žJ'bƒĪÅŪ±a¦±WY/ׄ[…,f™fyf)`żŪśĮZ$joēķ½_}œ’sŗ+É}ÖŻĖ“ĆūŠ»4Økp(h SĀ„p5Ā]0¦aŽį±[ā^ǧ'ŽOņ'÷I}˜–š¾;}|F«ŒŽ÷Óo„]L½”¼,9;%2­MŚ×Ō¬”āIMFÄeĘĪ15Ś5"⟰ĒĮ²§€«“c®- _…]D?$Ϲ@G—QĄal0 ėµķp_~5]ż©ü3„ćB~šąn©|č?«^Z"ĶOŠĒqĪ ±š…?ȋ÷~Ķ7Ųź{ī»–ż.Ūę+īėćŪģ{ļKEļmōņē ō ü„łńüŽ.Ōęw„P,—Eā1\j,ĶĒzrÉÕåIņIY’ó+Ż•UŹŁ„;Ūó‘j×Jh=“„Ś%MŠ’õŗś}£~[Wō£–1ŲXz’j›EĶ–ęsyę~’óõ[K’Čéä>ņÉQł©VŌT0ü*ˆ.O ×Ó±A+‚ā÷1?˜œlv+˜ŸĀā–ę–-ĶŅĄŗÕŹŚ:Ū.ŪņŪWڽŽ©`łÉNk•«ˆūž{ø'ŻūĀ»"ØSpįW菩‡aĆOF<u%śiŒ/GH\Łųa Ē]É©) ©Ńi“üéŃéŪÓ¾§īJ露"ewźāŌŽ©3Rz&§$Š_ū$¦Sō™ČńūĆ¢CZy’vyElł¬é–‘\Iv —ŖOF’ɛ¤Hčfl,ŠtVG}¬ÜPt\ū+”õr²4I\,•6IĒ$N®)Mā$į²°_č*|āółķž4’:_]_‚Ļć ÷åõ5ņMšöł|yż½żŪżßżyż{±:‹ń#š7L¾¼0»“+ŠÄ3"!•‘FH‡¤€”WīķōäPK™ V~(Ičäiźõ«šC«…ōÜ¢=Ō=ŸŽRŸ¬ļџb½ä2#uĘ#Ū7KšmĶqę:ó¬łę~’żõ“ “­Č ä6ņ.©‘iT}j$µ™ŗGQt~$č\lPßę.ćfkĄ—YW[Ľą2-C,,QÖ>ÖóÖŪŪ[ ū{ŗc…#ҹ,øŻUŽżĢ=Ś“į}ģŌ(86ä[ČŁŠ5a“ĆūE“lU'ŗnLÓŻbĒÅ­Œ?”p!ńZŅ×äøŌꩳSk¦.K™”r6%,5)58µnŹ“doR±„>qßrł'jLÄē°Ń![¼Ó\ĻģƬ?øņÜöېżLƤ&‘‹Č,ŹM• ߙŗi,Ö«yÕ>Š(Ū”ĮJl˜te“\Hŗƒ†śW<*.KB»Pčv™’ %Nóy_ cąœæ“’žo–ÆƋoŒoƒļŽĻā/ Ž;äü…ƒą=!P”Źāe¾8Øį  `ww‹?Ä,©‹“Vzīk€åyŻWP馬Tī(µ¶ĖJõ¦jŖyµÖŚ ķ0Ų!X/­wÓēéĒō÷ŗ ė„•1ŃŲjÜ2#Ę,kv0'šĢsęŪßśżŸÆ_žŸ—lŒŗŽ¼BśÉ4`?juņ#AėbƒīŇ1ՙ±ĢAęŲ•ŻČ~`3Ą{9…«d™ky… l}f-b[`m­ķ—ģÅŪ©ĪuĪt×^WE÷S÷PO¬÷²wdP‘`!ųXČŌŠ–aĀŻ?#īƅ»£7ʬɱ2vIܬų Ó›&ł“–%ÆHNKi“²/e{J·:eTræ$ObÅų%±-sDÅLŽZQ0l`š+÷iGœm9ĢNe­\u®ūšžH­"ÓČ~¤N¬1æ“1Oæ§U)d·üFZ G+Š|V—#¤Ę")>ö ³Ņ…Ÿüf~!æ„ßĖį+ņ æ$8čoągü7}ū|;|G|÷|’/ĶßĢ?ŪŁĻŹFŽ”@q0ĆA^ą „] †L±‹øV|!F#;ēHW$t2XŽN‰Qź+“”# ‡µ:AżW}§†h“¾Ś ķ²Ęk zM}°¾ķ÷S0Źæ= ÕH0+š]Ģ)&vŁżžūż2“¬C"—’§ÉĻhĄāTGj&uzKy°A{‚āÆŅ:ŸéĢ,ƒl¬ŠÓ,ĮUą&qW¹`K+Ėf‹`©j]jżi­fŪ`ćģ½ģ÷ķ’:r:7:3\»\„Ü7ŻŻ<ļo³ {šéąQ!eB™°aĖĆūDTŠŒ‹Ņ¢žEYžc(6iŽų;ńć,‰ėK$=L“ų$*¹Pņž¤;‰uĘĒłsLŠ©U+āQčŲ īŽ ÖFܦ3»„«Ź}cRčäO¢?ńѼilÖhW“‘Ś}m”6GVÖĀ÷$y.Ņ@(.PĀ{ž&攟Ą„ļjń¹ųŸķ6ōėė/źö8<ž,]8o»’µ?2P?0#p1@óåųŃüQ^„zż„mĀG!Il-.ļˆN©Ŗ4V:,łĄ ää[2§”Tś)ėŠ}VøÆ‡ŗL½ņK×kć“Śc¤g½™>NßŖßAū%Õ°^ęc}>7H3Õ¬fv7§›[Ģ‹ę»?śżæ÷ļx L&«’½°`’/Ąš¹©&Ō$č]lŠ,Püdz?żŽdj2c˜żĢ7¬Š¶ģ2ö1É5ē–sƹtK?ĖQ‹ÕŚ -hX›Ųö‚ٟ٫::²ąĮD×zWn÷QwĻĻ$oFŠķ ŃĮyCŽ…¬mž<|mDČ‚QjŌéč‰1esüŹ1?6-nO\łųĒńŻ>$x'Oš‘“$ńmü·Ų7£‘ŃyĆĮ ½.÷"GYŪ3īū/{‘-Ą>„P2ńĆ 5s…t—„+żƒ¤ j‚jQ‚@ēE%Ė¢Ž8KæēæóÆłü.~č­<Ÿ jxŲØ —ü żżąøŗžFž.žńžMžŪ~2P(Š=°:š( ­§šgx/ ļmĆźŒ›‰sÅ+"%;HŚ.½“bäśņTł„ģ—3”VŹlå“āS’ÕFźDĆkÕ£•Ņŗi ““Śg-éŁEŸ„Š_謑Өoüe,3Nļ ‹™eÖ2{›3Ģmę%óżożžĻūē< d"h'r¹ (“‰T5$č?ŌYź;(¾ݟ^Eß M:ӑYŒJ³„ŲæŲ=ģ|onēēŠ[ĘY®Z"¬­¬6[;Ūa[„}Øż¹½²cÆ#ɹŲäšć u/wgzŽxź{æz§å¾f†> V2ügųŗˆ&‘–Ø}QĶ¢ ‹  sDÄ.ŽŽ›—ÆĒK Å%qłbĖĒt‰šQ7¬SČŚ Powsgn{ ė+®זĶbv ó≠Ę?śŌRÕåJOå¬ŅL}­“V6Ź/¤i°hĀ+Œ ŽŖńūųwXłü¾ŸČ›—%>ČĒąĄg’’N’’j’V’ ’3æéĻ 4 LAnž ¤ń­”’užŹ¢÷vĆ{ńbSq–xAH=įōG’[®$“wŹoĮ 5”‘ŹNpŸG-«öQWØWA©Z=¤ĆFķ¶¦h)z-}¾L?«Ö½hæĘh¬—‹ĘŠC^³žŁĻœmī0ÆüŃļŻæj6±›øx#‹aĮŒ%ד—Ą€”`ˆöŌ4j/(ž„óŃ­čiōAś=ĪTe†1;˜7L$[ĪžgI®,7š;ÉQ–Ŗ–Y–‡–Dk_ė «ĒÖÉv ¶?²—vlt„8':ē —Ļ5Č­ø'x‚¼ė¼Å‚nõ ¶†l ©ś9ōļ°Üį×Ć{D0‘‹#“£6G„EƊŽó=¦mŽŸ9ĪÅŽˆ;W1.gģÅ#Ŗ]䛹:a‹C†'õpīėŽŸ¶…`÷~l/ŗ+¹ÅĢgdC»1pÜRŐūČĶä~ņQ¹±|CŠ•ņˆ­„öü“ĄRØ0?Īßå%ž"æū±_„į³w°I ĀDĄļ’éų©@d p Y`\`GąiĄÉ—å¢/_š”BMlĪCĀ!Yl.Īžż›/ł°[–X¹¾ūZł¾lUJ(½°zÆcżęR[byPßĀ}%µ.ڐß[Ķ”Āöœ oŃoé<ŲÆ¬Ńɘbl3n`}ƒž™Ķyę.óŖłį~’ėžqŪ@€ß±@ó“ É!hĄäҊmH £ÖP—±arŠUA«čk“Jg1-™™Ģ)†ĒŠé€ ½ĻquøÜ5Īmi`Ylyiɰ²ž¶Ł:ƒAöŽökö¼ŽŻŃĆłÄYĒuĮUĮ}Ź]ĮsŃSĻūÄŪ5Hš²5¤TčõŠÖa_†‹įƒ"¾a™^E¢.ˆņG5޾Ż9ęN 3%ę\ōĪØk‘Ė"n„÷ +Z.$)ų‰7ÄģŹēhn[iĶ^ nšõZ’ŚS™¦ŒQ*+-å R;q‹xN\ öß ‡łÕ 9ųÜØå§bÆl@ öāėšłų >x8Ų˜ Ūź EKŹŖZfƒ£óņņmųYŲØ>]h Ž;'HB.±¾Ć%Q±w”KW%S*f_"_•59ŅŁłŸ×$Ķ”ÖP‡ØėÕ[Ŗ¦¦iõAėµėŲ.±ze½'¶ēAż¹NiF £·1ĒŲgÜ7D#Ņ,zb.4÷˜×ĶæõūÆū7öüß÷o“’YdM²79 ų€TÉxŖՃšƒ 󒲂"ZŃSč}ō+ŚĶ”fz3«˜; Ē–d€ß²±hĮÅÜ.,øĀņ֒ ž²ŗl­m»±dŚŚŚ£#ÆU{ ®y.Ī=Ś-ŗz²=¼ļ mH†L aBLJź”ĆŽ…Õ ?1 āRDtdĻȓ‘īØJQ£”Čī‘§"ōš:į½Ā&…Ī9|/hŖ÷§ū»Óķhg˰šLc*ƙ„ŸļģŹw¹£RH¹-eŠu…4”²0Rč#œåÆd7E’uĮĄņÅ·äĒBƑ|W¾.ü˳üĄ,“ż HŃŁéæįÓՁ}k/4nŽ³Īs#7ū k…{'{ˆĖÅ")”:C½+’&å‘ŪÉó°srŠŅŌ·Wy„øÕ’j7uzRż¢†ieŃ}sAÆ5«žWor_£_Šæčn#?ŲoˆńqŌxfčF¬YŚleŽ0—˜’š7ĢOæõ‹$ µˆnĤ?÷O„Č$²"p¹ ńƒ ¢ŠP-رŌź€+Ó}é„ōyŚGĒ3u˜ŃĢ.ę5ĘVgǰūтé\n%÷Œ‹Į]3­ż­GĄó lkm¼­¦}Ż°·tuÄ:Ē;æ8»Īøņ»×¹Ć<Ó<¦ē/ļ7oĒ 'AuƒĻYb ķz#4#ltŲõ°ČšÖįĖĆįÜļWv½V$ljčÕ3ø`pĖ ŽÜž.‹³‰}žu+יyFn4ēéŗŖa§÷T&+y”­ŅkĮäńiB!S8ĘĒńÓń/|µ2š5›oĖā'AĆžšT (˜ĢcĮšĻčĮǁX)ĻŸRĄÅ§*:AėüCžņ­„æ…ĆĀg!R¬&7‹E›TBźä¼õrĖmäYņIł‡ f®lĘī¤Ą -ŌÉźõ©Źjy“&Śtß - Åčåõ®śßśnżž.éŃFi£­1ĪXkœ5Ž“™lV0Ū›£ĶeęóÖ’Ö/QƒčLŒ'Vljg„śūžÅ-Éär$čk’¦Ņ©šT_juäčģ éņoˆR!t\č!t£',=,+,(ģ1Žv†Nł\"xLŠ~ļCĻc÷>WG§ĻŽÓvÓāāb˜p*lxu»ÖA5” Š$”N‰_…MĀj<†CĮ³|+¤äĻ€ˆ‚Ż‘œ‹łåxü öĀ"©ĆWą‹ņyųt> k&™Ļä  ėźĆŸcńyĒą;ZČ £…-Āóž‹Åp©’4«åŽDŹłä¶PļøüUŽRŖ*ƒ”5h>YIUė©Ć‘7TQMŠŖūiĒŃ}6CC}ؾ\?©æĮöL;ō0¦£ż®_ »™aV5;›ćĶę!ó¶łł·~D^¢сM,A< D°>Ł ±—¼Gņd$6Lj<µ‘ŗJec…–„»ŅsčĆō;ŚĆ”bŗ3K˜‹ŒČd²-ŁŁģ9VaósݹuÜ .ŚŅ̲K&ŅŚŅŗŚśŽšŪ6ÄvMŲŽžÆŻāh &t9»:ρ)Ęŗ^ŗŹ¹W»Mw[Ļ OŒwˆ÷–7-hXŠÅ  ą&Į‹‚oÓ!yC„ō Ņ+¤NHDČ„ąĮƒšń:¼Õ=żŻc\}åÖKœe.ū“®Lķ$Ź™œĮźEµŁź„øŅGŽ$٤µb'1æČcć7lĀ4ŽTŪŹāĻń—łKč²xk?—Ÿ óżų>|o¾/?oMÓoåOńyž Ķ„QĀzįŗ  bMń/°śuQÓ¤†ą½ŅÉ*‘;ŹsĄ _åH„’Ņ_Y®\5ÄŖUÕžźRõ¬śM Åré M÷ŻÕdt_Ćt}»~S’„‘žƒŒhæ»X/^3č”»9É\m1ļ˜_~ėNä&Ŗm‰į Ą½ÄM,¾*Ł…œLn$/±a²ØŚ ˆłčĄ§I§ÓučĮō śż‹ŽaŖ0™ÕĢMĘ`ņ²ķŲłģV…‚ŻøÕÜc.ŌR×2ĆrÅb·Ö°Ī°Ž°†Ųš#GæŁŠĮ…÷ģ)ŽĮŽ‹Žg_ēygŒk€ė²+Ž=Č}ÉćéåĮšōÖõ.šŽóz‚* Zt2čaŠū wA÷‚ö *tßŪŁūĪSϳŻĶ»2]Uµ%ģĮ¶–)\{–nM1äEs»q@„%i}ŌcŠSé&?“zKįŅUq¼X^Ü ōŹ éB“& ^Įń–ą#ƒā/B©cüĒ ź%ģÓWų-D ł…ZBaŗ°]ø)šB±¢ŲSœ/?ˆ^äfgiŽtTś(…Čeåžņ"ł ’3 źõS–*ē”J¤ZĢ>O=¢¾QķZ>­)øavAūŖyōč¾a ‡ć ?SO0ŹķŒ1ĘJćøńԐŒp³Yō7Õ\g3ļżŃ/ŒČET$ZCˆyÄā2ń É² bły˜| Œ¦JQ­Ńk© ŌŹC¦[Ņćé-ōmZ”SЂÙMĢ=†5w`@A™ĶĆuä–r·9»„¢e“åˆE°²“El#l§mV{=ūRū;,ŅŠ0ĢŁĮ¹Ūi8kŗ»Žørŗø‚.Š{y¶zžylŽ|ŽzŽnpåHļ o[o /ć=įéę!))Kj ÖK×%QJ”kʃåUņešz,’³’oõ¾)ajiµ³:CżĶGjéZmm²óˆöB#õ½ŖŽŻ·Cæ®’ĄvÉcŌ6z!=7Š~„k7›˜żAļĢę}óėż²ˆņDsü,b3q–xA(DY Ś—œInƆłLŚąĄšT/jµ›ŗK‰T ]ŽīL’Mļ„£³˜FĢf+óa `{v.{† `É“äępē9+héeŁ€&Œ¶6±Ī³Žֵͱݵ…Ū[ŲWAĆLGd©ā( 6¼č“»jø¦¹Ī»LWawW÷B÷I÷[7é‰ņdzņ{ņz’ųŗĒ¤·’]. ·'ČŪä{²žū’*żKY©\€÷ĀŌRjG0ß.õØ!Q«¬u×fj»‘‚„—Ō[é£ō•ś øĻŠc’ ÷”ĘBcÆqķg5S̲f s0č}³yŹ|`~ū­_(‘I”%š}‰iX Ē‰‡`x7™ Śæ’!%t`1Ŗ8pu+†¤Séźtz>Zš%ĶaĒ4eĘ1ۘš`^¶5;ƒ=Ę~gćøzÜī÷…‹·4FŽžµØpaoė&ėkœ­„m‰ķ-ĢŽŠ>Ļ~ŪīuŌqüķøä`œeCœ;o”®Š®Ž®ł®ż®Ū®O.Ł`q뮯®®ČŚ|®7ĪÉĪĪ ŽxĒ ūW[IŪ(ė>ĖsNcCŲ¦śłAl0Ÿ F_ż26Ž 5\]¦d)ē䮲W>- “ŠJ8mš«‰XBL]¢"|ž"/'°(÷C­Āį¤pQø-<ĒĒĮ%&‚ź&‹ėÄ3ā‘–R„ŖX›3„½ŅCģĶ$¹*X}|T~#Ū”|JSe“²^¹Ŗd+š^GuŖŗS½«Jj­œÖQ›¬mÖ®jß47ø”¾Ž_Ÿ«ļ·’„ūrՍ.ĘDcµqĢxd ™eV6ۚĆĢłę6óŒłč~!D:QšhœH¬$·‰Æ‹-E6#wĄŸHŽJv¢&#.PŸ(;›®G¢’”OŠoi“iƌe¶0wуYlSv»}¢/Ļ Ą–¹ĻY-%-}-ė--nk%ėpėė'kŚpžķŠ¶—²²o·æµG:j;Ę:ö:^;ÜĪāĪvĪ ĪuĪĪ{ĪwΟNo7œ» [gˆó’£æĆåXiO“’ccl¬-„„,÷»¹M«T՘œN\4Żf㐣OÕ u”Ź©K@‚åńrAł«“Ž).Ł„7ā qµ8Iģ%6« U³Ä1R ½¢P1 šeįżņX?CÅYāüĒ"/Kł¤ŗR_4Žé®ÄKQr)°ŽDy‹|¹©”V:*S•Ź]EĀj)ÆvQ§C½;Ŗ Fi„“¶Ś8mvN{Æqz²³«>Yß ŸĆņ$Œ8øÆ©1ИķyŃxchhæüfM¬ĻŃę"s§yĪ|l~’­_0‘F” ź]‰±Äbq…xKčHŠüd-²+9Ä ”ņSuĮ³ØŌMźD¤›ŠĆčåō)ś=m‡‚M˜QĢę#1Él]v8»‰½Ē’\^®57ƒ;Ę}ćb-u,c-{-ļ,įÖjŠp§õµ5ĢVĶ6ҶĖöŚbÆ׌oŲe{’£š£—c¦c«ć¬ć‘ć³#ą’ćt½ģŲāćØåš8.ą³Ćķ;l%lG­­«-“„5·“•™²ĢXśEQ•É™ÄS3Ÿ9ĆČÖŪčwµzŚ\żŠ²D)£|‘WČMä0ł1ŚjTb¤ īCPf”8E%ūBĻX&}ÄAāq"Zn¹ø ™{C|‹•$å”*Kķ„Q ¼Ćš(EČ՚#å•ņ)ł­lÓė õ*‡•ē ©¦ŖÕŌžź,ß=0CVg+m“¶J;„½Ō=^/‹Ÿq–Ėaż”ЃŠ}5ŒĪĘXc™qĄøōdĶ8³ÖKŠĆRs·yĮ|bžų£_ QŒØMt"F`ĮlĆ?CøČ4²Ł‚ n#/’ļH‚ŠG4§†P‹ØżŌ}ЧĀé¢tsz½’> –°2¹™ĢPfhāÅV`{±‹Ń„ߣ(® \øš»Ī©\&˜b²e?4 ¶–³öµ®“^³ŖÖ [ŪxŪNŪ#&«eļkŸmßaæde÷Ūihęw98GĄžÄ~k×=³Ķ“¶Żµvµś-ƒ-?¹¶Ü%6‹Ģ¼Ąu5™zBę%§ļĮK»xc”¬/֒µj}ÕÆ,Sj(†|X&—‘ķņi·4]źóIQ+°#ŸŠwÄkČÖ āEń2»/>×°Q‚„©˜T[ź$”H;„ĖŅ{P^|×R./•ČOe ‹„¬Ņ^™ l/|Qœ õśź@u”zP}¤*j ¼×J„­ó=ÓT-Z/®7ÕėóAķ×õ/:g$eŒę ‡YŲ.gŒgoøĶ4“_S³Æ9ō·Ļ¼d>ū£_‘L!jķ°@g뉣Ä]$(Mę “uąĄqąųƒäņ;VLU‘jG¦–SG©Ē”LEŃ%čōH°ÄIś5Ķ2L-¦?³˜9μclA¶;‘ŻĮ>d .‹kĢį¶"IIK.KSĖxĖĖ# mĶmmjkŻb½c•‘§•mŻlÓm[m—lom*H1Å^Š^Ę^Ł^Ž,eĻc¶›¶ē¶}¶qp-ßu³:ßsü2W‡»Ā–cw39˜‰ōgŠĪ2œE|ĀæųŖQÉ8«W×ļk]5B[­VV³•µJs%\y„}Ųg>Xž¶!‡A—† ļāR^)J%įH‘2¤Ņ3YÅlm2§ƒž˜WĢēęĻßśy‰$¢¾51€˜Šü śš‰ P`y8p 9x +†'ƒØÜTuŖ 5ZM žQ*M‡‡ŃKé£ō3Ś ™JLWęof7sŸQ™D¶Ū‡]Čgß±N®׊›Čmēīq:—j©edYf9młhqZó[[‡X’±±>¶ Ö [N[8²«ķ/ŪXŪ(:Ķ6Į6Ś6°±…Ų¾ąóĘ[+Z5ĖNKs‹ĘżĆåįŽ²ŁóLyꝏŽ@EPÓHčG|4Ū›/Ę}ØīŠ×k“ź\µœŹ+»”>JEž_&÷ĒÖĻ”ņ/é‘tM¶VZ&ĀGį-—¦H³¤Åx’.é豅”-1r”œ[¶µü—<[Ž*Ÿ“_Įw‘J„‘2@™ƒĪ»®|‡órŖ5ŌīXœ›ÕKźGÕ¢„asvŌĘk«µŚÓßźŠėź½ō)ś:ż¤žDēuƑ…+®1Ęcl1NAīV3Ž,bÖ6;b½Ģ6כ‡Ģkę ó׿ˆDeDb<pāń“ąČXPD-²9Š\Hī"/‘oHŒ  PuØŌd°ą)ź9ŒDŠ6Ļ/¤÷Ó÷iŽ`Š1-Š„«™sĢ'ĘÅęg›  W±ēŁ/¬‡+̵ąĘrø+Ü.ÄRŌŅĀ2ҲŅrĀņĀ¢[b¬E­ ¬=”ĶėVėQėeė]ėSėKO¬·­ē@Ė¬c¬­ŠvŒõ†ež„®…³äŚq4·š-Ź^b3Ļéöō;Ŗ3õŽģ@¾&ŚoĶnę/c¤į2Öź„ō§Ś-S{¤ž­VRIõ¬2 ē:YäėņfyŠÜ]®#‘eLČ~é³ōZz =āx"½”>H?$ ŖÉ Š­œÜ@īŒü-oĖ÷Ač%Q) ß Pf+ېšļZMTĖĄyĆÕ%ź“žOõb×ŅzhS“õŚiķ¹¦üVƎŽCŸ¤ÆÖč÷±;ķF²QŹhlō1&ƒŪ7Œ÷Ų.Įf†YĘldvĒz™on2š7ĢWōóńD>¢јčNŒ"ę‰#Ä-ā=”dY² Ł›œD® ÷“7ȏ$IÅPEØś`Į)Ō:ź$õ”’Ø04N}ŗ/=“ŽN_„æbĖd15™^Ģ,øšĆ3l 0Åv-4üÄ:øÜąŠÜn?÷€øpK!KKĖ4Ė:ĖQĖĖ'‹fqYc­YÖBÖRÖņÖ Ö²ÖāÖ<ÖxdeĄņŻ9ÓŅ֒Óāćöq}ødī;’M`O2Ķ™Ÿō8:ˆ^ VŻO–!/aSæ4{›¦1[üČŲ­B†FkwŌŁj=5T}®lUF(u•t…VŽČēį¢yņ($jK(YI. 5 ČłpĄ³RrE¹–ÜTī(÷“G˳¼{ą¹Gņw™V¢”üJu„ƒ2\Y__v„šC-¦6Rū«3Õ-źõ r3‡V\kZŸ­mÓ.jo4ÄWŽė”OóŅoė_u»³ˆQÜ0Ü·Ż8k<1~œƒķYÅl‰ö›h.=œ0o™ÆĶģßś¹‰X"°Ģāo$č>ā"ńŒČ†c°B«’­Į“ė°Cļ’ßH–Š 6€‚“©5Ō1ź ¼ ‰źtz½MųœVéh¦8Īč0ęęóIƖBŽdW K_°Ė•ßćC…› DŚc)`©jiiémV\bYkŁjŁeكĒ6<_„Õ3n-Ļ p—øEš] ÷_Æ>K²Ū±œ Ń<ōEŖ-œӐ»‰Jijæé07UÆś<½Œž][£5×±D—a‹ęQuå6Ę„•RJ‰’żūä ņųq…¼*M‡/'ĖSåæå9ņ"4Ü&yŸ|R¾&?‘æČŖģÄß)ØTSZ+•éŹ,ĶŪX+“«UؽŌ)źZõ8‹_õh9µ*Z{l–%Ś>ķ†öY£õX½zƼ·R?ęū¬“F“QŌ×Ō>ĖXoÅņü÷™©f ³ŽŁģ>Ķ\z8mŽ1ߚ¾ßś¹ˆDN¢Q‡hKō.Ę=AÜ!> 8>™,†ӉŽŗ‰< ’ųIZ~+X):ZA¢īR?(B—„[ŅCčōnś:ż…¶0©LE¦Øp%öĢSF† ³Ųģv'{Ndø®$×.šĢ-ēörøĒPRę8K°%‡%Ł’—哤[-»EęŽqW¹]ÜL®WšssOąēNlūŒ™ĖT`~DĖŃoĮ§iŌ9²=iK‰¢Ä}ü‹ĆĶ#8#ć0®ōxż‘¶.ˆÖŽØ;Ōj-5A•@g{”yŹ`„„R ¼§øB Č_åwņKłöäSłXü#RR”IÅ©D*i ČJHß.ŹPe†²ZŁ×½RÅN(­6Vū@»Õźapśwt^"öf­Ÿ6¹yB{¤ek=WSS¬–©ś0Ć-ż˜/ŅČkT1Zś¦«°\./ æa1s˜y͊f¤ēH“ßs/čļ¾łĪō’Ń/šČ$Š5ˆD/b41‡XKģ'."~V\2“!ŁM. ·’§ÉGäÆß Av”ĘPKؽŌ5ź=eb¤ėŠŻé‰ō*¬™“v39™ŖL'høœ9ÄÜWøŲL¶2ŪŽĮ.bw³—Įų2ėåŅø\m@_n4÷7<¹šŪ„³‹ŪĶķä¶pką¶©Ü_\{®—‹spŁģ\|œl6³ĄäbŽŅ‹éŖ4UUƒņ‘KČŅhæÉø*o™CĶ,ŠaŲĻõč§~C›§µŠR5ŸzV]¤öV«ØI*„¾CgķNDƒu€2Õ@Űpņ*¹qäĮ³" ńJJm„)>ŽW„ü ٦Sn(Æ^±©qjA0^u°:C]ÆCć}U-V+¬ÕEjÕžŃöjW“·ą…P=·^Yo b˜©Æ×éwĮ ¤ę«d“0ś“Œ„Ę.ćœńČųf˜FČ”øYĖlcöū-07b}^4šžčē$¢ˆtD¢ Ń : Wīv8š8^ ląˆ¼dE²Ł $±,x ž@ŠĘ`ÉŌ ŚSĆØ¹Ōź õ„ņSN:.lN¤gŠĮõOižö¢ +3m‘„ó™Ģ%ę £1al¶*Ū–ż‹Į®a@ĒgąD݉FĖ„Z+ƒGq® Ų#Žóp:{“ŻĒ.`ū³ÕŁxös‚™ŹŌa‚˜ŪhŽ*“ŽėØJ!ūāg¾D DÆ_3‡›Yę3c6®kS?ŖĄUOėWµEZ'­fў«ūÕ9pK]µ€”Ź'åžrV٧lP–bCNĮR#ń e§ć}K“Ū•ƒŹ9då+å§b*^č_H­Ŗ¶Äי€­²×Åcõ—ŹżÖ®¾Óm¾¶U;£=Ö~iVd@Q\G]@ė ōmśiż‘žCgŒ(xƘƏ1ĮXll3NwĮ ²į·ē3+`»t1‡ =—š[Ķ# ‡ĒęG3š[?A¤±`źc±õĒO¬E\Ą żLhČŠD² Z°ه’[ĮÓäņ+–Lh¢"ՂźOM„VQØp”Ž5“g³-’t6½>†L SŠiÄōf&1+˜żĢuš”ĀxŁT¶8[:ögĒ!UW²Ū åIö"{½ĆŽĆqĻ.°ĒŲ]ŲÆ3Ł”š\e6eŲ—Ģf:ӒÉdüōz4’Ó ŽPƒØ\Ōr1Y鹛čH„—ĶQfAó+ŗ¤(ž•¾Aļ Ś"ąĮUXÕ“MQb.QGŖ°ō C /¶iJ¾TB£’¼Ģ׊”÷•'šŚgÅÆš[øš¬ę‹ŌSŪ«Ō‰šņøī–śN•U·–ŒµRGė ķęj›‘š÷“/©GĄyõęČĶÉśr}Æ~I”ūu«käĒÖźĒnŁb3nƍ–K¤™i–„ūZc‰Fz®2wa½\7ŸšŸMž·~v"˜(CŌ&Z=‰Ų0KсG‰kÄsd(A“)d²Ł ŽKl!ƒē?*é„R©’T=äčHjµ{ōõ¢éh:?] žg¬§Ńwч$Įä¶`ś1“™eĢ.ę,󐳌mćds°Yl1¶[—mŗ²=AżŲ’ÜWø ŪYMcƒX™yɜ™Œbš2¹’¹CƦ{"µ%ģØQT)JĘJīn}C,#!Y.˜cĶR¦lB‚–6h㚾HļˆĶNėµŚ$­­V]ØŖ/ą™­ź|u VGKµ6T) rKA&FA§0j4ŽJV3”Yqµ¢ZGm”vQŖćąßÕźnõ“zG}« ŖU‹%T@ßõ­,жh'”Żg¬Ķ=C/ÅŅU©ĻÕ7" néļuEwIŲœÕÖF?xo‘±Ł8f‰ŻI™!fŠY˳ Ü7؜d.ūķ3Ļ`}¾0æ˜ĀżBA€¹‰`ų¦Dgb1˜G¬Į|ś;”&CÉT²(YģMŽ!ē‘ČCä5ņ ­T*?U.ģKM¤ž”vQ琤?)ę£+cŃō§'ÓĖé=ō¤é/ša¢pę˃պ2ĆĮłĖ™ķĢQę2ó¹śƒaY;ėf½8ÜxʰćcŽććē@#’ I;!““~Dļ¤ĒÓ éDś;u=\‰²RWÉ Vy…˜Š>`‰sŲŪ•M p.²)ÅČ#ĻÖŪƃNż\±L†.,……aŃ~ŖO°ō’Ew-oFõQ»a„¶SŪ¢ÕŚĀŸ]Ōj?u>6E«.“’ ŻnŖĻÕoŖ¦:‘—y“²Z}šł_Ś4m¹¶[;«=Ō¾j&“K×K€:čCōéŲ›{õ `õ:e„ą½:XXŃ{‹­X×ŒēĘ4ŸŻĻ,gÖ3ۚ}$3Ģeę°ūó.čļŪżlDGda­UC“%zƁӥń±bĪwт‚ł­`¤h3,™äLr¹—<&üJd•‚=Z‹j‡›J-§vCĆGŌwФĆčLŗ Żd1 N\Méo“VLd `7ÖgŚ!U‡Ć‘s˜„ĢZf :r/öūš|3³ŠYˆžĘtƒęe™4ĘÉü o‚4§‚Õ‹Ņś9µƒNU”¼Ōcr5Ł…ĢIž ĮõhąJbÖ6ĆĢøŖPćÖŽ,“OY=JąŒżŚbd\{­ŗV@‹ÓČÓ/ź3¤ą,’šÕvØ“I݈Ē<ߍ¾< Æ^Å:y”~V*”¹ąą ­(ų ‰ÖŹM#lю‚^i>AffĮwõ Ż`¬ĶeśNżĖ]֝ ½üų©]A Ӎe ¾ć öPĻ0\f¬™éQÓlavC÷M¹Æ;3/›Ģ7ęwSü­Ÿ•A¤£ĖµˆęŲ0‰±Ä,b92ōHš>Xž? †€% bÉ4M &§`åm#é_‘>’Af"æźR įj)Īé)pÅJ¦tŅ“"ݘīJ„§ÓĖpīƒ0žCG•¶2įLY ž¬ĪŌcš ÕŚ`ķ“ß-ńV=ģ×ŅL~|N<÷I|˜^IE6—¦#élźµż[ ¦^‘;Čaųł\äbэ CæȞ$ó—q¦½QŲ°Æ”ą|4P-œWˆšŽvm8Uˆ4­­•ÖņĀaP’Ö4ä”Oż ų†ć‡š­ņč6Se”YŌĪŠ je“Š­£Öi9 žŪ®Ó®iĻ“ļHLž ē×+čp½ Õ§A»ķś ¤ęt‹½™i”0j!7ūcšÓ­6v§Œ[HΟæÕĖaęÄŸæ“9=0Ūe“łÆy ķ÷ōšĆ”žčēA¤ąß[ŠØJ4„{CĮsq¶G@ˆw„,D&`‹–¶AŽ®!÷`Ž%ߑ<ÉQ‘Tڰ6ՆźG§ęSk—¦_)rAÅ<Ų¦uqęūbq̄ޛéżōśłZ &ÉpŒs3.ĘĮXšŃšž/Šś&}’ŽA/›ō‚Ÿ Ó“HݧöPS©”»å_rŁט8IĢĄu˜N ßdO^“4ļĮ£p„ē6,Ę¤č “Pk½8Ģ®ū“'ŚymTœśc16ןaŻ”G;Fłµ|xĄb-†¬-ÆUÅ2i¬µō‡w'ƒGVb_Ä×ø«½ĘĘ4”\œž[·ŽŽFļ£AfÆÖ÷`kކv>6Bå…±XšĄyCŒ)ČĶĘć¼qĻxkd„é†÷ž£^ģĪöf_s„9ä°ĘÜaūŻ2Ÿ~żŃĻBxˆH,˜\HŠ Ų0͈ND_dčb±šŲs |‹4 ƒuP 9ŚIõĪŲBr=ĪÜ9ņŽo Y*ü\”ŖJ5„ŗRC‘¦’€-S—©ĒŌ'J¤8$j ]{±6Ż©:JN£ēAĖuōVp’~¬Éćō hu«ēņv;öĻRz=ŸŪtYœN¦ķō/x{?µ^ƏĶÉQ/°[ž&Ū!8ņrc$žńÄ/č·ŁSŹ 6?įŚžSĒČi،Ośel÷Yś@lĮņz¦ŠMś®¹ŽNÜ£m@/ĪmOŠF£’ŅĮ›ńē_xk”6 9Ėd9>o'T; æ=׿ŁYõp\p]ŌÅÕŃ ¼2]’G߬ĀŅ|ˆĢäį»PhW©ŁŠč` ĄŽœ V߉ÅyÕxb|4xƒ6ƒĄ ¹~«×Šlgö2‡!;ēšĖĶĶŲ.'ž÷°^>™Ł¦ü[?ŽpcĘƒį „įĄX”]‰Ä(l€’Rš0qžøC¼$¾ aA&‘łĄōµÉVdr(’töĢ>ųš6łlOP*dQŠŖIµ¤z€§R‹įÅ}Ōiź&õŒśiŚCĒŠiČՒŲ8uč&tkŗ#Ż Žźā€g½ p;ŗ>^‘.Bg /9ŚO=§.R;©ø:Zā{Ä`s>@Ļ ;ƒŚĆČÆX]‹ŃįpM~7Ļ"sś›ÕĶdS3ūŒ9ČŖ: ­ #ī:zžVj‡,-Žu /jšĻ{ķ)Rõ*üt=vH;€–܏?jG“ćŠė»Ͼų «{õh= YYFÆ©7Ó;ėą¹™śR(w@?§ßŃ_ė?u]w€ņ2°5+ ]?¤ę,c9Ė!ćhļ5ZO5¬hė$$FI$g£ßź 5'˜³Ģ°Sł£Ÿ 4–H#ņňņD ,ļ6Dw“ąčß ®"¶ˆ3Ä ā)ń9J a¢Ét²Y¬K¶ž­įdr²tøāł„üD hÄ`*Źƒ3\ƒjFu¦`!Ī –@Ē=Ōq¤ź]čš‘Ź¦h頃”M,E3č,:'ŽLĄž=3×ļśÕīģ,*:Ž$³@Z%m–öIĒ„‹Ņ-éi&L$‹Ü¢(nXC4i†£ÄT±5ŻNŖłŸ.&“?–sÉEČ§ÕšŖÖšq <$gÉ?ĮɍņNyæ|˜ŽxF>F—嫌+ņ_ņEøzR>‚ön“×ɋÉ)cä~ W‡‘[N%ņ=Tūg1'®Ļv¤¶t^Ś$M‘:ćāŁ„€t“½Oƒhh‡Ŗż·µ×Z ;“JX™™SaŻ!Ēļ%󃓙łŽ(j³`Ż`5ōÆ4Mū³ą§ š‡¾‘‹æyø\€ėŠ?‡±_Ć“ŗ$“VĮΰmhp|>.·‰Ē;<¼Z +ēŅ‘2 ŃšŖ Z[Ż­ĮÖ84s±µŽÅī¬u¦.Ł>;ĘNCāĢĒ֖·«³Õ­QĪ.z³ģŸŠĪM¬ĒƒdĻóøß]Śū+?•†Ś¤‚Y„ÜhčēRyš ƒ`{œdōs2Ż J'ńĀ»Ņ 0 Å ?9Š­/HõDKŃE ߉i øZl打ā/qG¼€”r<-#›œ_.NĀ©*×ÅŪČ]äŽņ 2Čy"Nłƒ ū.į~÷iƓš3qĄh:DZö:;)¦0.XÖĮŪJݤ~Ņptt:,\AŻ †§¤æpƧ’ +G³ņ3ĮÄ"¢Œ‹b ŚE_ŅÄ$u±X'¶”pĒÅyųų@¼É'•œmĶŗ“Kƒē× ZC®FõÜQW®ĶĻÕø¾Ķī3n™ 5Ž‚s/ÄMz;[ŒF¹š¬9E<Ü»‚>,“ĘÓüj²ą 7ķC¬ŚļķžvÜ$+s“ž¶Y?[óH ½¬tś/¬üVFŌ4=}¼¼’ĒƒAc;LŚ2«įԊąrĘ .­ćŸQŻķÜā7ņäI˜v%x‹FšŠ\é'¤°>B) ĄšrV5ׂ꣗v7Įś‘ ¼Š¤²֝±®Š^VBģ8/“Õ,eW¤ķ5D7;ٽé®cš½ŁpoĪ·ärļ;Gwø…ū=OĀO!śQP‡é¤LR\°0,'U!É4¢MttY8Jš$żčbų3<ü -=> Ó(8b,(f$’"›VDQæ„atAS‡‹ń(Ü<¼q=Hī%ćœ&å\÷šČ×č« žįr4ģLNźIĶHĆHĶådō‘Hš) ł9·’M>DJZ+ę³6”ŲõŃī":šBŗz+щ~°ļ+)~®āĒXµ³ģad˜ØRf;֖ķ;ĢŻk­5×:‘ąk0ÓEaJ<*ŁŽoé–d½‚G‚÷ƒwaŌ­ąMĘ-š½|ĄµĻ@ĖŖp7„ŪĒ[©­ōVV’I!²I9˜]‡Gm ßś¹øM‡qĖI)Ū­żpī¬u•¤ņ ÖyķHzĀG¤•|vxē|}»…Żę  ¹Ž·§‘Z³½½¬ĘöŸ.ūīā~/ģ`~:  ¤Ōh؃`^T“„ō%Yō©,l§ōƑšši.Zŗ?ÜM¦łĆEńžōœ\ć”!&ŠĄ2“m ‰’ĢnUQol’}Č©£™wĖÅäÅ b ­cNy”üxFœĄõ/qى‹0ö æ9J"ŚÅm×ŃĪēˆ)xl?kL)%ņ€]$ŗyW:ƒr®¦‘\ŚJ5Ųņ¬¬FYŗWlc’'Ņ~[ŲÕģvv’¹ß~‰j!Æo"·Ļ¶&ZƬŽVGµ>|¬h•9ŸĮŹÜøVVŚF°łKĒæéł)8g·rwAø$·’ Äjrļ¦V¼µ—5Č j?°B–Xkx–_INĒĮķŠu§{m©lCČ„ĆķņĄŗĻń»Ŗhf#»•ݵ`§/L!{- s®Å÷vĀ=½3öE¼ļŻį)é%?™ćĮķĆiɤ4.‚ŁńĮOÉą„aaef$Ǝ“ĀžŅ0i }N³ĢEq'\<Ź ^’nH÷] #R2»Y@ņS’įp²­æźŚ4{‰ždĘQb,ˆNәØį\xµ€±æóAy¶˜!~ߋqxÜ`ћ¶ŁV×D ‘®'‡õ+ē«h3y ču”ź£üŸŅŻ£Ų³;ģļ{ üĪī‚5Hułq™ęĻ¢'_±NY­äˆe(źtE¶čkõ…vV+m‚o5™zV]F=F®il5³Z‚U{n׃006O!•̳ÕÖ/pmõ»uŌ.[·¬‡Ös˶ “2ŚNN–Źhēp9W* ¹oŁŗvdĶ>ö`x7ģf¼–Ą¼ŸÉœ»š½Ć.zģ«4‡{ö#ūéÅJĀOEA}x`„ ‚©QŃ ¬ąOŠŃBIV¢Õ×”×7g}wFMūÓ,¾“&ĀÅ9Ņ"i98nBS÷I‡ąćYņƒäS)ˆ²zIŖ1Ģõ¤Õ¬ ™ŸęX<Ė‹ÆA“&˜Ö•&¢ȶd“b“ärsŃ–5„æ5pÕÆDifœ.:ƒėŚ4™æ„ÓŅi Ģ›EJīOŚj€bC?R“Člöó¼ż;w™ż#öe†’ŹKŁšĀŠR?jś-»J~?ĮlļaÖ7¢¬+Ą`!ģ™ÓÉSA&qLå§š±™ÖZÄķVĄ° pl›µ u<ĢćüI“»fݱ™×|ōńXŲ–ÖĪ€Rę¶ ŚÅpćņ“»ō»&¤ĢŽvwd€ußÓÓgÓŌ—‚ݶ}'ŗyŌrå¼z7ŠĪ‡dĻø_"~ĀUP†‚` *šŠ$óm"›‹aA:aIZįW0ńRiCri+ŗEWśįŲ8MēŗH®!įlEĶöK‡„4ĘKĢņ-Š|‚OŚdV¬‰ųł ż˜ģ“Ed#ÅęÄ=ߌœüœ¼3óūɖ "J„­|)=”nŅbž¹ķōŅÅ<óx2gw¶©.č•@ū?fü’…Ź\fÕīÅ=–¢EČ=Iu˜·Æģ’Ģć'šąŲĮ«0ņs~—&vÅŗ¢g`ēčŽ1ėØu$iå§ć`tŅ: NēAź XŻä^éo/į˜j{`Y$™$żž›,hE%æ“+°~ŌŁĶķ6.n}Įm8«k‚=™U6Å\bÆ“×ŁįŻÆ.vGp3¤–K(ēMŠ{zĻ\õ““šs˜ˆ`*w$ĆĄ0ƒ”…ÕœĖE±sćp±"¹ę©6k½±ŌBjƒfuÅū£^#ąäi2Iuźś“‹ęĻhŪvšÜGj=¢§„s`zUŗŖwI?įésŚČKéÕ?ĘK®{Ī·X—ąõItś®»UZĻcĻēy&šœżHœ­XUÕŁ¶b — öE³7*sż>Іn"Į-d~&°ĘūÓŖŚŚM™Åꤽ2øāghŁ'p2ƒż”ŹN[¢ąLØW/x˜ļ ×ų`n€ß‡s»;ELVéøŠŹFrń*˾FkŚuQȦ¤Ź¶`Ö -ļN§Œwł6“”²ÜV õŲÖm.rp¼cšī *r‰Ģyß»ērĻAĻa_"~’D0ą²ŠĮ0 —Ó±ž3”§9šÄ|xKaf©¤ō…TVŖ€®VĆėøX6gŪ¹höśøx‡›ćPŁÉ$‹Ņliž“¾,#'®_ŠÜ-Ņ6tw§ō+’ėų•ėw€Õ&zē4r ÷ž+Ķä±&įĄĆx†<[KT½ŹP–Õõ)z‘ctš{Źj½jŸe~C‡62;Kģy ų=¹|sŲÓ¶dfæĶZąY†”% ~ŖÅš§Ā`[č_Ęg\[|Šq›’ØńÜ£<+”2 ©^V•n æZńŲķ.p¬7Ļ5ˆg ĻĘŁ“ģ©ötq±,€?&¢Yš¬ZfV¢5V§Õ&ń4ÓfĢok<³ƒŌ \»ćœ}ąĖ’ģA' gĒHc’ćĆļF± †H¹WošźŹć“ē›ÓkźńØą÷‚ż½ĶŽ_d Ē ÷‚āfVųjfmó7ƒ|ž=œ FŲC™ćžųPOŅ…žÕĮnm’ĆhĒo:€M'šļĀ­{ĄØ>v?rć 4z88ę1'€Ōža:üšCYH^j/wńŚ€¦o±ķ0m½ßåŚ֙ƒŚVÜ—qn7ك»“õ‡4†§`÷ā? ÷A…½Ö\MfĄēāź" #c],“ĮJĶ“.žéQXŃlĢžƒi^ÕO]\‹2Æ%„R`[tĖ1Óš§J̹ƒń7š¶8×aŌżGnSƒ[W…a_s’ņš¬ X’US˜ēÉ˳fc ҳ=©Ų¶X¶4”mף›„öœ½¾Ļ \g6.2/§˜”# ¹ŸYū•ŁŪĀŗ’…Ł\7W”_Ė`čOĢō|ŠƒżĻc·˜ ›ēĆ¢…Üg1÷\:+yœ5 “NmäŃ·ø(ķ„Y{X?ūa×Į$¬Žćk'ķÓ°ģx]“’‚iW]ĢnĄ¶;.j\ܞ€Üs¹W`üč%"č`č čą˜ˆd"–¦ĖJO?śšˆi8Ø:øFlŒ‹m¼‹nrF X ķMƒś:8ˆ;XĢȀĀebdõ,ØrVę’Y¹Mfn‘ū}Ģ#|Ä#}Čc~Ąc§āy’ń¬±lA$ŪʶłŲRƒ-WŲ ¾fŸŸ³÷™‡ūĢČfę†}YŗĀ|]bÖ. Qē@öOÖżiš=ɬž`n”a’msĒqnĀÅā$÷?ĶćœįŃĪņØēyt™K.6W@ĒĮēš‹ŠM¶ä+ĖĮ鞋”ƒÕ#¶4Æglw"f‰Ø9ø9Č9Ųż'ōžļĻ’żłæ?’żć’ö½?’9v’ļīłžŸ>ė;EOŌóDovŻŃóDEwōü¢’'=’W5£å‰Jīčø£įŁqšœøi.\<·”Ēuņ|R~ÜÜńó’>œŪågäćžyx„\sĒSĘw8žū߆s;Ē”ßyōßļ¤v‰&±•:Ø9)8ŽE,±™¦OBĖIæyXżŸŗH'§Ÿ~FUÜ>ć4ŌF`ӂīįtŌ.t™^R_šĢ`zĢHzŹ›žś£4Kš#ͧkžDć\Fļ\%­¦®ż_Ęn±ŠV»\ZJæ]ĽēŠS§KS„ļ„ ō Ń4„”t¦ž“§žR7°īĄźhÅJiĀõX?ÕŁĪŠ¬Ŗ2lyqŃ|ą™ƒ=ĖČ>~ČźL ?tĘ\8¾Éh’éĻ;Ī%āöµ7ˆ%¢å`ńˆńŠMO÷÷w’ėHĢ[NŚrņV"–oPLD0½7Ų…¹ČűSŗ=ŌA-±…ęq;h"beÜZŁÅĖy•ƉūźP°źRĮis9Qšā¾>4y^ŹœÆżÆ•.H½}č¾ōPz$=–žüć1æ}Č­īJ·„ŅßŅeé¢tŽG9Į£äQwńč›y–µ<ŪRžuĻ>µ2~;‚n۟•Ō•ķló¶ÕVcŻ•C)Jø)J’N¦cĶ&OjH~Į’ĢĄ÷±KD.‘kļPss0KDĢIå··ČÄ7IĘ7ČĒ’ūHLĻļ’ó»nś*‰ƒ"éõ Ņ«AēR£éQēUټ.ӊ¹Ø}å¾jPĆŬ)Ś×%ėę¾6;ÄĘĮéŅlų“Žl`&wH{¤ŅQé$8żū«³!ļ½:ū‘Č(²ˆģ"§Č%r‹<"ļyųM.n‘[féEZ‘J$±"R„ ÆŠ°WŅSéŲ^cMœ“NńĢŁ‚Ņ&i=ˆ.Ϭ©ńlė©k­H6c_j»8–e‹ŗ(fgÕ~ˆSÄć‰WłWč¤7č%b÷Žqr‰ø9ؽĮĢi0bׯFu•†s…žó—;.¹ķźßĒ».ō¦©¾{}į ‚¶ķ¼§eŗčEŗļ‰¤bĖ?F)×š °*‹'½[•ÕZUŪÜ}¶;ś4Hī¾32›®båo•v»ˆ†W3“„ „ ?³Ļ|§™D°($ЋҢœūĪH-Q_4ĶEkŃNtEWŃMt=žetēŚ®ü¶#·jĶ­‹¢¶Ø.*‰ņā QBłÅ' ›^¤ÕhV‡į¾’ˆ­ų›­9G÷K;Įr Ū:õž>éµĮī0²Ž\+ąÅŠŌÜØĪG¬ćxf%ĄüØ’ Žæ ÷Ÿ‘KdŚūØ]žGvŗ°Ó†>üļćM·M|ÅįŻkDļ^™MÄĻyO9ÄE/ł{ļI@+Kāž\äź£‘­Č Ī;!Įm¬Ėµ¹8×JÖö”ė€tLś“u¼@–M7RĮ¬¬0ē3Q“*ƒUCę¾HōÅp1FL?ˆŁbX"Vˆ5b½ųEl[ÄV±Mlolćš-üęg±N¬ĖÅbī1[Lߋ±b„$ś€nŃR4uXĪ{`ED>˜ś±H-āŲv>—īĮĢóØķAéWĒÅøēTöeŖß5i‚²TCeJ±n󠤱šcń/küßŃ{ǼwŲ½\"n‰Ø9,sPs{÷ŚÅ»W/Ž$ĆīH¼ģ¼jį¼r”ų:ߛ×go$!ųŌvŽ×rŽ pŲ缣젗žu÷ é¤0ŽPÖUC[œ÷"Ū±ćF¢>Sq•…$7ēø€hÕUVł)ÓB™ÆŌ":÷)++Ŗ0£MD[ę·æ{„Ē41¬Öˆb‡Ų'‹?Ä9qYÜwÅ#ńL¼¶eUÖeC6eOŅ0łI“YȖx)žŠ‡ā޸νΉ“āˆŲ/v‚ģZ±LĢ?²FgOxŚ^WĖ’¢ ūnf*÷ŻLY<Ć?ư֎ ­›ÉC QՉģYö± S-ż‚Õ›‡¹ųY‰d~ōCĻyÅéz‰¼K|Ż)»wÆ<½›ƒšƒŲaūż;ć }ĄžĶŽļŽ}Œ½ ē’}īė޾č č¼Ę~É}_ņ6Ļō8éØĶ=¢#*鈎,4ø@iŠsLĪ·(K{¦Ÿ{,Ąd”r‹ÜVöż0št ¾=’^ćgfĒ9.'\+.ÖBįŚ¢zÅh1EĢKįÖvń›8!Ī‹kā¾x!ȄÉqr*ł#9³œSĪ+”‹Ź%åŅņ—r9ł+¹¢üµ;*Źų©œ\F.%—‹ČŸŹyär&9÷Œ“ĆeÆ,Ė/ÅÖĄEšü]ü ‡WĀĻébœ*z‰ö¬ŸŚlQ)ṕ‚'°ĘTń’Ā™“¶‘w~Ć ģco““)JZ  £B™•XŌÉų7ō‚ouÓažƒŻ=÷5ƛI¬s”ņźēčć܎øØ9ˆ9Hķ±wŪ»ģ_ķöwlglc8’oēēün·ūJ®ƒ¢ó>Éi÷½É7ļ,??ĮŹņĮ¾Xņf:\ļŠ+ę2Æ:n× Ž%7ÖĶFoÖ ;ēN’üäʅć7éD6ü§8^ō ֖õ?„Ł›.Į“mon|īݘ (g­"ņ SSn(7—ŪÉ]ä^ņyØSLĒžlUCQU”Į{sˆQ ēȂ‡(Ē)“+<\@§ ;£7uą`)|0;³’Ąü˜’Ą/Ń÷¹÷=G3Æ{»DäŽ'ńķ ‹Ū3­möV{‹½ŁŽdo“qĒĻIùģ¼"’ī}“}Üū0ä ųĻqŪ=.ēųųs¹Å9¦8gz_“1’/;±‡Ą»)`·„UŗEŚK69Ė>ßsJ aŅ‚\4Ŗ‚ØÉ:ļ€“Å<°e;)®0›–šĮ”åģr!0«"ח[‚@_yø…“gaävVŃ|¶j8(¶FU+±½yQŌd؆__ĘöH氏?ā†@°ŚóŁ-³’õü ~Žv¾AļAóŽĒī,Ų|˹ph/\rpŪębę ¶Į^oÆ³×ŚkģÕö*w¬LĪåÕ\æ.éŻÆmpqūn„ƒątŁ92Ē9ŖX°²Bń¾”ī1żyQż/@Æ&ŗŁ7č+LBY±:7»Ų#<„u&y2%3ą^Ī5F„ś$äV3_‡š¦›šĶ£å“Ø]¹¼\ Ęt‘Ā£åÅ ¶C>"—å;ņSŁ– %D‰V’)i”tJ%‹’MÉ”|āŽœJv~ĪØ¤wņS"Ÿ¢*ÆåĒņ-p=¢;å ņRyX•{Č­Y•Šį¼r9™ģĒ3GšŪÕčųܱsŅQułŲƒŗĒ+Üū,©f Ž0ÓE°£Ō*³’ ¢ń˜²ĻŃĪDß{ųzŽß]t5Óy÷Ģa£–‰œŪé"÷ę=“µ.f+ģåöR{‰½˜ń“;1œ’÷Ȝw0W‚āzĆķąæß=¶źĶq~2++Ü=¢?Ī÷šQī9ču#g:GƒĻ•–Ń ~•įų»ŲyDN—™½/)*’NZ$}*c6 r‹8€rŻÄŻL9VNS}.W•ɝäApmŽ¼ŹżdĶ9fž¹¬)įą‘AÉ„VJ+•o”śJ„•ŅAé¢ōPz+}•~Jžöår®ė “Vš) •ZJ„œRBł„Ó+ÉyMy!߆”Gå_åuņB“w88¶”k暅ä,r 9€ŖŽfĖö捋šć”¢ Var” ¤šxła¼p9ėu y“-žQ5śÅcŽ —Ⱦ÷ß+sлDęF31Ӊzé°n;:˜ˆÜÜßż\d/xļżĻYo‡ó“óާsŌĄR÷Xõī±ĪŃqGXx6ēS~^Ō!9ģĖŽv– µTG9[¹čĮœOcl‘öIąõwŻĻDEŗŲ埣BõP£žØŅdŅĀ:±ĖżdŪ#<. ­ĢŖŗŸl†-FńŹē廲%‡()įSA„ŒRMłVi§ōR†)ć•éŹ|e™²N٤ģPö(æ)æ+‡•£Œ#üPŁÆģęśMü~…²P™©|ƌR(]A»>h–Ė,J*”•‡pņŒ\%ϖǢѭA±¬\M%Ć>„‰æ“Vb›»Ē%…fĀtń=‚.„ ³5c.JKł’ŽŃś'ūµó‘ė{÷Ń;’–y‰¼KÄĪaŻĻ ą¼oķšm±‹Ū“˜—¶üuv#ÄTÖó±WœĀk^/Š•YžŒ¼X_ī &w,–7ˇ™Ó'(d¼’I)¤|„ŌSŚĀŖ1Ź e©²Q٧œP.)·”'Š„čj@TćÕjjõõCFZ5šJM¦ĘŖŖ_ÕUKyŖÜQ.+§@u»²4§eo„ ZA)‚ī¦TØėM”u·¼U ›āŗEIM ä›G`x€&9īŲ›6ZÜ•=3ÅVé!Ś"Śķ rw=Ös!R]rč;üļKdß7µ8ŹéøžƒŽIf÷sü`·Ö­t9ē 7vͰ§ƒŚū{{¢=ĮgµĒŲß%1ü4Ī=&d2·™a;GŹ-†…ėX;XG`ų%žõ!Ū”²eqRZT>?m½"®Ż”ŌŅ6 ōÖJ;єóŅ-Ҋ.¢Qš¢0³šŁ] c /»}ā4ŠłJä”$”bĢTSfl4ė¼Ī=”u%AÉŖSŖ*Ķ™é±Źę}ÜPž+†FŁÕ‚źējµŗZOm¢¶TŪ©Õ.jWFµ“ŚAm£6W©uŌjźWÜ® šCMŹ~мÆü„Sv*«•ŁŠRh±ZøęŃ$ķ©z[½ØŃuźõ{u0ülØVT ©a²¬Ž×;Ań{’Os„>›Ždūp0œÄ6& ēa½éņ]ń‰k>ūÓ ?GEć„7ČŁ™QųH}©,©üCfé ūüõ|DītŽÖŗś/¾ēpĻAļŲ²æs4s>¼KÄn-aIs>ėv?ĶźžéĖå dĢīōėyņFR ƒ]<«’K„z9YYI¹¢•+BvUæSē«ÕĆźUõ¹źÆLZ­”VI«£5ÕŚj]“žZ_m€61@ė§õŅŗjķµęZ}­ŗVN+ŖåŅ>Ņā4S{¦ž­W·«ĖŌ©ź µ­Z“u‘MS…z“äó Š:&~ƒ+¦ĒĖ’~ ģéżEIĒ!ņc2évŅ×h8XĢ(ĀÅ Vķfą{© ZQś}Šü‡z&²ļŽ›[žr3g"z‡\ō¶»ŗ¹¶,#?. ƒĢLāŻų”xģdo»‡Ż œ:ŲmķÖvK»…ŻœŃŒŃœĖ-¹Ī9¢µ+ŒģG£ż'ó( qĮ7Ÿė¾Ģśyet÷44æĻXg5„ę¤.ēœlĪ”~£Ż–,ŠK-²‹b$–oéMCÉŽKÄV˜w•vē—S“3æ įu e:ēõ:čž×+†ńٲ2•Äx„¼aāqŸĮ¹Ž ·}<«>Vżš*xŌ×:€Óm¶¶\ŪØķŅ~×Nhj“æ“+Œæøō'×üīžŻk•6_›¢ß–ZMķ -ƖV Õ^²~W׫3Õa8ē7jQõc5T}¢œ%³.$ŻtPŖ“›>@Ko£źėäiäŅFlwV9R~NSŻĮЉVCc>ĄļJĒi󳤔(hU©0śä|Fē ~/\öŻw[ƒ£Īė,Nc8Dŗß÷Ń[ [¹Ŗ9ż›£pµAąŃĖE®½ŻœšŪMģFvC»]ß®—4źós#»)8:Ēżwė 8×|óÉīż¬–KØ÷Kš‹&½ä”Žœ‘­Ÿ4.éŒl'„ō…-ŻAÆm©+N1ŻÜ‰ēݶˆ‚y…hŹĶä>čŅRy½ī©J£+®ŌQŗ£`kĄīžāGٜ3#öWg؛ŌSź5 ¾•ŌźĀ³ŃŚ<ķķ vQ{ IzˆžL’HĻ®ēÕ éÅō’śēŒzQ½ žGĻŖ§ÓōP]ÖkWĮs§¶RūQ¦uäqJk9“x“õ(®U§”ĖMŌ²dœõ•r5[Ó0s£čÆč‰;Љ”r t4'śń%ŁJ³B*ūJ|"bÅkøKZŒu"ŃGŸbŽs?G=ŗŽē°ļMź<œ„Ž–·č-$i&2oYd8<źļb× Īµ‚iĮ©ž]Ē®i×°«ŪÕģŖīØĘåv-».˜6ƶ`ŻĖEp<b>¼Ń=+Ōų’Ņ6ٲtt猈M$猈Óč?[É-ĪM=«(ź¢×MŒ3Åj±Ź/4’x6^-¹=Ģ›%’Ģŗ¾)«d÷üō±vŹh÷üĄ7L(­6U‡ąU{P; _+¦Õƒ?S“5pźšfk1z&½°^Qo ·ÓūčĆõ śt}޾P_¬/įļ"}¾>SŸ¢Õ‡č=ō6z}nYTϦ§Š= y^Ū«­Š&k}“ĘŚ—Zv¼ó„z~/PGąÆIH±ź åO¼w~ŲP)‰’šŹMł7ÖŪh¹­\QžŸ‘ 7²6ūŠ4śōĀ °Š'K=YՄѧŲą÷ÄUĻ7G*ŸNŹ-Ī1ēŽļ½Coœq˜7æ€ŪuļSõķŚąTĶ®lmW°ĖŪåģ²ī(Ēå īg9¾į ÜOMuBGĀßI°y ¾ŗ“ÕržææX)=õ Ō3ńŒ¤s¤uōõ³8Ÿāž‘“ØˆrvE_f‘7÷‰ó4t÷Ļ%)7x{F˳0/ŒvW ÕģĒ\mQĪ+¶’Z-ļ1›ūŌ[j@˦UŠŚić`Īaķžę×?֋ėµõĪśH}¶¾VߣŸŌ’ÖźA]7üFøeD3"ŒĆ4$ć™~[æ Ńw諥s”ŽMoؗƒ—)uUæÅć­ÅųgQVˆ”Ż æ.!§¶¦odWĆÕ(Į*eœŅ^ł_W°ŽV²ķķ@0'iō”8&ÖąģČ×łČŚÆ¤s$€™ōą&8KN)ž-~‰īē4§õw_ē<ęę–]ö6ø±. ½9$–)xŽw0g0ŖŁ“LŅ,š]Ż·Ÿ)g—±K»Ÿ)nsGq.ī~Ņ£·ų†Ū:Ÿ5ķćŸÓšÓÕ(ØsVě.~qØ{¶1ńŒĄ“Q­Ņ1śśKœĻ9#pYRK'1œÄ¹–ĢrA<>9œ†ŽŻü^^AĒ»"Ūr«ĢūNY¦üŽj†³ö««=Żs«ßx{nõµķxš®§×KėMōĮšl‹~Z ›F #»QĢØhŌ1šŒīF£æ1€æ}øÜėź•RF>#=˜ŹĘ°Ž7Ē€~MŲ›V×ōkpq‘6\k”•Õ2k~ķ6ž„#6E½?R5õ2n8“Wß ŪtćC£ˆQ„†?+ķĘ!ćOćŖqŪx`<6žŒūĘMć/ć“ń»±ĶXeĢ2¾3zMĄ9æ‘Ś0ŒŪśa}>Iļ¢W×ó鱜Sœq6–„eѼŚuzĘlµZKĶÆF«•CŹbe:ZDI®¼’WĖcčõeČ¢Ŗ|‰.ńƒč.¾”ēĘŠäŃ”ĘIčšłčX‰ų%¦—‡nötŌÓń>§5ģ³E;ß’¼Ī”$ō2ļŻČ™­PĶ`QģŹĆŗ’vQ»hå±sŁ9ģģvV;‹;²ŚŁģœ\—/铌å`a-lķž]č;÷¼ˆėqĄ?X?Ļ\ü2“°*”=%ēŒųߞ?=iĢ9›sŗśRĖń@xHœłåÆńžņņzŽ=ŁOC’œ¦0P™«ģ¦›ūČš•ÕĢŪ~õ”šœ¤ŅmŪ”ŻĘé ėMįĢzż¼®_­ŒQĘbc/½2ĀĶĶ÷ÓrŽv:Ī7›œ8¦¼AÆ)²>ö 8”C3‹Ā¬¼ “äŅŁiģ”vr;ĮŽ·ćģXžĘsŁł”jZūcnń jZ¶V"§6Cƒūć3ąųVŪłVƒGō÷)+ ł©­4XśÅŲGv~&…ˆt.ūš‹~4¾UxĆeńZÄȣњF“§ąžƒ^˜’U)§“bEÆPŽ)Ļ”ä$–fźXŗłßjs×J›¦ķ×^jõ$Ė_Ą.Ž(mt2f£‡Ļ4fi³•9Ö\e6'¹'›§ˆ§¼ēO}OcOSF#O]O5OYOa~“Ņćó<1Ļ›»ĢÅęwą]ÉüČ0ļƒā"c ¾™ĢsSß©OĆ2zż©fÖ[«‚*ŚiVŌµŽš[õ«WPłqJ3„hŅy1gŹ]å “!!Ÿ&a$m&Ć<”ā€ƒp–¢Rź$ü?'wģpÕUĻ?PĻIģ[c/§Æ;Īē|juų[ōš£~uÜO ™ō d»A(%X½łrˆp?k’ōicēSįĪēų³»g_H<ĒWkZ’0|uĻę|+Ģ[C?³I%i9pæYŅéŁÅ’¢EQBŌŁ›¹ųĀI“ӏKc„v§ķ­!æŻ“C”, ×Zƒ&T^įz„Ō6źdu‡zŻ,C^YŒ–…Ņå:‘5ĪźaFq£³±Š8cxĶüfų“ټjśĄ¦‚§µg˜g¦g­g·ē„ē¢ēŗē®ē¾ēžē–ēŠēOĻ!Ļ6ĻrĻž!ž6žŖžOARņ\4·Ā×nf3›é1’2~1Ę͌¤›[$›Iz ½ˆ­ßжą†Mµ‚Zøö7«jŒŚ}SÆ*æ°Õ”OÉ¢×äĶņü¼Ø+ß»éD9ÖoPśCZA‚i"•>x‹ß37½8īwģéØēž$ļ[E¶w“s }o$©„Ļ[ōj“X*)‹Į¼Ühf»xP 3­ŪŖ­Ų²M©ę_•Ÿ= †)ķPҼp°,ümLŸļĻ£ĻA©÷°zīŁŠ‹ßēhDgi“4OŚ,nKŖHNżR4=ń¾e$—ĖĀ hL%ņŚhy ™ó†ģU2¢œ­Hœzƕ“jµ½ś=ļ‘šF«Øõ×Öj7“”ze˜·C”ēĄÆf³˜ŁŁ\dž1½ž¼žžž•`öÄįĶä-ź­ģmąmķķģķéķåķįķāmćżÖ[Ķ[ʛ˛Źkzļzށń$OgO%OvųxÕÜbN‚ĆÅĶxó®±Ć˜DR-d„—õuśP’i”ōØ6OėĀjJ”ݧNB ąee½2B©«|¢ŹY\p\ƒF«ĀĄå䵚ø‡).°žĒŅ«JIi’ß]²ŸÓN»ź¹—乑]ījē4׳†9»į{-\ōŖ‚^i0(€f‘Ōš. žyAJŲ–õŹza=³žŗć™õœŸ-K¶ 0Œ²“įŒ‰gj«‹[ĀĄ‘ötžk'Éé¶-čŁŲ¾zRW¶t‘“]:MöōˆŌ4‡ 8yŁ:qļs¾­„ÜPī‡Śl“/ŹBIKjiŹ,WŽ+/čz„Õvź4Š{¢~ØUÖkæh÷µéŪSõ0ļKcˆ±Ó°f'sĪ–Āóµg°gƒēš'Ņ[Ģyēz·z’šŽš¾šj¾€/Üį õy|’ļ±÷Ŗ÷˜w‹ww“·=ųęņFxļyö{ęzz‚bFm3˜ŻĶ²fJó)u¬QßČIĒ8J«lOÌŌ/ć…٦tŚS¶oŠŚ\żT ØēŃüJżKł {ÕAž\Ž—ļˆķ¬Łęā3I‡Ų*}³”‘ҽ‡Ÿ“>ōr–<‚ŽķJbßā|āæ\i‰ļÕq¹W Ģnfv ¼óښ-ŁÆĄė‘ußŗcݲnŗć—ļY¹ž5zŠŅxؚģK””ßņ؃xŽŸxĪc¬"ŪŽqń«/u—ĘÓż~„;<–üāCÜÆ²h%‹™d—“ō†9­”i5©ķ©ÆTź±’ •ŹC%^-¬LP·«÷ŌŌ“…ĮŚFķ”–™Ä2G椧6źĀ¼‹ų]}s–yÉLå©åłŽsŌćõńvšĪń³T¾"¾Z¾N¾a¾)¾¹¾%¾¾å¾Å\šāįėękä+ļĖķ‹÷½ræ•p¼·•·¤7¹÷j;ÅÓĀSČšœ3—š=Ķ2ššš±–lZĮHI»ųY¢W!“Ž×¶’fjjµź^¾‰š—$sRY tQJ+ńŹMy£/ q„ńŌųŌģgī1žŠž ž“ž8ouļ$ļÆĒWČ׌7Õ·ĆwÕ§ųSųsś‹łĖū«ų«ū«ś+śKł ų3ścü¶ļoß>ßO ŪŲWļ{wy§x[Ą\æ÷OĻOž.žž0ĻYs”ف¦į5O°^Zy aŌ'ė õlśKm7NX—,óBŻĶJ«OĖ ²ö¦’cņ””ČóåNrI9Zž[¬gææÅkŅĄ\DécæÄöž€ōé¤ĒżÕóżR¼oĢųŽŒßķlG^¬g}ļuŃĖ@֌‡O^¼ī5Jy¶]uĻsŚ=ūĶQw³NX§¬?ĮńŖ{6©—–št®Ņ\hh%Ų~’Č3īg ½°#čŸ£ŸŻ]üvæÅƐØ"Z³³ĮļŁ3…\õ*/•É/ä”J öū;erF‘Ōōjyµ+ żˆ*ky“–Ś|ķ/-µ^WŸ„_Ń3팟 Å,gN” dGóv{ĀĄn¦÷ooF_ ßßE_“æ¤æ­¢’ˆ’o’Sæ0Ž€żWżGż›ü³żƒüżÅż©üĻ}‡`eß¾ß ļ:ļļWŽļĻ20,ā1=GxžśfFó!‰¦?ŖiœÕēé­õ¼ŗŠÓ&ŗßō„VWÓ©”Ķ4ŸjʇŹCy«<ŠšQ~ßOĶ-tqRśIźM;Īšæ'“‡›nzqÜļ Łs+ź¹E›ƒ÷9ģh÷"é·d¦kѽæ$µp¹—†<f›8Ž p¹>¬3 uÄ:hżfķµöX»{¬}Öė8žę÷£„Ļ-&ēņ“CkĀėž¬“„čö9¶&”m+!Õe•§ż9ü{"ŠĻOÅ×øĄ@÷;yψW"%ٳ™<†ŽpQ6•ģģsośś~å¾£Tæe>~VÆ«ÉČ-#i >ż+}‚~FOk“=ƒ>ß|n–!CŽóńŽõ^ņf÷õņķń…ų+ųæóļõæņgT t|˜XŲŲĶŲų9°80%00ŠZšŽń =±¤é3“gj×õå$ąBŗŖĮŚ“ś{tśžjY5F½Doķ¢SB”30°=«ŌO†™/:ˆ¢"”³B@;Ī”„ŸÓŽöp…ōrŠīšŁs³½ĪĶ.3Iöc`Ÿ“\ŚŗŚY•¾WĀ=ūT&”3 ŪFļ”™—ąŲqpŚoķ²vX[­MÖFėĘFk3?ķǃ`ų'Zz5Ņhj’kųÜČīĖē³nN¢>¼¹ˆT“ü9†µ¶SśSzH~IÅź+ėęĻI4¢cā 鳘܂ę°]¾-Ē(E`ßXÜ9EQ?†}Õéź>õ…šEkBŚ»”e×{č»õ(£± īU12%ZŻ*ßۘ”ēk vÉż­üżj t`p`Ką^ >¤PHv!żBF„|2:dHHϐ–!ÕC ‡|"‡œ¬Œ Ōä ˆĄA’dtõo« ˜OóķõŽš–ó†zxĘy*{b<§Ģ©fm3½b©7³q__«wÓ?ĮżŚ­YōŖŗ„­.ØjźļŹd„>Yś±¼E&W–SŃć×Ņ{ĖŅÆIė„!“ćĢońsŚß Ņē97½ģ·w¼=[ŃtZū(¼Æ£…ŻĄ®ó•µrŪ™I pĻ k>«ųÜÖa˜ö«µĢÖY«­Örkc9—VsĶFk;œ<„–žeŻEEMRO:;j\ m‚‚®Ē}oŅSKŸJÕ¤öŅHi>yėéŽ$‹X‘™õW ĘZÜ-n‘_ ё&Ė»å'ņJy„›2[٧ÜS"I5Õ¾źõØ*“¼Zm½&ōŠś\C9zś3£œ9ד͚žžäŽŽ0ÆøožĻš7ńļņ§ t ü ©2*dGȽØŠœ”_„~Ś “æ ż24_hšP-ōJȶ)!­BŠ„„†ü C[ržĆÅŽžĀ~ÉæŻ×ģcy7y»{ó{ŸŃ/:xrz˜+̶fvśż ü㉾ Ń'~Նjeéƒ'Ys Ռhč„RJ UNāīĶäœņ+ń«-Ŗ‹“ā>j$ķ8ė{ųŻ?'}žA‚pÜļg²ēOnvGkw¼Ļa_mW;‹’ü³1ļÉš=ōžW®ĄŖchä.°ū¬–Y?Yó­¹Ölkc6—ęsĶrk­{>̃ xÆ“,?’Ł.Œ6Mśfó=lĒk;NŹ…C·K3Żž~EzAMEśœßIŒ…§…*"+/ļ“_Ź•źŹ`e„rŚ}„ŗ³0H]ØRƒź'ą·E‹ÄoźYŒįĘUć3s²łŲ¬äYļIéź}ģ­ē;čĖļŸļt œ ņWH¦Š”sB…¾  ĖöYŲēa„Š…å ū(,,ģI艩•”ĆBk‡f }² ¤æ‰ł#0)P9‡łK€įF’k6üpŽ·Ž7Ī{Ģ󝧌G÷ģ0ū˜Ķ—Ę£ Yę ģŒ>§ŁōŌ k²¶[®~„F©§••oaą}y½Ü› Ó›¾YÅsłXŅA¶ąwŻMŸĪy¦÷[z.HŹ.ƒ™YĒū\¾²?G;sŚÓĀ#i6Ü» gH)ū`×/`·ŌZ`ͱ~“¦Zß[“¬‰ŒI\šŹ5sĄp%ųīĮÓųącŚD8*œ›4[Ūī`Ąo7±†Ų)# ¦Ō‹®³Œķ=)Ż”^J>‘LdEą`śŠqW| ƒ’ķ’ŸĖ™”ŚŹhe‹rC %ŕ%;ļ­oWo©hMµMtöįśK½±qĄČl1/›Å< hēż½O½-}W|µü'üå»łC„„†¶ ŻV#lbŲž°{a!įiĆs„ē Ļž-C9ōćm[­õ0l!|ū¼ĘY£­Ö0k(c—FsĶd0L<—šNTō,Yõ¹eą”ķBvE»1*=‘ēŻ‹’KR )æT…ž:Ģ}’h—ū"÷„ ‹aVQšó½8Hƒ/-‘ļŹiąßxå b+Į¾Ŗj3µ+yŽŗ_Õ“ Ś --½!«±Čˆ5»šĒͬžŃžēžVŽ›Žę¾Ū¾6žGžn wó€Ę³Šza[Ā"Āk„O?ž$<2āćˆ\łų›!".BŠų+|æi^ \ ’-ltXł0oŲ®Šžčģ5õːׁ%Z`3^šŅŲ×ߗĖwĶ;/”½ė=-=xN›cĶ/L;Ł[śż[=~^ū6˜ Q§¢žÉÕsŹ <šCåoy‘ÜRĪ.?D/Q\xÅ1驉ō‰‹Ÿóźu"~IŸNzys®>'{Ž#×÷µ»Ų­ģ†.ūJڟŚ9Čż äÕ~Ij¹š„Žf“q‰{ŪIÖšlõ·śX½¬žŒ^\źo ±FZć­ihé28ø Ę^ăVĄżŽĀāv58>Ąžät=šIé„āīkhĪwņ.“6Ņ#ĮĆKŅ-÷}Ül¢®˜!®‹œrGy”ü‡¬ŃĘ+הŒjµčµQŪņÆóƒ ńĄ¶Zr}§^ßxa47O™é<]=Ē<żū¼5|w}=ü&Ź—1dgH­Šū”=Ā^„5’=<]D»ˆ•W#‘™"?‹,Å(™32y¤y!bCĈˆFÜ’)¼ œ<62¬PŲŠi0ńQČōŅ!ÓŸųš—ö?õĶń}ķ¾ŽśŽļÆžĪžŒžóęx“udįōĘE}>Œ$:T+‰†nSū؟Ń7+½”BŠE‹čKRSåŻ8y)ĪH³ń–Üļįē“w§=uÓĖ/®ūĶ"{:źŁ ekFņ¬‚÷9ģĖDjŒB;ƒ4†k4½£d’-Ö“q6*9ģWw«³ÕĮjgµe“ćR«‡Õ.Žå6sįąFīu"éū~ByÄ¤ŚźōˆĮØöFrŌc;Ģmu¤ī·19Ÿ°uĪ*ŗ 6ž–Ą°¶ŲŽ‚6'®”ʶÜO Ø#ПŁ`6 *« ²Ü åb«'µ5zC3/šiAo§ĒņtńFśīū&ś? ¬”%©L -ö$llxBÄ؈ē#gD^ŒŒŠ*U-ŖIT«ØfQµ£ŹDe ŗ¹9rdd•ČųČÓ"¾Œ°Ā—‡× 7ĀW„U †Ī -zGĢr7MŲ £ż[}Ķ}ѾŽ6ŽŽōĶ,ž p°„łÜXf|k$GQ÷āśkm+-“v ŻØ§¦P’T¦Š‰b”(he9 œD‚I..I ¤ÖRŽ·ų=æ«n{8bļsÓĖJŗĆ ņÄ(W=Ū¢m5QøR°/;É%ö-ŪOŃæ ō…}(§ƒŽLTó;XÖן^­ŻóG7²¾e4²šZ-Ą±³Õ^¾ū„}$Ö79&¹›c*ŪĶyĘélĮ ¶*„XTŖģ~z„£{Nīyrœ3Lt—&JW„¾"9ż}ŗū}óäæe”vVשĄŠR‡ØŻŠÓå/¹„”~„•Ņ%C˜«I'=1ŽŽŽF¾ ž6‹&! ”æ… «ž,āPDżČS‘ŸEMŽŗõqtĶč~ŃS¢ēG/ˆž=2ŗCt„č¬Ńrō‘Ø©Qu¢’GˆY ņZʈ¼gĀ{†'’%¬:éf\hÖŠßBš†!óaįu’6’“Št¾ß½]½z{zy2yž4Gdī³Œ*†×ŲJŽÉ¬_Ѧi•µ€¶÷.¤¾PÖ*픬Ź-¦±ü”|Y̦?„×iÅķ„|Ių9Æ~:ƾœgÕĘ}¶ŃŻō⸟“=õl7ŹŃŚó¾T$Ć~…v^vϾf°×ūŽe D-;Z­@®UĒŖi}cUg|cÕ²źbKpķé"8„]wĆ^ēŪcždĀxäœä£Ś<ć8¶ą™XÅsøßgšEŅYœóSåāŗĢRńšūvĄærg¹æ|@Ī©,TV+Ī÷vU|ŹŗÅTyü«rBŻ£½Š§£Ģ žXfqŒ7]ģ?hņEhHؚ°žį #ŚD~u:Ŗ@ōŃĻ£KĒ ł%ę|Ģ£˜W1Ob®ĘŒYŹ5µc2Ē<‰ž9ŗ3H^Š•7źldÆČd‘ė#*¢§}Ćc‡ ū#“eØ:5$gȁ@Ӏ˜K"½ęįĖīūĆŪĖ›Ī{ČÓŻ“Īs”NŸĶ¼`Œ1Š‘Déµõ}ÖKĖ„ŻVē©uŌXõˆ2Rł\Ź&¹«œK~ –‹Öø’=ikøĄ[üœWĻ®Šžœö°‡ō¹ĪM/Žū %{v„uaĘō¾œ®÷la?”ÆŸÅłvįeĖH–“AoŖŁĪjv5­ŖÖ×ÖWV9«,£œUĮŖbÕ°źó»vÜf·_‚·[šN§^²nYĻÜo|͌Ö±{ņü?£O`”sęž”ī¹¤œs9ē?qĪo“½¬Ā"5 *·“ėÓåsĖeärņq9ZI£ü¦ĆÜovy*ĻVVØ{µ•śĘ³®'Ī;É{ĻūÆ·æ` RHÆŠņa¾š}į#VFRbźĘ,‹ ‰m»=VŠĖW%®i\³øšqEćRވ=;=¶IlĘŲė13b*ļŠž],ś|T—(ԌČģ‘[#ŹGœ ož4l`XDŲlĶΐj!·Éėü•ü}ć}¹}§¼=½x÷yŚy’yv›ķ͔棇‘É8«Ö‹č“Z-šąµ·šG½«ĢWź(±Źay8B’7‰n"ÆxFƒļĘZžwüœö°…ō¹ŌžK÷ƒõ“ŪÓüj”ž%Ż3·§ĮūLŲw—ž ø%œŸ¾ƒ{ݬ6VcxW¼ŹXŸ[Å­¢VFQ«„õ…ū= µ¬†°°£ū- £ĮšG¼Šé…kX[X æÓ#o‘ibģüv ÖĪųńCŪtĻåē_ē3§) —=ī9ŅrK}%ElŁ/‡É¶p>ן—?"•–—»“lœĻłĶÅ*[Ō?µóś£šā9ąéįĶä{åūĖ |Z5,:üyųˆ“‘Y¢[Å“=›!nzœ7¾BüšųĶń7ć­ųńgāWÄ÷Æ’aüŻøÕqķā2ĝ‰›>v|¼Ż'Ś=!*YŌĢČt‘?EdXž?|+}ńPhŠæC:†h!ß2vśkłŸū&ą ogo‚w‹§‘ĒļYkÖ1=ęZ£nl£I¤Öh“<ŚMŚOÕ«nWŗ  WńˆŖrؼO E…Eƒļ%}ö~Ī«ggńœƒn{Xc/¦»;éeŻĆN~\£ø—±CcĆc'ÅÄĘLŒŽˆeDõ‹|Ń%āqxūš‡ašĀī”"tDHLȼ@žĄ>]O«?ąmé x—{*y^˜3ĢRę}cŖQ’.8C/GŠYCH”Ōōd˜ļHÖ/år:ąģc”‹R?Üć}ü.æÅo“½üfŃŻG“%ŗ‘ ’ķĖ’.r’]ālŸm‘9.»ģŪwæĆ¤ˆ4›ŠVi—ܲZŻļ.Iü֒¬VN+õ©‹by0®ŅuµpĒ*p³”{ƜVfėc+#/Ųjv «:ų„s‘;äĻŃĒż\Åüń¬ķ|ƒoZ©īŽŖ~J?<*©āŠø&~¤é^“Ę /¹»ŚF”–Óė§ŒgF3ą©åmļ;ē(Ś1lRųāˆ‘'£®GĒÅFĽŒ;ß(į|§ɶ%KŸ|oņ¬)ź§Ø–āNņ™É›&Ļ—\M¾/ŁądŸ&ū;aXBŚ„uńEćwŕˆŪ›3v^LtĢ čĒQ £ŽE–Ä 3GĢ!ĻL ‹ ¦ YČ‚µPŃᾓ¾­ŽZŽWžižBžKę 3“y MgŅ{čéõć00§ö—:^-”>ÅĻk*!ŹN¹‹œ…3UT±—L^ģßš;~æ’ŽW»ķį{Ņg²DK»Żį » Ķžõ4ģÖmė< ŁIē[HšM3č„·ÕĘóJYŸĮ·¬ ö•ĀJ°žMĘåŌVZ®ĶdeŪ<02/·Ė¶©­hĖkYĮūĮKĮƒĮõĮ%ĮEĮ‘ü½ü5hæµŗā„ĖÉR„Y=š}ÅĪŚ©ŠÖ?¶p+-EK”v+½ŻśĢžj”ņ4ŃFj#­•ŹŠ0ž‹³ņBå‚ZO/k¬6Ī•ĶŚž!޾ž®!gB³‡×‹ł(*yĢ©˜c±«ćfÅ·OH—ģ§d¹“ÆJžUŠe)Œ”­S6JY-eTŹ;)¶„›¢|аŪ“7J.’O–"Ł ± ĆćŸĘՉŪūQ찘ŪŃ¢×¢¤"ļŅ÷‡ _–!lnhšŠŁ!`T“I¦§/Ņ·Ä[Ś{Å3Ą“Öó«ŁÄō™+ź†­/ԿփŚ"­Ŗ¦j«Ōśj8 Ś^IK‡**ßGaj‰(qPJ;žwüøų­²Ł3i#IŸŻōRÉżŽ’Ģ4ķp[A=ÆY§­ßš¾°oź× W«‡6–¶ NF°K*aVĄņƒĒ>.‡ZįV„;B¹^XOƒ×‚§‚{ƒ‚?'{Ū³¾Ž 6ŽN ö  ^ę€Ū#Y#żY!_ńČ÷ƒSƒĒƒ $ä“d›šRc)VŖ`—³®‹Y_Ś„„ŖB•;ˆn¤Ō2(ė4±TUūj£ōFw#Ģ|j†{»ųĪų§²…Ž ;Ł'źtōę+vL\Ūų* ““MMn¦h—āVо){§œņ›T]SÕOU$UõTQ©ž¤ü9eŻ”VŠq)’§ų!ydņ¾É®&K˜’ ®dÜŌŲ{1„bfDæ¤3®Lˆģq=¼rųΰĖ;ћ•ÓČ£zę˜%ĶkĘ#»ń‡Ž[’H?¬u×ŅiGČ0™Õ3ŹP%Ÿr]žL6 Š8E2qL!•ü_ń›aO“GŲ}ķNt÷ڤ—t‡ v2²§Mē¾L÷Žcm {üĄĢöµŚÓņŖį{…q» V*+|LK±$ėušEšYšIšQš!ćū^7ƒW‚gƒ‡ƒŪąŚÄ`Æ`ó`õ`©`É <śzłė­Æ–¾:šŗ:Ųµ f ZÆ_¼¾ż:2X987ø/ø?ø:Ų58†Ē<`µgKŪI§$Æø/ՒœodųōĪI=DP4‹¤L0s—ōPTPž¾Ž:Žc l†éaīaF3333ʖ)ff[fffŠŁ1%ffffŽ!FĮhzfk$'÷Ž·ūÖu")²s”Æ?źīś«V /NÜ%V‘ßÉįŌxŗ4³Ž/vłEÕ$Ƥ«e_å eOUcu]VŖ© %uœn¦ž˜>Ŗoo8l€‹˜*™Z›z›Žš.›¦˜\¦£ĘźĘs†Ņ†zXßV·Uū]SL3œ=®N©ź©–)?(J)fĖßÉ*ŹÖH’n’ŪāJā?DVŃ:!+\-0¶ó‹óĻ1Ķ™¼1<1o']‹~GM9ō,Ł•ÄÉ]DS"„oĄėā>lV󣿔€īDŚ#<ä$hVų“Ŗū’ƒ_ŗ½/ķo"h½Aśl 2}é”3Å÷K€ģł.yd—C Å­™c,hē7j]ōōä€kH2Ī…Zß¹æ.¾ęžsOø‡Ü]€ĮEī÷·›Ųւ+Ę 8?Ąčvā|āÄŻxF<+†ĘWĘ=‰i‰÷‰A [ā@|I|u¼`å"n?wŸ»ĆøwÉĖ©.ŠˆÆƒåˆ  ᯐž’æ‚AFĮ3”PŖt ś×E[bø°÷+D¢~§w3ćׅ9¢Ėā‹’7Ґ¬¾b‡ņ© g/±—5·µ<ż2=Ļ0Č0Ä°Āą7t3¾3Ž1}3•31?6 --=,°eŽY`žhśalh\gx˜ŲL7Y{@ó‰Õ²­ÕKU•e7şņ”¬…l·””ö–Ü—o©E „”p†€,ą«ł[™’Ģ%^;^=6ŠĒ©Ö”Ÿ\J–$c=qļZÄq¬ĘĒŽ¢]Qz¤1%r {ą7ŠRЇ’ļų„ł—nļSĆ@{čŅgõT)ą?*Šb įæI’,PĻMÉ% Ķ ¹³-ŠĪ ÉĀIs¾›„vŁ@ßu¼Xs‘;Åįžä¶sėø…@ū>•į¤ÜēıČD‡DƒÄŠÄ”ų±xÕų‰ŲöؚŲįŲ‡˜=^8>>ĪÅp€hżø"ń%±™«®•V€ļŪRay2f!ärł €›ƒĢ¢BƒÄYŁ ō3•2|·Į=Įhul>‡hC ©…ƛÉL¶]o•ü-m&Ÿ¤ųŖlÆŹÖ“×®Ö×[  › w 9†śĘ ʦ昹­åØÅhmcŻj…lϬ#­>KWĖE³ŌÜŚ4Ėø×p]’A—ҵµ4Łõź{*JUK9Mq].•gČHi鯒«āĀāU"B4Vč dń‡š£Ģ4FĀl䕱]”;Ņ~j>å¢.›‰ZÄg|.^  ŌŽŻAG¢&ō&2±"÷įÉp ų#“jų’_A~ł?ńėjœŖҧ-„é%’üśóü¼½ 9,HN½” ȝ5€óŁ“jĄ½`Č7ī`Üm€Üq€Ū6€Ūbn:7Šė«Ģ™€.>Ilüņ&¾½œÆ×Å’Ž]ŽuÕŒ•ŒŻˆī‹‹ŽŗcżcŃŲĆųą„™{Ą=Jf€<51• ]…g!gÉČRä,²8Ć xü®ƒŒ«"² “‰¾°īŸ-b-ņ½……šÄRņõžĪ{ŔĢ~¹$[¤˜| b³²šz›©¹®­ omŲtóŖįŠ!i˜jÜngŽb1X—YCÖ¶¶µ¶¤-Ó¾Ä>ČžŌVӶ۶42·1µ6¶1ü¢ļ„«]¢ŁĻ>:ZDÕM¹FńL®‘w•ż!…¤ķ$GŬx²(KŲYųHŠDp‹ß”’˜éĢ|åā‘¼5tb~”Bäb²y‹čGš‰½x3<€­ĘŖ`ѹhIō%2)†¼‚ēĄåÖ¬¹ū’Äļ?ż”Ą’ ō³æt{°‚ō‰§Ā ū=Żįpæ5 fõü%Ł$—ĀIČ,H2Č}ŠyŸ» rÉī7n 7-¹Ö\ Īˉ¹ÜÄõÄŚDļD±„/¾=^;nŠūcOccõcįčhߨ5ś$r2²+²8r?R1Z1Ö$Ž0Aqć8\3Sé© ć r°tó¹ ”/BV ėÖH¤2¬zHU¤ Ņ3’äėqČ}¤,ś=†Ā—żÉjTœšMæ¦ßńó³IįńISŁs¹K¹K•TwÕ¬ÕŃWĢūaxkˆ ¦j꣖µÖ)¶ū¶Eö[vĀŃıőåŲ>‡ģģ>[![5«Č’czl¼l8„?©» ½tbMźŗŖaŹ­Š—r•¼½l“4GRS²AœuŻV :ł{€†žęÕē=£ūŠ j1`ą9²$–ň»ų`  ±¶X݈ÖF€&[(ĶRøœ m€šü~’éļiüvüY_žƒ_ńŸų…@ś|–¼ŅĖļÉUĄżFƒ\˜>I¶hnj3cĄó^ƒtrž;Ąmį–rS¹”\W® WŽ3s$÷5q%±.Ń`—?Ægć_c;bÕcEGänEŚFāae¤Y¤M¤IdT¤E“YlA|bų»np“%R§XČ-ƒŗ¦gEŖ#Żņļ`ļD~Gžē"=žČ&ä¢G»”7цŲlŽ…p‘oÉńŸžI£{šJ2½łČ,ęKHWČŠŁJƒz-+Ō6×õї ōŖ?_˜”–žÖ ¶™ö±žs¤sžó¬ó³3ŪyĮ¹Ń9ÜYιĶŃŪ”³?¶Ī“t2W0鍓ÓÅ“) O£cK©[ŖF)7)īĖ)ymŁlé#‰]2AüRTU“CØĪŃ|,?ƌgPfOÅŪJ—¢ÆPķ©ri$­ ¾/‚ßĮaRģŚå-ąšĢŽ_@› f’…ßß?+ø’²égņKĒ’į_æ§ }MīéefrT²pæ: ĶŁ’29óø÷@é.€¬ńčq£¹\S®<Ąnw%±11,Q-'.Ä3ć5āŽ8Ļa±īŃĒ‘U$.^r†żįŒHßȎH¹h‹X«xįÄ愝k=5™ ®3 4z5…·Ā_ąĀĄĪ D‡ZPś9†E²fčCt¦Ą/ćc‰*$I”ŖÓėčtyŽ.^MFĀ—Š ‰*ˆĖHDR—¬²|ŠŠžź‡¬UkŌUŌ› { ³ŒÓM«ĶQ‹ŽÖŻ~ŚqŲŁĆõÅ„s—qOu«Żo]ē\ē]Ķ]Qg_ē9Gm»Ā·5Ļ4u3Ö6Õ[tz­IćfĖ«›©(+NɳeNYoé>IJÜF|@$.ųYŠNp‡_ŸiĄÜåµį½£ūÓqj.H”Čśä;adğxCü+6 sa×Ń~ Ć@Ś"Ix;ÜŽBŪ ?ńK??śļū×éūŸ{R›A/č}ņóĖ?žGżü_ž„ńk“¬ ÜĻ ZŲ÷œ»Źā6‚„9œū…«Åy8!ŠĢ;‰‰É‰ }ām|C¼s܇ć”ŲÖXłŲ­(?Z/27\*\&Ü;D„>«…ź† G2#ė"’hvōR¬p|iܐ — ķž²AÕ@‚žՂ×Ā)x‚¢ćŠsčet>Z üœ4ųxŽ ĒG­I õ˜zLOć½įäķć=āUb¾17ł7Ļ…/EGĽ~·„‡eūäÕBõnöˆĘŖ Ÿ`˜n|d¢,ć­5ķG>ēG×·ŻÓŁÓÉÓĪSĆóнÓ=Ü]Ō½Ųsjœõ7ķćmå¬Ió5Ó*ć C3}]a-ų ¶ØŗŖŖrøb­üŗ ’U‘N“<; ¾ÖŲ«ł2žbFŹ,ēéxŪčbōiŖ1õ†J’äz¢4qļ Zü&¬*ö€Ńó@[häOø œ‚vB­ņń+x¢ął_Įó£ō󇽩-©Õł÷ĻFžŽ ō‡łłSł3æ¤żļxrgrur6čī½ółW2i}=Ź}Īw $Ķ…ÜH®#W0įžJœJ,KōI”K ‰KńéńzqĄī[lPģJ“LtiŠ< ­±”ĒĮ«Į½Į}A4“2Ō>\"2:² 8ąūčäæŸ™Ø:Ȱ¤\[ŌŚqP+ų:Üi€j°—Ųa¬6­†B›£ßQG|"ōtKžœéŹņ×3P¬ ~wA?į\Ń~ńtÉ +]&­)³ČŠZŹAŖµźū,©ŻØj»›rĶÓ­ķ#œÆ]/ݳ=¼oĢóŚsҳĮ3Ącö,tCī ×qgsēuGŽż‚mŒµ“%h:fœi袯©+¤Õk”¬RmTW6T ”Æ‘Ż•2Ņf’õbŸØh—P$+ųĘĻą?cZ3Ax š|5ŽāQkA†9O“%²šéø?µĘņŠEhQō.2‘#ĒąĪ0ļ…ŚüÄ/}N^Vžó÷‚ē·éēéēGéēÓR£AŽĖČļļe@’S§čüžš*yėgžœŸņKŠJž ’aŠönē[ĶM®W$M.ń,±“„¶ ;š¼?ććÅā¢ųėŲ®XįX³h÷ČŚ°#Ü ä } Ž ī ī ž &ƒkBùaUdj¤r4Żė7&ī'29Pīj©£)'4ŗB?8 ?@&av|^›¶@(®@{āH­ćżĶ³2ݘߘŗüÅü§Ģ%¦æŽą²p°ØŖųd¢Ō(E¤µ¤÷¤e ä«Ē”U"¶…f“–ŌgĘ1³ĢJŲ³»\q·Ī»Ż;ĆŪÅŪÉ«8¶ņžóš=īs®–.ÄuŽYÓI8īŲVY[YHóć"C7}EV‹jüź,•O™R(eåŻdĖ„÷$ IWń!‘@Ō_x_PQ°›oāÆbĢ2ž¤P3½›*ZD+ņ1dŠĶxüÖ#°ĶhUō 21"ēį_a¼j÷/~é÷Ļžy’%żžÄńŌ¾ŌŽŌŗŌ’ŌŒTfj`ŖkŖUŖNŖ\žż~ŠKęä÷÷óÉÉĶ ’MNNf€üYųŸą÷øŌ.n7ˆk̹8˜{–Ų›˜˜hœP'ŽÄ·Ę»ĒÕńϱ-±f±µŃ“‘Ćį5”ŁA2ČmAs°Mpsšnpušų˜T‡ŗ„ģįåaCäfdF“F,;”ppO¹Ģ¤hxĄ]€ąQH€³ŽčTtš@#Ś#0v —Sƒč¢¼™¼g¼¼¼oLæN ęŻ<(^"ńIŚJ‡KqéfÉI{©Iʗ+Ŕ-TcA“xŖ1źśėĻt@AiŪ!ūē,÷\Ooļ=ļ~ļooļ$o¦·®wŖg¬ūƒė”ėo××}g p0f;bķl›oēZźĶŗ°ę {N}XuDy^ńT–™d-„ $ÅńŃ ”[ø@į÷ęæbZ1÷xMx÷č–ō Ŗ•Mf’4¹†šgńVų7l ¦ ¦)H srī‹ąCPūńKæ~’óežūgé÷—ži€³SćSƒSŻSmRõSóļŠSHʟü”|’’¬@AG'{&[&«&ŻI)HŸļ@ēŪCW®č “ ²Ä“ųŹxėø>~:Ö>–-mq‡ļw^ś‡ś£ž§ž;~6š)š. ” Ī ī(¾jC’šÅpæˆ.ś :/Ö .9fW5ł&Ł\u‹€z¶Y“ō+42ĆÕAß ś|Yō(z«Oü ÆP/Øå µHycx/˜›‚%¢wā^ōŒ“±l‚¬L. ¢LYÉ>É|飳MÅ'eJ„b+hśk÷ė8żP£ō‡f6¾ułŻļ"ļ ļļloomļIO%ĻWWĀUŚ]Öq tżķģīź(iægķcQ˜/GŠčsµg4+ŁńźAŖŹŃŠņ½²§Rž“ŽdŽų‰Č%š,|'Ø-ŲøŒa˜<Œ7¦é”ŠŚD&O ˆWų@ĮWaE°+hCV"„‡ Ū*ą£Pǟųż³’” Žł÷ `ś Ä<Š |£T•T1@Ó >šęnņÜæ `Żdń¤& %?žmį&rm@n‰Ä¹0Ń*”J</‹7ˆ+ābEc™Ń÷‘Ņqx[°u “’—¼RyóśęUĶېw.Ƥø’„æK`K`}šY°DčĻPF‹üéUʞĒöÄg':s2š_\‘jåAé™[ u:”Śž2A•aB¢ ģ)6CŒ"Ƥ‡Qo©_é ¼ĒĢįD±XZLvAVEžQ.U,‘ –ņ$Ēėŕ%Bi?ŁXł<Å&å6ÕFõö®†Š5Ōļ6ˆL‹ĢJėyŪ0ĒF×jÉ;Ü» ŲßūÄ3Ļó«[ā¶¹×øŗ¹k¹®}.ÜõÕŃÖžÕ:ÜB›7krt»“C5õŲBj½J§t**É;ÉfJOHBāJāY¢ĀRĀ„€Żł™ŗĢ^yŽQŗ,}‚ŖJ]%›“ƈ>DŸ…kš½Ą×Ÿ”ƒP Żŗķ+x,¬OBņńūg’ŲæS`ŅOą’“`šåßu¦ąT ł7Č 7ņ=pC¾öN¶ Žśßwī&H/øĄū¾ķģŸp%>ÄלƉųzŠŃŗ™…K‡:zūwēõ}U|…}ĄÖ}6_Ÿ6o}äÆp?=”é!$¼*\.ņ>²<Ś1V7Ž!ѓ+—lžŚI@ĖŪ‹Lķ½;2ž m†ŽCķa™‚ŽĮšsˆODgr;¹™Q½©,r8É3 9pĄųE¬ ö ˆ*Ń?‘śČ'x2l„Ļ@?ń+ŲæYpŅļ’:ązą€3JuKµNÕ š~ØLń€žó÷drOr]r.ŠŠīÉĘÉR ĮG¹'ܟ@?[qī]bm¢I‚‹ļ‰wˆ‹ćēb±#ŃOąž>4'ą÷ŪüTžĀ·/÷lī™Üƒ¹Ssūä.ĢUų ēéüp ˜ü,ś “ĮKįĪ$z+šė™Hq«R“įŖč> ÅĄßŪāåšćčSd@B."ģ&~›čC®$W‘sȑd=².HärŖ0½‰w‹Ÿ)®)Ż-Ū-„ü¦ĢSNV(=åĒeƒdČž–ßQ“S2Ŗ~Ŗ£Ŗ=źĖ,¢­Ŗ«?lė˜wYœ¶cöŠĪ&®ī±‹w¾w˜÷¶g«ū’»ž‡ōńöō~ōLõ”š¼vousvްµ¢–ᦐa–¾ī½f;YŻ[ÕI™”č®™҇ZRO¼@ōRXT8Gšßœš)Ģlā©yKi1½ˆ’R+H=¹(JœÄėćϰ>X]ˆŚŠ“H+$ žŪą P—’Āļ?o`ÜĆžĻ=˜’Ü-xƒ"żžYś ˜@š#ą`ś)ą ‡Ī=°+H”…’üäī·„ĖąģĄżV$*&€Ģiˆß©„V¬R“b¤lxxčyą_ć?Ÿ·Ē÷5w{īo¹Ūr÷ä®Ė”;/·µÆA^+…Ą@ĒąŅąõ 4‹rįœšŠČˆč«ŲÄ§dĮ.ćŪ )šł#äŸä5ā~ü„6ō ZƒĻ'ŗ‘„)=µ‡œHv “Ä °¦MČBōę™0,Y'k£č­,­śØZ§£Š)7)ÖÉĖŊآ…jˆŖĄÆ‚ŗ »]ŃÖ73 6.15s–†¶yö+ŽB®ŖnÆē‚ē±g‘‡ņ|sOōT÷¾÷>÷Nõ–ö^÷¼0įjįŹs„m„­ Ķqć£ßÆķ«)͊Õ1„O’ćr£¬&šŻßÅ_DEEć…E‹ų¦hõxēč ōQŖ,uœ¬L^$š.x6`æ”%Š«Hg$/„ŻšeØŪæų„`zKŚ’óZ‚.üŁ! žĮW-Š /•JA|’čÅä”ä–ä¢äŲd7ŠI8łœŪĶā*qįĀߟqgüļŲ÷ؔ؁čłČ†š¬P<€Zł7ä=óÕõŻؙr+åöĶ]»>7ž»Ļ·'晴Q`Lpm°6Č„=ƒß‚™”įœ›Ļą¾„Š Cń‘"QĶé}t?z.U‹dˆŲ LæÄOīķ _“ÆČ„äNr.i"'!ü, ō&ž+Ń5i¢ŹReØē³ŁwģõJeqÅł ł"…GłT™R¶SIŌ›©y­åé-§QkBĶQ3ĻZĀÖ×¾ÅńĆłÖµŌ}ĢżŌŻŌ“å^ć qæ7ęµŖämāYę¹V»V;­Žž¶?-عń‹~¢Ī£żŹWÆVĶPNRĢ”Æ’”>—P’ā)¢›B­pˆąæ-C2£y?čīōŖ#õ‚ü…|GōpH0 1öh¶‘ŽH^ÆA=~āW0’åæw(hśŽ ?3hĮ;h-€†¦÷Ž„wž±ł{Ēø¤?ł9ł2y'y8įźä”d/ĄA{’ćīrėøœ…»Ø™ŲĒB±Ļ±"±š€}ÅĀ[ƒ™™ž?óvłŖł6ä"¹sJåäf_Ģ) ,ģCó$ža%ĮC”sĮKŖ=Ģąo”Ņ‘L ž7“,ĀĒ?-€£åEy;™rĢ_ōhJB.Ę_b#š_=Ł„¬H¶$“æ“I²&õ‰¬IÖ'Ž‹Ø‰ĢoBD—OUÕÆŖÖ¢=§¹”™Č*U…Kž'Ū(ÆØh§h® )“ŖÖģiĶw-„·ŚM¦Õ&”9DZHleģ½'Ķ\·]£Żܙ·w›w÷Ø÷¾WSh”÷€'ŪķpOv]¬sż½³“4]44Ņ×nŠō`Ė«u*¾’VHä6Y iÉZBÕ¢nĀC ’SŁtŃ©ŠŽ˜LÄ\…oƊb§ŃĘč[d(B"ėą’š-¹Óæ ęg„ē/„ē÷¤ē椟"ģaIļ(ø 3“Ą©Ž’īœ.žæļ]›’„fĀÉÉ·É{Ɲ€…ĆA—( 8x[ĖufEb|hÜß E%їį?Cć~¹_™wĆw6·fĪ‰ģ¹Łõ²;gĆ9—rFē6ņÕĻ[ā˜züųĻų›””•a{“kla\œ<1Ų ā=U˜ēd3Sł|A9ž5ŽN¢ŒD ¬v' =сøGŌ ÷³(Ż›n@æ¤ĘŃ™BB¤Ø| źwvŽ–Ń_ѝÕĘ4õ4‹Ł)*„¢Oś=j™Pq_ŃYYUõZ]O³Q»IŃŻŃ73V5=3ZMLOĢÆ,ß­R»Łasŗ\*÷g·Ą{Č{Éū8ąQļaąŠjļĻ.·ŲŻßuÕłĢ²%-eĶk*Ć]k­HóR}HµF9_1O¾B¶[zS»ÅæŠv C‚‚­|œß(hMŽaŚCo꿆dɵ„‘ŲŠĀcÕ°[hō2![ąrš]Ø×Oü vĄ§'P|0½‡3=ĮąŸ·č 81525 õkžÜ¦€…é©/eŠŽ”Ø© dR,’¾ŚÓä…ä.Gū%k$eÉ×ÜoÜP®·(”IԊ_Ž!±ÜČĖš›ą‘Ą3?ā§óľ‘9ŁyYŁYHv­ģ³Ł“rśęĪšĶĶŪį—Ÿśvūoåiü›WB{"üŲõø”¦‚š |u•§ć/āl4?y[fĻH’MÜ*ÓūˆĄįu‰ÖdWĄŃ¼©L‚I1›ł£„Ań YCå<õHM/Żz=Ŗ_©ØYŹQT’Šł2Jv]¦VlSŽRżPg³›µu}õsõÅō+õ= — fć ćYÓqó)Ė%ė][–]ćōøv»W{¬Ž-^Ī»Ų[Ķ;Ł{ą(ń–ö pŸrłæ:æŚŸ[]–a¦†Az”ī,`zµG%UR J®’6‘dŠˆ|ĀrĀ‚Wüņ@Af ļŻ”¾DU¦Ž‘„ÉĆDā^ æ ®Ģ—h/4„LGTČNø2üźó/~’LšIO`JOšI30żļ?ø¤Š‰ G Nõ]¾sŖ}ŖUŖyžœČ: UTMUšZ$_„ •Żš1höĖ€’z‚ėø•Ü nFākÜo‹E$ai𔿾gŽqß֜Ł‚ģļY/²r²lŁ‹²ęŒĶ=ļ»™§š—õóĪóūūĘę ī?‰n‹HäpÆR½-ų ŹĶ,ē·¼ü%Ų*/ų“/b®Ó×ÉŹ„ ‰mĮa“1ŠŲMö Oó >-ųSøD8\ŌR2O&PŹŌ!v¦¶·n†īƒöšę.{M½RÅ*Ź»Č`Ł1ŁEu•‚żĘ–ÓžŃ}׏16ōŅgéŽéFėĆiĆćVÓóZĖoÖ£¶Įź®nī&žO –ōņnč)“Ķ;Ń³ÜżŠUʵĶiw|°„,MĢGŒå ·uĆ“NĶwõIÕjå4E¦|‚l¾ōwÉmqRTV4ZxA ōēßdŠ2+y(oż7õ õ˜lFŽ!wń–ųs¬ öŽĀčBĀü ׀C}āW`ž3Į®`Ģ’īC*˜’ņϼĻ~©Ž©ž€=šs’ ¦¶ČŸl¾Ļ¦’nrH£×¹}Üt® ·2q?Ž9ĘDæ‡Nœž¶yµņöåĪÜKdżu/ėųX!ūGv­Ü¾Ņy{}c}^ßõ\Qīõܧy“ƒ5"DüRb× ¹9µ®ˆķ%ŪšĪ3Æųƒ'>QČlg¶ŅgØqd?ā2č4Žį 1“:Ą›Äæ(‹D⢒zRNö—WŅLŌvŠÉu“k4ŪY);]-R­R(䤄„enE?ÕxV¢¢ƒ ‹ŒēĘ}†óś:śgŗēŗś½†MĘE¦ę©–ÖC¶Ēö3„«ūŽ[ź!€’ė¾÷„÷OĻY÷WW3×}§Ö9ŃžĶjµ,11Ę„ G<ŅĢe›ŖĶ*H™-’"Ė–BRhćDĒ„PŠ ü(Ӂ9ĖsóVŠ$IłČ~äßDā#Ž’† Ę"čd”®BČaøü ź’/~’LügZzŠH:…¦ļdļ˟bž9?5 dŃɀ‰é“žūD€¹ą÷Ņgt¤×Bą—sĮ÷‡•¤¶&+$Cœ3łŠ›ČIt‰gF'†QĄæiy+}OsŹdļŹŗŠŪµ knÖń¬*ŁsęVĪŻ”cČ)“S%Ē‘{ŠwÅo ­‹ÜˆJ¬å¶'¦ŖĄ"t/NŅ'xC™Ś|+æ”`„ą"’sš·œ^B-#Ÿļ“n¢!¹žÄo#ģ!ź!ī'9$Ż+¬”r°5“5t)mOķļš},]®©N)(®Ź¶K[K+ČŹ*꩞°ķ}MćDSŅĪkZėæėżcżFĆrć Ó$ó$ĖlėrŪVŠ'²[]>WCĻhļ ļD £„½ļUw–«­ė‰óµó„c ±N37v7défj‹h>©w©Ę+»(šÉ›ČŚIJ‹ĻŠBĀ2‰‚»|ó™×„wŒvŅ«(5 tŸ±DƒĒ±‰ŽĶ ~R9 7€_BžÅ/퀒LPNOA+˜¢U0G$½“:=C+=±|@1}®ŹšyŠÕ @]w—<žÜeŠä߃æ#’Jߗūš”BS•S¾äŃdēdœ›ŸX›)*@żBßüMöõ¬wY糦gÕĪRgI³fŻÉj]5{/šĆĆŁ²Ü!>‰’GąMčvd}¬Jb.÷2ɇFĮ|“4ސ|AżN›x™cLg~O~qąGy‹čŁŌ²;Ł“ÜH¶§&ņŠꊓāŲ'i+­(¢Ņ«K°š¶ZŸ¦§F¤9ÉāģTõCÕå EiyMYiY¶l±āŖšf¹®Ÿ”Šé‚Łgnm¾iŗmt5†īzžŃæŠo5ģ0n1­7Æ“,³®“m³’ę(ź¼ļ,ģ^ī‘yŪzEŽQŽ ž ī‹.§Kį*ī’9KŲ£Ö1–§¦ŹĘ£śŖŗ'šqlIŠ(ŽÉ÷Čžž–<Ćā’¢Ā‚$æ/#`†š^ŠučƔ‹ZO*ČEŸ˜‹3ųL†­FĶč.¤4rn æęć÷Ÿó;ҧēĢPN§˜t(˜£u “'’“Ž‚3©¶¦¶ŌŅgߜ½ėgĄŻdЁXČU€šežm…VAcAĘ- „O…;•n Iœk—DƅKy¹¹ósf·Ķ¶eßĶź™ž±ųG£å~”łQżG±¬'Y²‹äNńAž1$“6\$z)V7q‚,M‚‹¢_°Dqņ9ZBÆį¹˜Sƒæ?’Ź›M’F-¦¶P©vōgŽKža=q%‰Cš!“*ī(sU7ŌŲ$«Óg·±å4Ł©ģsužźr¼b‚ü¹l°ü¶BØ^«¹®cØf:dnb™n>fkZhfĄL S–ł/Ėkė3Ū {7GĀQŃ5Ū=Ų³ĪóĘ{xŗ¹Køž9o:';·9¾ŲŸŲę[o›ķ¦łN7Q+לTPUWꤖŃ@A«‰ˆ¶ ? Ęó1ř„¼݃¾OÕ “…Č­ ®Ćµų:̈mE=č¤"rnæƒż‹ß?ēWż3Eł< |0}bNś,æ?Ļ »bŹŪüyć$¤ø•‡A] QŠRčōbąjš˜OAæAk”ó÷‚š¦šp„āk¢Ūµ‚µżEó<¹#rĀŁ“³s³ZdķüüčóŻś]ö}ń÷e?Šdͽį+ļ_ųT…›GęGŸÄś'rs’Sƒ`=ŗ Ū‰?Įė|ŅI §›ń†ńz0vžG¦$3‹'ā ļŠ×čž?Łō ų=’æŲ5„ŗA™ŠrhtŹ‚Äpyø/¼ĮUIą:9 χ‹ĮE‘CHSä,T+u«—E;„w+:å}„r[ęōŹī™Uēõ]š­Ėמߜž(”}6õ· v ’ŃGŃ?"ÖØ"¶5¾‚Ū™śzGž#vōwtŚ ėH,”Äōt:—žĢĢć‹JĮ>ž¦ óœW†_^ų]$’”‘N–je>Ł2y_…BuEͱõ4ż5e4ߣŻleMša·ŗ¾z­ź­āˆB£ZÆöh.k›čŸt¦A¦©¦•¦Ž¦¢¦›Ę/†6ÜPܐ«oe<`¢Ķ™@]§ZKŁÕŽ+„3įųęč듻к/¹ę:w8ę€Uß1Ź1ŲŃÅ;ŲŚJYf»±«ž¬¶¼ęPŠ’JTń·ģ™ō„䇘/.#ź'Ü)ČåWåÆ`ņx-xGh=‡Š’}É7D+ā6^æˆUĒĪ£5Ń+HCäÜžś_ųżƒaĮI:’$™tMŸń4œS›>ć-™āCŗüĒ”įŠBh/tśńRmą)š!8 ·YŠlB: æ¦čQōR ¾šJp½āŗč¤°=Ō+ ņæó½Ģ}s=ūLVÆ”oƒæ.łZåūų¬Ŗ¹Eó6†„©h*ŗ<*Œ^Š¼ŽšbXāVr4NĻ Ō@h¤rŻæ ŪŃ{č£<‹ p¾šŒĄ,hÉ’Ä4ącĀEbN2UÖ‘}”m•ORŌUUb{h i[kk4šģö {™Ģ6boŖu?Õ`•[]­ ™¦ÕčIĆ#C7ććVć £Ū8ĆP°ļž“ŽaHV™Jš×šĖXZף&;Ž;×8k;ß9Ö8j9Å®/NĪń«£Š£§c¶#ą×с:&Ū‚³É|ŲųU_Q÷»¦({G5UŁHį-^*5J*ˆ»Š–ļd‚ ž!FČōēŻ„ĖŠAOę‚ńo…ßÅa7Šą’c3ä Üž ū‰[zodzżĆ’ÖтóĀß6¦ONŸĻG@rČ•~ך­ƒNæƒ`\Ƅ/ōӍ@5S IˆN|Å»ā/°ÖŲ“9śi¼€»Āßsžƒņļ‚ęŃtŸH7ĀōÉļßĮǐNc)@ČU‚Z€”9Ś ]„>ꕀŪƓį]š8Ū‘V€#ېkˆź±5Ų+¬>öQĮ%Rķbśˆ!\2d z]żķņ ūžåü•½& ˚ō£K–:g’o‘æ_ŠŽyĶŒŻ‹žˆœŠōŒöŠĶ‰#œ0u:• †+„1šŲe†ÄiRK×b \"¹H/*/ŗ&*$’ + ‹‹‰{Jr%Ķ„¤×dØāWÕRöˆf§v‡ī‰v»ę*;˜ż•Ȏe'/ģŹÖg,¢k޲oŲŻš½Śéŗņśś:†!Ä0HßR’\·F·Zg×sz›±·é½łŽł#@𕄉-aoēģä;oæėķāxąŲļīøēx ł¬Žćöc6ŚśÜ¼ÜōČ ŅwÓ>b»«yŖ«ŠÕņq²!Ņ‘’9ā=¢gB±°™`5’;SYĻćč®ō5Ŗ$µ‘“Sˆ0>’„e`/Švč3 ioąš8žĮPƒšü…Æ’Į° S’įTś\h’AFØ0Tj•?›l+t ä\\óącš{˜‡üެGī#tŠa{±x1¼¶Į•SæpļbæFŗ‡'‡¦—>ū'ųgųŠå6Čį²d7ĶŃäšņšЄƄWFĘGĖÄFÅ2¢ū"Āč3_®Åkr¼äŻäTaAV"3 ü„FŃ5ø•zÄĖć—ŽV ©EC…C…ø(SŌQ|]<^ŅNZXöJRÖcækīkki&²ÆÕ[‚mĒvf§±ūŁÆlsĶjĶbĶrĶk–_5ÕJuūuõĒõqżtż@}uż\×[WKļ5(ŒULżĶꘉg^c~ai“‚ü’rä98ŗ;f9DĪņβNž3ùŻésžr®pŒµ×“•³Ž²ØĢMŒ]ō惱^]MU\•–®•ģß………„żł(æ=s'å §_Rµ©ż¤4@”ē`½±÷hśÉ@ŽĆ½ąlh䔇CDBTž"Į×ŲæLL€R)|Wz‚*UƒZīMƒ6^š pÆ4@o|ĪŚV é‚ Ef"{,¤+śķ‚•ĄĻā}qF:Į›R7¹”ńAŃĻįg”'Į÷Bvža>uīۜ©9r¹©¼ŅĮļa:ś1śkģTģZ¬Gli“|“i“b¬Y|vĀ‘Œ&„®A»įčt śøyt(ŖĆ=äPŗ5s__šL°PøLŲC %Ā!ĀÅBJ“OtS¼_ņEŹŹ-Ź”j+ŌØŲ*jÆjžŖŗK±2vh„5ÕµÓ“W4S4;4}5Ė46ķ mmŻYCļŅ_ŠŸKƒö÷P_Žš»ĮkÜm|odMF‰©£9hŁk9ÜĪRNÜłĮńŹ”p.wś°«‰ė¹ė+ŪyŅŃÓ^ÄÖÕśÖrżиR’J[Gs[=@eUfĖoÉNJOJnˆæˆ¤¢:Āi‚›|–?¹Ź³ófŅYTkźY„Ü@ˆˆ©x‚}C{¢‘Čß #ę‚®V€_š{iģxńóWzJGĒ41€& TSi!;T ×źM­ī$š=.²ģLĄ½ļ°0{²YLGv"jt3ZŪ…¹~mšfŲ_Č|ųfź·$~=Ś2b «Bś  šWŽ&ßņÜ:¹Ūrīä~Č«,Ł$T‰d<+ÅĒęÅ6ÅŹĘū$¶r›“„ Ŗp¤/čEšŹxgģ `ųCōV†øI¾¤1ę3™/4ō8åėm……µD7D°Ä%%+v(«ęŖ)ÆČĖżŠŖź®źq€‡¤¦°ö¶væö h#5k’²4Cµ>m†n`į]¶. ?køgĄķæS†€į„a¤į­~¾>Ó°Ž8Ź<Ō:Ł®tNqŽqfÖMv6t½r)Ü7ėnīŗź¼ķhmÆd[nżŪ²Ō¼ĄxHėk‚ꄪ:J‘āH O$ŰŲ#ź$\#xĒ/̟¼ą•å­¢9źWź.Y‰ÜMh‰8ŠÅņŠč7¤ņ ēA£’ŏČG&­ ½Dąė$Óߕę 'T(gSØ{ž\ÜżŠC(™ąF Gģ…’‚Y¤ ²y‹€ļ-CŽ# Š,t8vĆš±x'܅ż¼‚>'O&JÄOFūGź…„~ v„óšåżį;›ŪÄ76ļ€0\;jŽOÜJHĢMĢKœILJōL“OģJlįz$G¦.@+‘×čD|hīʼnŗøėŽĪA‹a0¾Ÿ(Lķ”gó‚¼q Oń1#ųuõs„BqÉ9i!ł …]łL±An9dėä!E®²šjŽŖ¤ŗ»F³^[]»U#Ьe³ŲJšU „žŌVÕĶÓķŃŻÖÕÕ÷64ī2}č5,2t0\ÖKōt•tŻSĆHÓ\KQ»ŪyÖ©ru¬ėč:ģŖéąžć¾ģą¼źØę@ģ6ŪhėAĖS+ć-}QŻ* ĖTõPzØo'- ‡SÆÉśäQĀI¬ĘųT,†Gs‘H6<Ņæ|öńĆŅŲIĻäłKVĮæ©ó̐;’<įĘ ©…ę@Ū”+ŠWŠö*&Æ©…ŌGę#Oėj¢?€~ę!óŠr½£Ų l+¶[ŠZ ©!Üßń}±)Ńö‘Ęį~”£AW°~ąŽ’F^Ÿ¼ yHąymŽJ`ÜķÄ/‰—‰œ<éLŽātÉcÉ©©ž@9UhMüQƒŖE5$į#°ÖHO¤jÄāųr35–ń–ņ`†ĻōāÉyĆšė Š ׀$³RüNRMö»¼”ā¶|»ģ‰ō¼t˜¬Æ¼²ā¶¢žņ™ņ“Ŗ'{RsOÓC³-Ėf°æ³Łģ>Ķ­d–£ŗYŗrśī†F Å+L· ¬a‹~„ž/ŻvŻŻ\Ґe¬­ķVē`W†»—»[é~ę²ø'»ŗ—¹ĀŽĆöƶ‰¶§V—5ŪÜßä1"†žŗ3šņģ]Õ8e5…JŽČRFā7Mžą‚ęüm ĒkĻ;Fėč)Ō7Šą/%ˆ-ø Ÿ‡!X&B†!yš08ż‰ŲĒčIZ H ©!öß„=ĻŚ‚* U†@ķ”žŠdh tz%!'ÜžŸ…cpEd"rQ¢µP%ŗ9‚8;ĶĄĀŲlŹGmčJä$t>¹#Ń7ŽėßJ’01“ÕŸŅŖŃĶÕK Fć^S¦„Mćøźlå.ā¹ä™ęyī.ļ¦ŻĶŻ“Ü']ēgķ'm°m£õŗeµ¹©i“±aŽī¦ūUµDŁ\ᔋe©FRFœą]BŠ4@>ӛw•öŠ ©0Ł•¼CT&öā&|ĘƦ  d4‚GĀ(ó'~ž–ž ’Ę.Ø8łĖxgƒ\ q–čՃZCæÕ]ķ„®Bß \oƒ?ĄNdr × ‚†ūN!Š¢Ų3 „1>ŹĮĻa'|*ć2Ś8{9Žā…|ĮĮĮĒK źÆ@ƒ•C;Ćo"µc ć×͹¾Üī×$¹?91U¢ĆG.#!t~ƒKߦWPˆń˜e‘ĻH֕XBšØĘŌ_Ō@z<]ƒ~L-£šŠoyļł,Č0æŠ~ÅE&ÉiqŁé#I\|L‘H„¤=d?d[乊7ŖŚģv-;†z½WsU3VĖiŻŗ ŗ~ŗ@EŪ·¢7čgčéĘ褜ś)†[ʇ¦G–LŪ;{]ēq—Ą£ńR…ĪzG{~qævÉŻ”ūƒó)č~mUlS­K-½Ķ]Mqżr¤Ķ"ź­Ź E ¹V¦’Ś%5ÄżD…ÆVĮ`žEFÌä=¦ĖŅk)”@>#źGp¾“b³QÄą1p —_Śżx€}Ņ|ōŅ“ĘĢ eŚ@V±C€$Īҽ: ·tƒÜ¹:=bn’Ė%Š: ‡€rN@Ÿ ūŪF ¹Čut:&Ā/cGњČH8 $Ö¹I—’+v2š±…Æ?Į2ĮVA@hųøĢeF†E¦w˜»ÅMårī¤=Uj7’D“ŹĒnā‡ČęōdŚAµ$üŲs“J” °ņÄ#²*č»o©ņ“†öS'Ø+TSZĢdń ?ˆ:ˆå !ēˆ{KFHZJź‰KŠbĀL!>,ī'1HĻJ‡ĖźŹ—(ŚØ¦Ŗ³ÕåŁß€’]ŃōŃfi•ŗō‚t9Ś­C7ØéŻAŻERÕ?4ČM-Ģjė+[wĒdēb×wŠć-T„ŠSļtO÷0W_×"WkēĒR{ĢVÓÖĒŚČRŁü—q¦įœ~«®Øv«UTöQT[e&iI#ń(ŃįwAqĮž}ĘÅLåżEפwPBjłžhFœÅ‹ć[1[~Ś)H' ń?ń#AJåL£gŲŁARqƒŽ^š®(p½²½Ś ·t„śĶ]ķ€.@ . ’ ’„K#Ė4u6õA!½Aņ|‹ŻĮ@±ŌdņўøŠ—ĆwcFąFt&‚ į$4į'~PĻtĀL£gœsά+<Æ TdĪŚ ·“Ė?‰=}šé~č6” Éįš( jč#O@×£±ėŲX|#>ū-…=Ąņ°LlZ¹UM=JM>įź%zėÅ:E„?śKåõŻöåųVęķö?ōó恁¼@Ɛ+āŒIĮ„‡ėŹ]äŽ&uŠ'ų4: ‡H=µŹ¢ZŅwčA÷§ś“ˆ!Ä[ąėæcÅńū Õō ÆÓ‡čõō ŚÄĄÓ3Mł%Ļ„ŸE$ė¤5dKdÅew$…Äk…'ł†åü"l"҉ļ‹ūKHériTV\QNYYµNżū^3O+ӕŃéu“O4^U“ŠLŅu6`ĘNʎĘõF‹é«i—嬵²½˜³†;é^īŃy[Zēmķ=ķyäī ųwÉŁß9ŹńŌĪ·gŪzŪ–Gę2ę[&汆ń«ž­ö/¶¤ś4ĄÆŗ¼¬“Š$CMYØ9d€čJÜÅ«āū1;¶•¢s™CšÄ|ü0€Ÿx_zO¹%½Ā»4rå  P%Š÷źōZC ¹dBó@k?Ō3 į¦šlųģmAƒžGbĖšÉÄ"ā&.Ę-ųjü..Å£ ąm)u*‹{Źy¹‰ń“±Ń›įĖĮõžĻ¾Hn ·½ÆlžĪæšš-’’Š@ļ=āUŠwI|HüĘmL‚v#2¼7¹Š®ÅLąįßā—ä÷a¦óęŅ»(šŗNņČĒųl%®#?SÕx·x=y}xÆx=1ų“Og„N‘PÜNRL6Bž@ŽFĪ“PSųJbó§ 2… D˜ųˆ8CRYŚFvKžDQ_ÕE½€­i„½¤t‹“ļ4Y,„٬ż[Ϙ¦šū››²L“ł’™°†¬&ū[Ē}WĀwk¼U½ķ¼½f ŸKÜM]@oÆškŸl»ki™j6š˜¶›~766|ŃŽf-@?ū)jȋÉJJėJśˆWˆī…Ā6‚-üӘŁĪĆx=č+T!j)ɽ‰'xü(г .Dhd:ŒĄ“~āG}Sö™€r¦Ń+ü®aŠJ"Ęxä+į ēVö…}Õņfå=Ģ»‘70/āūš×.p.ø)t"R7Ö..Oäp Öčxb2]†?]øX4ZüI“Lha–ņ¶Ó Ŗ'5ųū|¢‘Ež¦•ĢX¦/ӏłƒĢĻö1⨨Ÿx¼d—tšL$‡ååeļ$—D]æ15™\ĘĻæ+X ¬&ŠŠ®‰K>I½ņķ j¼ś7ö„f“ö³ö©v¾V­żtśI ī7ģ4į–*–·ęgę?ĶÅ--Ū¬ćl;ķœŪ\Ż·Üc=‡<*o Æß3Öķuyœż'ķoķåģļ­c-CĢ5Ģ'LeL+Fc?}}ķ$6ŖŚ¤ģ©Ø%/+«$m!.Ž,z!Ō » žä#üvĢ~ž7€¾G•¢Ö’9ˆx…7ÄOaE±­Ø]‚š‘™0 OĪĒéE ŌS“34‹ęUŲU‡jģ@Mzv웲Ė>čō"įp/x'ĢĮ½ld%ŚėĻ%š“…Čr@«sšēÄlr/ц4.N¦Ī'%ÉĀܗxŁXŪČ£PƒĄÄ¼ī¾Ņ>Æ‡o9Ą°_Žā¼*y3}ķ|ėóÖłŪ"ĮxŲ;˜h=DW’‹˜žĀ¤hƒų®xŗdŗx€šæ%Ó HŠć(1uœōREčW¼«Œ›ß‹_†š_]`2¢¢:ā¹ā,ńW_@CHŅ©õ>k­»ŗd³=ÓńĘ%z_x_śęśźųŹųņūćżń·ņ‘u£ŹE‡¢ƒy†ä9Å”rŽ:yŅ£˜Ø’Į'‘Ē"S"Īś"|6_aoQĻT÷ļ®yŽvĮvĮ2ō«eTŅ«k-ÕQŹł-õ ¢Š–?ĀY¹ģ¦,³šęéžŌK²y’ˆ!6ą.|.&aQõ~<¤Ÿ ²/¹÷K½Ņxæę®L^=pΦH¤ Ҧo² ŗē5` E{”ūQū Óš ųFb?yžFē£+RÉ£d<ՎVč÷DމżŽihF8.z’½'sJśüŌ)m~äKī—t Ō{—T.yXņ_Éc“Ÿ'‰I““Ŗ'§&Ēżl”ŗ2­MĘėģVˆ03v!,SŠ)m”MJ %I"Äü.Čķf[³ ófrŪłmB_ńµHI?ŧ¢! „¹Ŗ©LQŅ•?Ōj­·ęŃö©åŌJQy‘XZ¾¹pK(#.ŻŅg©‰2AUō$愳œµˆż”}“ćŖc¦cšż³ķķ›m®żˆ£®k…{—§öÆća¼¢ļ‘/_ N`Ŗ±’T sd‘Ø^QŸ£&GŠŽŹƒęY½-źu°|p{äśČʑ« ž/ąŗĒ=cÜ9·ŽŖöA¶5ÖM _Ss£”ŽLė„ĪVNČiRqiØx!wč9ĪĻd_0•™M“F„Ž“ńŠęK›q/°“‚MBÉō£€ŻķŪōĆōĪ‹õ*#ÕA»?aņ#Ķ@½NŠ\†!‘łČärłķ„0Č)ԃĶĆ āŸš;ÄR¤é{Mm'‡’GČJōźaĆo`ńtĢŽqh$R7Ü$ēGfžŒČ“)•R¦üHN^<5yGņ#xģNö$—M>ŸŌ6©sRbņó“R¦UĖl–³ńĒčś|Š˜_žS®)ē‘/HÆÅŻĀrž-םĖĖłø¾ÜZžŪHŗX4²tD’ææO÷žqov]trŽ'6§Ķnżi¾dŚhĢ×ēj«ÕćŹ;Ł.ĒK ŗBaa4’‹įf²ß™śĢ~ŚE£’ČäU¢,±Ąa6å?ō“€ÜŻąž”·”A*zuaņš@gi“G:ƒz‘‘}s‘ÕČnäņÉ@\h-`÷;hqlV 'ˆ'IJ•—ŚJī#O’“­ØjTr6žŪ‡?’xƒēĒ/bóŃzaWŽ‘50cDڱŌ)\Ź ŸÕą[’|óƒżüYžÜ÷cāĻĆ)…ÓΤOϜ™}3T=Ž›Ø†Ģ@ī’š_Ā/ä/šĶųÜB.‘«ĻĶā0ž ϊ¤Éą–Ē•7Źy„»RI¾#Ż’S”>Ś4½³īŃ?i·µIŚWõ‚²W.)}Ź@GĄt« dóķłž|”¬“TŃõM¦HėŠoœuŗå‡9¹‰y£Łf¹a9hŻg›`÷9LĪ©®āīņž‘޾«ž…@»ĄĶĄøˆ>‘ƒ?‚õĮE³¢b£KE7ŠŖå‰*Õ)źc°wdJ`æ«Æ·w‰ē¬{Ŗ«Ÿ3сڷY,fókćœ~H;„>PBrA¹£“AüÆn’Š+Ć-`ӘĘĢŚOO¢RČÖäu¢± āK0ż?ōūÆ÷õ¼Š<‹@묹÷«±4^h‡$3ōõF 摙Ččž‘ĖČ $„ųŠ?Ń©čc“vkˆÓÄbY…rPŸÉ'd˜Œ”Sعdā ¾“ØJzČ D ¢Īaįš‡œY2ś§ēO’z?ŕņķēø”Z)ESź¤ O™2-%'åAŖ–^)cręķ¬ń9ƒĆ›€ž–’AĮÕģBą¾ēlĪĢ=c°élqn$—Ÿ?Ć/–ˆ3¤n ąz%Q)(W‘ŖJļ¤ ©ķӏėćõBś,ķŗz[”Ņņé$tōšB?a–°B˜xŒ_Ē·,Ņ å°¾Š<ĒzĢVÕ6ŅŅŲÜ“ϸbŌ4}3©µØ¶ŹłŹ¹ĮµÜöLņ%śMÆžØĄ©ĄŠˆ‘­ƒFŌ¼ØGQQŃ[£ļD÷ˆ~57jrŌÄØ]Įé‘ĪˆĻžńž~¾ŃŽ×ž8O¼ū0Ģ` Ū]Ks”)S£½QS‹ņ»EŅĆčbL4Ӟ¾ä¹„رä|ā#ž„u@Ā‹³Ļf¬HĻN;™¶3mhZ•“v©XjÕŌ-©ī“ćikҟfø³źe Źjs<¤ ŃJxY"HŽ%¶7 ü¹!õ•:Ī`ŠĆD–ä^qu If ó€Ę;C Ÿ,c U֓cŌßuÕŲ¦gh>­ŽŚ]I’ĒɍåµRč?Œ8EÜō H}$Z>![A«lŗj9d]kM6×0 ÕŪi»5Mߨ·4n™Ś[fY)›Ė®:j8IŽoĪ1īŗŽsŽ ¾fž}WdtšmTBōęčŃk£3¢‡ēÉČó,śØ'E 13Š20Ģ?×WHāŖ·°ē‰sż„ÕmŁnźh”Õ j1j „»¼Lŗ'šÅęĀf>›«Ēmbi¶s˜l6•Mv„-P™Ųūæč§ę¾³^ܳL_4Īv ^ošĶįŠZ&ĄģĶ…ę²ى÷|Œ$å)Ų ]¦ mžąĶˆR$K ,t9ŗ;½€>@wdā™ćt_ŗ2݈JćtŖ6ŁŽh'”2)';3-ż^š7½~zƒōiMSO§XR¦¶M–ž2£|VVVfVDöģœœ"į2H}‹®Ęnc‡°MŲX¬,~":u©Հ–˜KĢPv,7™ŠB¦]š‰‹E‡xCX&:¤r@­«ż„WЧjļÕßՍ „l%¹”4B¼#¼z‹&i¬„Ź«ä¦Źwå¢ZI/mŹ4 Ö8ĖAÓz}ƒšWķ NŃŚėn£©•łŽyå¤u³ĶdÆgŸo/ę8élč~ā±ūfśæśGĘD5.žĖs½%śst§čvQƒ3‚GDVč(˜čæä‹ń}ōĪö¶ótr1ÉÖŚņĮ“ĄčØ×Óź©m•Ńņvéč; »xŒoÄmg9¶s‰.HĻ„rČÄ’§~āD¢ żJēī„n ­„Snź GĘ"“łęzk‘mČä r y‰¤!Z퀮GĆhoŒĀćKˆÕäcj$ż¦āżŒ.Ķgj3_čōtŗ3Œ¹J¢,Ōļd5‚Ą ¢—B­³kféŅléō1iR;§ōIÉHMO»•¾%ó~V›ģįŁc²»åL m /€Ę» ֋ ż‚¬„é/ƒŽCQ¼8Q™¤©µšIcxī1čהæÉÕē łÄ†ā±œTBö+‰j9hž×µźŚµšzTÉÆ ÷H9¢O¼'v‹}%I^ ŒŸ¬,RgkōᦠęfټĆč§õS:Ėå®Ś@÷+“F]iīiij}c-g[a«c/āųāLr_ń÷Ÿ ōŠģu ŗiž˜ŗFя`īŽūóåąŽČä{DÉ8ģļsś&{1ļQ·īr9ŹŚęZlęÓĘT½—ÖU¤Ģ“IIąč=„ƒ<Ć7åv±Ū™¹LżBd'ņŽ’Ŗ ś¹€żb€ŖAö5ļģĪłK½)Č,Ƚe ŽVšĪ“ČUä!°{č”,Ś ŻŽ²@ļü=ž’ČG ¤—1G˜LEę%ķdN3uY `Ņ˜ lfżŅqŁøˆ½E.äÄd½L_ŸÖ:ķ|ŚŲ“©©ėR^„i-Ņfdf®Ī¶ē|ĢĪŹŽ’C†ķ°zŚēīC‡×8/Ü6¼)<‰Ęśā]‰bäi² K¢s˜YÜnČ‹Īœ›Æ% —ˆvÉ'+Š®ĪPYm‰–ØEh'ÕFźs%^¹!'KgŰpE˜%’^Kƒe:O5`ÄZc©‰¶6w2mÓÓÕ¼Źé/ł¢2_S ³QŠøi2-07±Ü·”“n±6µõ“æwīKž>%š>ā^ppt\žŠyŽF‡¢ GEłHKdŁČf‘½#«G®Œø(øļ?ēOņey‹yū{»Ÿ;½Ž6¶ć– ę·Ęš‡1źTe|EŹ‹‰ż”Q | n+±]˜+’č÷’šæ_Ēŗ‘¼°•*"µ`;µ‚ŽŅŠŒõf#‹Čz˜€}Š\.åŽCń¤Ÿ­‚öC 6+ŽĖDhÆ£²ŪŲ:ģĘĢ”dF³G¹u\Wv0“ĮŁÕŒŁE'Ż„†_Ń®į%Łķ3K¦‡Ņ.¤-L»žZ)uBꉓzū3łl9goNќb9Ws>„üHCšƒā¹ļA:Ķį߆§#—Š$ģ2>˜°“ĆÉļädj4ųhkĘĮŠÜ{ī=æB()^ßKˆ­Wķµś›Fh'ŌīŖ¤.Qd„½¼M¢$‹ŌPz 5oĖńŹK„æ*hŸ4Ük:k®m‰6—7\ŚEyØt]|$-WziŪōŹĘw㢩ł‚9Łģ³ ·”±Ö³=µļw®t÷óVõ#£—EóLŠŽ5)Ų3%P2¢kĈ%=#ä{€ lõŸómö~õōõÕXlŖj®iĪ0½3E˜˜OZZŁZ8–¹zzŗųž ,Œ,µ9zt‹č1‹Ōč3L$7—Ėd+±5Ł]LvGš;Ł*0wčjĢę=“‡]ĪÄŠŁ;¶äüČl16}KŚėŌ©SЧŽL“1-+:§YčuČö†‰°~nyyƒ|…Īū,wśčTĘa> Gš¾Šž.“tA¦tŃe\2÷•?*¢&U–k+~µÆŗJ= ŽRļJ…“Ē*qŠUł!ߔßČ:8ź&EPGؔ¶Aė¤70ŗ™Fšs#RŪ)+"n¢„2 ÓŁP«ļ1rŒ«Fc¤ńŃømšd Ś:[¹'x/śµČŲØõŃ]£+GĶ.Œ15°ŪÕ’ŽLaŽ7~ÄÕūÓÓÖćńzNŗ¶96ŁR-}ĶVÓ#ż ¶EŻ©œ‘_I¢T1W?‘oĪķdYčŸgé<ōt*lKŽČå÷H|ń’ŠG̹Gåž::)ŗK7h.£ęC_؈ģB!§Ą9ļ€zT„€&‘­Œ&¢ Їhql?Ö@¼#gŠEŁ&Ü*ī$kϜj}g?³ ™Rō%*žę™„L1¶4[ž‰£¬ÄUtTųrv»L1#:=˜Ö"õnŹÜ”ģ”œŌ›é×2ļe7 =Õß o «°†h“ŚÓ>ż īCSŃjŲ2ģī氌˜ ›— é¾Ģx6‘ĖāžāĀN!L—$ž•¾ČƔ8uŗzX=«n-K«¤zSY¦ōPžTŖ*”įŹA…TŪ‚¾ @£łz#ÆéŒQKOU‹*ļ„I•–I'å{ŹhX]Ō(ķ»öSĖŌ®h†Ž›L­a{×LOšoF„U<śIŌŖ -Y:¢øe+{W’z’5Ŗ’‘?ĮÖ÷·w«ē§{»»¾»6ųēŪK[³dŗÆļ’W?A*/Žó,ߘŪĀ’lką÷Hz2õ“l™»’l;ų?öņˆ‰Č½Gb-ąöö’ģ©ž ­eC®z§‘+°ņŸĮģ„"8Ŗ”“Śˆn@? U°óŲhŸPµ™õl}īū{Lwf+óiČ$ŅæQ(õ”zDe¶*Ū…Ž&ßcŃøš€œ¹Yó3F¤wHېś{*žjJu¤õHļ˜Y${NĪüP÷°€AJ «Šóč]4µay±X,‹%EģĮžb©Ųklv}†%śS“Ą"œ—ßČG ū«XV ˆČAQn„˜ŌĪźZõ6(ø \tøÆśŌwŹe¶2A™«P¾*ÅŌ)j’ŚV{ŖµŠWź-õeŚu²rP."§H¤ÜTŽ(ćJeČĪ%r3å.äźZõ¹©Ÿ1j[nي8'ø ßŖĄŪČ@”u8ø#ņTĽ€čē7ł_łžų>ųbżšßé÷ų—ūfz›xZ¹ŗģ®²Ī+öā¶u–ņęT㜾U[«nQŽĖO%ZŠ…žrˆĒłśÜ:6Ģ4eŽŠzõlš»’z īū?Žą+Š_ “—ŗ1”yŽā[sG®zWŲŸƒżRO…ęƒÖv˜ŒF³ŃFŲ}l.¾›@MgÖ±‡ŲZģ¦,SšŁĒwouķsf:¬ŽXūqk¼…2_‡ž¹H›§®PöŹw„XDģ.ģä3¹źÜ26•‰cöŠVzlĒxņ Q,÷żæ9˜ˆM@‰Žæ@æ R ŚĖÆćĖ~½K;x}%Ɓ\õ~Żńł3“Ōó¢E y6G»ßD5¬ ö[†o!ŖR³‰łĀģaZ1™ō †c+²o˜O“ŸžB] Ņ!ś7꿯ŽĢ‹»Ń®ČīpūŠ»ģnY]3Ēd\H/ž.ķ~ZTśŪōˆĢŃY?²[ĄüķAv @ĪķĀ`-š§8GÜĘGįõń"ø7ć®ć܋WĘwą³ „zAsģtn?Hø,¤ =Ä;bi”ä——Ėf`Ąuź}5C%5Ak”MՎi{“zŚ 5NMR¶+S•qŹeÆņJqŖMa:o«ijŠzA ĘØ_•Ą‹×„iŅxi­tOäxé7Q‡‹ƒäj=K’d*a½c_élļę]ģpG~ŽˆŠˆ ¬šsž¾(ßoGoowo ļPoțężī‰u?p^tdŁģ mE­ßĢĒLKŒ śm”:ųį<š_¤ŲZXĖćŹq³ŁĻL f3­Š©—d]ņQ˜XƒŪš™÷ĒOˆ č½8ņtõ¶ąž#!ūƒwīŽ»ņzé Īéõ~G£½Ń™ĄŃ|Ų(, ō›żį+Uƒ¾N@GK³Ł‚ģ}&ųļ6]ˆéŗ~¦§R¹ »{1|24&§Qvõ¬Ž™+2ŅÓū„G¤ūÓ¤ĻĪ(œÕ.ūpĪĒP^¤>øf_pJߏ?!JK‰‚Ä#|5>ļŽ7Ä«ā5šNų¼<į"›S[é·ĢA¶Ļ¼L¾#ż&½ߊ²T ž³„B¢8SvØg“ēś6£•Ű×sœvīwńīņ÷ Läų›ūļśŚų(ß^oyļFĻĻĻ(Ļ\OuP±ŠgŖė®ć’]“ĒŪ[GX†˜{˜Ś-ō–Z"šūlyÆō\4ÄśĀ<žW„Ē>gŹ3+iŠīA=$«‘ˆ¼Ä2܄OÅhlōæĒ/ż:Ÿ/ŠC5 ­Žąžc!ūV3Īy÷_õōŌ‹G»£ŃMčm”ĘŖa‹1_Œ'eÉŪ$J „ŖŠsŒyČųŲbģNv5›mĘĘšėS“GW E|1ś¦½hŲr¤ģ@VM˜Įké1éŪÓÖ§5I•Ń'Ė›Ó>“?LƄ'`±Ųf¼?!éÄ8B'$‚"ą«šĮų@|%žƒĻ Z‘­ØĮÆ+ŁŻÜ¾±šLč-–‚ņ_ §Ej µ½Ś|Ķ|5\Ż®Š4½§A›č“Yź[e R\1’ͧ”UŚ@"ŽU*©óŌ› `¤Š+½ä‰ŅJ±ŖŲOģ+Nw‰Ä9ąĢÕ¤Ęņ"e ŗ[«mō67·†mMV—äYź=į#ü‡| }ŽMŽ^ŽŖ^Ģ;ČÓĶsŚ3Ģė9éłįIsqåuXģ„Ķkķa¹b̦FŒ^F«®¶PĖˤK""–Ęš787€½ĶeęŅYT{ź&YžÜAˆøŒOĄpläæĒŠ@Ęy‘X¤&Ņé’{ŒĖB ö}¹ūŹžBīż—z~“(“ĪFhWt,ŗ=‡&”QX"vs~u‰w„‡DČä Ŗ]† óökįŚpE¹­\[.š{Éf° S›*IÜÄ~ ‘­įQ”å9[³eŻĖLÉP3¢Ņė¦uIŻ›jMo”Ł>;*®Š4G”|—¼ųg¼:į&6į(1ŲH4%.擹 ųQ$_ž •žS†ØŻŌ ZCY.“IQŅ)1Q¤Å°°QŒ’&J'$UŽ"7QpÕ¦…õC¦––vÖhūĒM摼ýƒ¼xuļĻ ĻqĻĻzŠn¼§…g­gœ×ćuzŹø†:>ŲŗXėZ¦˜’01}2B:§›µ(µœŅL%ķß ŃBž'qŲÓL€O£šPēČāäĀIĢÄY|4F‡ż{ü®ˆ8ŒĖ䞙ҁL‡ę¹ Øė ņy¤ävĪ’_½Ńčrō8ś5°ZŲth„ń x)%·=‰Ä#ņµ…žJۘĢG¶WžVßÉõēzpAV§‡‡šŲ)ō`ųuČŠÉ©˜]7«Ef«Œ*é‘iÓS§§NM[›q6+˜Ó!46\ł‚TC§” °­ųTXwϱ£ųM¢Y—X‹·Ā»įGą;_'F“ńTEŗS›ķśŻ‚ÓJ(,¶‘Ŗ+ĆĮ [Ŗv•SÖĖßdM)©,T¦»“ĘŚdõ€’W¹,O“;ĖåzrœÜDN”GĖGåhe”R[y'Ÿ—‡É•eƜ-5”NˆMÅjāxq¦ŌTž!o—mŹF„µ:Rū¦6Ķ7¶ųmģœŻ©īŠžržžhß”.x"Aæ$ĻOOœ÷µw„·8ųēGSū)ė–žę(óÓ摾Zū®¢ŖM)%·–f‰—Qˆē×rl»“Ń™ō Ŗ6u˜ĢO.' bŽćƱ,h ’}ü¼śåś« ķ„7tĻ9ČȾ3ĄėƐ$A•ÜÖņ{®z£Š%Š;Ÿ¢,V€Å$Č Ėxb,±„˜D<#FK©¦ō7ŗ³…©Éįń1bG±¶ŠēųɰžvŠGIžØˆ_EĖ!õĀKBŪrögŸČŗœy=ćzś­“ńiK ÅŒĪвŠdșź6ĄĶ³‘4t;öšēU¬Ž›89X·ā šSxeāob Y‹ŹG;!uu ÷½\ž?GH/ț•łŹļŹ*ł‡“OĀdMĪ¢æ(WWĘ+± Ż[y–ü§i9]ś.}‘>I_„LÉ+–§Č@»Žr¹#°C@>.EJGāЊK„å2­ĢS|ź3õŗ¦ ¦ę{–Z¶MöśĪu®AīvnÆ» »’»„{²{‰»‰;Ė=ßSÖŪ×»Ą;ņļ×uĒ8ڧقÖ*– ꓦ³ĘIż¤vQż[I‚WXFź!n¾ó„ak½d˲KģUžŚNśČ¹OŒĘs°AXńŸæ"!®Ü3¢ėA{里÷ÜÜpyɗ…šØ - Żķź-†ŽņE±ĀXGltśZų6ÜDō&oˆXņ ł'õ‰źDß§k3 ۚ+ ü€ägÄ ¾>¬ØÕl:Dn‚Õ{†Hd8:S0»|VBę̌ŹęŒņ#3ü™Ÿ2×fŻĶžžs9Ō/œn‡,A–¢e±ī@{°{x>b2ž ‹ĘgąOšzÄCX3å)ƒĪ _0÷Ų–Ü®, ^q©ŌHŽ!÷æH‰ĄÄÆÄ»b+±ŗøN¬/—NKOAO>vYŚ%­”H³¤©Ņ8i„4žm—īH,(›(·—kČyAõ“Ņi‰Ų_<)¶’*ɳ”ӞRÖØš–Ŗå3™RĢ¬{m”#ĪyÕUĒéžįjģźģ:ī2¹øø7»W{:{x{R=†ē»+Æ«Š³š£żŽķµõOK†é‚±hs9ŠßYłdH5Ä ĀuŽÅ÷ä.°‘ģxę OŸ¢ŠP«H9™Ą‰”x“ŗhßĻ“7RłuNōÆs‹&#K”»œ„é{ ŽI”V4/ZCёčBt/zĶ‚äkŠ-Ä^`% ū˜; 9Š|MŽ„ Šóé³ōŲ–­ŒmĖ.į†ŌUD³Oē n3óŚDĪ!bńlōr,¼1“(gqöž¬Ļ™-3ŃĢ =2{e.É\“95kAöģœ!$<2|+\ šThL8V;ŽUóą;?ÄCĄMˆūD²0¦Ó™¬—GłŃ|sa“ų]ZäVEź-Ö]āßB” ļćK Nq¶x^ _ԐźHU„ R)ŗe1©€ä“̰]š÷ĮR²TD.(sņké(č›ZL”˜G¬!EČŪäŸņe¬ZBsčqĘqS¼åw[E{‚c¶sæ«£»…{Žė®SÆøbŻ£Ż}ÜE=g=‚ļČėŌ|ŗś1ąö[@N–įŽs#ų'B¬“Pŗ&>lÉ÷¹4¶S^I–'>a«ŠvŠBÕ›Ģī›…eŹ|™iĻņdżéĢ:™µ"»{Ž3“5žN 7Av@®„NAE¬%v>Z°ˆkDŅG}¤NŃ«˜łl .™ ł×I"u”—J½ÄżB!^øÄēpł¹\¾¬pAp‰ Ė6ß_¹|"¾ßĮć{°w˜²ĢFŚJO¢Ād?ņ+‘H¼ĘŪą/±¶’ćś āCb`Ū$ Ż”½ĢG¶ ';Šā1„lI  Ÿ2­8’ƒ[Į9ųjü6¾ŒŠWų(L“ĪJłå³R¶XG¼)l–³ųCģMśOźŃ’¶@^†fēTĶ™—Ż"{Vv¾œI9±9öœŸŁ¶œ9+rPóŠž®ž>~Ž„õõ©+©%–õĘ÷įÄ"Ž$©3Ō$ŗ.óšÉĖ^eÓ¹ ü`Į!vˉŃ }¼„°‰×ųSÜQ.«Ļļ„|üC.lī ™‚UŒ\l -«›ŲYl&–3€ c€ņĀb©•ŌVj"U–Rŗų«Į$Šy¤†ņ4e›ŗD‹Óė©FUó~‹Õ–h_ęøķ¼åšļ&<›Żn÷i×2××—×­Āģ…ūģķłW¼s±3ŅYŹ±ĶžČš×2Ćm¼×Ī«'”«ņ'É*ż)Īžš…łIÜG¶vīõ{fŠ(ݟśL¶'Ÿ7=Ą›ą]Ńź?®ņė*fæ½7CzAŪūu~ĆYpĻļ@}N4­…v@’B—”ĒŠg(÷š=€ö0OĒGyȟä'ŹÄŒdæs[ł]üRžV ‡„$!Rl)Ś„«R9šÜ& Üž­¼P‚ŪĖ „}ŌJā=D«…ė‡žę¼Ģy’ó"§j(1ŗžs5ēP΁œĒ9ęP|hnčVHW ÷ļ’ —Gž?”ESŃ  M¬%j“ar?Ջö@ē-žcwp]yT*ܲ ķŠ»E“XDhČæżnp4_‡ŸĒ?Ŗ©&ōV ē…÷%z`ŗ*!TK‰łÄņāńœHJæIu„©(ö‘ZJµ„vŅ©Œr@Ż„!śwŻdjm^b¹fM³ł͜-\‡ŻŃž3īBīĒ®®k.ŌŻÜć¦Üc]µœŻ„iö«ö>“ó <ĒķZ›¦Ėś\m°:@™Ūč!¬·ę~8øŌi6/;—!˜~ōGŖõ€l@Ž źךŗų-,{Œ¶ż÷ś=źæWģƒLöۃ\Bž#iˆ„ŃņhŚīFļ Øūƒļl…Ÿļś›˜Jv :ӋŒkĪ›…›|Sį“Š]œ"Š“¤Ņ|¹£rRé«ō—e¹»ä”^Š'„o\*{™™M§~¢v I 7 ÆĻ½Rzļp±šŅ)ś-dłBeB­Bć ļ…°pńpæšį0œZ V\ d :«gćėˆś$Nķ”ŚŅ,³ž©Ä¾`Gp&XGŗŠüū²PSd„ b ”ž›ÅĶęĆ –ę‡ńųƼOØ+ Ö@"¾0Ń Ź•ćÄ^WÄ1(U’Ąv–śK“„"wN/]“ņ(ķ•”?Ń뙦˜×X–['ŚŲū:8wtNæū½ė­‹q×qĻw߀ææwUu)ĪAŽ}ö‚öE `{ĒXūOėX‹bŽbtŠKkŃj>„‚ÜQZ,>‚Ā žW,÷śƒ}™7t}“ŖA%+‘'‰ŠÄ)¼ ~śĒ}Č“’\?KEHq¤&Š_d ¬īƒČ ät d_-čćеčyō3Ŗcå±^Ų& ±ŅųXłSˆäņõœ6±øt~¦šFˆO‰å„ÅRYQŖ*G”3Jqu†z\ķ”J{ł»4H* ŽąkséĢxśYŒ˜‰ĒaöŻ Nż7r$œ:ZS78Ō5Ō#40464+“6t&ō3T"<<4NѹŽĢA§b qž8Dt uź(至D³{ŁrÜ)®"ž÷˜PH%‚Uż™ĖõąŹrøDn÷•+Äwā—ń7y\ų tž&ģ…nņCPÄbą”[Äļb”T&®­ŌEź'’ęKū”F€°ņé‘ŌB‰Ńŗźõ‘Ę6ÓSó-ĖŪ‹„a•ńe8CŒ#äUrµœ>ĒX¹)¼ Ī‘ŹeVy «³ƒž·žE”ÆŠGėō9z¶VT›£ĘŖÅÕåJ%eœ¼\ōƹ„l5gK1z5Ž,M$cēчą·aŅź†½įĢŠß”ķ”N”b!= 9až H4 ˆåÅoāƒ/y†lKeS“i‰Ķ|fžd°y¹qÜßʇ¹3Ü®˽`‡³UŁßŲ:ģųčLīp~˜+Źwį×ńÆxÆŠL˜IųYˆ‡‹æK.`÷2ņ ‡ż@ū·„Ļ#ūå4i¤“Uģ#­‘ćT^O4Ҍ[¦/f»õŗµŒ­ƒm“mmŠ}‘ćŖ³€«5tOĪŻĖżÄąYźÉć䖯³\5\ēĒM{{‚M¶&™ļ™ķō2Z”­”–[IsÄ{B„0”ÄUā¶±^v#€GōhŠ€­Ć‘Ó ±Ą·b%±3h½Ɵ¬#A¤4ņ'Ņk{°ßs$ 1Cw©”8 ŻÓGbE”µ.Äīc.¼~ /Gœ&Z’v*B˜āģ2®ˆšDģ-WV(Ŗ¦×MM9†Ē„›ĪNÓ}#Ņh¬ļÓŚh1š_ «»•xūń±Ą8棦ģ¦3żjMžĘ§ckŃTčÅäbxBø~8ž ^ʆҠߠ!_،Üõ2Š”‰/Ģ=˾)Qk©zdĘ3÷[ŸŻĻ†Y–“¹h.WœūĪÖd?3™yĢf“Ålbērƒ¹”Ü*ŠŠŹ7å—óļłß€)ī „Åab¶xMś(Ē*“eüUz#„H†\L®#wkĖNł4Ož§,ŃĪč„¦ꍖÖ8ŪiŪuŪG[9ūqūh‡ģ<čŌ]½\Æ\mÜgŻiīĆī8ąö7®¦ī¼ī ®­Žö¶wּ֚–K¦CÓ߂{Ž•?K&©Ž8WxĒWā×q 7’Maŗ3čŽō;*Śg/2NPÄ4܊ÆĘ cG!Ł’sżr‰Ź½d'čv ‘ŻČä-‚¢n“ xģ0tpĆWŌŽUdžbū±tģw|Āū!b%ŁŽŖI7gV°ž¦0Y:.·SŚØ6ķ­f6ņ›¶™ŅLēLŗ¹“¹Š¹¼¹“i’qSo„ĒčńzX»«–VFKm„}Ü ö ū’­Ēī`ŠŃŌx¢>ŽŪν©d@§nFĆgCóCĆC»CóĀS.@š31ŸŠ›ˆuDņ™H„QCčoĄ‹˜kĢ{ę9sšŅĀ¢¬‰ _}<{œ)ĶŠ°5īÓé$ś8³(įJpƒøĖĒńėy śjŠŠMLŪH±ņ39G^ —uŁ"—”»Ź«äsņC9KŽ“’€6=I=¢U5 ™’M6K“õŖµƒm»ķ°M±æ·ū.ggē g)×WŒ{¹{vnū$\;\«ŻŻŻ-\óm?­Ć­©–†–}&ÆqN›„WĘČˤ+"+Ę y–ļÅ=ƒ5x™©Źœ„’ /Rµ©;dSņ5эHͽ’ĆB, ŪTžąŸū?čH4R6÷z£‘%Č>h/æĪoˆD+£ķŃ čVō.F 7ĢĆnc6<?ē‡-W‚|Nn¤ęÓ»˜0;žÆ.śåJoµŽöQ›«3Ś™¼ę f“%æ„’„ŗ%ƲƼÅōŲØg8śRż+äįGå”ōJÉ_äśr^®6kcfRdwćm±‹h5xu} 9žŽ®ī>Ī,@F£±Ųe¬,¾ wK‰Hr/Y…ŗM%Š·éŅĢŠķs™ĘnJ§ŠLWĘÅ~g 1č.tŗ8ĶÓ{Ø ō&&ĄöeO²זŪĶ!|UžßKx ów[$ÅČ÷åłr/¹¢\Y®$–ŸĖå•QŹ*„“ŅCöÉf„’zZsL ęŖ™Ö[7[[?[Ŗm‹żoPOrĘ:g9³}]Y®ŹīZ|ߝm\‘ī}®ęαö7ց֯––§ę~¦{zčy• ÷Ń@īŗŠæÉ•÷ŒdW2^f¤7SE©Ćd ‡fÄ;¼Ž€Gy`WBļżs’ō+‡4Dŗ!ćåČ!`÷ęGk£=Š9č!ō5Ŗ@böŶaß°’ųdü#ŽČ”é¤>ROéǽą7‹ÓåĪź­°>^gŒ;Ę%Ó%ó fŻm™æ×µ³œ5_1E›Īˌh£Ŗq]’صP“K‹Æų¾א=AÆ£’ėˆŪø ļ…½ļ”c_w„ļv‡…7#Ļ€Žcåńµx&^ŲKä#·’%ØÓT-ś ]fļ ƒ±$ū \ŅÅL§iś.Åэč;“ŹĢ£ ś)už:DĶ„vPEčGō`ę-SƒŻĄā\· śh9¾ Šą ”¼č”†J˜”ļK-䃾Pė‡mĒ’€Ū§‚{6$® d¹—šJO`¶°(?YØ!QPm·¾×čcbąēŹ27£@E.Ū`«a«o?dß`«i•,%ĢoM˜ꏦĀĘ#-æZ]n­ė)ßÆĄYŁćōtꉑ…‰ąū1 ŠÖ@;!ĀĻĀ1hyü8ń„ @½-d2G]¢źŅéĪĢO¦ū”-Č5榟§°£ŁL$snK  Qqd=²Ł‚\Fž ƓƔ­ĘQ;)'=&™aLÓr°(7‘»ĪŻćŗq“ĮMæs„ų‘ü3¾·P˜Ø˜RO-¢Ļ1ž0õ2m2}05175Ē›ū™Ē›‡›;˜«šÓMKLĻLŃę5fŽŅĻņČzĀvĶ~ڱĀYÜ5Ż•ģLtvq<¶tpŲķ‡Ą77YŽY.Yš]¦Tżž¶_]§l”IoEŲA8Ģ;ų±\:Ū›Mbś1!z"m¦×RÅØódņ ńŠĆ¼ ~ė€eC®@/ m’Ń/R iŠōCf ›‘ Č„€öR ķ’ė(śµaµr÷š‘x=|N}ˆoÄpŅG=”öC{¹Ćų5B¼£Dk_õ1¦%ęKęßĮWźXģÖĖÖóÖ»ÖÓ¶HĒ"Ē»Ż6Ņg>fr™™×šjw“ź*Ł/ŻĀ|ÆĶezӟ©öŌRņ0¼Ņ«ąŲ ÅTEn…“ĆĖ‘śŲ8¼+ń“č ³÷œüfÆ}f(ÄĢ`­ÜTī%'ł¾ę¦sn{‚yO¢«ÓMØ£d6|Vcr ¹”œMĪ"w“85„Ź¢źÓėčHfcĄÄŽ`=\wnW›óĮ#ši nW/'lėČ”ŚI}ŸŃŠ“Ź``ī6›Ąś¼fŽ3'™śšÓrć¬1Ųt×il~iieėcæåŲćÜįBÜ[]󝵝Ó¬cPĆ#ė«ÕŗÓŅÄņĄg8ōĶäWČo–xKš Cųg\ī,[‰=ĆŌbīÓķéje£¶ÉūŠ>qb!^?ƒ5Å~¢SŃhōĢ?÷æ5!ł‘ß‘f@]³€ž.#6÷ü”¾č`‡ŸhÖ›…ŻÄ,žG ÷M#DXĶ•)zĄ]ę'[–ß%4–Š*•µ^Ęó–²–N–·–ŽŠ³ŽX Ų¼ĄFsü[mw-åĢ~ÓJÓ³a^l”Ń’V•=R~q'扛ČbśŠ‹i„®I%’õˆėŲC“šYžF‘$čļĒ[UÉęä2Hm¦~£OӍ˜dPÆ0w‰«ĻļåßšOł5|ež,ē‡>tŚ÷EXcŪ(žZ n’;%Qmɹ aO² eŠ™Š{īŠĶ˜ŪLIv"{ˆćž@öc+³~Ö Ģx›}Ą5D鎼H}®u5ŖuUĻŃ9$™R!2É†bä3>ĶM{ML»ĶæY[Š>ĪqŪyÅÕÓżÉÅ»Ś;w:pĒ-;e·½·.·Ž²¼3}7zéµq꿁vˆ‚īКņŅt2#ruųCBœTji pKmĖPKȲߚdlėi+jėjļķˆw®s|“9¬Ģ·Œć±é¦é­qWæÆÕSĖ{„ŠÅŲįLCčż×h…ާJĻ 7 'Wį<Čē’Æw€²+ŃÖ¶—m{W%ŪģXŪ¶tģ¤culŪ¶m£cŪö7“Óē\üwü£FFW—vÕ~×|ēóT­½6rė‚‹DC‚&Ė“gÉŽp’/¤stbCī7ˆĻ$l€uFč#ČĀPž1—’+ƶfnQÕčL]£ĘQ:}‹ŹL= W‘ÓČėd#j EŅ#éD`ō4[‘ĄŽe»²¹€ūÖ3ķ…łF£LŲŽ7øĀ&i†²Ik ŽóR+ƒ„ רlHĘ9żö»nŁĶ“&mśÕŗ`ūno¦’:¢9QÅH å`æŹļģKžW7rė:iķŠg“;źÅRrĖ-„…āS`—Iü7®%w—­ĒŽs’J§cą:Åaūu%ur9Q”ø…wĆu|9V»ƒöłoĻ’n!iĀšz7dŲūi°͌VƒY‚^@I,;Ö[‚=Ųāā·ńbÄ6"y†ģFå2g؈«ĘoŠIœbj5ÖC«ŒŻßVk°Ė'ŗÕŻ ī7w¬Ÿ9ųƗ½īĪek’łÉgN5߯ś<­üÉ^b¶¹„`󱽁»ŖÓ;©Tä(<VmƒĢFXl¾ŒČOt$ŽCɆŌ;j4ǬaJ€‡ĻāČ)dI©ōF¬/Ž'y/ĢlZ¦-³”ÉŹ‡)LE§?P}€qēĀ}žl@m¢ŖŠčŽĢc& [ ˜Oe'1‰™yEƦ§ė L/f{‚_.’3ؽ“ÜśAm£–L_„Ó[č'µZm¦6\7Ķ­fSė¾µÜŚaķ¶3ŗc¼~Ö°ft5Ņ£¹Į,šŸ)Ųł½wĻ;Oķ—VO³£^Ik®vSfČ'$V*%NŽņ•ų]\fn›ŻÄb`3ŠßąØKM ėĮ˜Dd&Nį-p ŸåĮ®ĄlyčV¤Ö?ł„EŠĄ,vG&"둳ČÄ:­DW¢W^ņc]±õŲG¬>…W#ŽåČūäHčŸß›MÉÕę7 ٤{ņuÆ~Ņ|T=Ā&œ)Ī:w¦ŪČ5Üqī3/QŠÅoį}qž²;YĶĒęEs9ĻX«—ÓV˵…Ą[›™¬ģDv}™šéB"böłyhŲœ ūzN!ō :9ų^"ų覜ĮßäĻ ŸaOMH™„­bqńøCčĒoąs[ŁFšµÆÓ%éntOą¹™Ō;²1Y…ģ;t•‡¾EwĢÄVbf§˜šŠ© “ųa&³Ü’l‡ŻBZi˜,«§ÕāÓ30c‰nčO4Ro©æ4ĪĀ>Čj³ŚÆ-|~£=ĀvјØ[˜ ųeøæĶ/źgõ\×q*Ł“­WF¼žD}¤|•“ĖõaśŽ %…e¼ģņķ =4‚ ˜•t!ś*ՉRØ„ĄŪ÷‰D"b^ ’„MĄ2cē`·Yč&Ųy’ĪÆ(RéLF6 ē‘÷ˆƒęFė£CŃõčmŌĄ°¾Ų Įšų7¼ q•ØK¾%§QÕéōLÜOīć— ‰„r_µ™žÅ¼fÕµŲģĪg›»ęļ”›×ėĖĮ!`ŠŠčh*%}›īĻü`*³}Ų¶ąžĻčštJĘaė³؃ģ]ö {ˆĀ}āŪ ÄvŅm9Aż ½×KeŒœ†jŠFo£ I[ƬżvC§„SĶIįsŹøū¼ŒĮ °vō%< õļz…üdž+÷¶ó¦ģšÖR³1ZĖ«Vļ[#}‹‰³€ßóē¹’Üa¶${ީϼ£‡Ņ‰č­`¬oČ1dzņіPˆ•xiü)6 K…C[£*ŗœįæņ«ōD¦ ›‹ČGÄGó‚=ŒD7£QļףJū1Üc6N}>$Z?ČT}:+“˜MĶUāgœ4U® ¦Ņs¦•Ķžh?·]g¹£¹õÜ{ĄÓGüųĄ ūټkĪ;£­Ūē­if9}ŖZN-­žąś±5`egļ3™Łō[*U“ä‰öš?ĆRāūˆ/pÄ$§Š˜Ģ"Fcė²—Ų–ąmgłłĀV1|Bī+“ŪÉååĢ2&ļ‘šK…ĮĄ[‰©„½\E6 ó‚Zģc@]%VŪ ŸLDe¢1f5“ć×õ^Ÿ±»Ųźlv8«r9ø\Īē’²W˜Ģģv¶·—;Ą7ĻÉĖÕ׌½ØQטb衜ó¬Ńö2盓ʭävt_9eœ?Ż7žpᶐ Łą²7ŃĖźwó69ćķyÖ:“6c©m•åņ )·4v_)a=Ųē$NēƳ»ˆÉ Š8īēUW.‘¹…ØE|Ēgāłš›X,¶mŠŠč¤Śņ+łõB¦"›‘Ė`ļ!Ų_St ŲĆS4†UĀĘ`Ē0/‚ĻĆe¢ń’hO°’ŃY˜ūW˜’Sx"v”“Ø?“wFqų‰ŪÆģ"ßBg•›Ļkżæ5X<šĻ{ŻÜŠĪ(»¢Ś–yXė \—š‰¼p¬§Ū€ķĘöfs°C™³t6śOu#ϽˆŠÄ-ā ł2ét!fÓ ø%=;‡Õø•°ū’ ØØHŁå2„<—ÅP²*µ”’J剼 ŽzIz&å2éč{dvr$ьČCÄŒŲJ¤§źŅÅ̟ājqӁb³ó łžüj> óe¹Ęl-ę $ģ1įVưŸų>RwåśN[ §4fåĶģV#{ŽSŃ}Gi*ļ³»ĢŻįÖ÷Jś'}.h/õüĘŽ_Žj/ĮŻlw¶Ŗ™E¦śķā+ƒe\n,S‹ ßłVüm°×«0÷O™ŸWĆ\D ÆS=©ģ§6‰’ó‰ā> Ļ€ŸÅ:c.¶Ś‘AW"Už[~ ~=“ŲŲ7_-ˆ6ūŪ…¾@ƒoLÄĪa^_‡ŪDāщ¤ æśt:°'ƒKĖ7ö‰…å—ŹmƱĻbµig©³Ż©ćņŽ{o¶_ Ųü® ŗśI½Ė°ĆūŲ?¬Äf%mœ\V,!Tą‹±e`k²ėŁĻģ|v ³ŒH·† °“ŠQsČämrµ—Z Ō2‘©Ī¤d˜v›…»Ļ-ę{uÄR3¹¾²^9„,QĪ*9Õ#ju­¹VQ+¬„Ōŗ«¼ŅZ,ÄggWÓĶØJdC¢<OT †Ū©Ėō&¦5ĖpC¹O\]ž_Jč,ĪÆ Ė„"ü<.;;‘)·¦0?čf9ū€G„órAµ¢Ó÷荍äfĢjbļsZ»­Üž®ā•õņyŪ½æü«žļžƧßČīUóBoƒ›ŪŁh57cĘķ¢ŗW™-3ņ© X{ ńē*>?œg!ģp‹čžĄž;ØŚ?}ņ 1†ČJ\Įūą‰ńƒX LĮ6 5Q]†Tž_łMG¶"WÆHZm‰NųõLpI°ŚŲ4ģ –čsŲß@ā=ä‡SóĶ38ūUłĀxQ”×(½“qĘ"ė/ū‚Įéåóv{}ü¬A‘`]0&ģv ®z%ŻTĪ2ūŽUÕģÆ-–Z õł„Üvų¾‡0iŁQģB6›™™CO»{Hļ ēĄ1˜‚bĆ²RµØ"ōDśpHÄ,aF±E”=_šē„ƒāQ ßĮj-…6JĶÆ–Ó>jŻõ¼Ę8c±į˜#ćZ'å‹ éøqL&ŗtŅ>’£6RKčLqö#;Ž‹ćgó1a†ŠHl-ż)½ļ]ŠĒøŃlepĮwĢq&bKsYRš)Q ŠŚ:cümģ5o[ŗÓĢiēīT—ņŚ{ŖÓļéßšš{½M^-Ļ÷ĪĮ¶o™ ZÆ„®”SŹŁ¤ Rvé®ŲČ„½švß]®!÷ˆmĻ"ģX&9³‡®ģ9ƒŹGŻ%‡iÉóDOā7āŽ·š­X=ŒĘV •ŃČb¤ā’ČÆä· ¹Š|CāŃ"°!'¢ūŠ7h2¬6ģ/5ŲßQ<Hč іüFN–Š˜gĢMö)g µą>l¬¤ÕƒµņŁolĢźōu‹z|*h °pd˜<Ŗvõ¹I.v{yMk/§Ršåøvp¤’ĘüĪŌ‚¦™ś9õšJCg¢_P©×ä —?į>§Ø ŌKź;U¶ą8¦-[š;»QĢ*•”§+øfčYõNŚDŅéķbęs”EŁ­Õę>½“ś^:)Äųgģ pćmōR Ģ™ąoŁń\:~_Vø*4戵¤–ұ„8@xÉ'ē'pMa Īä qWøžB2i™ĢŖø6G{„ŠŪxü1ėOū䯿Cœu+y½ģ> ¬YŌ+ī=tøėÜæÜNn»­¹S“ŠŹ é›čH·ÄCbWQoˆ 3ł8~.—œ[ĆęeO1„™FēöģM%¦“mH›ÜÆ[šz8ƒÆÄ*cßхhYō ²©šæņ›lG®!ߑD(x: ņ{ū+æiŲu, ŽߋDoāь|ž‹žJŸf¶³»¹›|$ö–xe·z@_;=²—ŚÕŻ-īmō³m‚Įł`i86ŗ"ĮFw«Ņ~¶ūDÆ­tĖņ‹øül7†`VŠyéĻT8īŽ—ɤB] g“eČÄNb#‘™|NV”ʃū•:ėĒ“bŪp=ųžĀ$q„tRfŌ²ZQ}¾~Yobl²8an²†ŁEĪN:§ÆżŹü”’©–’+ˆ‡øPR"v šėīwžō?!ĪÓI¤ÉRCI•ʉiÅMB”Ž;Ēq|V>Ę/ć 9„UrØf×FµÆŃY#µQŠhgö„1Ė)ėsSz}½^1OšŽ¹‹Ü²ī%'½[ŲĶäģ“nŸ“ßÕņ©’ōB< ö“ŠŪ…ā¾* ¦ļ9ۃ“s3—čĪ“ ģY—¢Øde!—•„XŒ—Ēæ`s±’Ų;t&Z}ĢEŹżÆüfžŹļä÷ó™4'£ūåW ›ŒżÅ=nĆM¢3q‡Øž×“J ö“šĻŽį𗄔ҹÓ?ĢRV~ū›ķ¹µ½åž§ąx04Ø(įāØŌ.øēž·÷X+¬ŌÖ4悦NŚ!œåŽueezŅ{،ŌerŁ”LGf 's7€—Ą¾z†ß#¾^„²ÓmčS4Źäc§r+ųū‚+åk(ŌMŚ}š‘Ņ»†³Ö©ęę„I½ŪnWw–żĢLj|Tó)¤*ā)^åßp"łł8a“@ŠŠæI‰åżņ=yšģĮĖ>é”8œšć;sż!åāü~š[.”w)żµ`ÌKślm“ŚvĘ7³©Żōޛ˳”7Ā.źęs~Ų;×Éi©ęq½‚f«ŗ²I>%YŅD1 „­|ž W¦Æk²+€ĖŅCčŌōŖ“ēq²+™”ƒmWś’ģϟł…ż÷3æ×ĄŸUĮN`^’łų×ŗÄQ"¹—¬;j.Żģ)1÷Ÿ[h-n—R*‹Ōś}£‚ÕĻĪčds‹½ō ^I‚Z~‰Å†ß½£Ī<»]ʾdę3ŚiÕ՘RO:ĄĻgGŃUž|E\'Ņ"MH°Ł—lE~!ŗ‰bT-ś21sčVП*ó»k-\‘®Ė[•Uźjm»~Č8­ _Ūéä:^/ļ©'śg¼–Ž ·‡=Ą|„’®­VŹĻÄeB+H!§Š_ø$dljwÅņŅYiŸ(ļeGÉ© ‘GKKČĒ÷ä r˹Y|c”¬8K åņ&u™žĘlj5§;µŒŚķ ŽŽģe}°9ČÆ7Ć«C{)ÜĪq;›“Ī^aĪ4&źm„æ)ץ’HŻÅgB Ų°]x‚乎-ĪŽaz1!³ƒ®O3ō*Ŗ:ERkČ:¤Dn'ZqļŠ'{胄ĮžF‡ ŁŃG`ė„žĻü~^[÷_ü²}‰Ęƒ’Ąc<ōļlü^‰ŲF¤#W’¹ØóTo:ưŲėÜc^KI‹åßŌcŚ*cü4ėanjļ‘×8Øė‡V”2v-Ö5 ƒńngŽ3Ķ™jĒY B?®rŹ0q'wŒéŒ§7#晚ĢKö"ƒ’<&ī‘gؙō\ś*ż‘Ī Ž“Įø®¼)EJõ¼ŚI+«6Ššł­–v/g¦;Āké7 Ž#‚~!’”;Ć&¬z%­3³›’Y͌·n[ķEĪ6÷ƒ÷Ž’¤ æśÕ¼ęĪ{Ø%šŠQN/©¹jŖ_ϰyHL-Na ļņ ¹\Üi¶%˲‹™’Ģkz ]ˆ~IM§JRßČd=Ņ ūe īį“šų7lø€ŒķF; ÉŠ+ČHķę÷Óß’żū—fčt+śµ±āXol3öˁ÷ĒĻž|ō2ń–hJ>$;ĀL¬ƒ‰ČČņÜīoŠå¤-r}µ®>ŻlmĻt¦ŗå¼öž {”#–=v)Z…±a1#v)œńüŁŽ47©=ÜŲÆJrQ”/÷č97] “'tĒ1š…Sdm#‡“ķ€ukĀĪsŲéÜahźÅRvyŸ|CĶ­kF~Ć4 ēŃf-+£­:Ļćīnļ…_;,͊^†‚ žßé갇™–qUū¬P§(š²Fī 'ȑ|Eš+—R^) Õ1ŚļĘ:3°hė­‘ oR’VŠ(=äéŅmq±8D*¤ģSėiuÄĢ`m¶»­×>få¶fšøŠk ]Ž©ź6õ:ū̓A•`Œ—Ū=iĖÖKż”v\„RIēÅVŠŚ‹„ŌĀ>7„«Ķ½‹IŽ×‘ń˜½tkڧ»¤¦®“cÉ¢äWb єˆ€ßć¹ńŲ¬"Fa› ćыČH¤č’Čļ_æ?ūłūĻĶ6AG¢›Š»ØŽĘŗak°ēXz0ųøOt%neČ#d9ź5¶Ń{께›ĆÆŠE䛬ķ†ńĆJćdt]/‹?5˜öŽE[£©Ń£hKģDlUŌ l óĒx®ŪŪŽmŚiiŒPėĮNe&ŅK(›:C>%R/ØT š÷¤TZŗ 4õ9ę3[’Ÿ(—Z*ŁŌ'jR½¾ŃČXhŌLaž6ūZÉķ…vZgÆÓ×måmņ'†µ#7ڬšß{×ÜĻv3 1Ÿč_µ¤Z3ŲDµI¹ Ļ–÷Čɔ›ŹµÖKæg$±žZO¬5fa£>B{ē+ōm)9RĖhµnśv£žÅŪ3lŻńœ%vn{¾ÕÄźj-·āģĆöē¢ūŁ»ļćAS?­×Ņ5œyężOm±zQ©*ŸO ė…°łvņåų»\7NēV±eŲ×ĢD&/óv_!ś 5ŸŖBńŌ.²3“ē]躊OģĘ»ąišŪŲ˜„Æč*“ g‘įHį’–ßæ} łˆxĄ§ Ńačzō&*ay±ŲRģ>| |#Dcā0‘…\F¦„¶QUĄÄ¶0żŲZ\i¾ŠŠG<#UQ,M5k®=ŹY»č™_*T£VŃŅč^4$&Åõ«ū^ Nųs¼4nV»©y]«„ŒWšŻø;ģ@öć2cč™tfS”ŁJē§ūŃéGō f ‹ņ ˆó›rHm¤Mfij”3ž4nmMƚn%¶7ŚGķ§öPgœėūǃ„(u”'¼ä׀{³æsÓZan0.Ą<ČZõ‰2Z)¦$Uņ(‹•Ņj ńzjcˆ9Պ· ;‰uĘŲŖæO/­UTÓ+Ūåoņ%³v@›·ŚĀœn­°:œńĪ+;‰=ĄJ6Ū R= ¾šŁ} “ŽĖć&scNZkĪ7UQöIĉBQA6ņUų×ܰ¾ClsVb×3uŽŁD7„]śՇŹB=%ē’5 =łˆų*¼ įē°”ÅtZµŠ“ȤŠņū÷ߏ6"žłū_=t0$}e±XKšžk˜×=ś/Sķ‘#HŽG„ Ń½˜¼¬Ī}į¾šžXK:!·V³é¬9Ķ2x·:ÄźąQX"Z ŪļSģZÜłøó±>Q±0I€ų‡Ü"N‚U&š›œCJ{Aŗńg”W sķ¹ÉĄf{Y•Ą4f*0‰ŁĀ\)į¬8Z¦Õ”źbµ 6^7Īé‘1¬}#°ēn+·½Ģ.ädv®;˜WÄ߈QĶhq˜*,,ņ¶9ž½ÅLj–7:źC“ėj] łEJi„¦r¦Æ­ÖPgœ61ū…uĀjc„5?ėŽ>H«®W“Ø”ĀŹpå‡2C-«eŃ{gĶŠ¾ģw«ŗõoÖeó³œ9ƌ¬GÖqū€3ŲżįŽpćŻyNū¤YCŸ¦īRL„•ōIh(Āb¾,Ųēx.w™ķÅ&ćµ+“Œ¹@’ äł¦Æ„SĒČAd>š¦ D{"5qŸ‰WĮe Ļ^Xfģ1:­ŠŖčQdRšæå÷óļ·“ Č9ä-b”9ŠŚčtzűŒXCl vSšrųdü!ž;1…ųA“"ļ“-Ø/Ō$ŗ ó9Ā.įfCƒ>†=ŖtŌR“²ß9©¼R~³ OŲ?*cćNÄe‹Ļ_0ī[“>t÷’ō–»‡ķīęVŻŃU‘ĻH9Ąneń‰šBø-ņ ņ­9‘C¹f'ĢėɹÕ&Ś2m†Ęé½õJa$/Œž&mõµ^XUķ'viēš3Ü]īżķæ |čl=ŖĪ Vy#œĻÖvs£1Ș£_вk»Ō:*®ĪPŅ)˜z\ uŠū§Ģ¤V]«·ÕŁŚf67źźS“jyu·2C©ŖųŹKp÷UźJķØĪÜĘ9 ī%gÓślT22EŒßĶ“–`±k;›œĖĪJg¤]Ļ*jdŌZ)#d^¾)^ «ų@ćøœÜmv(›™½ÉŒdņ0/ĮŒŖŅ}€źEe‡±‚lę~‡˜EŌ$Ų~#šb8 ōŁK‰ŻB'£åQ=ˆ @ņ’'æŸ?±9ƒ¼Ft4+Zķ.DO”_ŃTXM0ųĆ`…š!ų<9яøK”‡/@§Ņ³ƒéĻÖąųņĄ ū¤Œ°éŪ鎹ÅZčTń²łÅƒ~įĮčZli\ų!ń•ā7Ēu‹ÅEƃG¾į;އ° 0©ŻZLKÆśźUå®ÜRĪ'W’{əå,ņ:)¹4AÜ <ŗHžr_£O6.ƍ?Œ°ūv/?ĢĄóĒ­dv?ū½Äį]Ń«ģÖ…Ė¢eQ®ØSųŒ%¹sÅjö6cžNé=5UŪ¢¶R x©„~UGk zhœ2Š™żžu& YĒØ¬Ö9Ægę2Õ•9L÷§óП©T'*Lß°§LäbсČH¼ĀWā-ńų=lV³°SčP“Š”»€7óž'æĀHm¤+į*äņŃ“h9“: Żļc%°ž@ O°äx3Ų„ßa.'²łšlOż€ĶĒ|`vŪåg ĒDW¬øŚż¢łÄ¾ģ~÷r½B6–<_)~b<_1ī}47,²OåvÆ8„ķOę6c >Y«·Õkė}ōśv}‡žĢ8aü0‚•M6V›s¬Īö+{‚3Ć żv; l«;ÖgČ®Š=ĘŽéIĪn8fųUĆ ŃŽčUt$źŻ »ĶsŠŁ/Ģ÷ĘŲ™Eõ+Śp`“Z­©Ö]k¢żPÓFh·“Bś=»‘zy>TK®NR:+¹Ō ź Ū²«“SĪ)­Ō*Zż1μĒBk­°:M¶Ąwhõ.ąóŒÖś2½’Ž޳GN/mNó…ł<<Ļoåšp&wķĪfd1s˜šŒĶœ£ĒŠeh‘>N JQ2ušüł;`‡ü›˜FŌwøŽOĒką~‡•Åģ ŚĶ‹~Sļäł'æŸēÖśē濾m‹ŽG·£Ą ņCó.Ä®c.^¾ŚC<1†xET‡( Z—Ę˜µL'¶8—…Ļ.T'Hļä®jjhŠlvœ;ÉŪē›a«čhģ|\‹ų;ńcāŸÅµK»ž ^śiżžę•uK8™ģ Ö:sYŒ8‹ŁŽün>3O˜·ĢŹ–og“KŪKģŠĪw'¹{Įżā¾p'¹aūtrbĪū}̾k3N#§—“̽äęņ?ć"'v-:żŒN‡3Żoę±Mė•ŃÜxŖWÓßkė!Į–ŚnŌćō»ZgMÓŽ©ŸÕź±W˜ÉLUHļ"=‰®Öw š¬•ŒzB®";’9ČļÄb QŒąˆ£ųppw?Œ “ưhw “Č&čĖÜæņū÷ł×Įč—!‡‘ū‰&A‹¢ĶŃQčōŹaٰ&@0§1/ š Gt"NƒCĢ!]j0č~ŗ-“’żĄ^ęĪš÷[j"_Uzi5ŒŚ`×\Ēo\ SĘ~Óaū•’-¾F\ʘIaŚ ­ÜĖāMqI7ĮYi·¶ Ś—¬}°A,»·}Ź®g×µßŲÓĄšø›Ü›^?’²æ×_é·ń‹ūē¼²Ž17Į=ē uZ98KĀżą¦ōŽyéƒĄKó£ƒ0;£mщp^ŠŪæė²N ė8äwEÆ®#ś m³v]ĖŖŸ€½÷MO£׎«yÕ"j õ„š „ŌrkŌzjFµ›zV}oķ -śH髤šiŖ…¾M¬§×ņ«¼ŅH,2Ūt׎j»“̦=Q–Éw„xÉŸń$ߕ+ÄŁÜ]v9Ū‘ĶĮ~cö€g$ę,¤W‹NL?ÓķDå¢0ź0ģ¾*d귘hMd"Žć›agåĀæbŪ°īXNģ#$ŅĶ„¾AÖüåś'æ=ž”#żärAĮļ €FW"X ˜‘Ų^ģ lĄĪų6œ&ŖChdwņ0čj†¹É,`{s­łĪĀńŖT@9ÆN×› ķĄĶåMņ›…=£1±ģqgāžÄMŒććĀX–({Ų,Xź“°øŻ|ī-g…³ŹŁęŒrŽŚėķ"@q§œ5ĪA§ƒē½zī'„ĀaŹšhp$(ó³łµ¼é?Ļv(·˜ūĪżÓsüłžü ^(E=¢ķŃžh%ÄöpPŠČǼIN«“ŁČ8¤'č“S`ćŗ>[okt4mS2®C‡6^9¦¤Q§ĄV\ØöUŪĮkļŌBZ}­7¼÷¬Z\M­~QŽ«¬†kÉ“·źRõ›œųēRŅHy”ŅGż®śŚN Ö?”|ņļŅ&Q}Aę³p$wx„6›žEŲ“Ą,M˜ŒĢ7ś 4g ś7ś9l¾¾T Ź ®“ ÉvdN'BĆU%Bā6¾vVjü9¶kƒ„Ć^@"­Š4č d5Ņ ÉłO~?T iFæŁ‹ÜD¾!`-“/ŗ=~@AóöÅÖĮL‚×ĒēćĻĮ!ž"^ČŻdNj;UŠ~BOb*±I9žg„˜XQZ"§RohGŒVv§Ÿ»Ż»å ūF«c—cTܓX½XÉXÖčpIšŲ/čļšźzy¼\Ä·ø7Ē›ķts@7]ŅļVv{»Ć¼µž>?gŠ!¬MŒ ĮFVó‡]ƒ˜ŸĖ[åvp»ø·ÜŽą*“üŌAėĄ‡…£÷ыčÆ(U4 >.ō÷øYV.³Ŗ1_O ūo“¶O£õéśE£…™ŚüĶČ ūNŠŖ…ŌƒJvµ¤ŖØŗŚH=ÆÓ€µO„†Ż¢VVi)”o·j™ą#{ØK•įryØ|]n¬ąõ9õõ‰’Gy/Żˉw…B¾;‡qSŲ’¬ĢŽf¶0c™fĢļŒĀܦ׊żč²tŒ~Fm„S•ØDŌsr+ł'YŒČ‡0݈CœĄĒĮĪ šŲl¬öv6F“£aÓu@rüŹļߏ’k‹ Eę#»~=»¦fC«Ī@÷£ĻP+„uÄęcW0/ƒĀĻćńDGā8‘†œ?˜ŅčÅt†d³‹¹©ü`]ī«Ä“ś%s®}ĢŁē^ńō N8. bµb~¬},clAt2DŒĮó7{ū¼E^G/š¾yĖżÖ¾ąńšz¼A^Uń6»ˆ/T°Ńļ<[D3¢™Q³čCø&”Ā5žH˜Ž=īe7»—Éæź‡Į?yŠ/ø.ŒŲhZ¤DĀĒ~ ļoē ßzchąļßµŚm¶UĖ­ßՇB§®ÕKé,g2­‹ś^Ł«“UR( ”Āj m„6G+”U’R‡Øå“4śj½“>Zū R%µ‚*j:+Ļ”Ņjµ”ŗVy*ߓvˆū„”ą®'ų‡Ü_Ü[¶<ģ»)L¦$“Ša˜»ōvzݜĪKėō=j5„ŖN„¢¾ML&›’YHŒ²i‡d’'柏歌“F’züķĻg·ÕŠ @ Š č6ō*bŁĮ=&bG0ŗø+4(IT$–4Ł‚ü›¬@]¤šŅ4³¦—–O#{Hå*”_2¶X/lܵ¼ ~ |ø/"cUc­cķbIc¢FaŸ`ƟÓī=ö’ūma£5šžCŪÖóóų©ü”~¼OųǼ§^m’ƒĮ®üä ކ5£MшØ[t;ü(ĮuHńo²7ĘOD`• żAΰRt ڵˆŽ†“‚Ź~ w¼o„6«ūõü°ū:hŁ“8ų×K»é_5GŸ§£z6-ŹĀ WĖ ålź=-‹^A愃Ÿ¢ī¼}}%|ę,5—zMŖTPŹϼSŖŖCÕjźwå¤,˶ōó:.Ēł |J~'q+ŁĒLk&5C0÷é#ō zݚ.N'„Ąż¶‚é„…Ķwŗ³+™@ŗä0‡¾DIŠā›ų"¼-ž ’»ėO¬$&cg)«”!zY„“A²ż“ßĻĒæWDZ"‘™Čę’ž -¶DG¢kŃĖ(ЄƪĀW،½Ą’į š¹ų#< 1˜øEä'—’xg2zŻ„ Łūģ^n=æKx ¦•')‰µzcó•Ė)ģÖöŗłƒ§!»“ćFÅnYœ >łõ}Źæļ­šJx3ŻcšqǽN@)O!±Ļžy‹æŽæä3 ņ/x”ogĆĮŃpȉ԰dŠŅßčå÷Ņz㼾€ Ō`R †}Ā%aūšl°Ąw<ŹÉ6}S’[”ĒōłZķ¢:A .PŸƒ9Äk‰µš­[śEUU€węRK*Օõj[=|ÖpV,%‰ŖĄŌ6ŃPm©š ¾SÖ(żĮ'(Ē•Fź}0ÕVź(e”¼MŹ U9qœ°…/Ą d‹ĆģÕc¾ŅūčŁō`ŗŠf^ ‚¾Kķ”fRŻØŠTŠ ®«ÉAd525ł zmvV#Žįš:x2Ų~ė^ņa$v)ŚčuhŹVHÖ_łżėś!ę’¹žÄS„łuż‰†č t1lĄwh +ŽuĮcW”AKįCńćøE4"¶6Xą²!õźJ;ĢAf[•ĖĆēŹ‹ƒ¤ĖrQuÆVĢøl¦°;żŻŻžŌ‚ ęĘ6Ē.ĘĪÄģX,śųA/_õwyķ<ĪŪąöu»¼ę~Źąa8dĆĮ aP8H²·ü ~EHš(L«{%‰Ņ„uƒw~r?³÷ÅĶé]ņśūēü üķöoCžBšŃļä§óö8¾ŁŁ-Ó'ĀžšØ‘Zgõ›2UYưjuÜ’?ŌÄŚ#hɖj-NĻ®_Šiō‹:olÓ©1åƒ<^)ÆöūõœŽ™Õ»Ź\„“RGi،PN(Ø 5l2Vn*_–ŚK›Å ā¾ [,AföŠCčŗta:=Ņż–ŗ Ł-€ŽlF£’Āģ]#7’£ČĘd.R&ok‰D"Žxģ9/ŪųM°·ÖXģ ŗ)jčehŹH–ņūyżžrHS¤ĻÆėæG 8CGk¢=љč^ōŖa¹±fŲ$ģ0ö“¾’€$ĘωŅpŪ©ØÅT&č†vLRö9{ˆŪxKŒÉ”æÕŗś£®ÕÖ>įČ^-BP,ĢĶ‹^Gxlet#|üŒõł¼V^ä]…M6ßu<˟œøvN¹Ģ F½ƒB†äėGócæÅuŠUJ‡ķƒ™ž`o·ŪŽ=äę÷īx׿'žAæŒ/ū7¼%^}ĻöźøæģJÖ1£§Ž\’~VQ?¢}Q ŖŻ”ŻrCų] Ķ=@—SŌęź4UÓ$ż•ŃŪ$Ģ»F³®Ń_ß;±²]~#gVR) ”ƒŹr0ĮBJ2%¦dUŗ(÷”@žĄ7(qŠ“ˆŅT~)öp˜ń°ēŖŃ9Ąšpśuƒ:Eķ¦VRS”5[Rå©l”ūė±łĮŁ[“EĄŽ‰)D "7Į—=Ūćæć4læ)X},%ö Ż„öB £z™4C2’ŹOƒür"e‘Ęæ®æ“ š؋fE+‚kŒ‡Ļ¹†’XZ¬4čFģ1Ć«Į\_†iCģ#āČäk°ˆēT/Śg1ي\V>_l!­”9u öóļ2,ĖéįöńfśsƒįµppųWX1TĆŁAĢö‹ų¼”^Fó^»I½ ^ä ^„“£ö@)†^x)øÜ KžI“6V3īblC4!ģŌöSy—É-ć®s«x½?ü§ŽbÆ“÷fy·sĢNfėö6ė…y¼a >Sß©ÆŌ×@{āj.%‰¼Sz*ßV†©‡Ō•ź0µƒŗ\ĶÆ5ÕWwLĖśÓZfN0ėc“9J;¹œĢĖ)äEr¼RDI­üÆÉ‡åCņy¹€ņ\IÓņi…“6ŚuŽZX=.®ńmŁ|ōaj-$¶(eś8˜¹žTkŖ6ŲBV*FŃŌ3ņ4¹ŅkG–"““y‘X»Æ‘„x‡ļĆĒāuĮ>b{°įX%,†=@W¢Ń<(žB¦"ML’ä÷ļėŸuGžƒß\G>":š-‰¶@‡”ĖŃÓą1,ė„ĶĆĪc,žļŽoĮąÅ‰©Ä¢<¹…L˜Œ¦ģĄ[ģFn?OŲ)¾‘ņ+kÕTśX抙ÉnķLv7x[ż‘Aß Sš ęä6ō$¬ņkś’?B÷ā¼ņŽIƦ_1X 9­‰ŹG÷ĀŹĄ©Ā^ał0ž{,ÜŁqĖā²Ę„‹}÷£üä^NwsĒIįNv#ƚWÉū >éĆÜ嵏™£ĶļfY+%›7uLO¤'V¹ÆnQ¾Ė‹eFåŁņIČoŗŚUĶ¢&WsØ“¦é©«Ę=Ó³ŗļ0zu…,JKÄGāXi°¼G>%/ghęPWn$–/+ēŌĮ¢·Ö«ź]õēje„†\L\ŕcS/Č T9Ŗ.UŖEU¦ŹPE©¶667®F|ĻųŠqĶ£<ᐠ›÷ĶÉą<“§©óŅAÜd®ģ¶sRŁ“ĶŹĘļśsm—~Ģhg1ŽisŌ`w%”Ur‚¬ČdhČäŽņi9¦äU+ŽŅ^ɤVŅ:éżōćZQ˜ØZ•P©°ˆˆ…Ämb}é$vJš'õJi# ‚¹œ'…Ji­¤ž ¦ū¶J+¹„÷|ī£Ņ<µvŪ.ņ(y†{¦8UŃ)a×¶³£ŽH[«ÖU iƒõźFZc‘fØÓWé%g‘+Ƚä™ry‡“Tŗ)ÅĖu Č{eV™ ōP+hļŌŖ§%ѦåŌ•§C~‡…÷B;1^š ŸŃUJ)}/ˆūÅJā1D(SÕ Ś@­µź)ł„ÜBM.;›īC­"‡ !G“Č©ä r&¼L#'‘cąm½ÉdC²™öžD¾&΁õ…{3HÓwķ<Śó ¶ ģ‘ { ō9&* ś:rĢ[ŗ_łż×õĖŪžŗžõ ˜[Č'pųŌhQ“4čltze±tX¬/¶»Xļ|ū(t qČK΁–H‘ōX:5sŽĮVŠI%äėJ³äJEm§~Öaf¶FYš½Ķīā$rŪĮv÷^xƒ½/½÷νčvŗ—Ż—ī;·„wĄŸ¦¾!:]Vcu¢.įČP­Š3āŪĘĻĻ’=–;dżź®ķšöX«…ÕʚjM±FX‰­ĒĘJ½„–M-ÆLŹøÓŃSÆÆ'h¶šV(? ¾Jgä?ä”RR‰“rI=¤mŅGčŅ|rFy°¬+½”AŹDå²RYķ ō”;JmÄ B”¼PWĢ$M“fIßÅIbc± Ģä[a–˜^.ŖäRĒA+/“sKC…AÜu¦<=„ŚLYŗ±B!į&’€/'ÜJ‰cÅ0[ ¤$%݁ЋĻĶ·žóŸøgl"6)W_#¬Ē‹·„bĀ5~;|.!$J›& ju„”|Eœ$ēāŲOTUrђĒõ^˜¬ŪÄ ā AA–&é‘X^™źĄ›ßnG”9§}ˆDa œø½6o†ēÅ ü¶ ī—#± č|“0„ŽŽAÖ!ƒjHźņū÷óē4FŗAÆĪC6ü‡|E hŠĀ°1{ “Į/”Ÿa €Ē’…ķĄža^‚ļĒ)¢1™xB"ēC‡ö£~PC舣Ćtfsp’† <“ ]¦ułužņUs­µÄ¦Żķ0sC ³ĮītwĶX·›[Ć-»ń„{ĖK$ ‰(GT)JłŃ—°dT!V:~d¢ž‰Äx2–4ģģqoŲłLK¦ÖWUWQUW Ć®ŗ'¬"a#Ÿ–æĢŸ®‹„²J;uŚQ¹&g /ųzü®*Ēq帐O"œŻęˆ āBѐfˆį3»q™ŲWt)ęó”ķÅÖd*3MŁ…Ü+¾©š’ŸČ§äws9žÉÕąSĶ%[9+O”– ³ø4ģ#§2ƒšF8 mŲZqĀIā qŸxN¼%>A–_ˆą]!»ÓÄnx’$HÆ1°`jB$žā‡ńy@‡•ń48ģ¹ėī—ūŒC§üŽŠču°ōžH$ÕÆüžõüUyĄą ‘”ČLH÷08ą[hŠÄh.“Śf1z&Æ€u<…żĄŅCGOĆ’Ę8vVtĘn255›Š£—Ń™GĢ ¶—…·ZÄ$LάtRļjė}Œ3“<»”SŚįrū¹-Ü1īYw½;^/č’īc‡‚\;{hp283X¦ooHEQljÜ£x3ŃĘøāQŚą8zW§5TGÕ±ņ#é/iŖōFL$®j ēłv¼Č§ę»ń1įŖpH“䞏Ne#“įrį Ońł¹Jl f‰YČ®ä¦óy„łĀ9į’öąm$kÖpUw‹-[²Ģf¦ 3gĀĢĢĢĢĢĢč$ó„aĀĢĢĢĢVsw}GJꎯ»»’ļzäȶ¤Øė¼‡$ŁzĖžß:;tŪhk{tī½¶’ö öĻ6Æż¶ż”£Æs­s²³&p_äķøźhąlćlč wŸ]jswwåvųģ߬,Ū‘:š"Az…²B;a¤0OX'ģN€i7…{Ā#¬ūąå%`·[Xƒü0čUņ ‘‚Čßįwó³ų.|9>’‰ģ™Äõć*s ÜWd¹“=-ˆö~-}ō2ŪoüļW ¾éK&’%d;9Iī4mćOd˜t x=>–+>OęvqϐbJóż””ŸųÜšŽÓČ”ż-÷-¬‡¬ÅmēmmģĒaĒhg]Wtīō”eĀęxīzšĒ¾•ƒ"gE­ŽŽ#Ēm;+ö<ūēŠĘ^‰©s+zqōø_\ē1”sāóDb\b÷Äį©F§ö¦Ķš¶{šN©FĘ?‰{[7ölōøČŽ ”µÜĒC„P·Ć=3ÄŅյ¹ÉyĄłÉYÉuĢÕ$d’ū|hJh†Š!Ó\7œ=œÆms­,Æ,mIöĒŽ~®±!ķBĪ»]O[ķ5l/-µ--ZUIŒ-̶ĆVČ~Åļ0ķyU>Ē9{{{zLJīā<ä ¼³LEWsGˆ}‡µ‹„ŠP”ĻĶ7ę7ńÆłH”ØŠPč)ŒA/_%l^‡„#ĀQōō}Āva-˜7I€QM(ē3łgü1> Ž×Ļ‹ę÷’;õģĄƒū½ĀžO„Ķin“÷[šøįŠĖ¬Aüžżž›Ķ‘@Ē’d#ņĶ5(ØJ<432LĆßļ sš¾¢—‰ū“ė‰sœūĀ%ņ5łqh‚:_ ÷䮐Ć2ÉņŁŅÄzŻZ×öĢ6ĞÖqŪ±ŲŁÕU#¤œ»^蘰=žS^ā»ź;q5Ņ=;šE—ˆÉ»®·4vgģ³ŲC±­b·Å,Œž•=Ź]:öTÜŻų# õš'|K˜’ų ±:ÅĪŌŻSNœÆĘ¶M‰‰Šé9-¼XŲrwg÷(w/šoIČCWøkŒ3‡33·³±sžó«sØkeœŠWʹǑĖŃĘ>ĪZĒZˆzXņŁTūng_×*×lg6Ē[¢µŽå‘ą°T³“pÖÖŅh¶ ¶É¶³¶‡¶ ö‚ö2ööšv}0šīq»ĖawŚ]#y÷lż¬1“oėArģÄ/į/ņ~仂B čSaœ0 >“„Žž,¬„ćͦ€y=„fč ł…xŽņųĶč}īSó*wIc ך'p÷‘^Ę@ ³P ī¶NW›dłßÆ÷歂DӝŒ$³ńÓ½äō'±ÓÄąū…·….D ½NæQŗd}n(—Ģ]ęT.+ß|æµÆ.,¾ ąB‹néb}amgūn›jĻēxėŲāćźŅŁ="tQŲĻYÆł"²”5+ŗzLśŲ^ŠŃC±ŠžŞŒi]'ņČCQŪ¢µ˜üqwćĢø³qėćļ'|Nœš2”tⵄ½ń✱ c¾F'Fč>Ō38¬uX RäV÷¾­®cĪ8ēG/GKG;GGgĒeG¢Óć\č°;6bߓmė¬[-ń2—žļ/Ō°¾±…9 G/gMĒ[~ėĮą·Į™>CĒ< ³tµŽĮL–°²­±=µ}²¹ģ¦ķ-ɖÓVŪVN±Ü>œ-eyĮGń^>-ŌÆve?²ÉĒŹBS” ˜6©fÖa“0Dč\ė å…<@O>ņ×  ų|}>Šģr’[ĘõåŖré8…^„kč`Z¦„~`³’ $5Ię ~æŽŗ©„ŃøN#+ą'Éņ˜Ä|«ś“HWÓ#ōRL4“%7Ķä.Ēć’jĒ/ęoń>(ÅJd­ č‚6kėgkO›n[h/éš;;ŗĘ‡LpĻŻöĢS!¼µÆ_D|ä²HgTÉØ:ѽcbcGĒĪG}‹$š*v^“/źuDŃČRQ7¢JÄ“ŠuĒ=ˆż»5®Z|µųJń…ćwÅ]Ž}CcZE߈śŁ6¢gx ļkĻ9ō¶ęaƒCŻī‘®fĪ)Žoömö1öövö öY¶ŽĮß»Īiū~mécš«1}Y…¾–™ÖAŠĒŽö<öŪNĖ&a(öv•0H(%„ µ…Ē˜É hŻåą 9m}mmŪm{mĖlUmW­/,G-’5›-Źę³^fš~ī÷ ŖT’oĖO„‚^äßó‚o+'ŌšĆēŗAM{ Ż…NB+ģןBq!»ō>cņ+ųŃ|¾,Ÿ†×¹{ÜNn:מ+ÉEqŸé)8X/Z‘ĘŃÆä4YFś‘ź$Óoü~½’{8b{$˜‰dٽJ^?q€łheŚ’¢³éōˆW”ēŅ£ vĀ­ļꞠIß®äbŅZėæPÕ²Įā¶±ž°ö³q˜ĶŠę8ļ\ēZ²Š½ m°÷Vø1<āq„'²Ld”ØgѷჃĄĀ±›bsʖŽŁÕ=rTÄ䈤ČQ—£ļĒ|Œ™“c‰ķū(ĒĶŽé­D Žŗé‹|ć›~É;ÉŪĪkxĘ£…=v· ™čühļf?o›e[ĘDŪ²X{[ĘAŪ{ ‹½¾ ”B~¦ŪĘįgM,‚u’õ–õ¼µƒu;r}ąq¬<ĀO¾ŸŸĪē#o¤±L¶d±Ž¶°mĘ-޲U“¶^CÆ[bI“³~³<ó+¹%Ąļ ÉE; ';Ģßē@›…œB1”"”“PkˆĻ5ĮɒĄ5ƒ!P w—?ŹÆ…vvēkĄ=C‘€ĪBįF@=órNī=€Żļ&EŠ÷äź æĖÄĻü2’üh€uHk|4™C’ƒļįx†‘pš )“öļwąÜ &BĶ ^÷@“8ȽDŽ)Į÷@ź}Ā'bŖ6 ’P īļ¶µž“ö·ńöö*Žy͹õ&d›ūahEĻcoFß=_žˆ&›#ęGfŠ® mŪI“y¬Ó8&_4‰z‘%¢WD—ČTQW¢JDū¢ŸE݊rF÷ŠĪ“?zBTdŌR ×"āœ/)Ü>Į›×{ßSŌ3-LėėN‹4f“µ~·²Mµv¶ĢŽó6įæóݚ/Ģoä c²gpV~Ÿ Y"œLĀ-,…%B?į? ]i4÷ˆėƒ†[OX5mfÉm-`ėö°m±&ZyĖĢźV`˜ßr”Œ¦½”;Ķ}ā¼|A$ü|~œå/ĮX!f¢0zi¬8—GČ ŻtC­?½ćüF(näžbHž:÷€Ū‡ŻķÉUį2p’ćvč_K¤O7}E‘y¤ü.Ćoü¢q./)…Ś8 ŗœl#GÉuņ “Ńxš‹–£iOÜĘJzŽ¦ßi\µ&ׇ[€†ł÷ø$ßóó]¢eƒU,ė,Nė@ėWko³-µW‚7œ{]{Cīŗ3‡-÷|šFśł¦ųü¾³ŽØ1Ń#b ÄV‹e1bÖÄōŒ¹ż<źPäöˆO>ÉW?ā`„!DvĢy2byÄŲČŗQe£.D¶ŽÜQ—hź‹š=õ–ņnóTō¼ ė¶)ō°{†«”ćØm¬õ”剄¼µ±µ”„™0Š?ĄĶć†A7²p<7Īƒ£<¦ł¹Å‡¶|–O… ½;›źy›_… }ƒć,Š xŸŲw|m¤Ēž–¾Š•ŪJė=‹„¼vPøƒpV8Ž“q~²™ą v#_ū1;r˜æĮæ‚Vhq‚˜e²˓țč¦Čæåo½Ķü<~(&«Ÿ…wņøóÜzn× Ł3šūN/¢»  €ƒķo™EŗĀļŅ’Ę/ФCƒ/F6„‚.$ėq©s`ąGb0šŽžÓż:né(½Oż4Ģ® ]D0Œ/ŠĘMą_„²–Čo=­o¬l¢m®½2Ā ēQ×¹ī,aDzt²ų,n PAČ"! æʹšŪø ’ ćsńUłŽš²Åüvž:Ż>…g¼ŗ ć|@Ī%š‚ŹAcøå܌Ü2רä#‘«īsū±Æż°æ9”žÆé1ø_?¤—Ģ”‘{d'™N:ĮļŅńs’H’ ¾(©Lvč€c€ļJ“ĄchĻÉōĄH\³­IŪŃ!tŻ@ćŲ$ź "ąą!īēBjīˆI½É{ąś …WB!Ė‹ßŅĘzĻŚim¼½€Ct\qvż ³‡Åy Ļ"oššSį›}O#žG^‰ż.zKō›h9¦iģ˘ŃC¢®D“ōŻńś¼<^o{ļÆ/\ó.šdōø½+¼ė½…¼ļqū<‹==©äzn^ĆVÄZŪ²\(" į»A›Śó‡¹5t,)CVįųó‘ģūČ\¤Žx9N¾ćō ņŁ*Ś…f¤héC:x£-¹9ÜZąsš+…éÜ/ģf ™Ÿęǃ3µy;µćŗ@ķ6ps·€Ÿ›Ļ µ„‚NĒDļįĻ€a/Š’ż¼Š<ŹpRqž3’Šæ}=„{·Xwåė"I¤ęyž ¼o7Ł„—źyŸīĮ®w„hj*“d+™L:r@ķ~$5ɎQ°”u™B£#īƒŽ%o‰„‡#*EėĀC‡Ń¹t=D0œĖĆՀJĻįöĀ)8oK~™«¼0Yø-d±ŒFGüµųJ¶;¶~ö ŽwŽcĪ­®ķ!‡Ü·B/…ĶšÄ{÷zg„Ļšm‹Ų9**$zd“-f[̆Ųū1–˜ĆQ+"_łź†_õ„{:†[ėłźiäiÖ “{čĶP%ōkØßķ–9ģrč¶ŠA”žŠīÕ!oœ„ķ/­kNKMa5Ołd$å#ÜE®1לę$ŪŁ7–Ā’XöŚüjʦ›„bY#v‰eCsšH’o¬›Ćt6GŻ­+3ŚJÖsz—ėĢ_å?@oKņńŠĢ×ÜōėŖ\,.1Ž[ż¼’³ó鑟cnFĆ×óūĄĮkü`ų׿€Lśš ģ®š'€ī:LĮhWC¾Ÿ‰įæcö‚}¹Üœźyū=¶”Åi4żA.™ń¤-&1M?ń‘Th€ ĮŌ$Ķ‚ :ŽĢ 2š¹B!ÅčÄMSż~ųĪPā_>€…qŁŃē;sS¹mÜ ōĮ ˜¢qHĀ)|~ųÉ !ĀŅŻrÅRĺ͚ŪvÄÖĀęøéŲąœåš²Č½(“Võģņ4ņÖŸā›1,26ź{‰³)fPL÷čŃQs#ŪG\ Ÿźč‘ĀüaŁ<Ł<}Ƭ”OB‡ōYr6ÄåīįĪŗŅ}$ä««›«‘«—‹sķr\·]²\¾ńČUÜm°ź$żI»ĀϾĮé×°n¬8»gv5ḈĘ%ć†ńĢx`„›ĶM;kÉ:±ül“™ŻŒ4wš#XV¤øōę’i`Ngr»øwče“¹Z˜\'|4…Ā’ Ń?ĄĄ©ÜF0ēGm~žOĢr_~æķBĪ= ¼nBKļ )\ļN nEnŸĶzŠsš>ĢÄSīŲ>‘ėČUā2sVīv: ¬iD PżN­AĘl…¼’ś7~į$ 0?¾S°éƒ? \ū |}N¾‚릔y‘cĮįP– šĮ;ō+up‘ŌZ#«%sgøĻ\īI_~’‰ °YŠ…ŗ–ŻHŁs¬>Ūj[ūwū.ĒgwWūīģ”ĖBū…UńdšF…gńUŠØevżŠnŻ5*9ņ]DhD߯ջ˳ĶÓĒs?ģkčxwڐ[ĪFĪĪÕΣΓ®ž®®kĪ&ŽŠö¶•öŒö\¶›–ÅĀfØŠ)zÓwƒ\"ßI}Mŗ×¬ gĆLĪa„ūõ~z_}—ī5:»X³½ŁĶ,i>4ŖƒėĘas4ćHS2Ó| ×’.éąÖ#ߢėiSč‘Bö’æˆB\ųY8¦ø?²ŠAō6?ēA )ĮׁjDœŻ‚n~Nwė(ę{7¾“Œģ;•ålė‡vÅš„‹ōrÖš½x­`~öŅł“Ü+uą(NĄ¹GęČ+©‚ųى—Ä£Aä!őiź’–¤†ĪĮå¶"«žƒĻæ"? G½h’ė£K „3‘ߊkōåø¤ńśšŪłH½ĶƒŁ›‹³¢åĢž Xę[ Ko¤ł‘¶DūūdGg1Wŗ=d®»Ah’°ĻaĻKo„ÆaDdä§ČåQ4ŗYTėČŻŁ"Fū‡?F³[čyįYę)j†Œq„uŠ˜ƒöiö)övź8aÆmO±¾²t±”³ fņøi\n4ŻAlä;Į6³El2+Āl,++dn6² u] Ej÷ÕŁŚZż0liL7ęCJFy£¤±ŃųĆ|jve=XuV‰„f>(ÖGb„ČyAg“,DdŸ”Ä>ųP RŻš“kĮMĻ£R>x”…†v@'ĖĻD’Y%Ż ĘmAÆ_‹nøī8?ķĄ7zyĄY ’…»,±’ U®Ģeć\Ü'z‰nFĘź„īžrō)šłR2I³IüŸ‡Ä¢Aä"EHy(hxc€SŃ×`¾Ž‹Č›ĖĢę,†Ģ`*1«yŅØlŠ«čßµ5ZMS·©KŌiŖ(•H«zJm¤ÖBō|zq½Ø^XoŖŸ×K“Œ¹Ęjc£Š3ųJ§a–÷–¶ÖwÖ”¶xūū0G'ēśźÜĒÜ»B'†5š¤ńžõ– ļ¾/¼€Æ€Ļ^Ģ[γ-ģshźP›»NČ ×3§Ė™ĮQÄ^ŪÖŚ:ÅrHh/ԭц“yŸšūL6²«Ųݤ“–F&#a¼Ņ‰žU›¢Ęا•Jˆ²]ž!”+IŻÄĪŅ y£ŅP}­VÕ¦i[“]Śm‘¶D+Æ7;pŚdL€¢&=Ķfus¹qʘc 3ŗ'įęV z–@»Ń­ō%śT 4‰ P ć`ŅOäŠx`SœÆ6‡vfŻqźŒó-‘7k€Ÿį”1Pī9w Ü]“܍«&Äs ·y^;¦*Ü/éåŁ‚ö׃ŌFތ āg~QČ2YH^tĄņ¤:2Lxजihņ«1aūČI xŸ¼F0ˆƒFŃōpĀŅšŌ“Ų=›&Će/ŅgČin.WškĘ Ež>‰4ĆWĘäą¾“0Sxƒ6½Ć’ÉŗŽś‡ķ*EZäф΅®A!ÕÜBY膰Užļxļlļ3ļNogoOϜ°Ą+†źŗė†“uMrŖŽāŽvö™¶‹V‹µ¼%IČ,<ƒJ÷ĆN­F,Nė“Įl“łŹhk¬Ń×k鵌Z3­©6Zķ©,’ĆåŅ"©““P+ĪۊŪÄiҹ—Æ.U%5—VN« ŅB“¼Z~żžŪ°Äh?ĢgDŌC†Ż¼4{½&˜«ĘIs!+†ż“ŃŚčggpģiį!]ƒ9ōrĄĪŹGó™łh•łš@±!×’Ö¦ełĀ@7-2§Ą’z—ŃE’įŪżŠEŹ"yŗŃnĆ–!ļ·‚g„§ŅĖi¤ŹI¤+©¼ū?7|"L.p²4©6ƒö"ƒÉX2949ˆą `—¼ Ÿ‰ ' ƒg„iytĀ6“7”tņĢ>zŠB]A6ć†Į‹Ļ Ń¦F›Š^čšŪ¼š„„•õ½uø-)¤Ÿ£Ŗ3Ńņ.†3Į¾sąõ,ډ±0øNž£dœ­#©¶ÅüĘ/ 0ž¤G‡/€ ZŲ6Dæč •J&™æÜKŽ‘ äy żŽFh£>ģXvÜr`ŲG0™t%“ō4Óg*p‰šąFÜ`ģīiĢbz¾1œū.Ÿy梐˲Ģa]dĶh;lkkĻčˆtw.på 1BN»;‡¦»z8ōÆŠc”ķB/øÕŅ!-\=œcēģŁķml«­=­ŖÅgi!‚ślāŠs×”O [Ļźš«zz=öIi.ē.)Ķļō—KɛŅ!åQŹp’5Vqµ˜_:-µ‘©2OńؽÕ]ź]õ©ś@ż¢~TŖ.-Jū®Õīh3“‘ŚZMŅTż©ĪōŖąc_£›ŃÅøm“2Ms&sa—ĪņĄŗŸ¾¦”\®!üc.·¼Ļ}ä4čh¼0=īkv –ĻŹg¦Ń|(Ļó2÷Z{Ü[ėŒą:qµ¹¢\:4•Æhg‡°ŸćįRUѽĆį~wɲœŒBĒ«‚¾ÄĻB\h€±hóYid˜?ĮĄ&ČĖ]I?2ü7‚«Č&² Z:z—<#ļį…Rž½>+ż;&mD'ŃEČ4‡čeś‚ŖHey0‘}‚Ļ×C»oĘ/ć_šy„‰PŅZ–S–ņÖ+Öv6Æż”}¬ć§ÅuČÕ.Dp7pp×w×qēsæ éņŁE\ŠšņŲūŪ®Y]Ö喒–kB[į0Éwē>€żþ˜õ̽FøŃA_£U×)…ä(é‹ßć_āJ9žÓ—r0„¦’‘æ”xXĢ)m“ŠŹēä:ŹU„ˆ:A=¢ŽQŸ«ŸŌ·ź5u·zhŽ޲§}Q8ā-•®hĄĆœF}ššL'›Ąü¬ķ ÉIŪćøļ6x…IsøĒ| x‚»…>’ƒ3 ¤n>÷7 'Ę;xŽWĄ¼—h gŃŗÖ!¹Ž‚ö6€÷dåĀ9•>ū¶!» ¦Ķ”žŃż>“kH“‹Č0$ĢJPĖšs¢F£ fĀ÷ "ĆTÄ$×GŠiŚ?ˆą ų`īēvččQĢŪU4І?ˆF,ŠŅXt“Ü“(:JäŅīh‡SŽ7“é'j…š–C£™ųČ„å[  }āK ĖĪŅĒņŁŅĒŹŪVŁŹŁ_Ų÷9ę93ŗ’\$Äļ EZ™ģŹāŚķ,ēÜėxc/j·U¶Ö±¬Z ±4> »5ąĀ‹A÷O³Ņ쀙Ē\ ÆÆÕŖj³ŌŹQyÆtD<įߒņ0EK)ļßļĻ)N_ˆE„5Rzy‡\D٧¤Q©«ĻT]}§ŽPo«›Ō)ź~ 8]®öR§ŖćŌćŖ©FĀ‹h­”«GōŗĘ~#“¹Ųt³!ģ)+ ?ŗBÜ8ö!Įwl3i:®<äp ø‰; e|ȽÅģJ`¢‰TĪ€¦‚tó Č>ą®`®wĮ÷ę½īH~å¹Ühķ÷‘ޤé*:»Y—@)ÜļŁF梓€JęDj |ā…6‘ š¦ Rh-hhKčlä˜įhóӐE—"ĖlB#Ü%=ƒū|Zś³'5õB”3 ['Ö;£!N  āqči b¾ąė··cę‘ĒÖņ"_CŲ!Ä[fY<Ö%Ö¶ė¶Éč…ĻŁU}mœ9ßóvĒVūcŪŪAkœ5«å3”(5?’‰&u‰%-K¾¢“„aĢ/жĻzwšd¢ęŅFŖÆ”¢Ź$łŗÄI“Ä:āHq½xO“K¤öR²dJĖ䒬#„‚:O½ļ+Ø•Ń’Õ9@ī”zX]©6R_*”vŹBå¦āP9Õ«V–«Õ­—^Ī8e0Wš֍g©H²ƒ|%Y”į3”;ÆØ.X]x8q·:z JśŒ{ͽƒZ¾š/¹'hXWį+ĄŅ$ųŽH ×”«ÄåēRA;"͟¢[Ą¾”H•±«>Ŗ`·O ĻĶ€*6%eUäoüģH H£i‘Asƒ%B«BCĆö‡ŽE›˜…ęø~īļ^héIrŒ¾ W}M>”ßkČ5!h)‰43Ņi1Ldm°±'…ūx¼ō1Õij4›>˜·\4“tÆó…£BNĖZKVėktū)öåöćööū{»fŪg«j»o½gég¹(X…¢ü¤¢“Pž«šųAt ł›a„Ų~ó&2į)=‡¾MkÆ%h÷ÕŃjju—RA¹"7“mņk齤H²ōRŗ.Ż–üRy¼NI«&C5•ײk~hčFœĪØ3ŌTjze‰ÜTī$/’ļŹLöĖwäÖJ©uŃ '€ßr“”eīfĢś<ä‚PZŽö žĶ±oĮēÖŖCq†Bs’€Š~(éypķōō>_AÓ;5ځ®±yu:CS`^ĪŹ)˜ĖČóI`_OŚĶ=uŅoäö|5pčn•†ŪEńたƒ‘-><°4“© l…$Ś mp0 NEw\ˆVæ ZŗL܇[<=½LnBQ8~„¦*h‰v4żš–fūQŪCĒg"£ Īg>®ōž*†4½Ž7ų¦č†¹,ɖxė|ė ŲŶֶƶ×ÖĒvĀZßŚŅŗĆbµ“”9 :3”6Žō ”;;×k`ž@+#F½«Æ=‡wR—©-Õ<ŖG½¤ UžPr!…6W²*ūäņrF¹°ü§Ü]>(wTZ«ßÕõp»ī½@·ÆØŽRdł‡”Nj M”VIg€{¬œI–å8õ‰ŗ_›©@—ČiĪ3šÕŁJö :ŒÄüf£Mįž;ƒÆtŽåžąj  ä&q øUÜfn'PüŻž>ļ‡fnåÖpKĮ¼qøL'ä¼Źč|8/gŠ÷p£tZٰÆ*Ķe£ōoö~šØŽeAźü…Ÿ Ō M$é€jn4ĆbŠŠJČ”[Ā»ó`į(`8yf.\4€āZ²Éō/°ń |ń$9‡ŽqsņŸ WYy¶ÉäĶFc¦~F+«ŻROŖD{ \'[$‹©v5£ZYm®VP’PźŹMär ¹ųµM¾«lQ_Ŗ—ßnµµZNż¦dVĪ˱rF©©XEÜ+ž%Īżčł_„Wrˆj՞j‹õüĘ.#ƒ9Ł|c–`3ŲC–;” u Ǥö†ļCr栃…ĄĮVh#¹)˜ÖeÜjØé¬uПåHt3Ńõ‡q½Šźq€wĪĒī3Üę4ŻA—ŅqčÖ i)š\‘7Ī",:”½CZł?+Øj‡ š ŗšy 8¶rĮFp˶øNwŅ!#Č$š)d:ø8··šŗ÷°Ü‚’a'Š<^#§€ēerćwÖ <śę„éĄĒšĘÉP÷Ą{Ėē"ÉšƒL|7~/ó…„.Ā|(źSĮcIe©aÉo™(Paé£ōkZ“ö)ä3XõN¶RŻ£.V‡y˜ ęSæ*ŗ<^n%7ĄŖ/גŪĖu•Ęč—ą‚išÓXå¬ó?õ[Äeā9)BÉ®fоkIhö[Œ8s„yßĢÅFĄ=ŲĻ™8V)“ v}#½„ūgåRC«pMp„}į…ćŃĶgĮēr³¹é`åčH®#ZrM® )R'EkxŒéŽKWÓét ZYUĢ}"µŅÆŲÅcŲįŁ@ p)­ āĒ?'Œ šˆ˜É&)ŒZVĒ}kˆ>ß ×źęö‚–€šĆŒ—' }M®NĒĢ&s€č`ŗØ.#+ĄŠdpt8ŗ <†óHjċĪQšų揊öKāH6ĄÓĆŃn;ńųå|ߞ_ĮßęSų1ü]®>w:ŚŠB:{ŌYį%ō ¹É(sš“ŒTĘ>=µ¾_k„=EߍLrŠxųuQ_+ •8%•¢Ü—§ČķäĘrœ:ČĶånņł¹RJU•ÓJc嫼V.-»åKR7©‹ØūoųĻś7ūgįs_Ń/•Ņ%4S[©ē0Ö^³ÆyŁLĖz²Œ±RŲŻčń±čĀ=č\ŗ ų >‘ø”ƒÖ7N=¹@l8ÖPnķlĪÕEf)ĘåÄ%=ąŽ7śĪw>3mŗ z{)ģ”ä ’Ęģę4ģ~+¤“B@Źū? h(ЌB‹O\³AC į’ą`E\¶ō¶2OKŅJŃ ½°;é 6öEźŪˆD;8ø†` żokÖppv°ž„#’ģĄ$ŻžŽh<:G3䛡³jß±ÆˆYĻ-#gr“”9q‹‘§ļC?¦crĘa2®€ć­jbĀ~°,’e7»ōŚzœ^T’¢ÓŅ”łC#ø”>TߨļÕłźBå.Ün˜¦ 9.xlulšUŠūŃ$ßAWSW{$”·^Āé$ö`' õčµ7Ł+vŠmeSY'ęc°Īl6kŹŽ˜£”ž¬µŠź£eļŌZjŌŗźu'š»ŒF÷SMR+Ėäjr>hf=¹‘\NΊóU€ß9ł‹‰ÓďbEiæTR¾*wU\čöeµZCż†^ĪŲd؁ßvS3˰qģ$3X~xHąuˆHXšo’uG÷^Aw” ÜFćżF5Źs0у†ēŪ98Ž'ŸoĄ¼ėō =€D¾y:HkZ É%'ņŠätčodĻpŽš¦źĄ"xöoü,A<0"Ø”©ą‚æ“.0Ģ , §"@2°Š`‚Gęn¹€tf\#üӇ[µC“u&Ā×ß±ē@ć:P;ŽœöŪĄV±%Pœ™˜ŚIlŽ},PśµF²įl0ėgkĘŖ²,Š}7O˜Ķ2ę'c–‘Ļx¢OÖćõAZnĶ©ƒ6>Tf+įŹ{9IŽ€·Nž+Ļ’k£—WG®¬/—’]ņMi™4Iź'µ•źJU„¬Ņq®X\ō‰N1Q,(ß¾ųļųWųWśwĄļ¶ā“(~÷§[‰ū §¤ĖR “üŅŹe„™śQķÆÉZ_ż^żĻ żÜnŹh€#0k),+Ü~ōa``)dä~p±$ś=W{”¾Š0Q£:N2õÓąŻ#“Ø p½=pĢePĪQČ-mi]ZŚ™¹Ķ$ŸČ}d—ŻČG³ą5Żą“b’³%÷oühÅ®»‚ ˜ 5X˜čäF¹c^ Xyq>w·€Æ„‡ĒA =˜’Bī»Dn?ŪĘÖ²„H“p“żX7֎5g X-ąT‘•aÅYA–—egéĮ¶0ōÆęóعĀ`V4ŻęEc¤‘Ėø§ŌS!§dŅÖØćŃƓŃ ‰—b¹ä Č&‘+óŹiåBPŹ"ņs©räeq·xk;Zų ±®˜Cdž«ąŲixÜ+ü»:¹ĘæŽæÖÜ’·£’?A¬#.‹9„qŅ3©‚¼Sά¬Q2ŖėÕ,Ś:-µ>[7ōÖĘƇü²ųÅĢķfYjø’0t„‹šš00§2849f9݊Dv »tł 8¾Åé}äīŠkō<šŻŻ\CÓiPĪŽŠŽśČ@”QŌBSČKØņQ4°eČļƒį/ ‘/ —8(ÜÆųKC¹`<’hp*C‡³­_+¾Ź‚ļfÄĻSƒĒ1Ą< ×ęƒŲŌņ)»Ė®²3ģ0Žm3[‰œ? ėĻŗ°–¬.«ĢJ‚_ŁXZ8›‡Ł3Eó³łĀ¼cž7˜ėĶ9ę`³™Y9噱Įčl¤z“ō|śm­/’Ź6µ¹JՁʹ€œ˜ż)C¦¬.W”+É5±ŹŹååLņFi›XRäDĆ!fDA|ā’āÜé_ŌNś/ųwĆēV‚w“żĒüßüXYYœ$ž­R-)IņKµ^¼2Y1”~ź'µvG«ØļŠ#Œ>Ęy#Įģbī~EpTŪ śQȽ¶#k0’@ њHƒĄĮÅČŌ»ąm§Ą³«ĄńÖµß ū݇IJ(Ļ„“épŚ Ü«O+Įł²A;T…ß'ē]ÖA=Ē£ÆµC~,ŻĖ„]’7~’00 ”*dRŸ“@)}Ē_+¾JlČÅĀéĀĮZ®Ļ˜Ģ¾³÷Aż_;Āö°-ŠĖ…l…£ čb-VŽr‰Ģ˦µē@ķ‚yY ٜ‡tŁȕ7³˜ó•qИjŌ7b»ś,½‚.jÉZuMR“Õźź„“ņNž$wB2i‡öÖP® ÜŖ”ÕÕF3x.­•„lH’ūżł£ĒmCš< äö­dü{žw ?›‚Æ®A1ĆŲbWq¶xTÅüRéˆ"·’Č1ŹPå„RS=¬ęŠk‚ŽUæ¢g7ĘwLø§ūMÕ,Ģś²Mģ1 E hv½lł†š Łæ.ķN6ItŻA÷¢™fG€ę(ę_hzkįyóĮ¼±ĄŗmDÆ(́܆äņ ÓpģŪA’=G¢u·Bj/׏„\’Ā/ĄĄ@ hč?Fįø (¦b•_%ā»qø~$.ēę܆†T`Žd‘ĖČ%ٶŽ-ƒfŽG‚ģĪZ³zŠŹ"PÉÄ Fž0_¹sąŪfs¹9Żavr•Ķüf‚IŻIt¬~FĆ ōė ō0żŒ6DĖ”=Qg©%ŌWŹ%‡ņJŽ#/•§Ź#äŽr`X,,-{å£Ņ7±Æ˜O<Üöńū œ;ķ?Dļ”Sņ›~)僟ł³‹ÅQČ2×D]Ģ!µ‘V@73£ž–”Ź-„ ŗD%ZGķ²–GŸ©DzYh¼1ņA#›ų×ĒyYį*MČ(düSh‰‚†–”õ †}įiӀŃ2ŗLŪH7amĹd ·ŲNCW†Ku¦-ų/ōŅŠp* 7¼!wȰo˜=™ A6o†ōRī•ųüƒß/ž£”.š2^Ž, ”bĄ²ø­X|t}øD;+®i€y?ąƐ"o±‹Č•Ē[ól d^Mų\~–©ÄĘdóyZ¹ßÜ`.BB`¶7똄Ķģf”ÉŒ×Ęc³1Łhk1\Ę}}µŽQĻ¢æŃViM“pķ‚:Z-ؾW–) ”8å“|YŽ+ƑēÉćåąbm8 •7H¹„ƒbKč¦C$ŠP"śń!±ÆžæČ®PA¬ §k!öē‰Åē¢S*ģę”Z”ÄÓä»r&e°rUÉ¢ŽS_Ø„µ•š©5Ńwé£īd”2ǚ'MŠ¢›&#Y3$˜Ś¤?YHö’›ä ±Ņš‡– ¾²¤;ø5ś8hͧ ±Šyt6›ˆž1I§t³ 2g(g=µRŚł\&GČv²y,n½ŚöŸHŸ¹ Š‘ŲżüŅŠĄ#1æt1ō§\ņß+øzš³\Ū†k$M ŗł½d’½é@/ ›MY V %=óA5SĄ¼[8öęJs†9Ģģl6@Ļeʙ’ęM怱Ōf46ņNdĪ­ś ½ŒnŃĻjµ §Q‡Ø…Ōe—2H)ł™|QŽ̟ĆåÖšB‡|™3µtO\ ^uE–l„äRxU« ÓµfÄłb²ų—xR|(*b‚TVź*-DƗ„\rgy£üY.،Q®+éԁźe5£6Ž—S«ßÕ³ŒćhÕ M ²W/xÄUōŁtčĮ]” ÷`xF$āłĄĮš“)t“'–‚NŽßĘĻ‘@nņJWp“˜Z—-D³ćZŌēūLž’ä$ęa=YL¦’įPĻ6pæŠHž9 „’怆Zƒ*śo ư<’ZaųŽ?s±ćąy*Ė· zī]B (ēZ“„™č‘Xš’‹{‘ĢDļ&zĮ_ȗӠAķĶŚfq3³é55ć„qŃŲaĢĆ50 ”héõ©z=£žYŪ„ Ԋhšś·:\-­ZŌĖŹb„«RNIÆŲ•ņł¼¼Z:94³üEځōYT ‘>ˆ×Åcāq‹øN\%&ŃMhgÅ{ā{QC„ōR ©©4\J–.I’”InŒrA¶+UŃMī+Ō>ź Õ§µ×ökN½™¾EWõŠč2wŒ8³ ”ć¦éd„€_¦6…Å£ ·&£É rūžž˜$šlK^Z’ž tš„ĪpøŽč}qź…ó]€kk [VóŠÓü4+|Ļō4¤Ųä6’ĖA$Ļ•dG’.¤9ÜÆš\6ø˜(üWüžŃŠ‚æ0t©Ž’^.|ĻŽKXpYŻTįzō^ĮĮļ°+čę³]l#[Į沉š½š½ŗ¬2KdMGP9ļš§ĶŻę*poØŁŃ¬k–4³šįAō.½łĘ £ ”3Śų”_ŠWź}õņzøžX[ÆõŌžŠtģēdµ¶=ØĢRŗ)Օ|J¼bQ¾É÷å£ņ* XOĪ(’”ŽKs„nR5)˜č•¬čŠ(‹šČK”R‚”S*…NŲY--—I%NĪ’œA_hƬUŽ*9Ō~Č-­¾¶ZūŖGū¼„'ķŒĘ'#T#Ł|hzXYųß v»‰vÜ»<—l!Åpp±“ĮW””»źŠFąYK X-hŚMæ*°+‰¾‹f„ęzƒč}#ÆČ=t‘£d':É"°zš{{Øg5R]< 2HųĆļßT4€ā/&–#øģų:€\€w”˜LgŹoō^#µÜCę<ĒŽ±}l+ü`“s’YÖÆĖÅR#µ0ó«łŲ¼d„ļĶ7ǘ=0ÅĢÜf¬É™ŒĘ~c¹1;TĮČhpŠĪ}Č ķōBŗ]æƒģŁü“+Hį€võ‰²O™Æ Tš+•±0“āZ¤rĘj°&š×~c$šś¼|H%eŠźŖŅŠÓZX5Į¹?ń²ą]Aš›fnĘŠP¤č½FkøLŽ“=Pćš¾ńh~ݐ=ėA£Ką’Ʉé"’żćÖ?8žZäų vŒLCfIzŁ›ß“óŪÉ6Ł7‰ ūZ}åŃĪ3³ō<Ł|kŽ6OĮł’ ƒŠ€kšEĶ čč¢ńÄ8\0 ™³”Qģóė×”Wć”Złą~wŠhå‘_ž«;`ź©ŁT^}ŖU’•)J_„0Ģ­D+†ü\>ސ›•Dd™Ņ]éœō·“KŚÄž’öK'¤kŅs)Er¢ė“ÉåÅņłµģQJ*=q{÷•pµX~NµkjÓµkZ„ŽH_Ŗ?ŃÓ­Œ$ć‘ū=SųÉL`UąšĖ‘·ß1šqpdų²œ ·€B 8Jc”£Łf Å“Tp•Ĺ¢š»|ą]°4ŗé¢RĖpļ¹ēŪO¶Uč}SĄ¾¾¤#iŠł(÷Ė…ö7ūOüžAš× ąų_W¹vę‰Na_ƒč=‡ó݁ƒŸcGĮ¾@r xßø ÷5ū\Ņ!¹šč ĻĶ«HŻ›ĶÅęxō§ęč łŠ,ęć¶ń·±Ś˜dt3jy ńUæ¤o@bh¦ē×ŠĻŚX(Y&MV/Ŗ+‘*j©9T§ś 〲Z™Š¤ß ÜÉ«Ä(&0<%ÆGÆčM,+ēAĖ]X”r¤œZĪ)—kÉ µ‹Ąŗ›²_ŽUJ+]”EŹYEQrŖmÕ„ź-Ճ¶9U» 9ō*ś$żœn5Ź£ŒCF Ō³5ōćŒé7Óćųb^OBƒ¬čĘeƒļć6-bošw—߀ƒŒ8 £qąWF01'ø–§Ü8—f¦éiź v!ŌB ā'ŸČKpļ*9 o'kČō¾±déŽģŅźYī—ż;YÄöŸš‘_žZ’ąXÆI;ć_čø÷–½€v޲/ą}»ŃeW±ĮĘŽu„zVū/źłźy1ųĖHį„7”æ•õŹeøŅQ©­UŅ)ųį=łø¼M’‡¢v•;¢ķw•ūČĆ •Kä-ąé}ł§¦äRj‚ĮK”“Ź%^­Š|“C}­&`ZfhēŃŁKéC”ä?ōœF°ļŽ‚¦ÓīwĆdf6čĖä—Čoŗq1R :.ųt‘ š°×PC:ź¤ &&Æ4X©”«ńPĢH(lŚ#2.łÉõ.øw מ‹¬#ː\&!yöūš“:AõĢO²"}F#IžoųżßÕŌ”› g½ĻЌWģ)“ó6¼ļ<Žį`šź$6Mf#X֞5b"aēd©X(3 7Ńśö™kĶ9ęH³«ŁŠ,cę0#1o«Ę^cÜ„£ń§‘Ó3¾éWõmHŸōrz*]†ŠmŌF£ę×B“·ź)uµ:Vķ€ŻĪ«Ę©‚śUyīģR’ĄÄJ¤šĀH¦aŠ.B6½‰LyZ> Vž“Ƶ7`œ]IT (5”ĪŹ$eƒrQł¦ÄØeŌnp×óŖŖęŌZk “KÆC‚ڬæŌ挦P‡ĆĘw#½YχüüÄ“CY±įl%;1YłƒŌ€‚!ÓIRš·Ļ/cä×k+)PrAM=@,°ĀØØŚ(OM`÷ƒ|óAu/A9½õp¾łŠĪQ`t7°Æn»<)Šīžé3=ĄśŸżÆ,ųńó” ÷¾½÷Ќ€vŽ >/t6ØžŪŁ:øĮøß0ä²v¬!ü”Ų’~e_ų÷üŪž3ŗ5Œ¼F„!¢kķÓčżō:z.݉ž~\[¦ Ņźjy4·ö­l‡:_ ­«†>˜^õ؆ņQ¹§œQvCOg)#M›)Õąił”¬`d*ą• ˜fje”ZĄx 2¬=”ć•fCjžn–Ubp¬kß#ģ¹°©NZ‚1£°ūKĮ¢Įßł¹DnĆ'ŠŅ×ä-{‡Ļopž%p}ް;CŽA7w!s®!ĖĮ½drē$—vHžö•Į­ēĮŒ¤%±h®’æ’É»vŅoō©ó]P;ģ» õ¼‚ģyī·‹m āŠĻčEYä³Ąc/Y³2Ń||ä,šŲĖTō‡’śŲĖkć2p¹1ŽčjŌ1 Ōx­ŸG‡˜ ŌÕ’‚ŖöX;¦­Ń&i=“zZq¤Q¦©ļŌ;źiuŗV]ˆŌ?Lķ Mm¤VWĖŖE€eV°2µšˆ•ē²Ā/‹©•Ōśj{dŲ©ź*õ€zSż¢†hٵŖZwm¶¶m×³ėõõ‘ČO·t¦g7 •nAr!fV³Ž9ķēŌ3¾PƒugSŁzżCö“9‘`Éæ!ōnRĢläõŠĀ_æ/r źx XŻG;xˆÓ}pīp½ —<Õ<„¶·lD{\ߛōFĀłzā¶Zš¾ūž 9ŃżR#}z?ošæ}üćw’½@/ą|o·~±ļ»tæć· ķo2|ä³9«|ō%+‹c.¦š‚¹ß\gĪ5G!4/„Õüo9dࣵQŁČmDß¹;…1 .TLŌ©žV»¬ķŅ–¢MtÓjåą†‰H4ŗśY}ŖŽŽŌmj²ŗX„NTG"„öU{€™]qźóƒÕ1ź4༬=£Žr-•VÓŠG›„ż„ŻŠüZ¬^Ro‡ä“S sFv£®1ĢX‹éņ šėN`ß_˜BьĘqÕGJ›Į6"»=Ąd[ŃŹr’Rhi-‘B‡’‰HĖČZ²•ģ:æ^Ļ|½üJp]Ęłó@ī$\ņTsٌĖ&‘Åd.™åÖŠ‹t†v6&µĮ¾²Č.ł žą~QĮē[>’ēĒæ™÷ßŃ ¼īčG0už›}‘]nĆż.æcæń[‹ü2/ųlķ@֍µĘV :`ꅂž4_"w3·™ĖĢ)ę@hQ­ ĆMōŗqŠH6¦}¦F9#›į5$ż±~<˜©÷כźeō,z˜.iOµspØåŚdøa;$šrZp1ŠŹi|„>«.Ķ£ź!ųŚ^¬ż8wL=«^S £üP©®eԊh5µŚHØņķ¦ö]óźł0)ō%śQdN¦Ø¾1ŌXeœ5>įfAĢŪs„yGĮĆJ£ß‚Śl†{ÜĒdóŲ×,Šø*p«ĄßšŽģ?'ų[[Į¬Żąįߥź8ŗ}`ĒłĆPĢ}ŠĢķĄnŗśR0o&¼s,®;9Ø rgSRš\‘”DóĖŪO÷‹ ¾Jå’Ęļ?Ń pļgP;?}æ¼ļqP=oĮż.²3Aüž·ża›WĀzĮ)h–‰Å0'ųń7×£5{”ĆW¦1CLÉxn\4ö …NDŠiŒÄ@Pџ£;o‡ŽŠ;č5ĮĀ“ŗKµēŚ퐶I[ ‡@łZCU“’@2»–^KŠ"µPĶ® ÅA˜*^Ķ ĢāZhoU­)®5-a‹vš)jįzn½ŗŽ lߢ_Ńæé>¤–ĘĘpc%œłįBS­Ļžkī2oš?ž_{goSŁ·ńƒdH¢$•’'Ń[¦…2e¦ ¢x„L)I!Cf‘L” ”LŠz’!"9i0I‰¢¢Q{Ż÷ż~ļµö¶÷9hų<ļūßõ]Ÿ|Ī9{}Ö¾Æūś ēģߏę#zÖ£BėĆ«]ąÖ”ßē’_|ÅØ0nĘ/÷±ö‡E'āŖYńI‘%DÉ7Āću}5TnNǩϔöÓø¶?UK¼ē߇w±³}_uÜW.Œž—…ļ2ŠŽÓw2 ż¼zÉŖåŌóīó•ĖĮxō̬ßrō[H’7ŻMt£Éźž.žż·“å+‡<×e‹’õh«0Ǝf7w¤‹ÆIZŲ椊ŁcŽ3ÆŃFĮŌ1„LA.å`°5x‹ZbdšXŠ:hˆ†Åčé]ģłpKl^| GÅĘzŒµŽŻC•ŚźÄjÅjrԊՄ6iJ’ŲÕzrę:„%±õ±]±bكĀä×FĮƒĮą`f°:ų‚¬w™©F@Mü®9`rŲ+Łgģ0;Ē®ēdq—ø\S×Ń=å&»ÅTß;ŁŁÖĆźFwrl†s:£B_ōE71 g„³séķēóß<>~å^¤Ļ›L½2†ó†ā¼'©^»;ÄÕkLģ¬ÉsV v¹Šżq)½{ōī¾Sė÷gī;Bßą£ē~śTż|ž[A’°ŲĶ£š~ž 8Ā p»®qÖpȁ»³µ?P…~H ]D3’Z¼½½ƒ¼RĘ^ls„ n4KĶ4ź½GM+ÓĄ”7ELnó3™pÕčtÜŃ3hK=S=(KW‘?Ȇ”ćGÄŌUT c³cӉ‡ćÉgOćĶ!į1,6’Śä¹Ų“ŲŌ^ŪŪ;€ēr㺚A j¤ah·’^ļ— ?qóӉH>«9ˆzÅÉÓmčy¦Łv‡żŁžĆ«©Į+ėF„6Å½ĘžŻÉĪ\ž“¬ń į{“煉÷ÓÆžž[ĆÓF£įDāćԚ†Ū¦óļ>Ÿ„rcytdŚ0ĪėGÖģAäķDÕŅõšįäiµČ©C÷• ö,L”ĪGõłgś„ŗ/Q¹$ōóŃóĖż|ž{ ¾’žżµ0N%¦ŒrƒćlE¬Oq-Uh!—+¬b>§zŪ.°SØC§ö –µ—ŠIüj¾4[ȃ/›1Ä®䟼¦¤)dĪ0GɅ›čé_ Ę‚‡qāmAm¼X y‚¬tGcßĘö£ę²YzlslJmäßĶ|ö ^ŪĒć?óŅĪ¢š-Tź͉˜ż‚qĮ<|·38ä1Åł‰÷˜žf<‘ ¼— õn¶­l;É.„’9Dī+LFhčZ“żFŗīu^’.ō‹Q^H„ów’«OÖŗ—,MæöOl'|ʇĒ8>ēF Ü`|×—ØŁ“nÆ q³mZ+ōæõ’łŖ§U¦ņ,ĆĪø"|§Ÿ·˜ēĆ_é—P/Q»œJæOĆśs#`•{3Aż{<#śŲŽ,ßŌÕ&†–!ēŸļrøćö[²ąf:ĮŌ1#ģD¦fģšr¶ØĶksˆZ}­YDfz˜6¦±¹‰:°9ÓüBUńQ°†Śp&k>$x"茎ĶČ[5ƒŹA9źžbĮ„Į…Aœyv;ȹQ7_p>^ūWpgU źó=mƒīĮ@žååąĶą²ģÆĮ٦˜¹‘ÓÅ 53ĢJ³ĆüdņŅ3Ō“-¹ĘńDŒ÷ģ>{ÜęwWŗŖģŹŽTŁcÉųĖčw£ßq—o\ĪJūūčų»x“DĮĪhŅmü“֐ų“ÖžĘǃłŚ”ė“öõŹ#t łŽÖdĻęiw„źÕ Õ«vMZÉš=¶—Š;ųw“犒 63§Ö/Ź~'뷃žįc—fĄÕd@Aē‡|Ī=CģKŚŁµqĶ]#WÓU$ēuā ~zp!Qt{Ūæ{©¾­Hž)hĻ Ž~ Wæ&’ »£aS\QÖ\fņ™4œų%ŁrāĀ`F01 z£F'4i‰£n£h}ÜU›£NP7h@v»GZ퉔½P~,±xaš6•Źj•¬äŁ«LU“ėHĪ›Œó6™ż&0ēSµŌĮ{OP·¼BÖŽMå’Ėqå‰(-ép»IdŒD ĻBżrPYų{©–ǁžw…ÓÆ©Eŗ£O/tźKŒģĻяžäó^d»<Ś-ō];r^Kœ×Œ~½‘Ó{ļĘ“ėé2æ·=ē?Ö/?æF?_æģū?Ÿ? ‚¬soćĄ%8p.9šŗˆ‘nõŁ£ģÓÖī.²ąĶäü¤‚ßEÓɃÆRɌ±ØķZÓ× ¼Ģęć"¾7Ÿ›Ķf9N‡K[o¤¦)bĪ5ŁĶļĮa\³-ŲL‡±·É‚’?‡šäīąÓąĆą}œ¹…VQųćķąŽ€Ī[ƒķøķĪžŻņ›KˆŹ•L=sĻś8ÕŹTó*Śķ ēe! \m«‘—;RµLĄ{kģvv\{Ƅ«änĮ}ŻØ=ǹY䌵Ō{©?wg°¶AKįĄč.MČbŃääiķqX'āiŽĪ|ä'u: m[n…Ī-P;Ņ®q³&NŖW"|—{ä¾č}ŁĀæéeęTõ§ĻÉžĮ’öÅGŠČQŻρ¾‹xýJó2}Ä$zŪ¤‚­ćQ“y°»8K£Ÿ?`wŚ÷Ʉ‹ģ‹ģņAōōķmsŪ€®¾4ū?æĶj6_›ķԁĖqʋfŻa/ŗ‹¶ø±±©Mf¼Žśę s©¹Eņ˜& Žæ‘Ńüń;j™ ŽĶMō½ÕÆ4׹ćŚÄ䖦3Ļ6Œ(=ǼIĢü ×§ŁóØX*چÄĶīv(z1‘sź92_1*éŗ¼’Éķ#Ų£óˆ9ˆA_„śecm ¦ł;!•eå«ą śįōk34¼ZĒē&Ӓ~Vņ>¹ׯĪąyķź†ówU𰟾‹Ō+Få¹/ŃÓgæÓé—tąÉśłžŻGŠTF ~V”¾Šy‹z,³‚>Šv"¶pMčoråØü{°³‡ļĮž‚Žp½}‹jf‘t vĄ‡·²’˱’…ģY8ńØł g¼oŽ”»˜‹SĘŃaō§JģjŚŃg4G‹†¦Ž©ž•ˆ†åPØ,]€?®į³ ¦"Ō J62wšūųžnč6OO5óQnÓ×ę“ߕ°7‰ļ&#÷åjf6’÷Žą½üīr®¼&±³-Æg ńeł~%ÕĖvöóÖ*+‘­@x/¹’T”ž>5OL掁ŠĶńŲŻń£Å‰łÖÄōdĘŁÉ𰟠ņź]z—RyFļv÷ŃÓgæÓė—łÆ‘‚Qõō9Š÷š } \*y0EG†½Äc®‹{ĄŻK?߀üq}G/Ą…YÜ/T3{ģGDŅ7‰TÓķ8;„XŚÅŽOüŖ‹ĖŚbؘĒf”»ųŽŹfyq «¾×L'c!ö 6żP¤‡yEżżÉŚ›āG{>ėŒc»#Ÿ4ƒ8w¬yž¾|”YF•»…\÷ł™ˆ™Ļ^Jܾ߯CļCo:•źj½ź>Ŗ–lī<¼WŽŽ”Hņ•Ł(rü\^é·…:ą+bÓļ. ‘ķÜ“čN:„SfŠė”Ÿ~mĪ!GGSÆZbź5š¢¬~bīµ\8G©ēg»ü,„wŸÆ=£ģw*ż¢šŁQŸ1F1Ō+ø3Ģ‚Iś(źó ļ%fš*ĒS‹uż‰8wī'ś4Ę…Ń ĖetgI# ?¦B_A¼z‰|8Ź>…;ÅīĄUķuö**›‚č˜Ķž¢ä³•Ø·Ī¼šKé8ꛣ(3U§š)ō/šļT>›I?9ŖpŽ;øm {`/}ŹOĘP§ąyK1kŪŪ©”Āw£čLp%ļŪ]ō{ĒmNā} "gMŌ»ßu%# §Āž…ūV=?b °2ĒŃ/+ģļEV,~Äs4żZ/œŽśõųZ©uB¹J|G…pš²t|jŁGNƞłC÷łŚ3z?Ņ©łóÅŠŒ &£h4”¹šØā»A’ūßML ćč \Ų¶#ū7£š©åndG’õxA|˜ÕżN!ķ ÆXc—±~3ć*>Įš>€+šŚzčx=™±8^)H…“Oʈ{?āĖtž{Ģn4Żf>1›Šv+’~l>åk»yģKœvŲ3ĒÉq9ģ9xŗ(UJyž³¾½·wį' ! æˆļV¢ŻN²óĻ6«Ėē.u%ÉŪuˆ­Cõ†R¹L'Āü÷mę•ļe=ކśł{YĻj ļÄR&>Į\™j¦*Õd4łš8ü$l4[%E9?Qé'Ī#ē]~B=?mrN˜ū|ō>sŻö‚¾—HÖ¢Ā8¹pA܅Üh7Œš­7¹ĆGŅūܝŌ3µÉ†×±:—‡³dg:cbĒļµŪPq-9q±MoŸu|˜Ģx?õMS*œšŌ©ˆ­W£fQ[5 Pļäş¹mNō9Ófēæ3ł(_ĖkϵēŪ‹Ø‰Šįć²ģ‚Ŗtvģ]<߃Ō)OŚaÄķéō ’į禓ńŚ_l—×]„óʹj®!q£-UgŌĖžœKÆ“’Wŗ•Żū%;ś+•…µ=›šĀp¶” §_ˇ3ĢŃģkåG4{•NĪøŽóüTe©pBļŹŠy~>ĻO EŽóźEļŸ>}ōōœīoH<• “żą&ś‰5t„¾šYV3/ŗēĆHšŠ°+”„M\=ņaEW–š¦1/UMĄ¾’Īī'zm„zXM^|ÕĪÅ“©(FPįō!²vcŻ’m[ŚÄ×&tõˆ7SóT!‡U"&ŽŹ|^ÅVGļ:ØŽ˜³[“ >éN–}ŠēG“|™ņuę\÷Ķö öÓÅhw-;¬.Īké:PG÷%rŽC½9ģĢåäŠŲ³{ŲĒž^ō1ēļ¦zVšæ—•æD4’Mæ&f_Ė”Sā(Ē‘: [2®œ÷×ī2vAįpŅ+U=Ÿū¼ūNƟēT<ł·1 ž>’śß‹¾j8?ŌŠūšŁ0–öu»GØJŪ†³øX£jō÷e‰§~®3æĖMDż7f=?ĒéäĘ5v¹]Jt›Clb'¢ęH|3ˆ­ī| E&ÖvA›NdNtāć®|ķūØķI'Ž³‡ŪgŠl2n›Ķs½N¬|·o#’“GCåņ²Šŗ«]yW•ŻuWŲēõ¢ęEŽ›Ž÷¼zk‰4‡ī;ÄzüŹJeIówćĢ{āN~†9užµdx” čcÆYr"Öߣ#1]yI؝Ÿö:/Ģ{~z!;’Ģ}^æĢ•LŖ 3k˜ōaā÷2‘†IśšŌĒŅ™ģŽIį üpÖĆׄŻč’M<½ /Ö§ĀóóŌ„qcQbWÖ2õĶ8ņ"ė~“ÜN­ś¾\Guøœx·„īńü9›ŗg&.Fĝ?¦ņŁt¾6 ‡Ķį¬Eœ½ŒŹd5ĖfܽÕ¾fü„nYŁ3ēį¹Ė‰ē×įŗŚäčf®¾ėFķõ»n,ūoŃd)u‹Wļ#^é^^ūaVć7VŹėēļÅŻGą"4šs“Éł×ā§8R§b‹†ŹE®KLź%ē½"õž8BOš«‘įrø³QķB~Bqzšė\eWĻ5a7µĀu]Ł_OŅ’ŒäZ_`ļĶc.#·Æ§nńźķįėü’SéxØ_t/'ˆóNLĄĪ4›ńš_¦b½ćüdģ…įl¬Ÿ±Ģjē'¾¢Čł÷¼—JjMj˜Ń‰GĆßĢʉ‘ŠI/&œ©õ+C/.q‹āu ÕitGŠĮō½YƇ]grcٜ^Łßå ®¬‚šž>„ˆ°WąĶKPōV>ė–Ė…Ł]6bnē č>„¤ńµlī ”Ļəy‰Ķč Ó»ć¹JSÜ€Ūn&‚ßJžknpßū…Ź%^LēZ_!j.#–¬#Ŗ¤“÷vὯˆ>ß³æ±26Ō/ŗD4Åģ'` Ęg`S§`3žŃBpvR9?­Ķ|EÓC’Ģ{ R«™D>̜£ŖĘĒÓļOSŁų?Q½W³¾>õ:z?¾*9‰]ž¬{š o k×Ūõ¤bxˆµģ@–lž-šęm(ڇŌfÅ«”jejŸėńi9*kȟeP$óQ†Æ_Ėå9³"ń¹*«…^ y®ŪqŚ=hÖ–Ž“+?ļqüöW0’žē9®i†›M܍k]ĮUÆg~H\ŁIžŲĒkü–W|Œ58Īŗ8Ķ”GwšśłI?ėg™ż$ģé’ų¹œ™Šķģør ķ|ĶłO½— ™ “Ui”Oöā±ø’‘ÅuLķ2|…ź»Œ„’kˆF+ˆ¬Ž“‹Y©¹ģōD×Ps{4+9Ü AŃž¬l/4}oxU;¢k;Ö½ ul+źĆūˆ½÷œęø—G[rÖżœŻw?Č÷?Äóō@ÆŽŌRpŚpŖ“1üĢÉdč\Å<·ŻŽ`Ÿ­"Ūm@¹töąv^Å^vę×¼ŗĆ¼ŅŸBõb¬Š×/9C™˜ ōs°^Éhöt‡ܟ—˜²ŒtóŹyß%gæžN­cRɄ“ZFŁŃ×8ócjĒč£k:Łdńu=ė9óMöūRVo1k8MgćŠD±©č:™ˆ;˜;mŸAݧ‰½ĆéK†žÅ1Œ³Fpö(¾o ß?ē™ĢóMĆū3Ńk.‘`?óu¢är®buxØM쓹Źm\ļgqåņŠŽ Ż1ēļ–PĻÆQr/š¾‹f(£ Źæs$4‹&ö¼n^¹Äō×O¤aŖŽ•LueF_¦Ę×ŌL¹’„¢QiźŸź=źuŻČ*®g-½¶ļ°®+ńė[(¼ ß`µ—āÜמ걄³_ē»–ńżoń<+ń–æ“×Zz ü¤÷ł™éģØ¹Šķ\Ķn®k/Ńc?Ŗ}ƒć¼n?šZ~āużŹ+ōŚ”z ż’³[‰IJÆĆ?9šEŗ%ęæž/ŌĖHRĖ“õĢXń¤*šph¤k¤lBŪ¤ŗ‡ĀŲė>ŗvØó>”öZļ ū”Ļ8vsģś‡Ēīš;?Ÿe/Ļ·ćKžßßķ?ó ?ū[®ā0Wó=×õ#×wŒ+õwŽū•ė÷Ŗżźę•óÆ8Ņ.Ir /ćŽß=Ræ’’C9!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„"#’ "ēlibminc-libminc-2-3-00/testdir/vio_xfm_test/test-xfm.c000066400000000000000000000056161257462267400227500ustar00rootroot00000000000000#define _GNU_SOURCE 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include static VIO_Real tolerance = 1e-8; /*Windows compatibility hack*/ #ifndef HAVE_SRAND48 void srand48(long seed) { srand((unsigned int)seed); } #endif /*HAVE_SRAND48*/ #ifndef HAVE_DRAND48 double drand48(void) { return (double)rand() / ( + 1); } #endif /*HAVE_DRAND48*/ static int is_equal_real( VIO_Real e, VIO_Real a ) { return fabs(e-a) < tolerance; } /* Args: expected, actual. */ static void assert_equal_point( VIO_Real ex, VIO_Real ey, VIO_Real ez, VIO_Real ax, VIO_Real ay, VIO_Real az, const char* msg ) { if ( is_equal_real(ex,ax) && is_equal_real(ey,ay) && is_equal_real(ez,az) ) return; printf( "%s failure.\n" "Expected: %f %f %f\n" " Actual: %f %f %f\n", msg, ex,ey,ez, ax,ay,az ); exit(3); } int main( int ac, char* av[] ) { int N; VIO_General_transform xfm; if ( ac != 3 && ac != 4 ) { fprintf( stderr, "usage: %s N transform.xfm [tolerance]\n", av[0] ); return 1; } N = atoi( av[1] ); if ( input_transform_file( av[2], &xfm ) != VIO_OK ) { fprintf( stderr, "Failed to load transform '%s'\n", av[2] ); return 2; } if ( ac == 4 ) { tolerance = atof( av[3] ); printf( "Setting tolerance to %f.\n", tolerance ); } /*Set the same seed number*/ srand48(1); while (N-- > 0) { VIO_Real x = 500.0 * ( drand48() - 0.5 ); VIO_Real y = 500.0 * ( drand48() - 0.5 ); VIO_Real z = 500.0 * ( drand48() - 0.5 ); VIO_Real tx,ty,tz; VIO_Real a,b,c; if(general_transform_point( &xfm, x,y,z, &tx,&ty,&tz ) != VIO_OK) { fprintf( stderr, "Failed to transform point %f,%f,%f \n", x,y,z ); return 3; } /* Check that general_inverse_transform_point() and invert_general_transform() behave sensibly. */ if(general_inverse_transform_point( &xfm, tx,ty,tz, &a,&b,&c ) != VIO_OK) { fprintf( stderr, "Failed to invert transform point %f,%f,%f \n", tx,ty,tz ); return 3; } assert_equal_point( x,y,z, a,b,c, "general_inverse_transform_point()" ); invert_general_transform( &xfm ); if(general_transform_point( &xfm, tx,ty,tz, &a,&b,&c ) != VIO_OK) { fprintf( stderr, "Failed to transform point %f,%f,%f \n", x,y,z ); return 3; } assert_equal_point( x,y,z, a,b,c, "general_transform_point() / inverted xfm" ); if(general_inverse_transform_point( &xfm, x,y,z, &a,&b,&c ) != VIO_OK) { fprintf( stderr, "Failed to invert transform point %f,%f,%f \n", x,y,z ); return 3; } assert_equal_point( tx,ty,tz, a,b,c, "general_inverse_transform_point() / inverted xfm" ); } return 0; } libminc-libminc-2-3-00/testdir/vio_xfm_test/test_byte.xfm000066400000000000000000000001401257462267400235360ustar00rootroot00000000000000MNI Transform File Transform_Type = Grid_Transform; Displacement_Volume = test_byte_grid_0.mnc; libminc-libminc-2-3-00/testdir/vio_xfm_test/test_byte_grid_0.mnc000066400000000000000000000502411257462267400247540ustar00rootroot00000000000000‰HDF  ’’’’’’’’”P’’’’’’’’`ˆØˆØTREE’’’’’’’’’’’’’’’’ąHEAPXČminc-2.0@øøTREE’’’’’’’’’’’’’’’’č HEAPX(ˆdimensionsinfoimage0SNOD HhPp TREE’’’’’’’’’’’’’’’’ˆ0HEAPX8 vector_dimensionxspaceyspacezspace SNOD(Pp š 80 X x X x TREE’’’’’’’’’’’’’’’’ˆ0HEAPX˜ P8TREE’’’’’’’’’’’’’’’’pHEAPXX0HŲųTREE’’’’’’’’’’’’’’’’€!HEAPX0image-maximage-minimage(SNOD°ŲųĄ° `ident@vfonov:wpa139079.wireless.mcgill.ca:2013.08.19.17.20.37:16062:1 0 minc_version2.2.00 %ŒR 0 length pSNODp Š(p0 %ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 P comments'X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄ˜ %ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 X comments/Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄ˜ %ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 X comments.Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄHh historykMon Aug 19 17:20:37 2013>>> minccalc -express A[0] test_float_grid_0.mnc test_byte_grid_0.mnc -byte -clob  (Č"Ų deflatex*%ŒR @ dimordervector_dimension,xspaceSNOD(š'p  # ?@4 4’ 8varidMINC standard variable 8versionMINC Version 1.0 0vartypevar_attribute ø( ?@4 4’ deflate°4%ŒR @ dimordervector_dimension,xspace 8varidMINC standard variable 8versionMINC Version 1.0 0vartypevar_attribute xH deflate č>%ŒR P dimorder&vector_dimension,xspace,yspace,zspace 8varidMINC standard variable 0vartypegroup________ 8versionMINC Version 1.0 0 completetrue_PTREE’’’’’’’’’’’’’’’’W0MTREE’’’’’’’’’’’’’’’’X‡MTREE’’’’’’’’’’’’’’’’ĀßMx^c``pP[¶q~ģń~ Ķ |žˆ^Pžų'ˆVąĖ’‚H|˜K’ō°—§WI’ÖæK’ō°ĖåņA’¤GĶ{"IŅ=ę׳$IŗĒü~ŗ$I÷˜ŸW’¤{œwæüž–ę}¢ę×Ū¶™õRó’¦*ü›5D‡ƒs“Y/uyćW”ēD‡™ÜdÖK]gś··T”ēD‡™ÜdÖKĶżOUč9Ńa&7™õR'Ü¢Bω3¹É¬—šūŸ¾ =':Ģä&³^ź„@Tč9Ńa&7™õRs’SzNt˜ÉMf½Ō 7€ØŠs¢ĆLn2ė„ęž§ÆBω3¹É¬—:įzNt˜ÉMf½ŌÜ’T…žfr“Y/uĀ *ōœč0“›Ģz©¹’髊s¢ĆLn2ė„NøD…žfr“Y/5÷?U”ēD‡™ÜdÖKpˆ =':Ģä&³^jīś*ōœč0“›Ģz©nQ”ēD‡™ÜdÖKĶżOUč9Ńa&7™õR'Ü¢Bω3¹É¬—šūŸ¾ =':Ģä&³^ź„@Tč9Ńa&7™õRs’SzNt˜ÉMf½Ō 7€ØŠs¢ĆLn2ė„ęž§ÆBω3¹É¬—:įzNt˜ÉMf½ŌÜ’T…žfr“Y/uĀ *ōœč0“›Ģz©¹’髊s¢ĆLn2ė„NøD…žfr“Y/5÷?U”ēD‡™ÜdÖKpˆ =':Ģä&³^jīś*ōœč0“›Ģz©nQ”ēD‡™ÜdÖKĶżOUč9Ńa&7™õR'Ü¢Bω3¹É¬—šūŸ¾ =':Ģä&³^ź„@Tč9Ńa&7™õRs’SzNt˜ÉMf½Ō 7€ØŠs¢ĆLn2ė„ęž§ÆBω3¹É¬—:įzNt˜ÉMf½ŌÜ’T…žfr“Y/uĀ *ōœč0“›Ģz©¹’髊s¢ĆLn2ė„NøD…žfr“Y/5÷?U”ēD‡™ÜdÖKpˆ =':Ģä&³^jīś*ōœč0“›Ģz©nQ”ēD‡™ÜdÖKĶżOUč9Ńa&7™õR'Ü¢Bω3¹É¬—šūŸ¾ =':Ģä&³^ź„@Tč9Ńa&7™õRs’SzNt˜ÉMf½Ō 7€ØŠs¢ĆLn2ė„ęž§ÆBω3¹É¬—:įzNt˜ÉMf½ŌÜ’T…žfr“Y/uĀ *ōœč0“›Ģz©¹’髊s¢ĆLn2ė„NøD…žfr“Y/5÷?U”ēD‡™ÜdÖKpˆ =':Ģä&³^jīś*ōœč0“›Ģz©nQ”ēD‡™ÜdÖKĶżOUč9Ńa&7™õR'Ü¢Bω3¹É¬—šūŸ¾ =':Ģä&³^ź„@Tč9Ńa&7™õRs’SzNt˜ÉMf½Ō 7€ØŠs¢ĆLn2ė„ęž§ÆBω3¹É¬—:įzNt˜ÉMf½ŌÜ’T…žfr“Y/uĀ *ōœč0“›Ģz©¹’髊s¢ĆLn2ė„NøD…žfr“Y/uéo··’ żš’üņōśü÷_’|¹~üńē]žöyĻ×Ļūpūxūńķēoæžóó~ü¾Ūļ7Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óĢ3Ļ<óžßy’Bo{¾libminc-libminc-2-3-00/testdir/vio_xfm_test/test_short.xfm000066400000000000000000000001411257462267400237330ustar00rootroot00000000000000MNI Transform File Transform_Type = Grid_Transform; Displacement_Volume = test_short_grid_0.mnc; libminc-libminc-2-3-00/testdir/vio_xfm_test/test_short_grid_0.mnc000066400000000000000000000506521257462267400251560ustar00rootroot00000000000000‰HDF  ’’’’’’’’ŖQ’’’’’’’’`ˆØˆØTREE’’’’’’’’’’’’’’’’ąHEAPXČminc-2.0@øøTREE’’’’’’’’’’’’’’’’č HEAPX(ˆdimensionsinfoimage0SNOD HhPp TREE’’’’’’’’’’’’’’’’ˆ0HEAPX8 vector_dimensionxspaceyspacezspace SNOD(Pp š 80 X x X x TREE’’’’’’’’’’’’’’’’ˆ0HEAPX˜ P8TREE’’’’’’’’’’’’’’’’pHEAPXX0HŲųTREE’’’’’’’’’’’’’’’’€!HEAPX0image-maximage-minimage(SNOD°ŲųĄ° `ident@vfonov:wpa139079.wireless.mcgill.ca:2013.08.19.17.20.29:16061:1 0 minc_version2.2.00 ŒR 0 length pSNODp Š(p0 ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 P comments'X increases from patient left to right 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄ˜ ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 X comments/Y increases from patient posterior to anterior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄ˜ ŒR 0 length  8varidMINC standard variable 0vartypedimension____ 8versionMINC Version 1.0 X comments.Z increases from patient inferior to superior 0spacing regular__ 0 alignmentcentre 8step ?@4 4’$@ 8start ?@4 4’YĄHh historymMon Aug 19 17:20:29 2013>>> minccalc -express A[0] test_float_grid_0.mnc test_short_grid_0.mnc -short -clob  (Č"Ų deflatex*ŒR @ dimordervector_dimension,xspaceSNOD(š'p  # ?@4 4’ 8varidMINC standard variable 8versionMINC Version 1.0 0vartypevar_attribute ø( ?@4 4’ deflate°4ŒR @ dimordervector_dimension,xspace 8varidMINC standard variable 8versionMINC Version 1.0 0vartypevar_attribute xH deflate č>ŒR P dimorder&vector_dimension,xspace,yspace,zspace 8varidMINC standard variable 0vartypegroup________ 8versionMINC Version 1.0 0 completetrue_PTREE’’’’’’’’’’’’’’’’W0MTREE’’’’’’’’’’’’’’’’X‡MTREE’’’’’’’’’’’’’’’’ĖßMx^c``pP[¶q~ģń~ Ķ |žˆ^Pžų'ˆVąĖ’‚H|˜æ~’ėß×_—ē~xžśåēćńåēó×·ß/ÆæŽžūžćóīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķnw»ŪŻīv·»Żķī’}÷uTE#libminc-libminc-2-3-00/testdir/vio_xfm_test/verify_xfm.c000066400000000000000000000051441257462267400233530ustar00rootroot00000000000000#define _GNU_SOURCE 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include static VIO_Real tolerance = 1e-8; static int is_equal_real( VIO_Real e, VIO_Real a ) { return fabs(e-a) < tolerance; } /* Args: expected, actual. */ static void assert_equal_point( VIO_Real ex, VIO_Real ey, VIO_Real ez, VIO_Real ax, VIO_Real ay, VIO_Real az, const char* msg ) { if ( is_equal_real(ex,ax) && is_equal_real(ey,ay) && is_equal_real(ez,az) ) return; printf( "%s failure.\n" "Expected: %f %f %f\n" " Actual: %f %f %f\n", msg, ex,ey,ez, ax,ay,az ); exit(3); } int main( int ac, char* av[] ) { VIO_General_transform xfm; FILE *in; int line=1; if ( ac != 4 ) { fprintf( stderr, "usage: %s transform.xfm control_table.txt tolerance\n", av[0] ); return 1; } if ( input_transform_file( av[1], &xfm ) != VIO_OK ) { fprintf( stderr, "Failed to load transform '%s'\n", av[1] ); return 2; } tolerance = atof( av[3] ); if(!(in=fopen(av[2],"r"))) { fprintf( stderr, "Failed to load table '%s'\n", av[2] ); return 2; } /*Set the same seed number*/ while (!feof(in)) { VIO_Real x,y,z; VIO_Real tx,ty,tz; VIO_Real ttx,tty,ttz; VIO_Real a,b,c; VIO_Real ta,tb,tc; int check=1; char line_c[1024]; check=fscanf(in,"%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg,%lg",&x,&y,&z,&a,&b,&c,&ta,&tb,&tc); if(check<=0) break; if(check!=3 && check!=9) { fprintf( stderr,"Unexpected input file format at line %d , read %d values!\n",line,check); return 3; } if(general_transform_point( &xfm, x,y,z, &tx,&ty,&tz ) != VIO_OK) { fprintf( stderr, "Failed to transform point %f,%f,%f \n", x,y,z ); return 3; } if(general_inverse_transform_point( &xfm, x,y,z, &ttx,&tty,&ttz ) != VIO_OK) { fprintf( stderr, "Failed to invert transform point %f,%f,%f \n", tx,ty,tz ); return 3; } if(check==3) { fprintf( stdout,"%.20lg,%.20lg,%.20lg,%.20lg,%.20lg,%.20lg,%.20lg,%.20lg,%.20lg\n",x,y,z,tx,ty,tz,ttx,tty,ttz); } else { sprintf(line_c,"Line:%d Fwd ",line); assert_equal_point( tx,ty,tz, a,b,c, line_c ); sprintf(line_c,"Line:%d Inv ",line); assert_equal_point( ttx,tty,ttz, ta,tb,tc, line_c ); } line++; fgetc(in); } fclose(in); delete_general_transform(&xfm); return 0; } libminc-libminc-2-3-00/testdir/vio_xfm_test/verify_xfm_table.txt000066400000000000000000000472601257462267400251240ustar00rootroot00000000000000-63.402019580826198819,-90.401326166465906908,-17.496658954769401362,-65.207488403333371707,-89.272934398224819574,-17.665692585275468929,-61.547825646604380267,-91.403700048853153248,-17.356427374784260564 -50.853954860940596916,35.428183898329699275,-20.427647745236800603,-52.853209078185514613,37.007376178326744309,-21.111950685693610552,-48.855264806008825929,33.738187460823333197,-19.844430932633247977 -58.854997251182801676,-80.569618800654993152,10.734443319961400221,-60.777813035946245179,-78.689801601399665287,11.807564826983362849,-56.904215146403785752,-82.359407994801998143,9.5220486758126305915 88.84468604810540171,66.833427594974594399,84.38791534863409538,89.527133020998675761,65.161379487255558729,84.218930885315870682,88.126092612139800053,68.62088304848379039,84.585693484756788507 44.748682156205198623,-4.4559392612427499714,-14.827424800023399243,46.721061160310426885,-5.0044807167406402115,-14.485823481271223656,42.799247759587814244,-3.9722969889350938644,-15.104829795091767153 -3.7856702227145397899,-64.623936126008601377,-82.39259216934439678,-4.022331452788366235,-63.04065662440987694,-82.189521784170011642,-3.5631284591306622289,-66.31780068191169164,-82.559072460786708803 18.487881543114799143,20.797214517369898346,12.522705271840100849,19.586000947189013743,22.725979338181755196,13.30202741639526387,17.445199270401438696,18.938735145316165642,11.615366798439868745 19.114023027941598798,51.781122433021700147,-22.929526865482298348,20.244647812622108773,51.565885062052068122,-24.008623404404218604,18.039552869068860019,52.021639655945264735,-22.012180541735272499 86.341535346582503507,50.012982264161102819,-13.937151059508300577,87.163542103283063511,50.01145610749961179,-13.467749754583190125,85.475990208064061449,50.01467886077206515,-14.326569936921412562 6.7493110895156895879,65.500986995175495053,42.086753575131297112,7.1709206808483703099,63.851290408709253654,40.719208514810034671,6.3524854784684521292,67.25647661652772058,43.255071038403819728 96.231893310323400215,56.485349126160102173,-33.689257828518698545,96.231893310323400215,56.485349126160102173,-33.689257828518698545,96.231893310323400215,56.485349126160102173,-33.689257828518698545 76.827046927064699844,81.409699143841905311,-97.866653371602296829,78.143009012693440241,79.610006710814758435,-99.690773979619521583,75.455769646210967494,83.085174022376790504,-96.185399355545428079 92.47217401862140207,-87.946314923465294555,-44.218557700514800501,93.090208023786502167,-86.770744435489220336,-45.836591705679900599,91.854140013456301972,-89.121885411441368774,-42.600523695349700404 50.584940006956500724,-2.9630736447870700268,-41.466258233413100243,52.584589648890897706,-3.3252749481025705158,-42.918266448831388971,48.586896884657740259,-2.6438248130643726697,-39.857815583649340851 -92.095870804041595648,70.903884060680894663,88.167698122561006358,-92.584372866473657382,69.001770980656175425,87.320770122601487628,-91.578687207422788674,72.805997140705613901,89.158936239799501777 91.172363981604604533,93.587602023035302068,-0.95705329440534103735,91.79039798676970463,92.412031535059227849,1.0429467055946588516,90.554329976439504435,94.763172511011376287,-2.9570532944053411484 34.506537532433902982,-70.220146328210802267,-95.896287867799401283,36.252592838902401695,-68.318033248186083028,-97.557965728735723587,32.807603914696358061,-72.122259408235521505,-94.364753952326196895 -61.512701958417899561,-36.97883142158389802,-52.940190117806203318,-63.383524686347357147,-38.43007840775962336,-52.423751680486404325,-59.605763671423389383,-35.413480997723112864,-53.546879957721777998 -44.740792689845001462,36.605115514248602437,-24.700619280338301564,-46.713089682674379333,38.088245917534415241,-26.047692168745211205,-42.791466911472952006,35.008122709297218478,-23.545510242117924093 -39.177104691043503237,-68.874593125656204506,-24.14825074374680014,-41.062785060225557743,-67.019523462725288709,-25.414095848536277344,-37.332150933832132012,-70.797697881351126625,-23.066819440544808373 -75.871123606339097023,95.870770746841998289,-49.545640358701298567,-75.871123606339097023,95.870770746841998289,-49.545640358701298567,-75.871123606339097023,95.870770746841998289,-49.545640358701298567 -6.8717538844793999431,41.629402991384303334,-37.523843720555298376,-7.3009199562143747286,42.639057802793765006,-39.341881517474114105,-6.4677873102018592633,40.519710423088433515,-35.612964384446286203 20.930570689961299991,90.490062581375198647,-8.4319101180881297353,22.106141177937374209,89.314492093399124428,-7.2563396301120555165,19.755000201985225772,91.665633069351272866,-9.607480606064203954 96.840871404856400773,92.303807148709893227,99.91209423169489412,96.840871404856400773,92.303807148709893227,99.91209423169489412,96.840871404856400773,92.303807148709893227,99.91209423169489412 44.263194873929002426,50.775230070576100161,-53.135373257100603439,46.230307192610553102,50.682851624519912548,-52.58304945350594295,42.320650891218193124,50.878209034520409659,-53.784229030776721459 54.582246765494296881,54.77431816980239887,-48.776784120127601341,56.561148473769129907,54.186320484583234247,-48.983463574201195456,52.589832321171797958,55.431020014393844519,-48.5355535963133633 -10.074225021526199697,-8.4917095955461299184,-10.380484908819200385,-10.696621689637114017,-9.5143883912120195134,-9.2564516966868115588,-9.4867505350009562193,-7.5763253383044828482,-11.342454271264845289 11.340577853843599954,38.172177365049698494,90.619978960603503992,11.958611859008700051,39.347747853025772713,89.444408472627429774,10.722543848678499856,36.996606877073624275,91.795549448579578211 -81.627650512382402326,-36.431636475026600408,87.494676932692499349,-82.712473546166563665,-37.866463771483275025,86.768462315745722435,-80.485368778819051272,-34.897775179974495074,88.344633020633622777 47.619480453431599187,-4.8429748974740496337,-34.064954845234801439,49.613726463668797351,-5.4394568093662627462,-36.029271418968995988,45.638055593490278738,-4.316776948620269394,-32.096697997498829125 82.89770716801290007,-2.3277394007891398786,-94.06722155399620533,83.911719909721966815,-2.6013815751158224643,-95.578105790077714232,81.82998626203772119,-2.0846573645808419428,-92.67467098549505522 -13.601210247725200375,-40.272705163806698181,-30.025877803564100077,-14.429259648134207694,-41.422010468556244689,-31.929278740943281889,-12.818592477122871642,-39.01996160957661175,-28.253538083074403175 2.1467462647706301304,45.083931181579799841,-77.107884548604502584,2.2805172433078224614,45.68943509889604826,-76.034639642061691234,2.0209394121256232246,44.40788939087091336,-78.020182779622871294 99.440179672092199326,52.064751181751496745,83.398892777040600777,99.440179672092199326,52.064751181751496745,83.398892777040600777,99.440179672092199326,52.064751181751496745,83.398892777040600777 2.067969972267750034,0.79604224301874604297,36.424233391881003286,2.1968036267749249113,0.8909320736043304656,34.541785380508521541,1.9468043093004574473,0.71201857692512893205,38.192961519602896203 55.404428811743898109,91.115471627563195511,18.709957087412501409,57.306541891768617347,89.939901139587121293,18.091923082247401311,53.409323157742662147,92.29104211553926973,19.327991092577601506 82.50002814456820488,59.334891755133902791,-48.161765327677102277,83.536212942679185289,58.237509429555245788,-48.459197948606302475,81.408960817808534216,60.536389060442360233,-47.818613377731566061 -13.341206172481200554,-5.0610555335879299221,49.613109370693599942,-14.154282951483803998,-5.6844209890943719898,49.549549415949542208,-12.572848968691673122,-4.5109261987892841361,49.666628565700392528 36.641568737104499576,90.525474585592704102,-4.4854094740003302988,38.543681817129218814,89.349904097616629883,-2.4854094740003302988,35.010007156946059581,91.701045073568778321,-5.660979961978014785 -78.775132354348897934,79.076461680233506968,-82.118166564032406995,-80.01136810943218336,77.14389752851199944,-81.863660682270563029,-77.493078498666250198,80.910452790720768235,-82.313738746872104457 14.507596427574799947,96.918418910354404261,78.533542482182397748,14.507596427574799947,96.918418910354404261,78.533542482182397748,14.507596427574799947,96.918418910354404261,78.533542482182397748 83.149664755910606573,-92.424562107771606634,-86.975721176713705063,84.149629942882270939,-91.534015985811663541,-87.608855656093979292,82.096735446977575634,-93.215652754511566513,-86.450468017796438858 -61.103362962603597452,-3.2016354613006101637,90.607162378728403951,-63.005476042628316691,-3.2016354613006101637,89.431591890752329732,-59.201249882578878214,-3.2016354613006101637,91.78273286670447817 -91.197353135794401169,-73.054158966988296697,-91.981703368946895694,-91.741386645548303136,-71.152045886963577459,-93.320651331066670764,-90.621377661187665353,-74.956272047013015936,-90.747622887255516844 89.538680855184793472,-47.223267378285498808,-91.23954004608090429,90.182435085695260568,-47.54969187059430169,-92.517301873589431693,88.860829560074350297,-46.860388061004172755,-90.065372693224134082 11.78471241146329973,-0.95130787231028102191,-53.086048038676402427,12.507399742162215617,-1.0649793508142331167,-52.542796938635405013,11.102205466267848877,-0.85070658730561743432,-53.724251992800660105 79.068375285714893153,43.573992885649197149,55.412544263526797295,80.290198081197203805,44.362549740739744664,56.377949053569309967,77.789989587698684659,42.697925430864948737,54.611105265649989349 -69.804655574262099549,-75.164416059851603791,-36.064994987100398305,-71.429760864378010865,-73.171583357645161527,-37.964990907742866,-68.125578062738711083,-77.141281698615486562,-34.105625902819106443 -45.728138322010600803,84.439434716478004361,4.1669365018606203677,-47.686322383788720458,82.859865477067344841,5.8234019591742569588,-43.788163917854937779,85.909979644584097969,2.3807353387245155929 -24.642592994496201442,-20.248403027653701258,-81.216686312109203527,-26.04039253540680221,-22.159315723504104056,-80.799265550521752743,-23.306075088815880747,-18.415800714835928176,-81.56364592181056139 66.456492478027897164,-99.926031660288600733,86.111894436180605794,68.175190117052991923,-99.917336160567671755,85.633696309111897449,64.691411465653331447,-99.933756053588766122,86.67157390263491834 62.443835614249103116,69.197096955031199172,-78.867098130285697266,64.293088129695121324,67.327315562214081979,-78.071974295184901393,60.555509897958984311,71.13048412961735778,-79.543644595227675609 -1.1684469413012299466,7.212839694693680137,91.128052305430202296,-1.1684469413012299466,8.388410182669755244,89.952481817454128077,-1.1684469413012299466,6.0372692067176050301,92.303622793406276514 75.671220803633303831,-5.401376588270069945,37.715566065162398957,77.05475021385115042,-6.0664682216954997074,35.910361551770179744,74.228875065090392127,-4.8139929153840173015,39.385045201061544162 -10.762621648609599134,-8.917811699211599219,32.311025867238598153,-11.425452104746542048,-9.9854857994445485048,30.340046753193057327,-10.136775826692964841,-7.9594735400413689774,34.271737027077527671 -93.051702063530697728,97.024544421583399867,1.038860343396659891,-93.051702063530697728,97.024544421583399867,1.038860343396659891,-93.051702063530697728,97.024544421583399867,1.038860343396659891 21.640899917110800743,-29.216913320124099585,-34.169085137546097997,22.897453613463671473,-31.145246021968283401,-36.131501469333379362,20.443075344859582998,-27.245287693034477172,-32.199632647025744348 12.258653575554500748,-41.447429498657598401,13.661653874442000856,13.008982422777464194,-42.476614714080433544,14.2301035864883616,11.549933534262281754,-40.317697398776651596,12.99156247158787636 -15.07793175987900014,35.835130885243401622,-42.283663712441899918,-15.990102627710482963,37.381904080138426139,-43.622884458607330771,-14.214938669451440134,34.176090635915336691,-40.775670949091228579 -35.550243454053997993,-86.520223272964400962,79.864286165684504226,-37.347412428636303616,-85.029937169084419679,80.503253627620594557,-33.803165888998357502,-87.88701814594253392,79.130623335234062665 -86.451691016554804037,-74.484560312703294471,-73.452746216207700058,-87.276697500084338799,-72.492594540845146867,-71.858058717130575133,-85.581810537563214325,-76.469919165435072728,-74.846519247071569225 -53.282451629638700297,65.794079983606906126,-65.709276217967300227,-55.271558062191523675,64.123239796755342468,-63.749286838970284919,-51.28461811873851417,67.568639113621870251,-67.679894230863155258 24.058960238471598814,62.688968842849099872,49.788849847391198011,25.430119166154490529,61.266560713325119991,49.754393945791534293,22.748801757620455533,64.225413669385176263,49.817927215446616174 3.5080270376056401638,47.83119633793830161,25.354303885251301409,3.7272516676162132931,48.09442461784710332,23.91486680550154631,3.3018836284138548542,47.536880158766457782,26.969292734935109479 -68.623528536409097001,-7.3156357742846003234,36.812701355665900849,-70.290292536593568684,-8.2078947223966842728,34.951190091608616228,-66.90415011193852024,-6.5221901794700443133,38.553253389079081614 -49.678858881816303494,16.650798590853799652,76.659869588911504934,-51.678753147250482414,18.380144395493299925,77.802802836289529864,-47.684062148290479399,15.038329571268885587,75.353098070435237332 -86.108595458790702537,-91.604690346866803452,84.137368155643301293,-86.943589464065851757,-90.617762520302406415,84.01332194943429954,-85.229375189532973423,-92.48139886689742184,84.282550895969606586 -80.260683968663201426,-0.048428680747747400448,70.104510243982105067,-81.423163098362195456,-0.054127053641923564276,72.001254961836608004,-79.043225070924080455,-0.04336779791684056834,68.144393536894000363 22.716983780264900616,-53.621980641037197302,43.3680380228907012,24.025320396082559427,-53.177405844446397509,42.196160551880211642,21.468598128320653728,-54.119286999455695764,44.351678431804899105 46.353635704144799945,2.641319902613759929,64.286992605775594711,48.340215966432445782,2.9633518515868595422,66.20245981942905189,44.384443954453452363,2.3573900239970257964,62.470681981793916293 -58.339169342070796631,-93.07934241369369488,-69.363273261114997581,-60.257539781866036321,-92.265770332108758112,-67.479248255382358934,-56.402959481771851813,-93.80205541902813593,-71.112327124991182359 92.339081410318598842,-20.573136210441600014,34.258465608581900597,92.957115415483698939,-22.475249290466319252,32.356352528557181358,91.721047405153498744,-18.671023130416880775,35.890703567489978809 96.833262918516993523,-41.997467353940002965,64.558217255398602674,96.833262918516993523,-41.997467353940002965,64.558217255398602674,96.833262918516993523,-41.997467353940002965,64.558217255398602674 -24.645874230191100906,-2.2909145336598197851,-20.932385604828599668,-26.043822299375598561,-2.5693114099531237926,-21.695856771342704405,-23.30920906531633463,-2.0453382215941973143,-20.282314149398064984 -69.660550821572499558,73.647852847352595518,-46.940754354000098658,-71.290840268205627694,71.661563639410587712,-47.479076497119592659,-67.976434235157540797,75.63906386364806167,-46.308339834972677806 29.711865959688999794,-3.9213345851749199511,66.545391455292701721,31.319349675049419801,-4.4033006085072488034,68.51751308663753548,28.163081212690670441,-3.4965776227090152872,64.616352209849566179 42.928531952202298783,37.478062463924302961,-3.3204906154424000952,44.878881365284080118,38.885803545236562684,-1.436417393907104989,41.007066466941232363,35.956415512570899295,-5.0606059078654119787 -77.604763349518194104,2.9758527874946598679,30.294026667252200724,-78.897739544833854097,3.3396508659044918943,28.377988363098538116,-76.253801405538553126,2.6551995573870703637,32.261198683990151892 22.316118888556999877,55.16445199027660351,26.504652854055201772,23.605301467932299886,54.528378325825883621,24.915166534145264166,21.086470079732691829,55.874190477690163448,28.258766066665632621 -29.720003670081499791,-68.581536831334204862,-62.590012000873699094,-31.327787624921917597,-66.740657438889570585,-60.76457432377128498,-28.170906756408562899,-70.494550342853145253,-64.505932478733285507 -88.892067177221193219,33.47042598761620269,-68.656617216765894796,-89.575150380214410006,35.19180452762029887,-66.703314502933878316,-88.171010262673505053,31.651595296964185167,-70.527791105902096547 -1.1151147074997400654,61.63785820826890216,-85.432132100686402509,-1.1843667822795889144,60.309428289272766222,-85.824849023838353901,-1.0499646780058744522,63.078468089762942839,-85.112968310161818408 -65.414152992889299298,96.784434840083093832,68.802160164341302107,-65.414152992889299298,96.784434840083093832,68.802160164341302107,-65.414152992889299298,96.784434840083093832,68.802160164341302107 -67.896111169829993059,-53.034971840679602906,38.595892675221001866,-69.587554046546358677,-52.663785759220743898,36.855485185952112204,-66.153083273413685106,-53.450244925764735626,40.188251369800155999 93.285638187080593298,67.977166874334201907,15.245906868949500534,93.903672192245693395,66.075053794309482669,14.627872863784400437,92.6676041819154932,69.879279954358921145,15.863940874114600632 54.157174145802898124,-90.203342726454096123,-86.354536144062905123,56.11648084850954632,-89.051676609294432296,-86.876254596879732617,52.179647345746836606,-91.226391697176950402,-85.921714564553127502 12.328677996993100408,76.826105592772393038,-12.607849761843700875,13.083083104723298717,74.845288557284547437,-11.843776905634614494,11.61608952825726071,78.768415513609696177,-13.23825061288436622 -5.5342199280858004329,-38.439462892711198094,28.090190654620499799,-5.8803908458875380205,-39.760833548004463012,26.327785352139276398,-5.208582400564047532,-37.006142409610532695,29.978047159021407708 -31.975234579294898651,12.657177308574299346,22.025297023355999926,-33.66236877795773097,14.076802198986669623,21.088737384303652078,-30.343854794449697465,11.359088348372850419,23.102066600775199134 -83.112684125080704689,-97.321148682385697271,-42.930039670318400624,-84.11471111713687776,-97.006230827319370746,-44.073983293177711573,-82.057583805565428747,-97.6008967644795149,-41.610256777690167951 -75.410741381347193624,-71.720643015578403379,43.723274907097199105,-76.806124699778706599,-69.76677896871596829,42.609842588771122962,-73.956527955173996247,-73.705557270217028076,44.65431594767844814 -81.538787623867406751,-62.968447338789701462,50.83668064326050029,-82.635509632776816602,-61.521746799018082186,50.976336836595557145,-80.388312913960419337,-64.529258156126545032,50.719692087816959258 87.590722925961003398,-67.916571069508805181,29.882466048002200409,88.34308291772379107,-66.165827975034900987,27.995445256812072188,86.798513460554787002,-69.788153159440383888,31.736070466081056196 -40.612881397828502372,12.873990042135099898,85.615948773920493409,-42.5209937850875761,14.258368147584658203,85.226703683670152145,-38.745423414456723776,11.585163854110870574,86.071518279737418311 0.84278578869998499457,23.374361312016798564,-3.4574446734041002216,0.89506956210520205452,25.357692041352219547,-1.5825751229783140417,0.79359329676052459135,21.426859526226802899,-5.1856457901383326003 67.31467666104440184,7.216867338865999848,21.967382077127698636,69.025283605376529295,8.0978052456281464089,21.040002953987769985,65.553414737200071727,6.4338562704730914987,23.033726995848283536 75.583177153021097183,76.43485460430379419,-37.85023810341949968,76.970723088830240499,74.449402757816358189,-39.646150824755437725,74.136807531809850502,78.387039140574600538,-35.954976865421507171 -9.4912388827651703593,49.284896394237897255,45.375613681971998403,-10.079216053044698143,49.370027866464646138,44.550698372911185174,-8.9366025368415051844,49.190013662948224749,46.057604956888134495 66.524467198178200533,6.624667020514610094,-47.146981442347197344,68.260287224857933097,7.4367007066114592106,-47.647410554736232768,64.739410097972381664,5.9047396649974634997,-46.559130432027615143 59.191036364063599251,10.344389500096399459,-63.116912497207501076,61.108018848768224984,11.552605998158078648,-61.259409702423759825,57.245047796495981629,9.2482794205813156907,-65.05340433384887433 libminc-libminc-2-3-00/volume_io/000077500000000000000000000000001257462267400166375ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Geometry/000077500000000000000000000000001257462267400204325ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Geometry/colour.c000066400000000000000000000126151257462267400221060ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "internal_volume_io.h" /* ----------------------------- MNI Header ----------------------------------- @NAME : make_Colour @INPUT : r g b @OUTPUT : @RETURNS : VIO_Colour @DESCRIPTION: Packs the three components, which are in the range 0 to 255, into a VIO_Colour type, unsigned long. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Colour make_Colour( int r, int g, int b ) { return( make_rgba_Colour( r, g, b, 255 ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_r_0_1 @INPUT : colour @OUTPUT : @RETURNS : red component @DESCRIPTION: Returns the red component of the colour in the range of 0.0 to 1.0 @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_Colour_r_0_1( VIO_Colour colour ) { return( (VIO_Real) get_Colour_r(colour) / 255.0 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_g_0_1 @INPUT : colour @OUTPUT : @RETURNS : green component @DESCRIPTION: Returns the green component of the colour in the range of 0.0 to 1.0 @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_Colour_g_0_1( VIO_Colour colour ) { return( (VIO_Real) get_Colour_g(colour) / 255.0 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_b_0_1 @INPUT : colour @OUTPUT : @RETURNS : blue component @DESCRIPTION: Returns the blue component of the colour in the range of 0.0 to 1.0 @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_Colour_b_0_1( VIO_Colour colour ) { return( (VIO_Real) get_Colour_b(colour) / 255.0 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_a_0_1 @INPUT : colour @OUTPUT : @RETURNS : alpha component @DESCRIPTION: Returns the alpha (opacity) component of the colour in the range of 0.0 to 1.0 @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_Colour_a_0_1( VIO_Colour colour ) { return( (VIO_Real) get_Colour_a(colour) / 255.0 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_Colour_0_1 @INPUT : r g b @OUTPUT : @RETURNS : VIO_Colour @DESCRIPTION: Takes the three components, each in the range of 0 to 1, and packs them into a colour. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Colour make_Colour_0_1( VIO_Real r, VIO_Real g, VIO_Real b ) { return( make_Colour( (int) (r * 255.0 + 0.5), (int) (g * 255.0 + 0.5), (int) (b * 255.0 + 0.5) ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_rgba_Colour_0_1 @INPUT : r g b a - alpha (opacity) @OUTPUT : @RETURNS : VIO_Colour @DESCRIPTION: Takes the four components, each in the range of 0 to 1, and packs them into a colour. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Colour make_rgba_Colour_0_1( VIO_Real r, VIO_Real g, VIO_Real b, VIO_Real a ) { return( make_rgba_Colour( (int) (r * 255.0 + 0.5), (int) (g * 255.0 + 0.5), (int) (b * 255.0 + 0.5), (int) (a * 255.0 + 0.5) ) ); } libminc-libminc-2-3-00/volume_io/Geometry/colour_def.c000066400000000000000000000107671257462267400227320ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : make_rgba_Colour @INPUT : r g b a @OUTPUT : @RETURNS : VIO_Colour @DESCRIPTION: Packs the four components into a colour. Each component must be in the range 0 to 255. Depending on what graphics library is being linked with, if any, for instance, GL or OpenGL, this library function may be overridden by another to define the correct method of packing bytes into a colour. If no graphics are involved, then the byte order does not matter and the code here can be used. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Colour make_rgba_Colour( int r, int g, int b, int a ) { VIO_Colour c; unsigned char *byte_ptr; c = 0; /* to avoid used-before-set compiler messages */ ASSIGN_PTR(byte_ptr) = (void *) &c; byte_ptr[0] = (unsigned char) a; byte_ptr[1] = (unsigned char) b; byte_ptr[2] = (unsigned char) g; byte_ptr[3] = (unsigned char) r; return( c ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_r @INPUT : colour @OUTPUT : @RETURNS : red component @DESCRIPTION: Returns the red component of the colour in the range 0 to 255. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_Colour_r( VIO_Colour colour ) { unsigned char *b; ASSIGN_PTR(b) = (void *) &colour; return( (int) b[3] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_g @INPUT : colour @OUTPUT : @RETURNS : green component @DESCRIPTION: Returns the green component of the colour in the range 0 to 255. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_Colour_g( VIO_Colour colour ) { unsigned char *b; ASSIGN_PTR(b) = (void *) &colour; return( (int) b[2] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_b @INPUT : colour @OUTPUT : @RETURNS : blue component @DESCRIPTION: Returns the blue component of the colour in the range 0 to 255. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_Colour_b( VIO_Colour colour ) { unsigned char *b; ASSIGN_PTR(b) = (void *) &colour; return( (int) b[1] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_Colour_a @INPUT : colour @OUTPUT : @RETURNS : alpha component @DESCRIPTION: Returns the alpha (opacity) component of the colour in the range 0 to 255. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_Colour_a( VIO_Colour colour ) { unsigned char *b; ASSIGN_PTR(b) = (void *) &colour; return( (int) b[0] ); } libminc-libminc-2-3-00/volume_io/Geometry/gaussian.c000066400000000000000000000173431257462267400224200ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "internal_volume_io.h" /* ----------------------------- MNI Header ----------------------------------- @NAME : scaled_maximal_pivoting_gaussian_elimination @INPUT : n size of matrix, n by n a matrix n_values number of values to solve for @OUTPUT : row permutation array filled in by this function solution on input, the values, on output the solution, size n by n_values @RETURNS : TRUE if successful @DESCRIPTION: Performs scaled maximal pivoting gaussian elimination as a numerically robust method to solve systems of linear equations. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL scaled_maximal_pivoting_gaussian_elimination( int n, int row[], VIO_Real **a, int n_values, VIO_Real **solution ) { int i, j, k, p, v, tmp; VIO_Real *s, val, best_val, m, scale_factor; VIO_BOOL success; ALLOC( s, n ); for_less( i, 0, n ) row[i] = i; for_less( i, 0, n ) { s[i] = VIO_FABS( a[i][0] ); for_less( j, 1, n ) { if( VIO_FABS(a[i][j]) > s[i] ) s[i] = VIO_FABS(a[i][j]); } if( s[i] == 0.0 ) { FREE( s ); return( FALSE ); } } success = TRUE; for_less( i, 0, n-1 ) { p = i; best_val = a[row[i]][i] / s[row[i]]; best_val = VIO_FABS( best_val ); for_less( j, i+1, n ) { val = a[row[j]][i] / s[row[j]]; val = VIO_FABS( val ); if( val > best_val ) { best_val = val; p = j; } } if( a[row[p]][i] == 0.0 ) { success = FALSE; break; } if( i != p ) { tmp = row[i]; row[i] = row[p]; row[p] = tmp; } for_less( j, i+1, n ) { if( a[row[i]][i] == 0.0 ) { success = FALSE; break; } m = a[row[j]][i] / a[row[i]][i]; for_less( k, i+1, n ) a[row[j]][k] -= m * a[row[i]][k]; for_less( v, 0, n_values ) solution[row[j]][v] -= m * solution[row[i]][v]; } if( !success ) break; } if( success && a[row[n-1]][n-1] == 0.0 ) success = FALSE; if( success ) { for( i = n-1; i >= 0; --i ) { for_less( j, i+1, n ) { scale_factor = a[row[i]][j]; for_less( v, 0, n_values ) solution[row[i]][v] -= scale_factor * solution[row[j]][v]; } for_less( v, 0, n_values ) solution[row[i]][v] /= a[row[i]][i]; } } FREE( s ); return( success ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : scaled_maximal_pivoting_gaussian_elimination_real @INPUT : n coefs n_values values @OUTPUT : values has the solution on output @RETURNS : TRUE if successful @DESCRIPTION: Performs gaussian elimination on a type-VIO_Real matrix, first copying it into temporary storage, which is modified as the gaussian elimination is performed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL scaled_maximal_pivoting_gaussian_elimination_real( int n, VIO_Real **coefs, int n_values, VIO_Real **values ) { int i, j, v, *row; VIO_Real **a, **solution; VIO_BOOL success; ALLOC( row, n ); VIO_ALLOC2D( a, n, n ); VIO_ALLOC2D( solution, n, n_values ); for_less( i, 0, n ) { for_less( j, 0, n ) a[i][j] = coefs[i][j]; for_less( v, 0, n_values ) solution[i][v] = values[v][i]; } success = scaled_maximal_pivoting_gaussian_elimination( n, row, a, n_values, solution ); if( success ) { for_less( i, 0, n ) { for_less( v, 0, n_values ) values[v][i] = solution[row[i]][v]; } } VIO_FREE2D( a ); VIO_FREE2D( solution ); FREE( row ); return( success ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : solve_linear_system @INPUT : n coefs - n by n matrix values - size n list @OUTPUT : solution - size n list @RETURNS : TRUE if successful @DESCRIPTION: Solves a linear system of equations, finding the solution t t vector that satisfies [coefs] * [solution] = [values] @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL solve_linear_system( int n, VIO_Real **coefs, VIO_Real values[], VIO_Real solution[] ) { int i; for_less( i, 0, n ) solution[i] = values[i]; return( scaled_maximal_pivoting_gaussian_elimination_real( n, coefs, 1, &solution ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : invert_square_matrix @INPUT : n matrix - n by n matrix @OUTPUT : inverse @RETURNS : TRUE if successful @DESCRIPTION: Computes the inverse of a square matrix. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL invert_square_matrix( int n, VIO_Real **matrix, VIO_Real **inverse ) { VIO_Real tmp; VIO_BOOL success; int i, j; for_less( i, 0, n ) { for_less( j, 0, n ) inverse[i][j] = 0.0; inverse[i][i] = 1.0; } success = scaled_maximal_pivoting_gaussian_elimination_real( n, matrix, n, inverse ); if( success ) { for_less( i, 0, n-1 ) { for_less( j, i+1, n ) { tmp = inverse[i][j]; inverse[i][j] = inverse[j][i]; inverse[j][i] = tmp; } } } return( success ); } libminc-libminc-2-3-00/volume_io/Geometry/inverse.c000066400000000000000000000051051257462267400222520ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : compute_transform_inverse @INPUT : transform @OUTPUT : inverse @RETURNS : TRUE if successful @DESCRIPTION: Computes the inverse of the given transformation matrix. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL compute_transform_inverse( VIO_Transform *transform, VIO_Transform *inverse ) { int i, j; VIO_Real **t, **inv; VIO_BOOL success; /* --- copy the transform to a numerical recipes type matrix */ VIO_ALLOC2D( t, 4, 4 ); VIO_ALLOC2D( inv, 4, 4 ); for_less( i, 0, 4 ) { for_less( j, 0, 4 ) t[i][j] = Transform_elem(*transform,i,j); } success = invert_square_matrix( 4, t, inv ); if( success ) { /* --- copy the resulting numerical recipes matrix to the output argument */ for_less( i, 0, 4 ) { for_less( j, 0, 4 ) { Transform_elem(*inverse,i,j) = inv[i][j]; } } #ifdef DEBUG /* --- check if this really is an inverse, by multiplying */ { VIO_Transform ident; concat_transforms( &ident, transform, inverse ); if( !close_to_identity(&ident) ) { print_error( "Error in compute_transform_inverse\n" ); } } #endif } else make_identity_transform( inverse ); VIO_FREE2D( t ); VIO_FREE2D( inv ); return( success ); } libminc-libminc-2-3-00/volume_io/Geometry/newton.c000066400000000000000000000112011257462267400221030ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #define STEP_RATIO 1.0 /* ----------------------------- MNI Header ----------------------------------- @NAME : newton_root_find @INPUT : n_dimensions - dimensionality of domain and range function - function to find the solution to - takes as arguments the function_data pointer, the position to evaluate, and passes back the values[n_dim] and derivatives[n_dim][n_dim] function_data initial_guess[n_dimensions] desired_values[n_dimensions] function_tolerance delta_tolerance max_iterations @OUTPUT : solution[n_dimensions] @RETURNS : TRUE if successful @DESCRIPTION: Performs a newton root find of a function by taking steps of x' = (desired - f(x)) / grad(f(x)), where x starts at initial_guess and is updated until the f(x) is close to zero. x is passed back in solution. @METHOD : @GLOBALS : @CALLS : @CREATED : May 10, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL newton_root_find( int n_dimensions, void (*function) ( void *, VIO_Real [], VIO_Real [], VIO_Real ** ), void *function_data, VIO_Real initial_guess[], VIO_Real desired_values[], VIO_Real solution[], VIO_Real function_tolerance, VIO_Real delta_tolerance, int max_iterations ) { int iter, dim; VIO_Real *values, **derivatives, *delta, error, best_error, *position; VIO_Real step_size; VIO_BOOL success; ALLOC( position, n_dimensions ); ALLOC( values, n_dimensions ); ALLOC( delta, n_dimensions ); VIO_ALLOC2D( derivatives, n_dimensions, n_dimensions ); /*--- initialize function position to the initial guess */ for_less( dim, 0, n_dimensions ) position[dim] = initial_guess[dim]; iter = 0; success = FALSE; best_error = 0.0; while( max_iterations < 0 || iter < max_iterations ) { ++iter; /*--- evaluate the function and derivatives */ (*function) ( function_data, position, values, derivatives ); /*--- compute the error between the desired values and the current */ error = 0.0; for_less( dim, 0, n_dimensions ) { values[dim] = desired_values[dim] - values[dim]; error += VIO_FABS( values[dim] ); } /*--- if this is best so far, record it */ if( iter == 1 || error < best_error ) { best_error = error; for_less( dim, 0, n_dimensions ) solution[dim] = position[dim]; if( error < function_tolerance ) { success = TRUE; break; } } /*--- find the step (delta) to solve the linear system of function value and derivatives */ if( !solve_linear_system( n_dimensions, derivatives, values, delta ) ) break; /*--- compute the size of the step to see if it is small */ step_size = 0.0; for_less( dim, 0, n_dimensions ) { position[dim] += STEP_RATIO * delta[dim]; step_size += VIO_FABS( delta[dim] ); } if( step_size < delta_tolerance ) { success = TRUE; break; } } /*--- free memory */ FREE( values ); FREE( delta ); VIO_FREE2D( derivatives ); FREE( position ); return( success ); } libminc-libminc-2-3-00/volume_io/Geometry/points.c000066400000000000000000000046311257462267400221160ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : create_orthogonal_vector @INPUT : v @OUTPUT : ortho @RETURNS : @DESCRIPTION: Creates a vector which is orthogonal to v. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Jul. 11, 1995 D. MacDonald - made more numerically robust @MODIFIED : Feb. 8, 1996 D. MacDonald - changed from noncolinear to orthogonal ---------------------------------------------------------------------------- */ VIOAPI void create_orthogonal_vector( VIO_Vector *v, VIO_Vector *ortho ) { VIO_Real x, y, z; x = (VIO_Real) Vector_x(*v); y = (VIO_Real) Vector_y(*v); z = (VIO_Real) Vector_z(*v); fill_Vector( *ortho, y+z, -x-z, y-x ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_two_orthogonal_vectors @INPUT : v @OUTPUT : v1 v2 @RETURNS : @DESCRIPTION: Creates two vectors which are perpendicular to each other and to the given vector. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_two_orthogonal_vectors( VIO_Vector *v, VIO_Vector *v1, VIO_Vector *v2 ) { create_orthogonal_vector( v, v1 ); CROSS_VECTORS( *v2, *v, *v1 ); } libminc-libminc-2-3-00/volume_io/Geometry/splines.c000066400000000000000000000271571257462267400222670ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /*--- Weighting functions which define the splines, all of which are interpolation splines, with the exception of the quadratic spline */ static VIO_Real constant_coefs[1][1] = { { 1.0 } }; static VIO_Real linear_coefs[2][2] = { { 1.0, 0.0 }, { -1.0, 1.0 } }; static VIO_Real quadratic_coefs[3][3] = { { 0.5, 0.5, 0.0 }, { -1.0, 1.0, 0.0 }, { 0.5, -1.0, 0.5 } }; static VIO_Real cubic_coefs[4][4] = { { 0.0, 1.0, 0.0, 0.0 }, { -0.5, 0.0, 0.5, 0.0 }, { 1.0, -2.5, 2.0, -0.5 }, { -0.5, 1.5, -1.5, 0.5 } }; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_linear_spline_coefs @INPUT : @OUTPUT : coefs 2 by 2 array of coefficients @RETURNS : @DESCRIPTION: Passes back the basis matrix of the linear spline. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_linear_spline_coefs( VIO_Real **coefs ) { int i, j; for_less( i, 0, 2 ) for_less( j, 0, 2 ) coefs[i][j] = linear_coefs[i][j]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_quadratic_spline_coefs @INPUT : @OUTPUT : coefs 3 by 3 array of coefficients @RETURNS : @DESCRIPTION: Passes back the basis matrix of the quadratic spline. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_quadratic_spline_coefs( VIO_Real **coefs ) { int i, j; for_less( i, 0, 3 ) for_less( j, 0, 3 ) coefs[i][j] = quadratic_coefs[i][j]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_cubic_spline_coefs @INPUT : @OUTPUT : coefs 4 by 4 array of coefficients @RETURNS : @DESCRIPTION: Passes back the basis matrix of the cubic interpolating (Catmull-Romm) spline. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_cubic_spline_coefs( VIO_Real **coefs ) { int i, j; for_less( i, 0, 4 ) for_less( j, 0, 4 ) coefs[i][j] = cubic_coefs[i][j]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : cubic_interpolate @INPUT : u - position to evaluate, between 0 and 1 v0 - four control vertices v1 v2 v3 @OUTPUT : @RETURNS : interpolated value @DESCRIPTION: Performs cubic interpolation, where a value of u = 0 returns v1, a value of u = 1 returns v2, and intermediate values smoothly interpolate. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real cubic_interpolate( VIO_Real u, VIO_Real v0, VIO_Real v1, VIO_Real v2, VIO_Real v3 ) { VIO_Real coefs[4], value; coefs[0] = v0; coefs[1] = v1; coefs[2] = v2; coefs[3] = v3; evaluate_univariate_interpolating_spline( u, 4, coefs, 0, &value ); return( value ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_univariate_interpolating_spline @INPUT : u degree - 2,3,4 for linear, quadratic, or cubic coefs[degree] - control vertices n_derivs - number of derivatives to compute @OUTPUT : derivs - 1 + n_derivs values and derivatives @RETURNS : @DESCRIPTION: Passes back the interpolated value and n_derivs derivatives in the derivs array. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void evaluate_univariate_interpolating_spline( VIO_Real u, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ) { evaluate_interpolating_spline( 1, &u, degree, 1, coefs, n_derivs, derivs ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_bivariate_interpolating_spline @INPUT : u - position to evaluate v degree - 2,3,4 for linear, quadratic, or cubic coefs - control vertices, size degree * degree n_derivs - number of derivatives to compute @OUTPUT : derivs - (1 + n_derivs) * (1 + n_derivs) values and derivatives @RETURNS : @DESCRIPTION: Passes back the interpolated value and derivatives in the derivs array. derivs is a 1D array that is conceptually 2 dimensional, indexed by dx and dy, where dx and dy range from 0 to n_derivs, indicating which value or derivative. For example 0,0 refers to the interpolated value whereas 1,0 refers to the derivative of the function wrt u. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void evaluate_bivariate_interpolating_spline( VIO_Real u, VIO_Real v, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ) { VIO_Real positions[2]; positions[0] = u; positions[1] = v; evaluate_interpolating_spline( 2, positions, degree, 1, coefs, n_derivs, derivs ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_trivariate_interpolating_spline @INPUT : u - position to evaluate v w degree - 2,3,4 for linear, quadratic, or cubic coefs - control vertices, size degree * degree * degree n_derivs - number of derivatives to compute @OUTPUT : derivs - (1 + n_derivs) * (1 + n_derivs) * (1 + n_derivs) values and derivatives @RETURNS : @DESCRIPTION: Passes back the interpolated value and derivatives in the derivs array. derivs is a 1D array that is conceptually 3 dimensional, indexed by dx, dy, and dz, where dx, dy, and dz each range from 0 to n_derivs, indicating which value or derivative. For example 0,0,0 refers to the interpolated value whereas 1,0,1 refers to the derivative of the function wrt u and w. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void evaluate_trivariate_interpolating_spline( VIO_Real u, VIO_Real v, VIO_Real w, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ) { VIO_Real positions[3]; positions[0] = u; positions[1] = v; positions[2] = w; evaluate_interpolating_spline( 3, positions, degree, 1, coefs, n_derivs, derivs ); } #define MAX_DIMS 100 /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_interpolating_spline @INPUT : n_dims - dimensionality of the spline, >= 1 parameters - u, v, w,... position of spline degree - 2,3,4 for linear, quadratic, or cubic n_values - number of values to interpolate at the point coefs - [n_values]*[degree]*[degree]... control vertices n_derivs - number of derivatives to compute @OUTPUT : derivs - (n_values) * (1 + n_derivs) * (1 + n_derivs) * ... values and derivatives @RETURNS : @DESCRIPTION: Passes back the interpolated value and derivatives in the derivs array. derivs is a 1D array that is conceptually multi-dimensional, indexed by v, dx, dy, dz, etc., where dx, dy, dz, etc. each range from 0 to n_derivs, and v ranges from 0 to n_values-1. For example, if n_dims is 3 and n_values is 4, then the 4D index of derivs[2,0,0,0] refers to the interpolated value of the 3rd component of the 4 valued function. derivs[1,0,1,1] refers to the derivative of the 2nd component of the 4 valued function with respect to v and w. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void evaluate_interpolating_spline( int n_dims, VIO_Real parameters[], int degree, int n_values, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ) { int d, degrees[MAX_DIMS], n_derivs_list[MAX_DIMS]; VIO_Real *bases[MAX_DIMS]; if( degree < 1 || degree > 4 ) { print_error( "evaluate_interpolating_spline: invalid degree: %d\n", degree ); return; } if( n_dims < 1 || n_dims > MAX_DIMS ) { print_error( "evaluate_interpolating_spline: invalid n dims: %d\n", n_dims ); return; } switch( degree ) { case 1: bases[0] = &constant_coefs[0][0]; break; case 2: bases[0] = &linear_coefs[0][0]; break; case 3: bases[0] = &quadratic_coefs[0][0]; break; case 4: bases[0] = &cubic_coefs[0][0]; break; } for_less( d, 1, n_dims ) bases[d] = bases[0]; for_less( d, 0, n_dims ) { degrees[d] = degree; n_derivs_list[d] = n_derivs; } spline_tensor_product( n_dims, parameters, degrees, bases, n_values, coefs, n_derivs_list, derivs ); } libminc-libminc-2-3-00/volume_io/Geometry/tensors.c000077500000000000000000000226111257462267400223000ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : multiply_basis_matrices @INPUT : n_derivs n_degs m1 m2 @OUTPUT : prod @RETURNS : @DESCRIPTION: Performs a matrix multiply of the basis matrix with the powers of the u's positions. Steps through the matrices in the appropriate strides. Could use the more general multiply_matrices below, but is done this way for speed. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void multiply_basis_matrices( int n_derivs, int n_degs, VIO_Real m1[], VIO_Real m2[], VIO_Real prod[] ) { int i, j, k, m2_inc; VIO_Real *m1_ptr, *m1_ptr1; VIO_Real *m2_ptr; VIO_Real sum, *prod_ptr; m1_ptr = m1; prod_ptr = prod; m2_inc = 1 - n_degs * n_degs; for_less( i, 0, n_derivs ) { m2_ptr = m2; for_less( j, 0, n_degs ) { sum = 0.0; m1_ptr1 = m1_ptr; for_less( k, 0, n_degs ) { sum += (*m1_ptr1) * (*m2_ptr); ++m1_ptr1; m2_ptr += n_degs; } m2_ptr += m2_inc; *prod_ptr = sum; ++prod_ptr; } m1_ptr += n_degs; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : multiply_matrices @INPUT : x1 - x size of first matrix y1 - y size of first matrix m1 - first matrix sa1 - x stride of first matrix sb1 - y stride of first matrix : y2 - y size of second matrix (x size must be y1) m2 - second matrix sa2 - x stride of second matrix sb2 - y stride of second matrix sap - x stride of product matrix sbp - y stride of product matrix @OUTPUT : prod - product of m1 * m2 @RETURNS : @DESCRIPTION: Multiplies the two matrices m1 and m2, placing the results in prod. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void multiply_matrices( int x1, int y1, VIO_Real m1[], int sa1, int sb1, int y2, VIO_Real m2[], int sa2, int sb2, VIO_Real prod[], int sap, int sbp ) { int i, j, k; VIO_Real *m1_ptr, *m1_ptr1, *m2_ptr; VIO_Real sum, *prod_ptr; m1_ptr = m1; prod_ptr = prod; sb2 -= y1 * sa2; sap -= y2 * sbp; for_less( i, 0, x1 ) { m2_ptr = m2; for_less( j, 0, y2 ) { sum = 0.0; m1_ptr1 = m1_ptr; for_less( k, 0, y1 ) { sum += (*m1_ptr1) * (*m2_ptr); m1_ptr1 += sb1; m2_ptr += sa2; } *prod_ptr = sum; prod_ptr += sbp; m2_ptr += sb2; } m1_ptr += sa1; prod_ptr += sap; } } #define MAX_DEGREE 4 #define MAX_DIMS 10 #define MAX_TOTAL_VALUES 4000 /* ----------------------------- MNI Header ----------------------------------- @NAME : spline_tensor_product @INPUT : n_dims positions[n_dims] degrees[n_dims] bases[n_dims][degrees[dim]*degrees[dim]] n_values coefs [n_values*degrees[0]*degrees[1]*...] n_derivs[n_dims] @OUTPUT : results[n_values*n_derivs[0]*n_derivs[1]*...] @RETURNS : @DESCRIPTION: Performs the spline tensor product necessary to evaluate. Takes as input the number of dimensions, the position to evaluate, the basis matrices defining the interpolation method, and the control vertices (coefs). The resulting values and derivatives are placed in the 1D array results, conceptually as a (1+n_dims)-D array. @METHOD : @GLOBALS : @CALLS : @CREATED : Jan 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void spline_tensor_product( int n_dims, VIO_Real positions[], int degrees[], VIO_Real *bases[], int n_values, VIO_Real coefs[], int n_derivs[], VIO_Real results[] ) { int deriv, d, k, total_values, src; int ind, prev_ind, max_degree, n_derivs_plus_1, deg; int static_indices[MAX_DIMS]; int *indices, total_derivs; VIO_Real *input_coefs, u_power, u; VIO_Real static_us[MAX_DEGREE*MAX_DEGREE]; VIO_Real static_weights[MAX_DEGREE*MAX_DEGREE]; VIO_Real *us, *weights; VIO_Real *tmp_results[2], *r; VIO_Real static_tmp_results[2][MAX_TOTAL_VALUES]; VIO_BOOL results_alloced; /*--- check arguments */ max_degree = 2; total_values = n_values; total_derivs = 0; for_less( d, 0, n_dims ) { if( degrees[d] < 2 ) { print_error( "spline_tensor_product: Degree %d must be greater than 1.\n", degrees[d] ); return; } if( degrees[d] > max_degree ) max_degree = degrees[d]; if( n_derivs[d] > total_derivs ) total_derivs = n_derivs[d]; total_values *= degrees[d]; } /*--- determine if fixed size storage is large enough, if not allocate memory */ if( n_dims > MAX_DIMS ) { ALLOC( indices, n_dims ); } else { indices = static_indices; } if( max_degree > MAX_DEGREE ) { ALLOC( us, max_degree * max_degree ); ALLOC( weights, max_degree * max_degree ); } else { us = static_us; weights = static_weights; } if( total_values > MAX_TOTAL_VALUES ) { ALLOC( tmp_results[0], total_values ); ALLOC( tmp_results[1], total_values ); results_alloced = TRUE; } else { tmp_results[0] = static_tmp_results[0]; tmp_results[1] = static_tmp_results[1]; results_alloced = FALSE; } input_coefs = coefs; src = 0; /*--- do each dimension */ for_less( d, 0, n_dims ) { deg = degrees[d]; n_derivs_plus_1 = 1 + n_derivs[d]; /*--- fill in the top row of matrix of powers of u = [1 u u^2 u^3 ...] for evaluating values */ u = positions[d]; u_power = 1.0; us[0] = 1.0; for_less( k, 1, deg ) { u_power *= u; us[k] = u_power; } /*--- fill in the rest of the n_derivs_plus_1 by degrees[d] matrix: 1 u u^2 u^3 ... 0 1 2u 3u^2 ... 0 0 2 6u ... ... */ ind = deg; for_less( deriv, 1, n_derivs_plus_1 ) { for_less( k, 0, deriv ) { us[ind] = 0.0; ++ind; } prev_ind = VIO_IJ( deriv-1, deriv-1, deg ); for_less( k, deriv, deg ) { us[ind] = us[prev_ind] * (VIO_Real) k; ++ind; ++prev_ind; } } /*--- multiply the u's matrix by the spline basis to create weights */ multiply_basis_matrices( n_derivs_plus_1, deg, us, bases[d], weights ); total_values /= deg; if( d == n_dims-1 ) r = results; else r = tmp_results[1-src]; /*--- multiply coefficient weights by the coefficients */ multiply_matrices( n_derivs_plus_1, deg, weights, deg, 1, total_values, input_coefs, total_values, 1, r, 1, n_derivs_plus_1 ); src = 1 - src; input_coefs = tmp_results[src]; total_values *= n_derivs_plus_1; } /*--- check to free memory */ if( n_dims > MAX_DIMS ) { FREE( indices ); } if( max_degree > MAX_DEGREE ) { FREE( us ); FREE( weights ); } if( results_alloced ) { FREE( tmp_results[0] ); FREE( tmp_results[1] ); } } libminc-libminc-2-3-00/volume_io/Geometry/transforms.c000066400000000000000000000604411257462267400230010ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : make_identity_transform @INPUT : @OUTPUT : transform @RETURNS : @DESCRIPTION: Fills in the transform with the identity matrix. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void make_identity_transform( VIO_Transform *transform ) { Transform_elem( *transform, 0, 0 ) = 1.0; Transform_elem( *transform, 0, 1 ) = 0.0; Transform_elem( *transform, 0, 2 ) = 0.0; Transform_elem( *transform, 0, 3 ) = 0.0; Transform_elem( *transform, 1, 0 ) = 0.0; Transform_elem( *transform, 1, 1 ) = 1.0; Transform_elem( *transform, 1, 2 ) = 0.0; Transform_elem( *transform, 1, 3 ) = 0.0; Transform_elem( *transform, 2, 0 ) = 0.0; Transform_elem( *transform, 2, 1 ) = 0.0; Transform_elem( *transform, 2, 2 ) = 1.0; Transform_elem( *transform, 2, 3 ) = 0.0; Transform_elem( *transform, 3, 0 ) = 0.0; Transform_elem( *transform, 3, 1 ) = 0.0; Transform_elem( *transform, 3, 2 ) = 0.0; Transform_elem( *transform, 3, 3 ) = 1.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_to_identity @INPUT : transform @OUTPUT : @RETURNS : TRUE if transform is close to identity @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL close_to_identity( VIO_Transform *transform ) { #define TOLERANCE 0.001 VIO_BOOL close; VIO_Real expected_val; int i, j; close = TRUE; for_less( i, 0, 4 ) { for_less( j, 0, 4 ) { if( i == j ) expected_val = 1.0; else expected_val = 0.0; if( Transform_elem(*transform,i,j) < expected_val - TOLERANCE || Transform_elem(*transform,i,j) > expected_val + TOLERANCE ) { close = FALSE; } } } return( close ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_origin @INPUT : transform @OUTPUT : origin @RETURNS : @DESCRIPTION: Passes back the origin of the transform, i.e., where the point (0,0,0) would be transformed to. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_origin( VIO_Transform *transform, VIO_Point *origin ) { fill_Point( *origin, Transform_elem(*transform,0,3), Transform_elem(*transform,1,3), Transform_elem(*transform,2,3) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_origin @INPUT : origin @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the origin of the transform, i.e., where the point (0,0,0) would be transformed to. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_origin( VIO_Transform *transform, VIO_Point *origin ) { Transform_elem(*transform,0,3) = (VIO_Transform_elem_type) Point_x(*origin); Transform_elem(*transform,1,3) = (VIO_Transform_elem_type) Point_y(*origin); Transform_elem(*transform,2,3) = (VIO_Transform_elem_type) Point_z(*origin); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_origin_real @INPUT : transform @OUTPUT : origin @RETURNS : @DESCRIPTION: Passes back the origin of the transform, i.e., where the point (0,0,0) would be transformed to. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_origin_real( VIO_Transform *transform, VIO_Real origin[] ) { origin[VIO_X] = Transform_elem(*transform,0,3); origin[VIO_Y] = Transform_elem(*transform,1,3); origin[VIO_Z] = Transform_elem(*transform,2,3); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_x_axis @INPUT : transform @OUTPUT : x_axis @RETURNS : @DESCRIPTION: Passes back the x axis of the transform, i.e., the vector to which the vector (1,0,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_x_axis( VIO_Transform *transform, VIO_Vector *x_axis ) { fill_Vector( *x_axis, Transform_elem(*transform,0,0), Transform_elem(*transform,1,0), Transform_elem(*transform,2,0) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_x_axis_real @INPUT : transform @OUTPUT : x_axis @RETURNS : @DESCRIPTION: Passes back the x axis of the transform, i.e., the vector to which the vector (1,0,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_x_axis_real( VIO_Transform *transform, VIO_Real x_axis[] ) { x_axis[VIO_X] = Transform_elem(*transform,0,0); x_axis[VIO_Y] = Transform_elem(*transform,1,0); x_axis[VIO_Z] = Transform_elem(*transform,2,0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_x_axis @INPUT : x_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the x axis of the transform, i.e., the vector to which the vector (1,0,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_x_axis( VIO_Transform *transform, VIO_Vector *x_axis ) { Transform_elem(*transform,0,0) = (VIO_Transform_elem_type) Vector_x(*x_axis); Transform_elem(*transform,1,0) = (VIO_Transform_elem_type) Vector_y(*x_axis); Transform_elem(*transform,2,0) = (VIO_Transform_elem_type) Vector_z(*x_axis); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_x_axis_real @INPUT : x_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the x axis of the transform, i.e., the vector to which the vector (1,0,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_x_axis_real( VIO_Transform *transform, VIO_Real x_axis[] ) { Transform_elem(*transform,0,0) = (VIO_Transform_elem_type) x_axis[VIO_X]; Transform_elem(*transform,1,0) = (VIO_Transform_elem_type) x_axis[VIO_Y]; Transform_elem(*transform,2,0) = (VIO_Transform_elem_type) x_axis[VIO_Z]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_y_axis @INPUT : transform @OUTPUT : y_axis @RETURNS : @DESCRIPTION: Passes back the y axis of the transform, i.e., the vector to which the vector (0,1,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_y_axis( VIO_Transform *transform, VIO_Vector *y_axis ) { fill_Vector( *y_axis, Transform_elem(*transform,0,1), Transform_elem(*transform,1,1), Transform_elem(*transform,2,1) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_y_axis_real @INPUT : transform @OUTPUT : y_axis @RETURNS : @DESCRIPTION: Passes back the y axis of the transform, i.e., the vector to which the vector (0,1,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_y_axis_real( VIO_Transform *transform, VIO_Real y_axis[] ) { y_axis[VIO_X] = Transform_elem(*transform,0,1); y_axis[VIO_Y] = Transform_elem(*transform,1,1); y_axis[VIO_Z] = Transform_elem(*transform,2,1); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_y_axis @INPUT : y_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the y axis of the transform, i.e., the vector to which the vector (0,1,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_y_axis( VIO_Transform *transform, VIO_Vector *y_axis ) { Transform_elem(*transform,0,1) = (VIO_Transform_elem_type) Vector_x(*y_axis); Transform_elem(*transform,1,1) = (VIO_Transform_elem_type) Vector_y(*y_axis); Transform_elem(*transform,2,1) = (VIO_Transform_elem_type) Vector_z(*y_axis); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_y_axis_real @INPUT : y_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the y axis of the transform, i.e., the vector to which the vector (0,1,0) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_y_axis_real( VIO_Transform *transform, VIO_Real y_axis[] ) { Transform_elem(*transform,0,1) = (VIO_Transform_elem_type) y_axis[VIO_X]; Transform_elem(*transform,1,1) = (VIO_Transform_elem_type) y_axis[VIO_Y]; Transform_elem(*transform,2,1) = (VIO_Transform_elem_type) y_axis[VIO_Z]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_z_axis @INPUT : transform @OUTPUT : z_axis @RETURNS : @DESCRIPTION: Passes back the z axis of the transform, i.e., the vector to which the vector (0,0,1) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_z_axis( VIO_Transform *transform, VIO_Vector *z_axis ) { fill_Vector( *z_axis, Transform_elem(*transform,0,2), Transform_elem(*transform,1,2), Transform_elem(*transform,2,2) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_z_axis_real @INPUT : transform @OUTPUT : z_axis @RETURNS : @DESCRIPTION: Passes back the z axis of the transform, i.e., the vector to which the vector (0,0,1) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_transform_z_axis_real( VIO_Transform *transform, VIO_Real z_axis[] ) { z_axis[VIO_X] = Transform_elem(*transform,0,2); z_axis[VIO_Y] = Transform_elem(*transform,1,2); z_axis[VIO_Z] = Transform_elem(*transform,2,2); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_z_axis @INPUT : z_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the z axis of the transform, i.e., the vector to which the vector (0,0,1) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_z_axis( VIO_Transform *transform, VIO_Vector *z_axis ) { Transform_elem(*transform,0,2) = (VIO_Transform_elem_type) Vector_x(*z_axis); Transform_elem(*transform,1,2) = (VIO_Transform_elem_type) Vector_y(*z_axis); Transform_elem(*transform,2,2) = (VIO_Transform_elem_type) Vector_z(*z_axis); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_transform_z_axis_real @INPUT : z_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Sets the z axis of the transform, i.e., the vector to which the vector (0,0,1) would be transformed. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_transform_z_axis_real( VIO_Transform *transform, VIO_Real z_axis[] ) { Transform_elem(*transform,0,2) = (VIO_Transform_elem_type) z_axis[VIO_X]; Transform_elem(*transform,1,2) = (VIO_Transform_elem_type) z_axis[VIO_Y]; Transform_elem(*transform,2,2) = (VIO_Transform_elem_type) z_axis[VIO_Z]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_change_to_bases_transform @INPUT : origin x_axis y_axis z_axis @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a transform that translates the point (0,0,0) to the specified origin. The point (1,0,0) is transformed to the specified origin plus the specified x_axis. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void make_change_to_bases_transform( VIO_Point *origin, VIO_Vector *x_axis, VIO_Vector *y_axis, VIO_Vector *z_axis, VIO_Transform *transform ) { Transform_elem( *transform,0,0 ) = (VIO_Transform_elem_type)Vector_x( *x_axis ); Transform_elem( *transform,0,1 ) = (VIO_Transform_elem_type)Vector_x( *y_axis ); Transform_elem( *transform,0,2 ) = (VIO_Transform_elem_type)Vector_x( *z_axis ); Transform_elem( *transform,0,3 ) = (VIO_Transform_elem_type)Point_x( *origin ); Transform_elem( *transform,1,0 ) = (VIO_Transform_elem_type)Vector_y( *x_axis ); Transform_elem( *transform,1,1 ) = (VIO_Transform_elem_type)Vector_y( *y_axis ); Transform_elem( *transform,1,2 ) = (VIO_Transform_elem_type)Vector_y( *z_axis ); Transform_elem( *transform,1,3 ) = (VIO_Transform_elem_type)Point_y( *origin ); Transform_elem( *transform,2,0 ) = (VIO_Transform_elem_type)Vector_z( *x_axis ); Transform_elem( *transform,2,1 ) = (VIO_Transform_elem_type)Vector_z( *y_axis ); Transform_elem( *transform,2,2 ) = (VIO_Transform_elem_type)Vector_z( *z_axis ); Transform_elem( *transform,2,3 ) = (VIO_Transform_elem_type)Point_z( *origin ); Transform_elem( *transform,3,0 ) = 0.0; Transform_elem( *transform,3,1 ) = 0.0; Transform_elem( *transform,3,2 ) = 0.0; Transform_elem( *transform,3,3 ) = 1.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_change_from_bases_transform @INPUT : origin x_axis y_axis z_axis @OUTPUT : @RETURNS : @DESCRIPTION: Makes a change of bases transform, so that points are transformed to be relative to the given axes. For instance the origin is transformed by the change of bases transform to be point (0,0,0). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void make_change_from_bases_transform( VIO_Point *origin, VIO_Vector *x_axis, VIO_Vector *y_axis, VIO_Vector *z_axis, VIO_Transform *transform ) { Transform_elem(*transform,0,0) = (VIO_Transform_elem_type)Vector_x( *x_axis ); Transform_elem(*transform,0,1) = (VIO_Transform_elem_type)Vector_y( *x_axis ); Transform_elem(*transform,0,2) = (VIO_Transform_elem_type)Vector_z( *x_axis ); Transform_elem(*transform,0,3) = (VIO_Transform_elem_type) - DOT_POINT_VECTOR( *origin, *x_axis ); Transform_elem(*transform,1,0) = (VIO_Transform_elem_type)Vector_x( *y_axis ); Transform_elem(*transform,1,1) = (VIO_Transform_elem_type)Vector_y( *y_axis ); Transform_elem(*transform,1,2) = (VIO_Transform_elem_type)Vector_z( *y_axis ); Transform_elem(*transform,1,3) = (VIO_Transform_elem_type) - DOT_POINT_VECTOR( *origin, *y_axis ); Transform_elem(*transform,2,0) = (VIO_Transform_elem_type)Vector_x( *z_axis ); Transform_elem(*transform,2,1) = (VIO_Transform_elem_type)Vector_y( *z_axis ); Transform_elem(*transform,2,2) = (VIO_Transform_elem_type)Vector_z( *z_axis ); Transform_elem(*transform,2,3) = (VIO_Transform_elem_type) - DOT_POINT_VECTOR( *origin, *z_axis ); Transform_elem(*transform,3,0) = 0.0; Transform_elem(*transform,3,1) = 0.0; Transform_elem(*transform,3,2) = 0.0; Transform_elem(*transform,3,3) = 1.0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : concat_transforms @INPUT : t1 t2 @OUTPUT : result @RETURNS : @DESCRIPTION: Concatenates the two transforms returning the result in the argument, 'result'. Correctly handles the case where the result transform is also one of the operands. Transforming a point by the 'result' transform will give the same point as first transforming the point by 't1', then by 't2'. @METHOD : Multiplies result = t2 * t1 @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void concat_transforms( VIO_Transform *result, VIO_Transform *t1, VIO_Transform *t2 ) { int i, j, k; VIO_Real sum; VIO_BOOL result_is_also_an_arg; VIO_Transform tmp, *t; /*--- check if the result transform is same as one of the arguments */ if( result == t1 || result == t2 ) { result_is_also_an_arg = TRUE; t = &tmp; } else { result_is_also_an_arg = FALSE; t = result; } /*--- perform multiplication */ for_less( i, 0, 4 ) { for_less( j, 0, 4 ) { sum = 0.0; for_less( k, 0, 4 ) { sum += Transform_elem( *t2, i, k ) * Transform_elem( *t1, k, j ); } Transform_elem( *t, i, j ) = sum; } } if( result_is_also_an_arg ) *result = tmp; } /* ----------------------------- MNI Header ----------------------------------- @NAME : homogenous_transform_point @INPUT : transform x y z w @OUTPUT : x_trans y_trans z_trans @RETURNS : VIO_Status @DESCRIPTION: Transforms the point (x,y,z,w) by the homogenous transform matrix, resulting in (x_trans,y_trans,z_trans). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status homogenous_transform_point( VIO_Transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real w, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ) { VIO_Real w_trans; *x_trans = Transform_elem(*transform,0,0) * x + Transform_elem(*transform,0,1) * y + Transform_elem(*transform,0,2) * z + Transform_elem(*transform,0,3) * w; *y_trans = Transform_elem(*transform,1,0) * x + Transform_elem(*transform,1,1) * y + Transform_elem(*transform,1,2) * z + Transform_elem(*transform,1,3) * w; *z_trans = Transform_elem(*transform,2,0) * x + Transform_elem(*transform,2,1) * y + Transform_elem(*transform,2,2) * z + Transform_elem(*transform,2,3) * w; w_trans = Transform_elem(*transform,3,0) * x + Transform_elem(*transform,3,1) * y + Transform_elem(*transform,3,2) * z + Transform_elem(*transform,3,3) * w; if( w_trans != 0.0 && w_trans != 1.0 ) { *x_trans /= w_trans; *y_trans /= w_trans; *z_trans /= w_trans; } return VIO_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : transform_point @INPUT : transform x y z @OUTPUT : x_trans y_trans z_trans @RETURNS : VIO_Status @DESCRIPTION: Transforms the point (x,y,z) by the transform matrix, resulting in (x_trans,y_trans,z_trans). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status transform_point( VIO_Transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ) { return homogenous_transform_point( transform, x, y, z, 1.0, x_trans, y_trans, z_trans ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : transform_vector @INPUT : transform x y z @OUTPUT : x_trans y_trans z_trans @RETURNS : @DESCRIPTION: Transforms the vector by the specified transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status transform_vector( VIO_Transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ) { return homogenous_transform_point( transform, x, y, z, 0.0, x_trans, y_trans, z_trans ); } libminc-libminc-2-3-00/volume_io/HeaderFiles000066400000000000000000000011221257462267400207310ustar00rootroot00000000000000# The master header gets installed into $(INSTALL_INCDIR), while the # rest go into a subdirectory, $(INSTALL_INCDIR)/volume_io MASTER_HEADER = Include/volume_io.h VOLIO_HEADERS = \ Include/alloc.h \ Include/arrays.h \ Include/basic.h \ Include/def_math.h \ Include/files.h \ Include/geom_structs.h \ Include/geometry.h \ Include/internal_volume_io.h \ Include/multidim.h \ Include/progress.h \ Include/string_funcs.h \ Include/system_dependent.h \ Include/transforms.h \ Include/vol_io_prototypes.h \ Include/volume.h \ Include/volume_cache.h libminc-libminc-2-3-00/volume_io/Include/000077500000000000000000000000001257462267400202225ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Include/internal_volume_io.h000066400000000000000000000022021257462267400242610ustar00rootroot00000000000000#ifndef VOL_IO_INTERNAL_VOLUME_IO_H #define VOL_IO_INTERNAL_VOLUME_IO_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #ifndef VIOAPI #if defined(_MSC_VER) #define VIOAPI /*__declspec(dllexport)*/ #else #define VIOAPI #endif /* _MSC_VER not defined */ #endif /* VIOAPI not defined */ #include #endif libminc-libminc-2-3-00/volume_io/Include/volume_io.h000066400000000000000000000040161257462267400223720ustar00rootroot00000000000000#ifndef VOL_IO_VOLUME_IO_H #define VOL_IO_VOLUME_IO_H #ifdef __cplusplus extern "C" { #endif /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io.h,v 1.13 2005-05-19 21:19:27 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : volume_io.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Header for Volume IO API @METHOD : @GLOBALS : @CALLS : @CREATED : July 15, 1991 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #ifndef VIO_PREFIX_NAMES #define VIO_PREFIX_NAMES 1 /* Don't allow old-fashioned namespace pollution */ #endif /* VIO_PREFIX_NAMES */ #include #include #include #include #include #include #include #include #include #ifndef VIOAPI #define VIOAPI /*TODO: Could be used for dll linking in Windows*/ #endif /* VIOAPI not defined */ #include #ifdef __cplusplus } #endif #endif /* VOL_IO_VOLUME_IO_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/000077500000000000000000000000001257462267400222205ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Include/volume_io/alloc.h000066400000000000000000000253741257462267400234760ustar00rootroot00000000000000#ifndef VOL_IO_ALLOC_H #define VOL_IO_ALLOC_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/alloc.h,v 1.19 2005-05-19 21:19:27 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A set of macros for allocating 1, 2, and 3 dimensional arrays. @METHOD : Requires the file alloc.c linked in. @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #ifdef NO_DEBUG_ALLOC #define _ALLOC_SOURCE_LINE #define _ALLOC_SOURCE_LINE_ARG_DEF #define _ALLOC_SOURCE_LINE_ARGUMENTS #define PRINT_ALLOC_SOURCE_LINE #else #define _ALLOC_SOURCE_LINE , __FILE__, __LINE__ #define _ALLOC_SOURCE_LINE_ARG_DEF , char filename[], int line_number #define _ALLOC_SOURCE_LINE_ARGUMENTS , filename, line_number #define PRINT_ALLOC_SOURCE_LINE \ print_alloc_source_line( filename, line_number ); #endif /* ----------------------------- MNI Header ----------------------------------- @NAME : ALLOC @INPUT : n_items @OUTPUT : ptr @RETURNS : @DESCRIPTION: Macro to allocate n_items of the type ptr points to, assigning : ptr. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define ALLOC( ptr, n_items ) \ ASSIGN_PTR(ptr) = \ alloc_memory_1d( (size_t) (n_items), \ sizeof(*(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : FREE @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Macro to FREE the ptr. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define FREE( ptr ) \ free_memory_1d( (void **) &(ptr) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : REALLOC @INPUT : ptr : n_items @OUTPUT : @RETURNS : @DESCRIPTION: Macro to change the number of items that ptr points to, assigning : ptr. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define REALLOC( ptr, n_items ) \ realloc_memory( (void **) &(ptr), (size_t) (n_items), \ sizeof(*(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : ALLOC_VAR_SIZED_STRUCT @INPUT : element_type : n_elements @OUTPUT : : ptr @RETURNS : @DESCRIPTION: Macro to allocate a structure with a variable size, assigning : ptr. : To use this, the variable length array must be the last element : of the structure. : : Use: : typedef struct : { : int n_items; : double variable_length_list[1]; : } var_struct; : : var_struct *s_ptr; : : ALLOC_VAR_SIZED_STRUCT( s_ptr, double, 15 ); : : s->n_items = 15; : s->variable_length_list[0] = 1.0; : s->variable_length_list[1] = -2.0; : ... : ... : s->variable_length_list[13] = 1456.0; : s->variable_length_list[14] = 1234.0; @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define ALLOC_VAR_SIZED_STRUCT( ptr, element_type, n_elements ) \ ASSIGN_PTR(ptr) = alloc_memory_in_bytes( \ (size_t) (sizeof(*(ptr))+((size_t)(n_elements)-1) * sizeof(element_type)) \ _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : VIO_ALLOC2D @INPUT : n1 : n2 @OUTPUT : : ptr @RETURNS : @DESCRIPTION: Macro to allocate an n1 by n2 array, assigning : ptr. @METHOD : Allocates a single chunk size n1 by n2, and a list of size n1 : pointers, which are each assigned to point into the single : chunk. Therefore, only 2 malloc's are required. @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_ALLOC2D( ptr, n1, n2 ) \ ASSIGN_PTR(ptr) = alloc_memory_2d( (size_t) (n1), (size_t) (n2), \ sizeof(**(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : VIO_FREE2D @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Macro to free the 2 dimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_FREE2D( ptr ) \ free_memory_2d( (void ***) &(ptr) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : VIO_ALLOC3D @INPUT : n1 : n2 : n3 @OUTPUT : : ptr @RETURNS : @DESCRIPTION: Macro to allocate an n1 by n2 by n3 array, assigning : ptr. @METHOD : Similar to VIO_ALLOC2D, this requires only 3 mallocs. @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_ALLOC3D( ptr, n1, n2, n3 ) \ ASSIGN_PTR(ptr) = alloc_memory_3d( (size_t) (n1), (size_t) (n2), \ (size_t) (n3), sizeof(***(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : VIO_FREE3D @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees a 3 dimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_FREE3D( ptr ) \ free_memory_3d( (void ****) &(ptr) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : ALLOC4D @INPUT : n1 : n2 : n3 : n4 @OUTPUT : : ptr @RETURNS : @DESCRIPTION: Macro to allocate an n1 by n2 by n3 by n4 array, assigning : ptr. @METHOD : Similar to VIO_ALLOC2D, this requires only 4 mallocs. @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_ALLOC4D( ptr, n1, n2, n3, n4 ) \ ASSIGN_PTR(ptr) = alloc_memory_4d( (size_t) (n1), (size_t) (n2), \ (size_t) (n3), (size_t) (n4), \ sizeof(****(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : FREE4D @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees a 4 dimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_FREE4D( ptr ) \ free_memory_4d( (void *****) &(ptr) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : ALLOC5D @INPUT : n1 : n2 : n3 : n4 : n5 @OUTPUT : : ptr @RETURNS : @DESCRIPTION: Macro to allocate an n1 by n2 by n3 by n4 by n5 array, assigning : ptr. @METHOD : Similar to VIO_ALLOC2D, this requires only 5 mallocs. @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_ALLOC5D( ptr, n1, n2, n3, n4, n5 ) \ ASSIGN_PTR(ptr) = alloc_memory_5d( (size_t) (n1), (size_t) (n2), \ (size_t) (n3), (size_t) (n4), (size_t) (n5), \ sizeof(*****(ptr)) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : FREE5D @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees a 5 dimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_FREE5D( ptr ) \ free_memory_5d( (void ******) &(ptr) _ALLOC_SOURCE_LINE ) #endif /*VOL_IO_ALLOC_H*/ libminc-libminc-2-3-00/volume_io/Include/volume_io/arrays.h000066400000000000000000000151531257462267400236770ustar00rootroot00000000000000#ifndef VOL_IO_ARRAYS_H #define VOL_IO_ARRAYS_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/arrays.h,v 1.13 2004-10-04 20:23:51 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : arrays.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Macros for adding to and deleting from arrays. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #define DEFAULT_CHUNK_SIZE 100 /* ----------------------------- MNI Header ----------------------------------- @NAME : SET_ARRAY_SIZE @INPUT : array previous_n_elems new_n_elems - desired new array size chunk_size @OUTPUT : @RETURNS : @DESCRIPTION: Sets the number of items allocated in the array to a multiple of : chunk_size larger than new_n_elems @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define SET_ARRAY_SIZE( array, previous_n_elems, new_n_elems, chunk_size ) \ set_array_size( (void **) (&(array)), sizeof(*(array)), \ (size_t) (previous_n_elems), \ (size_t) (new_n_elems), \ (size_t) (chunk_size) _ALLOC_SOURCE_LINE ) /* ----------------------------- MNI Header ----------------------------------- @NAME : ADD_ELEMENT_TO_ARRAY @INPUT : array : the array to add to : n_elems : current number of items in the array : elem_to_add : the item to add : chunk_size : the chunk_size for allocation @OUTPUT : @RETURNS : @DESCRIPTION: Adds an element to the end of an array, and increments the n_elems @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define ADD_ELEMENT_TO_ARRAY( array, n_elems, elem_to_add, chunk_size) \ { \ SET_ARRAY_SIZE( array, n_elems, (n_elems)+1, chunk_size ); \ (array) [(n_elems)] = (elem_to_add); \ ++(n_elems); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : DELETE_ELEMENT_FROM_ARRAY @INPUT : array : the array to add to : n_elems : current number of items in the array : index_to_remove : the index of the element to delete : chunk_size : the chunk_size for allocation @OUTPUT : @RETURNS : @DESCRIPTION: Deletes an element from an array, sliding down subsequent : elements, and decrements the n_elems @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define DELETE_ELEMENT_FROM_ARRAY( array, n_elems, index_to_remove, chunk_size ) \ { \ (void) memmove( (void *) ((unsigned long) (array) + \ (unsigned long) (index_to_remove) * (unsigned long) sizeof(*(array))), \ (void *) ((unsigned long) (array) + \ ((unsigned long) (index_to_remove)+1)* (unsigned long) sizeof(*(array))),\ ((size_t) (n_elems) - (size_t) (index_to_remove) - 1) * sizeof(*(array)) );\ \ --(n_elems); \ \ SET_ARRAY_SIZE( array, (n_elems)+1, n_elems, chunk_size); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : ADD_ELEMENT_TO_ARRAY_WITH_SIZE @INPUT : array : n_alloced : n_elems : elem_to_add : chunk_size @OUTPUT : @RETURNS : @DESCRIPTION: Adds an element to an array where a separate n_allocated and : n_elems is maintained. n_allocated will always be greater than : or equal to n_elems. This routine is useful so that you don't : have to call SET_ARRAY_SIZE everytime you remove an element, : as in done in DELETE_ELEMENT_FROM_ARRAY @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define ADD_ELEMENT_TO_ARRAY_WITH_SIZE( array, n_alloced, n_elems, elem_to_add, chunk_size ) \ { \ if( (n_elems) >= (n_alloced) ) \ { \ SET_ARRAY_SIZE( array, n_alloced, (n_elems) + 1, chunk_size );\ (n_alloced) = (n_elems)+1; \ } \ (array) [(n_elems)] = (elem_to_add); \ ++(n_elems); \ } #endif /* VOL_IO_ARRAYS_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/basic.h000066400000000000000000000116161257462267400234570ustar00rootroot00000000000000#ifndef VOL_IO_BASIC_H #define VOL_IO_BASIC_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/basic.h,v 1.35 2005-05-19 21:19:27 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : basic.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A set of macros and definitions useful for all MNI programs. @METHOD : @GLOBALS : @CALLS : @CREATED : July 15, 1991 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #include #ifdef __sgi #include /* --- for memcpy, etc. */ #else #include /* --- for memcpy, etc. */ #endif #include #include /* --------- define TRUE and FALSE ------------------------ */ #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif /* These are the internal typedefs, which are aliased to their "classic" * volume_io names below, if the new VIO_PREFIX_NAMES macro is set to * zero. */ typedef char *VIO_STR; typedef int VIO_BOOL; typedef double VIO_Real; typedef signed char VIO_SCHAR; typedef unsigned char VIO_UCHAR; typedef enum { VIO_OK=0, VIO_ERROR, VIO_INTERNAL_ERROR, VIO_END_OF_FILE, VIO_QUIT } VIO_Status; /* --------- gets the address of a 2-d array element in a 1-d array ----- */ #define VIO_IJ( i, j, nj ) ( (i) * (nj) + (j) ) /* --------- gets the address of a 3-d array element in a 1-d array ----- */ #define VIO_IJK( i, j, k, nj, nk ) ( (k) + (nk) * ((j) + (nj) * (i)) ) /* --------- Absolute value, min, and max. Bear in mind that these may evaluate an expression multiple times, i.e., ABS( x - y ), and therefore may be inefficient, or incorrect, i.e, ABS( ++x ); ------------------ */ #define VIO_ABS( x ) ( ((x) > 0) ? (x) : (-(x)) ) #define VIO_FABS( x ) fabs( (double) x ) #define VIO_SIGN( x ) ( ((x) > 0) ? 1 : (((x) < 0) ? -1 : 0) ) #define VIO_FSIGN( x ) ( ((x) > 0.0) ? 1.0 : (((x) < 0.0) ? -1.0 : 0.0) ) #ifdef MAX #undef MAX #endif #define MAX( x, y ) ( ((x) >= (y)) ? (x) : (y) ) #define MAX3( x, y, z ) ( ((x) >= (y)) ? MAX( x, z ) : MAX( y, z ) ) #ifdef MIN #undef MIN #endif #define MIN( x, y ) ( ((x) <= (y)) ? (x) : (y) ) #define MIN3( x, y, z ) ( ((x) <= (y)) ? MIN( x, z ) : MIN( y, z ) ) #define VIO_IS_INT( x ) ((double) (x) == (double) ((int) (x))) #define VIO_FLOOR( x ) ((long) floor(x)) #define VIO_ROUND( x ) VIO_FLOOR( (double) (x) + 0.5 ) #define VIO_CEILING( x ) ((long) ceil(x)) #define VIO_FRACTION( x ) ((double) (x) - (double) VIO_FLOOR(x)) #define VIO_ENV_EXISTS( env ) ( getenv(env) != (char *) 0 ) /* --------- macro to determine the size of a static array, e.g., int array[] = { 1, 3, 9, 5 }; ------------ */ #define VIO_SIZEOF_STATIC_ARRAY( array ) \ (int) ( sizeof(array) / sizeof((array)[0])) /* --------- interpolate between a and b ------------------- */ #define VIO_INTERPOLATE( alpha, a, b ) ((a) + (alpha) * ((b) - (a))) #define VIO_DEG_TO_RAD (M_PI / 180.0) #define VIO_RAD_TO_DEG (180.0 / M_PI) /* for loops */ #define for_less( i, start, end ) for( (i) = (start); (i) < (end); ++(i) ) #define for_down( i, start, end ) for( (i) = (start); (i) >= (end); --(i)) #define for_inclusive( i, start, end ) \ for( (i) = (start); (i) <= (end); ++(i) ) #define for_enum( e, max, type ) \ for( (e) = (type) 0; (e) < (max); (e) = (type) ((int) (e)+1) ) #define CONVERT_INTEGER_RANGE( x1, min1, max1, min2, max2 ) \ ((min2) + (2 * (x1) + 1 - 2 * (min1)) * ((max2) - (min2) + 1) / \ ((max1) - (min1) + 1) / 2) #define HANDLE_INTERNAL_ERROR( X ) \ handle_internal_error( X ) #endif /* VOL_IO_BASIC_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/def_math.h000066400000000000000000000017571257462267400241520ustar00rootroot00000000000000#ifndef VOL_IO_MATH_H #define VOL_IO_MATH_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/def_math.h,v 1.9 2004-10-04 20:23:51 bert Exp $ ---------------------------------------------------------------------------- */ #include #endif libminc-libminc-2-3-00/volume_io/Include/volume_io/files.h000066400000000000000000000031211257462267400234700ustar00rootroot00000000000000#ifndef VOL_IO_FILES_H #define VOL_IO_FILES_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/files.h,v 1.10 2005-05-19 21:19:27 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : files.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Types for use with the general file io routines of the library. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #include typedef enum { ASCII_FORMAT, BINARY_FORMAT } VIO_File_formats; typedef enum { READ_FILE, WRITE_FILE, APPEND_FILE } VIO_IO_types; #endif /* VOL_IO_FILES_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/geom_structs.h000066400000000000000000000202261257462267400251110ustar00rootroot00000000000000#ifndef VOL_IO_GEOM_STRUCTS_H #define VOL_IO_GEOM_STRUCTS_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/geom_structs.h,v 1.24 2007-12-03 14:19:35 rotor Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : geom_structs.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Types and macros for accessing points, vectors, colours, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* Define the structure of a point in world coordinates */ #define VIO_N_DIMENSIONS 3 #define VIO_X 0 #define VIO_Y 1 #define VIO_Z 2 /* ----------------------------- MNI Header ----------------------------------- @NAME : Point type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A 3D point type and macros for manipulation. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ typedef float VIO_Point_coord_type; typedef struct { VIO_Point_coord_type coords[VIO_N_DIMENSIONS]; } VIO_Point; /* --- access the given coordinate of the point */ #define Point_coord( point, coord ) ((point).coords[coord]) /* --- access x, y, or z coordinate of the point */ #define Point_x( point ) Point_coord( point, VIO_X ) #define Point_y( point ) Point_coord( point, VIO_Y ) #define Point_z( point ) Point_coord( point, VIO_Z ) /* --- assign all 3 coordinates of the point */ #define fill_Point( point, x, y, z ) \ { \ Point_x(point) = (VIO_Point_coord_type) (x); \ Point_y(point) = (VIO_Point_coord_type) (y); \ Point_z(point) = (VIO_Point_coord_type) (z); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : Vector type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A 3D vector type and macros for manipulation. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ typedef struct { VIO_Point_coord_type coords[VIO_N_DIMENSIONS]; } VIO_Vector; /* --- access the given coordinate of the vector */ #define Vector_coord( vector, coord ) ((vector).coords[coord]) /* --- access x, y, or z coordinate of the vector */ #define Vector_x( vector ) Vector_coord( vector, VIO_X ) #define Vector_y( vector ) Vector_coord( vector, VIO_Y ) #define Vector_z( vector ) Vector_coord( vector, VIO_Z ) /* --- assign all 3 coordinates of the vector */ #define fill_Vector( vector, x, y, z ) \ { \ Vector_x(vector) = (VIO_Point_coord_type) (x); \ Vector_y(vector) = (VIO_Point_coord_type) (y); \ Vector_z(vector) = (VIO_Point_coord_type) (z); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : Colour type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A 4 by 8-bit component colour type and macros for manipulation. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* bert - redefined 'Colour' to be an 'int' instead of a 'long'. Jason * Lerch found that MNI-Display was displaying garbled images when built * for 64 bits on the SGI. * * Apparently the graphics functions in volume_io rely on Colour being * exactly 4 bytes, so you get weird results on 64-bit architectures * if Colour is a 'long'. */ typedef unsigned int VIO_Colour; #define MULT_COLOURS( prod, c1, c2 ) \ { \ VIO_Real r, g, b, r1, g1, b1, r2, g2, b2; \ r1 = get_Colour_r_0_1(c1); \ g1 = get_Colour_g_0_1(c1); \ b1 = get_Colour_b_0_1(c1); \ r2 = get_Colour_r_0_1(c2); \ g2 = get_Colour_g_0_1(c2); \ b2 = get_Colour_b_0_1(c2); \ r = r1 * r2; \ g = g1 * g2; \ b = b1 * b2; \ (prod) = make_rgba_Colour_0_1( r, g, b, get_Colour_a_0_1(c1) ); \ } /* --- component-wise sum of two colours, returned in sum */ #define ADD_COLOURS( sum, c1, c2 ) \ { \ int _r, _g, _b, _r1, _g1, _b1, _r2, _g2, _b2; \ _r1 = get_Colour_r(c1); \ _g1 = get_Colour_g(c1); \ _b1 = get_Colour_b(c1); \ _r2 = get_Colour_r(c2); \ _g2 = get_Colour_g(c2); \ _b2 = get_Colour_b(c2); \ _r = _r1 + _r2; \ _g = _g1 + _g2; \ _b = _b1 + _b2; \ if( _r > 255 ) _r = 255; \ if( _g > 255 ) _g = 255; \ if( _b > 255 ) _b = 255; \ (sum) = make_rgba_Colour( _r, _g, _b, get_Colour_a(c1) ); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : Surfprop type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A Surface property type and macros for manipulation. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ typedef float VIO_Spr_type; typedef struct { VIO_Spr_type a, d, s; VIO_Spr_type se; VIO_Spr_type t; } VIO_Surfprop; /* --- access the given element of the surface property */ #define Surfprop_a( surfprop ) ((surfprop).a) #define Surfprop_d( surfprop ) ((surfprop).d) #define Surfprop_s( surfprop ) ((surfprop).s) #define Surfprop_se( surfprop ) ((surfprop).se) #define Surfprop_t( surfprop ) ((surfprop).t) /* --- assign all elements of the structure, s */ #define fill_Surfprop( s, amb, diff, spec, spec_exp, trans ) \ { \ Surfprop_a(s) = (VIO_Spr_type) (amb); \ Surfprop_d(s) = (VIO_Spr_type) (diff); \ Surfprop_s(s) = (VIO_Spr_type) (spec); \ Surfprop_se(s) = (VIO_Spr_type) (spec_exp); \ Surfprop_t(s) = (VIO_Spr_type) (trans); \ } /* ----------------------------- MNI Header ----------------------------------- @NAME : Transform_2d type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A 2D transform type and macros for element access. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ typedef double VIO_Transform_elem_type; typedef struct { VIO_Transform_elem_type m2d[2][3]; } VIO_Transform_2d; #define Transform_2d_elem( t, i, j ) ((t).m2d[i][j]) /* ----------------------------- MNI Header ----------------------------------- @NAME : Transform type @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: A 3D transform type and macros for element access. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ typedef struct { VIO_Transform_elem_type m[4][4]; } VIO_Transform; #define Transform_elem( t, i, j ) ((t).m[j][i]) #endif /* VOL_IO_GEOM_STRUCTS_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/geometry.h000066400000000000000000000214221257462267400242250ustar00rootroot00000000000000#ifndef VOL_IO_GEOMETRY_H #define VOL_IO_GEOMETRY_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/geometry.h,v 1.12 2004-10-04 20:23:51 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : geometry.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Macros for performing geometric operations on points and vectors. For most macros, the return value is the first argument. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #include #define Point_coord_cast(x) ((VIO_Point_coord_type) (x)) #define Real_cast(x) ((VIO_Real) (x)) #define RPoint_x( p ) Real_cast(Point_x(p)) #define RPoint_y( p ) Real_cast(Point_y(p)) #define RPoint_z( p ) Real_cast(Point_z(p)) #define RPoint_coord( p, c ) Real_cast(Point_coord(p,c)) #define RVector_x( p ) Real_cast(Vector_x(p)) #define RVector_y( p ) Real_cast(Vector_y(p)) #define RVector_z( p ) Real_cast(Point_z(p)) #define RVector_coord( p, c ) Real_cast(Vector_coord(p,c)) /* --- private point and vector operations for use by public routines */ #define POINT_SCALAR_EXP( result, v, op, scalar ) \ { \ Point_x(result) = Point_coord_cast( RPoint_x(v) op Real_cast((scalar)) ); \ Point_y(result) = Point_coord_cast( RPoint_y(v) op Real_cast((scalar)) ); \ Point_z(result) = Point_coord_cast( RPoint_z(v) op Real_cast((scalar)) ); \ } #define POINT_EXP2( result, v1, op, v2 ) \ { \ Point_x(result) = Point_coord_cast( RPoint_x(v1) op RPoint_x(v2) ); \ Point_y(result) = Point_coord_cast( RPoint_y(v1) op RPoint_y(v2) ); \ Point_z(result) = Point_coord_cast( RPoint_z(v1) op RPoint_z(v2) ); \ } #define POINT_VECTOR_EXP2( result, p, op, v ) \ { \ Point_x(result) = Point_coord_cast( RPoint_x(p) op RVector_x(v) ); \ Point_y(result) = Point_coord_cast( RPoint_y(p) op RVector_y(v) ); \ Point_z(result) = Point_coord_cast( RPoint_z(p) op RVector_z(v) ); \ } #define VECTOR_SCALAR_EXP( result, v, op, scalar ) \ { \ Vector_x(result) = Point_coord_cast( RVector_x(v) op Real_cast(scalar) ); \ Vector_y(result) = Point_coord_cast( RVector_y(v) op Real_cast(scalar) ); \ Vector_z(result) = Point_coord_cast( RVector_z(v) op Real_cast(scalar) ); \ } #define VECTOR_EXP2( result, v1, op, v2 ) \ { \ Vector_x(result) = Point_coord_cast( RVector_x(v1) op RVector_x(v2) ); \ Vector_y(result) = Point_coord_cast( RVector_y(v1) op RVector_y(v2) ); \ Vector_z(result) = Point_coord_cast( RVector_z(v1) op RVector_z(v2) ); \ } /* --- interpolate between two points, 0 results in p1, 1 results in p2 */ #define INTERPOLATE_POINTS( interp, p1, p2, alpha ) \ { \ Point_x(interp) = Point_coord_cast( (1.0-Real_cast(alpha))*RPoint_x(p1) + \ Real_cast(alpha)*RPoint_x(p2) );\ Point_y(interp) = Point_coord_cast( (1.0-Real_cast(alpha))*RPoint_y(p1) + \ Real_cast(alpha)*RPoint_y(p2) );\ Point_z(interp) = Point_coord_cast( (1.0-Real_cast(alpha))*RPoint_z(p1) + \ Real_cast(alpha)*RPoint_z(p2) );\ } /* --- interpolate between two vectors, 0 results in v1, 1 results in v2 */ #define INTERPOLATE_VECTORS( interp, v1, v2, alpha ) \ { \ Vector_x(interp)= Point_coord_cast( (1.0-Real_cast(alpha))*RVector_x(v1) + \ Real_cast(alpha)*RVector_x(v2) );\ Vector_y(interp)= Point_coord_cast( (1.0-Real_cast(alpha))*RVector_y(v1) + \ Real_cast(alpha)*RVector_y(v2) );\ Vector_z(interp)= Point_coord_cast( (1.0-Real_cast(alpha))*RVector_z(v1) + \ Real_cast(alpha)*RVector_z(v2) );\ } /* --- get a point on a ray, returning it as 'point' */ #define GET_POINT_ON_RAY( point, origin, direction, distance ) \ { \ Vector_x(point)= Point_coord_cast( RPoint_x(origin) + \ Real_cast(distance)*RVector_x(direction) );\ Vector_y(point)= Point_coord_cast( RPoint_y(origin) + \ Real_cast(distance)*RVector_y(direction) );\ Vector_z(point)= Point_coord_cast( RPoint_z(origin) + \ Real_cast(distance)*RVector_z(direction) );\ } /* --- add and subtract points and vectors, returning correct type */ #define ADD_POINTS( sum, p1, p2 ) POINT_EXP2( sum, p1, +, p2 ) #define ADD_VECTORS( sum, v1, v2 ) VECTOR_EXP2( sum, v1, +, v2 ) #define ADD_POINT_VECTOR( sum, p, v ) POINT_VECTOR_EXP2( sum, p, +, v ) #define SUB_POINTS( diff, p1, p2 ) POINT_EXP2( diff, p1, -, p2 ) #define SUB_VECTORS( diff, v1, v2 ) VECTOR_EXP2( diff, v1, -, v2 ) #define SUB_POINT_VECTOR( diff, p, v ) POINT_VECTOR_EXP2( diff, p, -, v ) /* --- return the dot product of two points, two vectors, or one of each */ #define DOT_POINTS( p1, p2 ) \ ( RPoint_x(p1)*RPoint_x(p2) + \ RPoint_y(p1)*RPoint_y(p2) + \ RPoint_z(p1)*RPoint_z(p2) ) #define DOT_VECTORS( v1, v2 ) \ ( RVector_x(v1)*RVector_x(v2) + \ RVector_y(v1)*RVector_y(v2) + \ RVector_z(v1)*RVector_z(v2) ) #define DOT_POINT_VECTOR( p, v ) \ ( RPoint_x(p)*RVector_x(v) + \ RPoint_y(p)*RVector_y(v) + \ RPoint_z(p)*RVector_z(v) ) /* --- component-wise scaling of points and vectors, result is in ps/vs */ #define SCALE_POINT( ps, p, scale ) POINT_SCALAR_EXP( ps, p, *, scale ) #define SCALE_VECTOR( vs, v, scale ) VECTOR_SCALAR_EXP( vs, v, *, scale ) /* --- return the magnitude of a vector */ #define MAGNITUDE( v ) Real_cast(sqrt( DOT_VECTORS(v,v) )) /* --- set vn to the value of the vector v, normalized to length 1 */ #define NORMALIZE_VECTOR( vn, v ) \ { \ VIO_Real _mag_; \ \ _mag_ = MAGNITUDE( v ); \ if( _mag_ != 0.0 ) \ SCALE_VECTOR( vn, v, 1.0 / _mag_ ); \ } /* --- set c to the cross product of vectors v1 and v2 */ #define CROSS_VECTORS( c, v1, v2 ) \ { \ Vector_x(c) = Point_coord_cast( RVector_y(v1) * RVector_z(v2) - \ RVector_y(v2) * RVector_z(v1) ); \ Vector_y(c) = Point_coord_cast( RVector_z(v1) * RVector_x(v2) - \ RVector_z(v2) * RVector_x(v1) ); \ Vector_z(c) = Point_coord_cast( RVector_x(v1) * RVector_y(v2) - \ RVector_x(v2) * RVector_y(v1) ); \ } /* --- returns TRUE if the points/vectors are equal */ #define EQUAL_POINTS( p1, p2 ) \ ( Point_x(p1) == Point_x(p2) && \ Point_y(p1) == Point_y(p2) && \ Point_z(p1) == Point_z(p2) ) #define EQUAL_VECTORS( v1, v2 ) \ ( Vector_x(v1) == Vector_x(v2) && \ Vector_y(v1) == Vector_y(v2) && \ Vector_z(v1) == Vector_z(v2) ) /* --- given a plane normal, plane distance, and a point, returns the distance of the point from the plane, in fractions of the normal length */ #define DIST_FROM_PLANE( normal, d, point ) \ ( DOT_POINT_VECTOR(point,normal) - Real_cast(d) ) /* --- converts the 'point' to a 'vector' */ #define CONVERT_POINT_TO_VECTOR( vector, point ) \ fill_Vector( vector, \ Point_x(point), Point_y(point), Point_z(point) ) /* --- converts the 'vector' to a 'point' */ #define CONVERT_VECTOR_TO_POINT( point, vector ) \ fill_Point( point, \ Vector_x(vector), Vector_y(vector), Vector_z(vector) ) #endif /* VOL_IO_GEOMETRY_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/multidim.h000066400000000000000000000267131257462267400242260ustar00rootroot00000000000000#ifndef VOL_IO_MULTI_DIM_H #define VOL_IO_MULTI_DIM_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/multidim.h,v 1.7 2005-05-19 21:19:27 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : multidim.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Multidimensional variable type arrays. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 14, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define VIO_MAX_DIMENSIONS 5 /* -------------------------- Data_types ------------------------- */ typedef enum { VIO_NO_DATA_TYPE, VIO_UNSIGNED_BYTE, VIO_SIGNED_BYTE, VIO_UNSIGNED_SHORT, VIO_SIGNED_SHORT, VIO_UNSIGNED_INT, VIO_SIGNED_INT, VIO_FLOAT, VIO_DOUBLE, VIO_MAX_DATA_TYPE } VIO_Data_types; typedef struct { int n_dimensions; int sizes[VIO_MAX_DIMENSIONS]; VIO_Data_types data_type; void *data; } VIO_multidim_array; /* ------------------------- set value ---------------------------- */ /* --- private macros */ #define SET_ONE( array, type, asterisks, subscripts, value ) \ (((type asterisks) ((array).data)) subscripts = (type) (value)) #define SET_GIVEN_DIM( array, asterisks, subscripts, value ) \ switch( (array).data_type ) \ { \ case VIO_UNSIGNED_BYTE: \ SET_ONE( array, unsigned char, asterisks, subscripts, value);\ break; \ case VIO_SIGNED_BYTE: \ SET_ONE( array, signed char, asterisks, subscripts, value);\ break; \ case VIO_UNSIGNED_SHORT: \ SET_ONE( array, unsigned short, asterisks, subscripts, value);\ break; \ case VIO_SIGNED_SHORT: \ SET_ONE( array, signed short, asterisks, subscripts, value);\ break; \ case VIO_UNSIGNED_INT: \ SET_ONE( array, unsigned int, asterisks, subscripts, value);\ break; \ case VIO_SIGNED_INT: \ SET_ONE( array, signed int, asterisks, subscripts, value);\ break; \ case VIO_FLOAT: \ SET_ONE( array, float, asterisks, subscripts, value);\ break; \ default: \ case VIO_DOUBLE: \ SET_ONE( array, double, asterisks, subscripts, value);\ break; \ } #define GET_MULTIDIM_TYPE_1D( array, type, v0 ) \ (((type *) ((array).data)) [v0]) #define GET_MULTIDIM_TYPE_2D( array, type, v0, v1 ) \ (((type **) ((array).data)) [v0][v1]) #define GET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2 ) \ (((type ***) ((array).data)) [v0][v1][v2]) #define GET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3 ) \ (((type ****) ((array).data)) [v0][v1][v2][v3]) #define GET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4 ) \ (((type *****) ((array).data)) [v0][v1][v2][v3][v4]) #define SET_MULTIDIM_TYPE_1D( array, type, v0, value ) \ (GET_MULTIDIM_TYPE_1D( array, type, v0 ) = (type) (value)) #define SET_MULTIDIM_TYPE_2D( array, type, v0, v1, value ) \ (GET_MULTIDIM_TYPE_2D( array, type, v0, v1 ) = (type) (value)) #define SET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2, value ) \ (GET_MULTIDIM_TYPE_3D( array, type, v0, v1, v2 ) = (type) (value)) #define SET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3, value ) \ (GET_MULTIDIM_TYPE_4D( array, type, v0, v1, v2, v3 ) = (type) (value)) #define SET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4, value ) \ (GET_MULTIDIM_TYPE_5D( array, type, v0, v1, v2, v3, v4 ) = (type) (value)) /* --- public macros to set the [x][y]... voxel of 'array' to 'value' */ #define SET_MULTIDIM_1D( array, x, value ) \ SET_GIVEN_DIM( array, *, [x], value ) #define SET_MULTIDIM_2D( array, x, y, value ) \ SET_GIVEN_DIM( array, **, [x][y], value ) #define SET_MULTIDIM_3D( array, x, y, z, value ) \ SET_GIVEN_DIM( array, ***, [x][y][z], value ) #define SET_MULTIDIM_4D( array, x, y, z, t, value ) \ SET_GIVEN_DIM( array, ****, [x][y][z][t], value ) #define SET_MULTIDIM_5D( array, x, y, z, t, v, value ) \ SET_GIVEN_DIM( array, *****, [x][y][z][t][v], value ) /* --- same as previous, but don't have to know dimensions of volume */ #define SET_MULTIDIM( array, x, y, z, t, v, value ) \ switch( (array).n_dimensions ) \ { \ default: \ case 1: SET_MULTIDIM_1D( array, x, value ); break; \ case 2: SET_MULTIDIM_2D( array, x, y, value ); break; \ case 3: SET_MULTIDIM_3D( array, x, y, z, value ); break; \ case 4: SET_MULTIDIM_4D( array, x, y, z, t, value ); break; \ case 5: SET_MULTIDIM_5D( array, x, y, z, t, v, value ); break; \ } /* ------------------------- get multidim value ------------------------ */ /* --- private macros */ #define GET_ONE( value, vtype, array, type, asterisks, subscripts ) \ (value) = vtype (((type asterisks) ((array).data)) subscripts) #define GET_GIVEN_DIM( value, vtype, array, asterisks, subscripts ) \ switch( (array).data_type ) \ { \ case VIO_UNSIGNED_BYTE: \ GET_ONE( value, vtype, array, unsigned char, asterisks, subscripts );\ break; \ case VIO_SIGNED_BYTE: \ GET_ONE( value, vtype, array, signed char, asterisks, subscripts );\ break; \ case VIO_UNSIGNED_SHORT: \ GET_ONE( value, vtype, array, unsigned short, asterisks, subscripts );\ break; \ case VIO_SIGNED_SHORT: \ GET_ONE( value, vtype, array, signed short, asterisks, subscripts );\ break; \ case VIO_UNSIGNED_INT: \ GET_ONE( value, vtype, array, unsigned int, asterisks, subscripts );\ break; \ case VIO_SIGNED_INT: \ GET_ONE( value, vtype, array, signed int, asterisks, subscripts );\ break; \ case VIO_FLOAT: \ GET_ONE( value, vtype, array, float, asterisks, subscripts );\ break; \ default: \ case VIO_DOUBLE: \ GET_ONE( value, vtype, array, double, asterisks, subscripts );\ break; \ } /* --- public macros to place the [x][y]...'th voxel of 'array' in 'value' */ #define GET_MULTIDIM_1D( value, vtype, array, x ) \ GET_GIVEN_DIM( value, vtype, array, *, [x] ) #define GET_MULTIDIM_2D( value, vtype, array, x, y ) \ GET_GIVEN_DIM( value, vtype, array, **, [x][y] ) #define GET_MULTIDIM_3D( value, vtype, array, x, y, z ) \ GET_GIVEN_DIM( value, vtype, array, ***, [x][y][z] ) #define GET_MULTIDIM_4D( value, vtype, array, x, y, z, t ) \ GET_GIVEN_DIM( value, vtype, array, ****, [x][y][z][t] ) #define GET_MULTIDIM_5D( value, vtype, array, x, y, z, t, v ) \ GET_GIVEN_DIM( value, vtype, array, *****, [x][y][z][t][v] ) /* --- same as previous, but no need to know array dimensions */ #define GET_MULTIDIM( value, vtype, array, x, y, z, t, v ) \ switch( (array).n_dimensions ) \ { \ default: \ case 1: GET_MULTIDIM_1D( value, vtype, array, x ); break; \ case 2: GET_MULTIDIM_2D( value, vtype, array, x, y ); break; \ case 3: GET_MULTIDIM_3D( value, vtype, array, x, y, z ); break; \ case 4: GET_MULTIDIM_4D( value, vtype, array, x, y, z, t ); break; \ case 5: GET_MULTIDIM_5D( value, vtype, array, x, y, z, t, v ); break; \ } /* ------------------------- get multidim ptr ------------------------ */ /* --- private macros */ #define GET_ONE_PTR( ptr, array, type, asterisks, subscripts ) \ (ptr) = (void *) (&(((type asterisks) ((array).data)) subscripts)) #define GET_GIVEN_DIM_PTR( ptr, array, asterisks, subscripts ) \ switch( (array).data_type ) \ { \ case VIO_UNSIGNED_BYTE: \ GET_ONE_PTR( ptr, array, unsigned char, asterisks, subscripts );\ break; \ case VIO_SIGNED_BYTE: \ GET_ONE_PTR( ptr, array, signed char, asterisks, subscripts );\ break; \ case VIO_UNSIGNED_SHORT: \ GET_ONE_PTR( ptr, array, unsigned short, asterisks, subscripts );\ break; \ case VIO_SIGNED_SHORT: \ GET_ONE_PTR( ptr, array, signed short, asterisks, subscripts );\ break; \ case VIO_UNSIGNED_INT: \ GET_ONE_PTR( ptr, array, unsigned int, asterisks, subscripts );\ break; \ case VIO_SIGNED_INT: \ GET_ONE_PTR( ptr, array, signed int, asterisks, subscripts );\ break; \ case VIO_FLOAT: \ GET_ONE_PTR( ptr, array, float, asterisks, subscripts );\ break; \ default: \ case VIO_DOUBLE: \ GET_ONE_PTR( ptr, array, double, asterisks, subscripts );\ break; \ } /* --- public macros to return a pointer to the [x][y]'th voxel of the 'array', and place it in 'ptr' */ #define GET_MULTIDIM_PTR_1D( ptr, array, x ) \ GET_GIVEN_DIM_PTR( ptr, array, *, [x] ) #define GET_MULTIDIM_PTR_2D( ptr, array, x, y ) \ GET_GIVEN_DIM_PTR( ptr, array, **, [x][y] ) #define GET_MULTIDIM_PTR_3D( ptr, array, x, y, z ) \ GET_GIVEN_DIM_PTR( ptr, array, ***, [x][y][z] ) #define GET_MULTIDIM_PTR_4D( ptr, array, x, y, z, t ) \ GET_GIVEN_DIM_PTR( ptr, array, ****, [x][y][z][t] ) #define GET_MULTIDIM_PTR_5D( ptr, array, x, y, z, t, v ) \ GET_GIVEN_DIM_PTR( ptr, array, *****, [x][y][z][t][v] ) /* --- same as previous, but no need to know array dimensions */ #define GET_MULTIDIM_PTR( ptr, array, x, y, z, t, v ) \ switch( (array).n_dimensions ) \ { \ default: \ case 1: GET_MULTIDIM_PTR_1D( ptr, array, x ); break; \ case 2: GET_MULTIDIM_PTR_2D( ptr, array, x, y ); break; \ case 3: GET_MULTIDIM_PTR_3D( ptr, array, x, y, z ); break; \ case 4: GET_MULTIDIM_PTR_4D( ptr, array, x, y, z, t ); break; \ case 5: GET_MULTIDIM_PTR_5D( ptr, array, x, y, z, t, v ); break; \ } #endif /* __MULTIDIM_H__*/ libminc-libminc-2-3-00/volume_io/Include/volume_io/progress.h000066400000000000000000000036311257462267400242400ustar00rootroot00000000000000#ifndef VOL_IO_PROGRESS_H #define VOL_IO_PROGRESS_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/progress.h,v 1.10 2005-05-19 21:19:28 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : progress.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Defines type used for progress reporting. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include typedef struct { VIO_BOOL force_one_line; VIO_BOOL first_msg_displayed; VIO_BOOL one_line_flag; int n_steps; int n_dots_so_far; int total_n_dots; VIO_Real start_time; VIO_Real previous_time; VIO_Real update_rate; VIO_Real sum_xy; VIO_Real sum_xx; VIO_STR title; VIO_Real last_check_time; int check_every; int next_check_step; int last_check_step; } VIO_progress_struct; #endif /* VOL_IO_PROGRESS_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/string_funcs.h000066400000000000000000000033411257462267400250760ustar00rootroot00000000000000#ifndef VOL_IO_STRING_H #define VOL_IO_STRING_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/string_funcs.h,v 1.9 2005-05-19 21:19:28 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : string_funcs.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Macros for string manipulations @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include #define VIO_EXTREMELY_LARGE_STRING_SIZE 10000 #define VIO_END_OF_STRING ((char) 0) #define VIO_COPY_MEMORY( dest, src, n_items ) \ (void) memcpy( (void *) (dest), (void *) (src), \ (size_t) (n_items) * sizeof((src)[0]) ) #endif /* VOL_IO_STRING_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/system_dependent.h000066400000000000000000000031161257462267400257440ustar00rootroot00000000000000#ifndef VOL_IO_SYSTEM_DEPENDENT_H #define VOL_IO_SYSTEM_DEPENDENT_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #if HAVE_FLOAT_H #include #else #if HAVE_VALUES_H #include #ifndef DBL_MAX #define DBL_MAX MAXDOUBLE #endif /* DBL_MAX not defined */ #endif /* HAVE_VALUES_H */ #endif /* HAVE_FLOAT_H */ #include #ifndef EXIT_FAILURE #define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif #ifdef __cplusplus #define ASSIGN_PTR( ptr ) (*((void **) &(ptr))) #else #define ASSIGN_PTR( ptr ) (ptr) #endif /* To allow gcc and clang to warn upon mismatch of format string and parameters. */ #if defined(__GNUC__) && (__GNUC__ >= 4) #define VIO_FORMAT_FUNCTION(func, p1, p2) __attribute__((format(func, p1, p2))) #else #define VIO_FORMAT_FUNCTION(func, p1, p2) #endif #endif libminc-libminc-2-3-00/volume_io/Include/volume_io/transforms.h000066400000000000000000000063431257462267400245750ustar00rootroot00000000000000#ifndef VOL_IO_TRANSFORMS_H #define VOL_IO_TRANSFORMS_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/transforms.h,v 1.14 2005-05-19 21:19:28 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : transforms.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Types for defining general transforms. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* --- the list of supported transform types */ typedef enum { LINEAR, THIN_PLATE_SPLINE, USER_TRANSFORM, CONCATENATED_TRANSFORM, GRID_TRANSFORM } VIO_Transform_types; /* --- the user transformation function */ typedef void (*VIO_User_transform_function)( void *user_data, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ); /* --- the general transformation type */ typedef struct VIO_General_transform { VIO_Transform_types type; VIO_BOOL inverse_flag; /* --- linear transform */ VIO_Transform *linear_transform; VIO_Transform *inverse_linear_transform; /* --- thin-plate spline transform */ int n_points; int n_dimensions; VIO_Real **points; VIO_Real **displacements; /* n_points + n_dim + 1 by */ /* n_dim */ /* --- grid transform */ void *displacement_volume; VIO_STR displacement_volume_file; /* --- user_defined */ void *user_data; size_t size_user_data; VIO_User_transform_function user_transform_function; VIO_User_transform_function user_inverse_transform_function; /* --- concatenated transform */ int n_transforms; struct VIO_General_transform *transforms; } VIO_General_transform; #endif libminc-libminc-2-3-00/volume_io/Include/volume_io/vol_io_prototypes.h000066400000000000000000001563571257462267400262110ustar00rootroot00000000000000#ifndef VOL_IO_PROTOTYPES_H #define VOL_IO_PROTOTYPES_H #include "basic.h" #include "minc2_structs.h" /*transforms*/ VIOAPI VIO_STR get_default_transform_file_suffix( void ); VIOAPI VIO_Status output_transform( FILE *file, const char *filename, int *volume_count_ptr, const char *comments, VIO_General_transform *transform ); VIOAPI VIO_Status input_transform( FILE *file, const char *filename, VIO_General_transform *transform ); VIOAPI VIO_Status output_transform_file( const char *filename, const char *comments, VIO_General_transform *transform ); VIOAPI VIO_Status input_transform_file( const char *filename, VIO_General_transform *transform ); VIOAPI void create_linear_transform( VIO_General_transform *transform, VIO_Transform *linear_transform ); VIOAPI void create_thin_plate_transform_real( VIO_General_transform *transform, int n_dimensions, int n_points, VIO_Real **points, VIO_Real **displacements ); VIOAPI void create_thin_plate_transform( VIO_General_transform *transform, int n_dimensions, int n_points, float **points, float **displacements ); VIOAPI void create_grid_transform( VIO_General_transform *transform, VIO_Volume displacement_volume, VIO_STR displacement_volume_file ); VIOAPI void create_grid_transform_no_copy( VIO_General_transform *transform, VIO_Volume displacement_volume, VIO_STR displacement_volume_file ); VIOAPI void create_user_transform( VIO_General_transform *transform, void *user_data, size_t size_user_data, VIO_User_transform_function transform_function, VIO_User_transform_function inverse_transform_function ); VIOAPI VIO_Transform_types get_transform_type( VIO_General_transform *transform ); VIOAPI int get_n_concated_transforms( VIO_General_transform *transform ); VIOAPI VIO_General_transform *get_nth_general_transform( VIO_General_transform *transform, int n ); VIOAPI VIO_Transform *get_linear_transform_ptr( VIO_General_transform *transform ); VIOAPI VIO_Transform *get_inverse_linear_transform_ptr( VIO_General_transform *transform ); VIOAPI VIO_Status general_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status general_inverse_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status general_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status general_inverse_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI void copy_general_transform( VIO_General_transform *transform, VIO_General_transform *copy ); VIOAPI void invert_general_transform( VIO_General_transform *transform ); VIOAPI void create_inverse_general_transform( VIO_General_transform *transform, VIO_General_transform *inverse ); VIOAPI void concat_general_transforms( VIO_General_transform *first, VIO_General_transform *second, VIO_General_transform *result ); VIOAPI void delete_general_transform( VIO_General_transform *transform ); VIOAPI VIO_Status mni_get_nonwhite_character( FILE *file, char *ch ); VIOAPI VIO_Status mni_skip_expected_character( FILE *file, char expected_ch ); VIOAPI VIO_Status mni_input_line( FILE *file, VIO_STR *string ); VIOAPI VIO_Status mni_input_string( FILE *file, VIO_STR *string, char termination_char1, char termination_char2 ); VIOAPI VIO_Status mni_input_keyword_and_equal_sign( FILE *file, const char keyword[], VIO_BOOL print_error_message ); VIOAPI VIO_Status mni_input_real( FILE *file, VIO_Real *d ); VIOAPI VIO_Status mni_input_reals( FILE *file, int *n, VIO_Real *reals[] ); VIOAPI VIO_Status mni_input_int( FILE *file, int *i ); VIOAPI void output_comments( FILE *file, const char *comments ); VIOAPI VIO_STR get_default_tag_file_suffix( void ); VIOAPI VIO_Status initialize_tag_file_output( FILE *file, VIO_STR comments, int n_volumes ); VIOAPI VIO_Status output_one_tag( FILE *file, int n_volumes, VIO_Real tag_volume1[], VIO_Real tag_volume2[], VIO_Real *weight, int *structure_id, int *patient_id, VIO_STR label ); VIOAPI void terminate_tag_file_output( FILE *file ); VIOAPI VIO_Status output_tag_points( FILE *file, VIO_STR comments, int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], VIO_STR *labels ); VIOAPI void free_tag_points( int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], char **labels ); VIOAPI VIO_Status initialize_tag_file_input( FILE *file, int *n_volumes_ptr ); VIOAPI VIO_Status output_tag_file( VIO_STR filename, VIO_STR comments, int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], VIO_STR labels[] ); VIOAPI VIO_Status input_tag_file( VIO_STR filename, int *n_volumes, int *n_tag_points, VIO_Real ***tags_volume1, VIO_Real ***tags_volume2, VIO_Real **weights, int **structure_ids, int **patient_ids, VIO_STR *labels[] ); VIOAPI VIO_BOOL input_one_tag( FILE *file, int n_volumes, VIO_Real tag_volume1[], VIO_Real tag_volume2[], VIO_Real *weight, int *structure_id, int *patient_id, VIO_STR *label, VIO_Status *status ); VIOAPI VIO_Status input_tag_points( FILE *file, int *n_volumes_ptr, int *n_tag_points, VIO_Real ***tags_volume1, VIO_Real ***tags_volume2, VIO_Real **weights, int **structure_ids, int **patient_ids, VIO_STR *labels[] ); VIOAPI void evaluate_thin_plate_spline( int n_dims, int n_values, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real pos[], VIO_Real values[], VIO_Real **derivs ); VIOAPI VIO_Status thin_plate_spline_transform( int n_dims, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status thin_plate_spline_inverse_transform( int n_dims, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Real thin_plate_spline_U( VIO_Real pos[], VIO_Real landmark[], int n_dims ); VIOAPI VIO_Colour make_rgba_Colour( int r, int g, int b, int a ); VIOAPI int get_Colour_r( VIO_Colour colour ); VIOAPI int get_Colour_g( VIO_Colour colour ); VIOAPI int get_Colour_b( VIO_Colour colour ); VIOAPI int get_Colour_a( VIO_Colour colour ); VIOAPI VIO_Colour make_Colour( int r, int g, int b ); VIOAPI VIO_Real get_Colour_r_0_1( VIO_Colour colour ); VIOAPI VIO_Real get_Colour_g_0_1( VIO_Colour colour ); VIOAPI VIO_Real get_Colour_b_0_1( VIO_Colour colour ); VIOAPI VIO_Real get_Colour_a_0_1( VIO_Colour colour ); VIOAPI VIO_Colour make_Colour_0_1( VIO_Real r, VIO_Real g, VIO_Real b ); VIOAPI VIO_Colour make_rgba_Colour_0_1( VIO_Real r, VIO_Real g, VIO_Real b, VIO_Real a ); VIOAPI VIO_BOOL solve_linear_system( int n, VIO_Real **coefs, VIO_Real values[], VIO_Real solution[] ); VIOAPI VIO_BOOL invert_square_matrix( int n, VIO_Real **matrix, VIO_Real **inverse ); VIOAPI VIO_BOOL newton_root_find( int n_dimensions, void (*function) ( void *, VIO_Real [], VIO_Real [], VIO_Real ** ), void *function_data, VIO_Real initial_guess[], VIO_Real desired_values[], VIO_Real solution[], VIO_Real function_tolerance, VIO_Real delta_tolerance, int max_iterations ); VIOAPI void create_orthogonal_vector( VIO_Vector *v, VIO_Vector *ortho ); VIOAPI void create_two_orthogonal_vectors( VIO_Vector *v, VIO_Vector *v1, VIO_Vector *v2 ); VIOAPI VIO_BOOL compute_transform_inverse( VIO_Transform *transform, VIO_Transform *inverse ); VIOAPI void get_linear_spline_coefs( VIO_Real **coefs ); VIOAPI void get_quadratic_spline_coefs( VIO_Real **coefs ); VIOAPI void get_cubic_spline_coefs( VIO_Real **coefs ); VIOAPI VIO_Real cubic_interpolate( VIO_Real u, VIO_Real v0, VIO_Real v1, VIO_Real v2, VIO_Real v3 ); VIOAPI void evaluate_univariate_interpolating_spline( VIO_Real u, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ); VIOAPI void evaluate_bivariate_interpolating_spline( VIO_Real u, VIO_Real v, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ); VIOAPI void evaluate_trivariate_interpolating_spline( VIO_Real u, VIO_Real v, VIO_Real w, int degree, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ); VIOAPI void evaluate_interpolating_spline( int n_dims, VIO_Real parameters[], int degree, int n_values, VIO_Real coefs[], int n_derivs, VIO_Real derivs[] ); VIOAPI void spline_tensor_product( int n_dims, VIO_Real positions[], int degrees[], VIO_Real *bases[], int n_values, VIO_Real coefs[], int n_derivs[], VIO_Real results[] ); VIOAPI void make_identity_transform( VIO_Transform *transform ); VIOAPI VIO_BOOL close_to_identity( VIO_Transform *transform ); VIOAPI void get_transform_origin( VIO_Transform *transform, VIO_Point *origin ); VIOAPI void set_transform_origin( VIO_Transform *transform, VIO_Point *origin ); VIOAPI void get_transform_origin_real( VIO_Transform *transform, VIO_Real origin[] ); VIOAPI void get_transform_x_axis( VIO_Transform *transform, VIO_Vector *x_axis ); VIOAPI void get_transform_x_axis_real( VIO_Transform *transform, VIO_Real x_axis[] ); VIOAPI void set_transform_x_axis( VIO_Transform *transform, VIO_Vector *x_axis ); VIOAPI void set_transform_x_axis_real( VIO_Transform *transform, VIO_Real x_axis[] ); VIOAPI void get_transform_y_axis( VIO_Transform *transform, VIO_Vector *y_axis ); VIOAPI void get_transform_y_axis_real( VIO_Transform *transform, VIO_Real y_axis[] ); VIOAPI void set_transform_y_axis( VIO_Transform *transform, VIO_Vector *y_axis ); VIOAPI void set_transform_y_axis_real( VIO_Transform *transform, VIO_Real y_axis[] ); VIOAPI void get_transform_z_axis( VIO_Transform *transform, VIO_Vector *z_axis ); VIOAPI void get_transform_z_axis_real( VIO_Transform *transform, VIO_Real z_axis[] ); VIOAPI void set_transform_z_axis( VIO_Transform *transform, VIO_Vector *z_axis ); VIOAPI void set_transform_z_axis_real( VIO_Transform *transform, VIO_Real z_axis[] ); VIOAPI void make_change_to_bases_transform( VIO_Point *origin, VIO_Vector *x_axis, VIO_Vector *y_axis, VIO_Vector *z_axis, VIO_Transform *transform ); VIOAPI void make_change_from_bases_transform( VIO_Point *origin, VIO_Vector *x_axis, VIO_Vector *y_axis, VIO_Vector *z_axis, VIO_Transform *transform ); VIOAPI void concat_transforms( VIO_Transform *result, VIO_Transform *t1, VIO_Transform *t2 ); VIOAPI VIO_Status transform_point( VIO_Transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ); VIOAPI VIO_Status transform_vector( VIO_Transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_trans, VIO_Real *y_trans, VIO_Real *z_trans ); VIOAPI void *alloc_memory_in_bytes( size_t n_bytes _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void *alloc_memory_1d( size_t n_elements, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void *alloc_memory_2d( size_t n1, size_t n2, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void *alloc_memory_3d( size_t n1, size_t n2, size_t n3, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void *alloc_memory_4d( size_t n1, size_t n2, size_t n3, size_t n4, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void *alloc_memory_5d( size_t n1, size_t n2, size_t n3, size_t n4, size_t n5, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void realloc_memory( void **ptr, size_t n_elements, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void free_memory_1d( void **ptr _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void free_memory_2d( void ***ptr _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void free_memory_3d( void ****ptr _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void free_memory_4d( void *****ptr _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI void free_memory_5d( void ******ptr _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI size_t get_total_memory_alloced( void ); VIOAPI VIO_BOOL alloc_checking_enabled( void ); VIOAPI void set_alloc_checking( VIO_BOOL state ); VIOAPI void record_ptr_alloc_check( void *ptr, size_t n_bytes, VIO_STR source_file, int line_number ); VIOAPI void change_ptr_alloc_check( void *old_ptr, void *new_ptr, size_t n_bytes, VIO_STR source_file, int line_number ); VIOAPI VIO_BOOL unrecord_ptr_alloc_check( void *ptr, VIO_STR source_file, int line_number ); VIOAPI void output_alloc_to_file( VIO_STR filename ); VIOAPI void print_alloc_source_line( VIO_STR filename, int line_number ); VIOAPI void set_array_size( void **array, size_t type_size, size_t previous_n_elems, size_t new_n_elems, size_t chunk_size _ALLOC_SOURCE_LINE_ARG_DEF ); VIOAPI VIO_BOOL real_is_double( void ); VIOAPI VIO_BOOL file_exists( VIO_STR filename ); VIOAPI VIO_BOOL file_directory_exists( VIO_STR filename ); VIOAPI VIO_BOOL check_clobber_file( VIO_STR filename ); VIOAPI VIO_BOOL check_clobber_file_default_suffix( VIO_STR filename, VIO_STR default_suffix ); VIOAPI VIO_Status make_backup_file( VIO_STR filename, VIO_STR *backup_filename ); VIOAPI void cleanup_backup_file( VIO_STR filename, VIO_STR backup_filename, VIO_Status status_of_write ); VIOAPI void remove_file( VIO_STR filename ); VIOAPI VIO_Status copy_file( VIO_STR src, VIO_STR dest ); VIOAPI VIO_Status move_file( VIO_STR src, VIO_STR dest ); VIOAPI VIO_STR expand_filename( const char *filename ); VIOAPI VIO_BOOL filename_extension_matches( VIO_STR filename, VIO_STR extension ); VIOAPI VIO_STR remove_directories_from_filename( VIO_STR filename ); VIOAPI VIO_BOOL file_exists_as_compressed( VIO_STR filename, VIO_STR *compressed_filename ); VIOAPI VIO_STR get_temporary_filename( void ); VIOAPI VIO_Status open_file( VIO_STR filename, VIO_IO_types io_type, VIO_File_formats file_format, FILE **file ); VIOAPI VIO_Status open_file_with_default_suffix( const char *filename, VIO_STR default_suffix, VIO_IO_types io_type, VIO_File_formats file_format, FILE **file ); VIOAPI VIO_Status set_file_position( FILE *file, long byte_position ); VIOAPI VIO_Status close_file( FILE *file ); VIOAPI VIO_STR extract_directory( const char *filename ); VIOAPI VIO_STR get_absolute_filename( VIO_STR filename, VIO_STR directory ); VIOAPI VIO_Status flush_file( FILE *file ); VIOAPI VIO_Status input_character( FILE *file, char *ch ); VIOAPI VIO_Status unget_character( FILE *file, char ch ); VIOAPI VIO_Status input_nonwhite_character( FILE *file, char *ch ); VIOAPI VIO_Status output_character( FILE *file, char ch ); VIOAPI VIO_Status skip_input_until( FILE *file, char search_char ); VIOAPI VIO_Status output_string( FILE *file, VIO_STR str ); VIOAPI VIO_Status input_string( FILE *file, VIO_STR *str, char termination_char ); VIOAPI VIO_Status input_quoted_string( FILE *file, VIO_STR *str ); VIOAPI VIO_Status input_possibly_quoted_string( FILE *file, VIO_STR *str ); VIOAPI VIO_Status output_quoted_string( FILE *file, VIO_STR str ); VIOAPI VIO_Status input_binary_data( FILE *file, void *data, size_t element_size, int n ); VIOAPI VIO_Status output_binary_data( FILE *file, void *data, size_t element_size, int n ); VIOAPI VIO_Status input_newline( FILE *file ); VIOAPI VIO_Status output_newline( FILE *file ); VIOAPI VIO_Status input_line( FILE *file, VIO_STR *line ); VIOAPI VIO_Status input_boolean( FILE *file, VIO_BOOL *b ); VIOAPI VIO_Status output_boolean( FILE *file, VIO_BOOL b ); VIOAPI VIO_Status input_short( FILE *file, short *s ); VIOAPI VIO_Status output_short( FILE *file, short s ); VIOAPI VIO_Status input_unsigned_short( FILE *file, unsigned short *s ); VIOAPI VIO_Status output_unsigned_short( FILE *file, unsigned short s ); VIOAPI VIO_Status input_int( FILE *file, int *i ); VIOAPI VIO_Status output_int( FILE *file, int i ); VIOAPI VIO_Status input_real( FILE *file, VIO_Real *r ); VIOAPI VIO_Status output_real( FILE *file, VIO_Real r ); VIOAPI VIO_Status input_float( FILE *file, float *f ); VIOAPI VIO_Status output_float( FILE *file, float f ); VIOAPI VIO_Status input_double( FILE *file, double *d ); VIOAPI VIO_Status output_double( FILE *file, double d ); VIOAPI VIO_Status io_binary_data( FILE *file, VIO_IO_types io_flag, void *data, size_t element_size, int n ); VIOAPI VIO_Status io_newline( FILE *file, VIO_IO_types io_flag, VIO_File_formats format ); VIOAPI VIO_Status io_quoted_string( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_STR *str ); VIOAPI VIO_Status io_boolean( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_BOOL *b ); VIOAPI VIO_Status io_short( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, short *short_int ); VIOAPI VIO_Status io_unsigned_short( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, unsigned short *unsigned_short ); VIOAPI VIO_Status io_unsigned_char( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, unsigned char *c ); VIOAPI VIO_Status io_int( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int *i ); VIOAPI VIO_Status io_real( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_Real *r ); VIOAPI VIO_Status io_float( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, float *f ); VIOAPI VIO_Status io_double( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, double *d ); VIOAPI VIO_Status io_ints( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int n, int *ints[] ); VIOAPI VIO_Status io_unsigned_chars( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int n, unsigned char *unsigned_chars[] ); VIOAPI void set_print_function( void (*function) ( VIO_STR ) ); VIOAPI void push_print_function( void ); VIOAPI void pop_print_function( void ); VIOAPI void print( VIO_STR format, ... ); VIOAPI void set_print_error_function( void (*function) ( char [] ) ); VIOAPI void push_print_error_function( void ); VIOAPI void pop_print_error_function( void ); VIOAPI void print_error( char format[], ... ) VIO_FORMAT_FUNCTION(printf, 1, 2); VIOAPI void handle_internal_error( char str[] ); VIOAPI void abort_if_allowed( void ); VIOAPI void initialize_progress_report( VIO_progress_struct *progress, VIO_BOOL one_line_only, int n_steps, VIO_STR title ); VIOAPI void update_progress_report( VIO_progress_struct *progress, int current_step ); VIOAPI void terminate_progress_report( VIO_progress_struct *progress ); VIOAPI VIO_STR alloc_string( size_t length ); VIOAPI VIO_STR create_string( const char *initial ); VIOAPI void delete_string( VIO_STR string ); VIOAPI VIO_STR concat_strings( VIO_STR str1, VIO_STR str2 ); VIOAPI void replace_string( VIO_STR *string, VIO_STR new_string ); VIOAPI void concat_char_to_string( VIO_STR *string, char ch ); VIOAPI void concat_to_string( VIO_STR *string, VIO_STR str2 ); VIOAPI int string_length( const char *string ); VIOAPI VIO_BOOL equal_strings( const char *str1, const char *str2 ); VIOAPI VIO_BOOL is_lower_case( char ch ); VIOAPI VIO_BOOL is_upper_case( char ch ); VIOAPI char get_lower_case( char ch ); VIOAPI char get_upper_case( char ch ); VIOAPI VIO_BOOL string_ends_in( VIO_STR string, VIO_STR ending ); VIOAPI VIO_STR strip_outer_blanks( VIO_STR str ); VIOAPI int find_character( VIO_STR string, char ch ); VIOAPI void make_string_upper_case( VIO_STR string ); VIOAPI VIO_BOOL blank_string( VIO_STR string ); VIOAPI VIO_Real current_cpu_seconds( void ); VIOAPI VIO_Real current_realtime_seconds( void ); VIOAPI VIO_STR format_time( VIO_STR format, VIO_Real seconds ); VIOAPI void print_time( VIO_STR format, VIO_Real seconds ); VIOAPI VIO_STR get_clock_time( void ); VIOAPI void sleep_program( VIO_Real seconds ); VIOAPI VIO_STR get_date( void ); /*The rest of transform functions*/ VIOAPI VIO_Real convert_voxel_to_value( VIO_Volume volume, VIO_Real voxel ); VIOAPI VIO_Real convert_value_to_voxel( VIO_Volume volume, VIO_Real value ); VIOAPI VIO_Real get_volume_voxel_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4 ); VIOAPI VIO_Real get_volume_real_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4 ); VIOAPI void set_volume_voxel_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, VIO_Real voxel ); VIOAPI void set_volume_real_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, VIO_Real value ); VIOAPI void set_volume_interpolation_tolerance( VIO_Real tolerance ); VIOAPI int evaluate_volume( VIO_Volume volume, VIO_Real voxel[], VIO_BOOL interpolating_dimensions[], int degrees_continuity, VIO_BOOL use_linear_at_edge, VIO_Real outside_value, VIO_Real values[], VIO_Real **first_deriv, VIO_Real ***second_deriv ); VIOAPI void evaluate_volume_in_world( VIO_Volume volume, VIO_Real x, VIO_Real y, VIO_Real z, int degrees_continuity, VIO_BOOL use_linear_at_edge, VIO_Real outside_value, VIO_Real values[], VIO_Real deriv_x[], VIO_Real deriv_y[], VIO_Real deriv_z[], VIO_Real deriv_xx[], VIO_Real deriv_xy[], VIO_Real deriv_xz[], VIO_Real deriv_yy[], VIO_Real deriv_yz[], VIO_Real deriv_zz[] ); VIOAPI void convert_voxels_to_values( VIO_Volume volume, int n_voxels, VIO_Real voxels[], VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ); VIOAPI void get_volume_value_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ); VIOAPI void get_voxel_values_5d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ); VIOAPI void get_voxel_values_4d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ); VIOAPI void get_voxel_values_3d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ); VIOAPI void get_voxel_values_2d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ); VIOAPI void get_voxel_values_1d( VIO_Data_types data_type, void *void_ptr, int step0, int n0, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ); VIOAPI void get_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real voxels[] ); VIOAPI VIO_Status initialize_free_format_input( VIO_STR filename, VIO_Volume volume, volume_input_struct *volume_input ); VIOAPI void delete_free_format_input( volume_input_struct *volume_input ); VIOAPI VIO_BOOL input_more_free_format_file( VIO_Volume volume, volume_input_struct *volume_input, VIO_Real *fraction_done ); VIOAPI int get_minc_file_n_dimensions( VIO_STR filename ); VIOAPI int get_minc2_file_n_dimensions( VIO_STR filename ); VIOAPI Minc_file initialize_minc_input_from_minc_id( int minc_id, VIO_Volume volume, minc_input_options *options ); VIOAPI Minc_file initialize_minc_input( VIO_STR filename, VIO_Volume volume, minc_input_options *options ); VIOAPI Minc_file initialize_minc2_input( VIO_STR filename, VIO_Volume volume, minc_input_options *options ); VIOAPI int get_n_input_volumes( Minc_file file ); VIOAPI VIO_Status close_minc_input( Minc_file file ); VIOAPI VIO_Status close_minc2_input( Minc_file file ); VIOAPI VIO_Status input_minc_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int start[], int count[] ); VIOAPI VIO_BOOL input_more_minc_file( Minc_file file, VIO_Real *fraction_done ); VIOAPI VIO_BOOL input_more_minc2_file( Minc_file file, VIO_Real *fraction_done ); VIOAPI VIO_BOOL advance_input_volume( Minc_file file ); VIOAPI void reset_input_volume( Minc_file file ); VIOAPI int get_minc_file_id( Minc_file file ); VIOAPI void set_default_minc_input_options( minc_input_options *options ); VIOAPI void set_minc_input_promote_invalid_to_zero_flag( minc_input_options *options, VIO_BOOL flag ); VIOAPI void set_minc_input_promote_invalid_to_min_flag( minc_input_options *options, VIO_BOOL flag ); VIOAPI void set_minc_input_vector_to_scalar_flag( minc_input_options *options, VIO_BOOL flag ); VIOAPI void set_minc_input_vector_to_colour_flag( minc_input_options *options, VIO_BOOL flag ); VIOAPI void set_minc_input_colour_dimension_size( minc_input_options *options, int size ); VIOAPI void set_minc_input_colour_max_dimension_size( minc_input_options *options, int size ); VIOAPI void set_minc_input_colour_indices( minc_input_options *options, int indices[4] ); VIOAPI void set_minc_input_user_real_range( minc_input_options *options, double minimum, double maximum ); VIOAPI VIO_Status start_volume_input( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], nc_type volume_nc_data_type, VIO_BOOL volume_signed_flag, VIO_Real volume_voxel_min, VIO_Real volume_voxel_max, VIO_BOOL create_volume_flag, VIO_Volume *volume, minc_input_options *options, volume_input_struct *input_info ); VIOAPI void delete_volume_input( volume_input_struct *input_info ); VIOAPI VIO_BOOL input_more_of_volume( VIO_Volume volume, volume_input_struct *input_info, VIO_Real *fraction_done ); VIOAPI void cancel_volume_input( VIO_Volume volume, volume_input_struct *input_info ); VIOAPI VIO_Status input_volume( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], nc_type volume_nc_data_type, VIO_BOOL volume_signed_flag, VIO_Real volume_voxel_min, VIO_Real volume_voxel_max, VIO_BOOL create_volume_flag, VIO_Volume *volume, minc_input_options *options ); VIOAPI Minc_file get_volume_input_minc_file( volume_input_struct *volume_input ); VIOAPI void create_empty_multidim_array( VIO_multidim_array *array, int n_dimensions, VIO_Data_types data_type ); VIOAPI VIO_Data_types get_multidim_data_type( VIO_multidim_array *array ); VIOAPI void set_multidim_data_type( VIO_multidim_array *array, VIO_Data_types data_type ); VIOAPI int get_type_size( VIO_Data_types type ); VIOAPI void get_type_range( VIO_Data_types type, VIO_Real *min_value, VIO_Real *max_value ); VIOAPI VIO_BOOL set_multidim_n_dimensions( VIO_multidim_array *array, int n_dimensions); VIOAPI void set_multidim_sizes( VIO_multidim_array *array, int sizes[] ); VIOAPI void get_multidim_sizes( VIO_multidim_array *array, int sizes[] ); VIOAPI VIO_BOOL multidim_array_is_alloced( VIO_multidim_array *array ); VIOAPI void alloc_multidim_array( VIO_multidim_array *array ); VIOAPI void create_multidim_array( VIO_multidim_array *array, int n_dimensions, int sizes[], VIO_Data_types data_type ); VIOAPI void delete_multidim_array( VIO_multidim_array *array ); VIOAPI int get_multidim_n_dimensions( VIO_multidim_array *array ); VIOAPI void copy_multidim_data_reordered( int type_size, void *void_dest_ptr, int n_dest_dims, int dest_sizes[], void *void_src_ptr, int n_src_dims, int src_sizes[], int counts[], int to_dest_index[], VIO_BOOL use_src_order ); VIOAPI void copy_multidim_reordered( VIO_multidim_array *dest, int dest_ind[], VIO_multidim_array *src, int src_ind[], int counts[], int to_dest_index[] ); VIOAPI Minc_file initialize_minc_output( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], int sizes[], nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_General_transform *voxel_to_world_transform, VIO_Volume volume_to_attach, minc_output_options *options ); VIOAPI Minc_file initialize_minc2_output( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], int sizes[], nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_General_transform *voxel_to_world_transform, VIO_Volume volume_to_attach, minc_output_options *options ); VIOAPI VIO_Status copy_auxiliary_data_from_minc_file( Minc_file file, VIO_STR filename, VIO_STR history_string ); VIOAPI VIO_Status copy_auxiliary_data_from_minc2_file( Minc_file file, VIO_STR filename, VIO_STR history_string ); VIOAPI VIO_Status copy_auxiliary_data_from_open_minc_file( Minc_file file, int src_cdfid, VIO_STR history_string ); VIOAPI VIO_Status copy_auxiliary_data_from_open_minc2_file( Minc_file file, mihandle_t minc_id, VIO_STR history_string ); VIOAPI VIO_Status add_minc_history( Minc_file file, VIO_STR history_string ); VIOAPI VIO_Status add_minc2_history( Minc_file file, VIO_STR history_string ); VIOAPI VIO_Status set_minc_output_random_order( Minc_file file ); VIOAPI VIO_Status set_minc2_output_random_order( Minc_file file ); VIOAPI VIO_Status output_minc_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int file_start[], int file_count[] ); VIOAPI VIO_Status output_volume_to_minc_file_position( Minc_file file, VIO_Volume volume, int volume_count[], long file_start[] ); VIOAPI VIO_Status output_minc_volume( Minc_file file ); VIOAPI VIO_Status close_minc_output( Minc_file file ); VIOAPI VIO_Status output_minc2_volume( Minc_file file ); VIOAPI VIO_Status close_minc2_output( Minc_file file ); VIOAPI void set_default_minc_output_options( minc_output_options *options ); VIOAPI void copy_minc_output_options( minc_output_options *src, minc_output_options *dest ); VIOAPI void delete_minc_output_options( minc_output_options *options ); VIOAPI void set_minc_output_dimensions_order( minc_output_options *options, int n_dimensions, VIO_STR dimension_names[] ); VIOAPI void set_minc_output_real_range( minc_output_options *options, VIO_Real real_min, VIO_Real real_max ); VIOAPI void set_minc_output_use_volume_starts_and_steps_flag( minc_output_options *options, VIO_BOOL flag ); VIOAPI VIO_Status get_file_dimension_names( VIO_STR filename, int *n_dims, VIO_STR *dim_names[] ); VIOAPI VIO_STR *create_output_dim_names( VIO_Volume volume, VIO_STR original_filename, minc_output_options *options, int file_sizes[] ); VIOAPI VIO_Status copy_volume_auxiliary_and_history( Minc_file minc_file, VIO_STR filename, VIO_STR original_filename, VIO_STR history ); VIOAPI VIO_Status output_modified_volume( VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_Volume volume, VIO_STR original_filename, VIO_STR history, minc_output_options *options ); VIOAPI VIO_Status output_volume( VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_Volume volume, VIO_STR history, minc_output_options *options ); VIOAPI void convert_values_to_voxels( VIO_Volume volume, int n_voxels, VIO_Real values[], VIO_Real voxels[] ); VIOAPI void set_volume_value_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void set_volume_value_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void set_volume_value_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ); VIOAPI void set_volume_value_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ); VIOAPI void set_volume_value_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ); VIOAPI void set_volume_value_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ); VIOAPI void set_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real voxels[] ); VIOAPI void set_n_bytes_cache_threshold( int threshold ); VIOAPI int get_n_bytes_cache_threshold( void ); VIOAPI void set_default_max_bytes_in_cache( int max_bytes ); VIOAPI int get_default_max_bytes_in_cache( void ); VIOAPI void set_default_cache_block_sizes( int block_sizes[] ); VIOAPI void set_cache_block_sizes_hint( VIO_Cache_block_size_hints hint ); VIOAPI void initialize_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ); VIOAPI VIO_BOOL volume_cache_is_alloced( VIO_volume_cache_struct *cache ); VIOAPI void flush_volume_cache( VIO_Volume volume ); VIOAPI void delete_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ); VIOAPI void set_volume_cache_block_sizes( VIO_Volume volume, int block_sizes[] ); VIOAPI void set_volume_cache_size( VIO_Volume volume, int max_memory_bytes ); VIOAPI void set_cache_output_volume_parameters( VIO_Volume volume, VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_STR original_filename, VIO_STR history, minc_output_options *options ) ; VIOAPI void open_cache_volume_input_file( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_STR filename, minc_input_options *options ); VIOAPI void cache_volume_range_has_changed( VIO_Volume volume ); VIOAPI void set_cache_volume_file_offset( VIO_volume_cache_struct *cache, VIO_Volume volume, long file_offset[] ); VIOAPI VIO_Real get_cached_volume_voxel( VIO_Volume volume, int x, int y, int z, int t, int v ); VIOAPI void set_cached_volume_voxel( VIO_Volume volume, int x, int y, int z, int t, int v, VIO_Real value ); VIOAPI VIO_BOOL cached_volume_has_been_modified( VIO_volume_cache_struct *cache ); VIOAPI VIO_BOOL volume_is_cached( VIO_Volume volume ); VIOAPI void set_volume_cache_debugging( VIO_Volume volume, int output_every ); VIOAPI VIO_STR *get_default_dim_names( int n_dimensions ); VIOAPI VIO_BOOL convert_dim_name_to_spatial_axis( VIO_STR name, int *axis ); VIOAPI mitype_t vio_type_to_minc2_type( VIO_Data_types vio_data_type); VIOAPI VIO_Data_types minc2_type_to_vio_type( mitype_t minc_data_type); VIOAPI mitype_t nc_type_to_minc2_type( nc_type nc_data_type, VIO_BOOL signed_flag ); VIOAPI VIO_Data_types minc2_type_to_vio_type( mitype_t minc_data_type); VIOAPI VIO_Volume create_volume( int n_dimensions, VIO_STR dimension_names[], nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI void set_volume_type( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI void set_volume_type2( VIO_Volume volume, mitype_t minc_data_type, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI nc_type get_volume_nc_data_type( VIO_Volume volume, VIO_BOOL *signed_flag ); VIOAPI mitype_t get_volume_minc2_data_type( VIO_Volume volume); VIOAPI VIO_Data_types get_volume_data_type( VIO_Volume volume ); VIOAPI void set_rgb_volume_flag( VIO_Volume volume, VIO_BOOL flag ); VIOAPI VIO_BOOL is_an_rgb_volume( VIO_Volume volume ); VIOAPI void alloc_volume_data( VIO_Volume volume ); VIOAPI VIO_BOOL volume_is_alloced( VIO_Volume volume ); VIOAPI void free_volume_data( VIO_Volume volume ); VIOAPI void delete_volume( VIO_Volume volume ); VIOAPI int get_volume_n_dimensions( VIO_Volume volume ); VIOAPI VIO_BOOL set_volume_n_dimensions( VIO_Volume volume, int n_dimensions); VIOAPI void get_volume_sizes( VIO_Volume volume, int sizes[] ); VIOAPI void set_volume_sizes( VIO_Volume volume, int sizes[] ); VIOAPI unsigned int get_volume_total_n_voxels( VIO_Volume volume ); VIOAPI void compute_world_transform( int spatial_axes[VIO_N_DIMENSIONS], VIO_Real separations[], VIO_Real direction_cosines[][VIO_N_DIMENSIONS], VIO_Real starts[], VIO_General_transform *world_transform ); VIOAPI void convert_transform_to_starts_and_steps( VIO_General_transform *transform, int n_volume_dimensions, VIO_Real step_signs[], int spatial_axes[], VIO_Real starts[], VIO_Real steps[], VIO_Real dir_cosines[][VIO_N_DIMENSIONS] ); VIOAPI void set_voxel_to_world_transform( VIO_Volume volume, VIO_General_transform *transform ); VIOAPI VIO_General_transform *get_voxel_to_world_transform( VIO_Volume volume ); VIOAPI VIO_STR *get_volume_dimension_names( VIO_Volume volume ); VIOAPI void delete_dimension_names( VIO_Volume volume, VIO_STR dimension_names[] ); VIOAPI VIO_STR get_volume_space_type( VIO_Volume volume ); VIOAPI void set_volume_space_type( VIO_Volume volume, VIO_STR name ); VIOAPI void get_volume_separations( VIO_Volume volume, VIO_Real separations[] ); VIOAPI void set_volume_separations( VIO_Volume volume, VIO_Real separations[] ); VIOAPI void set_volume_starts( VIO_Volume volume, VIO_Real starts[] ); VIOAPI void get_volume_starts( VIO_Volume volume, VIO_Real starts[] ); VIOAPI void set_volume_direction_unit_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ); VIOAPI void set_volume_direction_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ); VIOAPI void get_volume_direction_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ); /* These next 5 functions may be called by other modules in the library, * but they are not truly public. That is why they are not declared VIOAPI. */ VIO_BOOL is_volume_dimension_irregular(VIO_Volume, int); long get_volume_irregular_starts(VIO_Volume, int, long, VIO_Real *); long get_volume_irregular_widths(VIO_Volume, int, long, VIO_Real *); long set_volume_irregular_starts(VIO_Volume, int, long, VIO_Real *); long set_volume_irregular_widths(VIO_Volume, int, long, VIO_Real *); VIOAPI VIO_Real nonspatial_voxel_to_world(VIO_Volume, int, int); VIOAPI long nonspatial_world_to_voxel(VIO_Volume, int, VIO_Real); VIOAPI void set_volume_translation( VIO_Volume volume, VIO_Real voxel[], VIO_Real world_space_voxel_maps_to[] ); VIOAPI void get_volume_translation( VIO_Volume volume, VIO_Real voxel[], VIO_Real world_space_voxel_maps_to[] ); VIOAPI void reorder_voxel_to_xyz( VIO_Volume volume, VIO_Real voxel[], VIO_Real xyz[] ); VIOAPI void reorder_xyz_to_voxel( VIO_Volume volume, VIO_Real xyz[], VIO_Real voxel[] ); VIOAPI void convert_voxel_to_world( VIO_Volume volume, VIO_Real voxel[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ); VIOAPI void convert_3D_voxel_to_world( VIO_Volume volume, VIO_Real voxel1, VIO_Real voxel2, VIO_Real voxel3, VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ); VIOAPI void convert_voxel_normal_vector_to_world( VIO_Volume volume, VIO_Real voxel_vector[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ); VIOAPI void convert_voxel_vector_to_world( VIO_Volume volume, VIO_Real voxel_vector[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ); VIOAPI void convert_world_vector_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real voxel_vector[] ); VIOAPI void convert_world_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real voxel[] ); VIOAPI void convert_3D_world_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real *voxel1, VIO_Real *voxel2, VIO_Real *voxel3 ); VIOAPI VIO_Real get_volume_voxel_min( VIO_Volume volume ); VIOAPI VIO_Real get_volume_voxel_max( VIO_Volume volume ); VIOAPI void get_volume_voxel_range( VIO_Volume volume, VIO_Real *voxel_min, VIO_Real *voxel_max ); VIOAPI void set_volume_voxel_range( VIO_Volume volume, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI void get_volume_real_range( VIO_Volume volume, VIO_Real *min_value, VIO_Real *max_value ); VIOAPI VIO_Real get_volume_real_min( VIO_Volume volume ); VIOAPI VIO_Real get_volume_real_max( VIO_Volume volume ); VIOAPI void set_volume_real_range( VIO_Volume volume, VIO_Real real_min, VIO_Real real_max ); VIOAPI VIO_Volume copy_volume_definition_no_alloc( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI VIO_Volume copy_volume_definition( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ); VIOAPI VIO_Volume copy_volume( VIO_Volume volume ); VIOAPI VIO_Status grid_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status grid_inverse_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); VIOAPI VIO_Status grid_inverse_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ); #endif /*VOL_IO_PROTOTYPES_H*/ libminc-libminc-2-3-00/volume_io/Include/volume_io/volume.h000066400000000000000000000446011257462267400237050ustar00rootroot00000000000000#ifndef VOL_IO_VOLUME_H #define VOL_IO_VOLUME_H /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/volume.h,v 1.57 2006-04-07 14:47:15 rotor Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : volume.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Types for use in dealing with volumes. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #ifdef HAVE_MINC1 #include #endif /*HAVE_MINC1*/ #ifdef HAVE_MINC2 #include #endif #include #include typedef struct { VIO_Real global_image_range[2]; VIO_STR dimension_names[VIO_MAX_DIMENSIONS]; VIO_BOOL use_starts_set; VIO_BOOL use_volume_starts_and_steps; } minc_output_options; extern VIO_STR XYZ_dimension_names[]; extern VIO_STR File_order_dimension_names[]; /* -------------------------- volume struct --------------------- */ #define ANY_SPATIAL_DIMENSION "any_spatial_dimension" #define MI_UNKNOWN_SPACE "unknown___" #ifndef HAVE_MINC1 /*Varios definitions to compile Volume_io without MINC1 API*/ #define MI_ORIGINAL_TYPE 0 typedef int nc_type; #define MAX_VAR_DIMS 100 #define MIxspace "xspace" #define MIyspace "yspace" #define MIzspace "zspace" #define MItime "time" #define MItfrequency "tfrequency" #define MIxfrequency "xfrequency" #define MIyfrequency "yfrequency" #define MIzfrequency "zfrequency" #define MIvector_dimension "vector_dimension" /*TODO: use minc2 definitions here?*/ #define NC_BYTE 1 #define NC_CHAR 2 #define NC_SHORT 3 #define NC_INT 4 #define NC_FLOAT 5 #define NC_DOUBLE 6 #endif /*HAVE_MINC1*/ #include typedef struct { VIO_BOOL is_cached_volume; VIO_volume_cache_struct cache; VIO_multidim_array array; VIO_STR dimension_names[VIO_MAX_DIMENSIONS]; int spatial_axes[VIO_N_DIMENSIONS]; nc_type nc_data_type; VIO_BOOL signed_flag; VIO_BOOL is_rgba_data; VIO_Real voxel_min; VIO_Real voxel_max; VIO_BOOL real_range_set; VIO_Real real_value_scale; VIO_Real real_value_translation; VIO_Real separations[VIO_MAX_DIMENSIONS]; VIO_Real starts[VIO_MAX_DIMENSIONS]; VIO_Real direction_cosines[VIO_MAX_DIMENSIONS][VIO_N_DIMENSIONS]; VIO_BOOL voxel_to_world_transform_uptodate; VIO_General_transform voxel_to_world_transform; VIO_STR coordinate_system_name; VIO_Real *irregular_starts[VIO_MAX_DIMENSIONS]; VIO_Real *irregular_widths[VIO_MAX_DIMENSIONS]; } volume_struct; typedef volume_struct *VIO_Volume; /* ---- macro for stepping through entire volume */ #define BEGIN_ALL_VOXELS( volume, v0, v1, v2, v3, v4 ) \ { \ int _i_, _sizes_[VIO_MAX_DIMENSIONS]; \ int _size0_, _size1_, _size2_, _size3_, _size4_; \ \ get_volume_sizes( volume, _sizes_ ); \ for_less( _i_, get_volume_n_dimensions(volume), VIO_MAX_DIMENSIONS ) \ _sizes_[_i_] = 1; \ _size0_ = _sizes_[0]; \ _size1_ = _sizes_[1]; \ _size2_ = _sizes_[2]; \ _size3_ = _sizes_[3]; \ _size4_ = _sizes_[4]; \ \ for_less( v0, 0, _size0_ ) \ for_less( v1, 0, _size1_ ) \ for_less( v2, 0, _size2_ ) \ for_less( v3, 0, _size3_ ) \ for_less( v4, 0, _size4_ ) \ { #define END_ALL_VOXELS \ } \ } /* ------------------------- set voxel value ------------------------ */ /* --- public macros to set the [x][y]... voxel of 'volume' to 'value' */ #define SET_VOXEL_1D( volume, x, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, 0, 0, 0, 0, (VIO_Real) value ); \ else \ SET_MULTIDIM_1D( (volume)->array, x, value ) #define SET_VOXEL_2D( volume, x, y, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, y, 0, 0, 0, (VIO_Real) value ); \ else \ SET_MULTIDIM_2D( (volume)->array, x, y, value ) #define SET_VOXEL_3D( volume, x, y, z, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, y, z, 0, 0, (VIO_Real) value ); \ else \ SET_MULTIDIM_3D( (volume)->array, x, y, z, value ) #define SET_VOXEL_4D( volume, x, y, z, t, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, y, z, t, 0, (VIO_Real) value ); \ else \ SET_MULTIDIM_4D( (volume)->array, x, y, z, t, value ) #define SET_VOXEL_5D( volume, x, y, z, t, v, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, y, z, t, v, (VIO_Real) value ); \ else \ SET_MULTIDIM_5D( (volume)->array, x, y, z, t, v, value ) /* --- same as previous, but don't have to know dimensions of volume */ #define SET_VOXEL( volume, x, y, z, t, v, value ) \ if( (volume)->is_cached_volume ) \ set_cached_volume_voxel( volume, x, y, z, t, v, (VIO_Real) value ); \ else \ SET_MULTIDIM( (volume)->array, x, y, z, t, v, value ) /* --- public macros to place the [x][y]...'th voxel of 'volume' in 'value' */ #define GET_VOXEL_1D_TYPED( value, vtype, volume, x ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, 0, 0, 0, 0 ); \ else \ GET_MULTIDIM_1D( value, vtype, (volume)->array, x ) #define GET_VOXEL_2D_TYPED( value, vtype, volume, x, y ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, y, 0, 0, 0 ); \ else \ GET_MULTIDIM_2D( value, vtype, (volume)->array, x, y ) #define GET_VOXEL_3D_TYPED( value, vtype, volume, x, y, z ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, y, z, 0, 0 ); \ else \ GET_MULTIDIM_3D( value, vtype, (volume)->array, x, y, z ) #define GET_VOXEL_4D_TYPED( value, vtype, volume, x, y, z, t ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, y, z, t, 0 ); \ else \ GET_MULTIDIM_4D( value, vtype, (volume)->array, x, y, z, t ) #define GET_VOXEL_5D_TYPED( value, vtype, volume, x, y, z, t, v ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, y, z, t, v ); \ else \ GET_MULTIDIM_5D( value, vtype, (volume)->array, x, y, z, t, v ) /* --- same as previous, but no need to know volume dimensions */ #define GET_VOXEL_TYPED( value, vtype, volume, x, y, z, t, v ) \ if( (volume)->is_cached_volume ) \ (value) = vtype get_cached_volume_voxel( volume, x, y, z, t, v ); \ else \ GET_MULTIDIM( value, vtype, (volume)->array, x, y, z, t, v ) /* --- public macros to place the [x][y]...'th voxel of 'volume' in 'value' */ #define GET_VOXEL_1D( value, volume, x ) \ GET_VOXEL_1D_TYPED( value, , volume, x ) #define GET_VOXEL_2D( value, volume, x, y ) \ GET_VOXEL_2D_TYPED( value, , volume, x, y ) #define GET_VOXEL_3D( value, volume, x, y, z ) \ GET_VOXEL_3D_TYPED( value, , volume, x, y, z ) #define GET_VOXEL_4D( value, volume, x, y, z, t ) \ GET_VOXEL_4D_TYPED( value, , volume, x, y, z, t ) #define GET_VOXEL_5D( value, volume, x, y, z, t, v ) \ GET_VOXEL_5D_TYPED( value, , volume, x, y, z, t, v ) /* --- same as previous, but no need to know volume dimensions */ #define GET_VOXEL( value, volume, x, y, z, t, v ) \ GET_VOXEL_TYPED( value, , volume, x, y, z, t, v ) /* ------------------------- get voxel ptr ------------------------ */ /* --- public macros to return a pointer to the [x][y]'th voxel of the 'volume', and place it in 'ptr' */ #define GET_VOXEL_PTR_1D( ptr, volume, x ) \ if( (volume)->is_cached_volume ) \ /* handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else */ \ GET_MULTIDIM_PTR_1D( ptr, (volume)->array, x ) #define GET_VOXEL_PTR_2D( ptr, volume, x, y ) \ if( (volume)->is_cached_volume ) \ handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else \ GET_MULTIDIM_PTR_2D( ptr, (volume)->array, x, y ) #define GET_VOXEL_PTR_3D( ptr, volume, x, y, z ) \ if( (volume)->is_cached_volume ) \ handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else \ GET_MULTIDIM_PTR_3D( ptr, (volume)->array, x, y, z ) #define GET_VOXEL_PTR_4D( ptr, volume, x, y, z, t ) \ if( (volume)->is_cached_volume ) \ handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else \ GET_MULTIDIM_PTR_4D( ptr, (volume)->array, x, y, z, t ) #define GET_VOXEL_PTR_5D( ptr, volume, x, y, z, t, v ) \ if( (volume)->is_cached_volume ) \ handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else \ GET_MULTIDIM_PTR_5D( ptr, (volume)->array, x, y, z, t, v ) /* --- same as previous, but no need to know voxel dimensions */ #define GET_VOXEL_PTR( ptr, volume, x, y, z, t, v ) \ if( (volume)->is_cached_volume ) \ handle_internal_error( "Cannot get pointer to cached VIO_Volume.\n");\ else \ GET_MULTIDIM_PTR( ptr, (volume)->array, x, y, z, t, v ) /* --- returns the conversion of the 'voxel' value to a real value */ #define CONVERT_VOXEL_TO_VALUE( volume, voxel ) \ convert_voxel_to_value( volume, voxel ) /* --- returns the conversion of the 'real' value to a voxel value */ #define CONVERT_VALUE_TO_VOXEL( volume, value ) \ convert_value_to_voxel( volume, value ) /* --- assigns 'value' the value of the [x][y]...'th voxel of 'volume' */ #define GET_VALUE_1D_TYPED( value, vtype, volume, x ) \ { \ GET_VOXEL_1D_TYPED( value, vtype, volume, x ); \ value = CONVERT_VOXEL_TO_VALUE( volume, value ); \ } #define GET_VALUE_2D_TYPED( value, vtype, volume, x, y ) \ { \ GET_VOXEL_2D_TYPED( value, vtype, volume, x, y ); \ value = CONVERT_VOXEL_TO_VALUE( volume, value ); \ } #define GET_VALUE_3D_TYPED( value, vtype, volume, x, y, z ) \ { \ GET_VOXEL_3D_TYPED( value, vtype, volume, x, y, z ); \ value = CONVERT_VOXEL_TO_VALUE( volume, value ); \ } #define GET_VALUE_4D_TYPED( value, vtype, volume, x, y, z, t ) \ { \ GET_VOXEL_4D_TYPED( value, vtype, volume, x, y, z, t ); \ value = CONVERT_VOXEL_TO_VALUE( volume, value ); \ } #define GET_VALUE_5D_TYPED( value, vtype, volume, x, y, z, t, v ) \ { \ GET_VOXEL_5D_TYPED( value, vtype, volume, x, y, z, t, v ); \ value = CONVERT_VOXEL_TO_VALUE( volume, value ); \ } /* --- same as previous, without knowing number of dimensions of volume */ #define GET_VALUE_TYPED( value, vtype, volume, x, y, z, t, v ) \ switch( (volume)->n_dimensions ) \ { \ case 1: GET_VALUE_1D_TYPED( value, vtype, volume, x ); break; \ case 2: GET_VALUE_2D_TYPED( value, vtype, volume, x, y ); break; \ case 3: GET_VALUE_3D_TYPED( value, vtype, volume, x, y, z ); break; \ case 4: GET_VALUE_4D_TYPED( value, vtype, volume, x, y, z, t ); break; \ case 5: GET_VALUE_5D_TYPED( value, vtype, volume, x, y, z, t, v ); break; \ } /* --- assigns 'value' the value of the [x][y]...'th voxel of 'volume' */ #define GET_VALUE_1D( value, volume, x ) \ GET_VALUE_1D_TYPED( value, , volume, x ) #define GET_VALUE_2D( value, volume, x, y ) \ GET_VALUE_2D_TYPED( value, , volume, x, y ) #define GET_VALUE_3D( value, volume, x, y, z ) \ GET_VALUE_3D_TYPED( value, , volume, x, y, z ) #define GET_VALUE_4D( value, volume, x, y, z, t ) \ GET_VALUE_4D_TYPED( value, , volume, x, y, z, t ) #define GET_VALUE_5D( value, volume, x, y, z, t, v ) \ GET_VALUE_5D_TYPED( value, , volume, x, y, z, t, v ) /* --- same as previous, without knowing number of dimensions of volume */ #define GET_VALUE( value, volume, x, y, z, t, v ) \ GET_VALUE_TYPED( value, , volume, x, y, z, t, v ) /* -------------------- minc file struct -------------------- */ typedef struct { int arent_any_yet; } volume_creation_options; typedef struct { VIO_BOOL promote_invalid_to_zero_flag; VIO_BOOL convert_vector_to_scalar_flag; VIO_BOOL convert_vector_to_colour_flag; int dimension_size_for_colour_data; int max_dimension_size_for_colour_data; int rgba_indices[4]; double user_real_range[2]; } minc_input_options; typedef struct { VIO_BOOL file_is_being_read; /* input and output */ int cdfid; int img_var; int n_file_dimensions; long sizes_in_file[MAX_VAR_DIMS]; long indices[MAX_VAR_DIMS]; VIO_STR dim_names[MAX_VAR_DIMS]; VIO_Volume volume; int to_volume_index[MAX_VAR_DIMS]; int to_file_index[VIO_MAX_DIMENSIONS]; int minc_icv; VIO_STR filename; /* input only */ VIO_BOOL end_volume_flag; VIO_BOOL converting_to_colour; int rgba_indices[4]; int n_volumes_in_file; int valid_file_axes[VIO_MAX_DIMENSIONS]; int n_slab_dims; int spatial_axes[VIO_N_DIMENSIONS]; VIO_General_transform voxel_to_world_transform; minc_input_options original_input_options; /* output only */ int img_var_id; int min_id; int max_id; double image_range[2]; VIO_BOOL end_def_done; VIO_BOOL ignoring_because_cached; VIO_BOOL variables_written; int dim_ids[MAX_VAR_DIMS]; VIO_BOOL outputting_in_order; VIO_BOOL entire_file_written; nc_type nc_data_type; VIO_BOOL signed_flag; double valid_range[2]; int image_dims[MAX_VAR_DIMS]; int src_cdfid; int src_img_var; #ifdef HAVE_MINC2 mihandle_t minc2id; #else void* minc2id; /*just in case*/ #endif /*HAVE_MINC2*/ } minc_file_struct; typedef minc_file_struct *Minc_file; #define MNC_ENDING "mnc" /* --- recognized file formats */ typedef enum { MNC_FORMAT, FREE_FORMAT, MNC2_FORMAT, MGH_FORMAT, NII_FORMAT } Volume_file_formats; typedef struct { Volume_file_formats file_format; Minc_file minc_file; /* for non-minc format files only */ FILE *volume_file; int slice_index; long sizes_in_file[VIO_MAX_DIMENSIONS]; int axis_index_from_file[VIO_MAX_DIMENSIONS]; VIO_Data_types file_data_type; VIO_BOOL one_file_per_slice; VIO_STR directory; VIO_STR *slice_filenames; int *slice_byte_offsets; unsigned char *byte_slice_buffer; unsigned short *short_slice_buffer; void *generic_slice_buffer; VIO_Real min_value, max_value; void *header_info; } volume_input_struct; /* --------------------- filter types -------------------------------- */ typedef enum { NEAREST_NEIGHBOUR, LINEAR_INTERPOLATION, BOX_FILTER, TRIANGLE_FILTER, GAUSSIAN_FILTER } VIO_Filter_types; #endif /* VOL_IO_VOLUME_H */ libminc-libminc-2-3-00/volume_io/Include/volume_io/volume_cache.h000066400000000000000000000074361257462267400250350ustar00rootroot00000000000000#ifndef VOL_IO_VOLUME_CACHE_H #define VOL_IO_VOLUME_CACHE_H #include "volume.h" /* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. @VERSION : $Header: /private-cvsroot/minc/volume_io/Include/volume_io/volume_cache.h,v 1.11 2005-05-19 21:19:28 bert Exp $ ---------------------------------------------------------------------------- */ /* ----------------------------- MNI Header ----------------------------------- @NAME : volume_cache.h @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Volume block caching mechanism for treating large volumes as if they are in memory. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 14, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #include typedef enum { SLICE_ACCESS, RANDOM_VOLUME_ACCESS } VIO_Cache_block_size_hints; #define CACHE_DEBUGGING #undef CACHE_DEBUGGING typedef struct VIO_cache_block_struct { int block_index; VIO_SCHAR modified_flag; VIO_multidim_array array; struct VIO_cache_block_struct *prev_used; struct VIO_cache_block_struct *next_used; struct VIO_cache_block_struct **prev_hash; struct VIO_cache_block_struct *next_hash; } VIO_cache_block_struct; typedef struct { int block_index_offset; int block_offset; } VIO_cache_lookup_struct; typedef struct { int n_dimensions; int file_offset[VIO_MAX_DIMENSIONS]; VIO_STR input_filename; VIO_STR output_filename; nc_type file_nc_data_type; VIO_BOOL file_signed_flag; VIO_Real file_voxel_min; VIO_Real file_voxel_max; VIO_STR original_filename; VIO_STR history; minc_output_options options; VIO_BOOL writing_to_temp_file; int total_block_size; int block_sizes[VIO_MAX_DIMENSIONS]; int blocks_per_dim[VIO_MAX_DIMENSIONS]; VIO_BOOL output_file_is_open; VIO_BOOL must_read_blocks_before_use; void *minc_file; int n_blocks; int max_cache_bytes; int max_blocks; int hash_table_size; VIO_cache_block_struct *head; VIO_cache_block_struct *tail; VIO_cache_block_struct **hash_table; VIO_cache_lookup_struct *lookup[VIO_MAX_DIMENSIONS]; VIO_cache_block_struct *previous_block; int previous_block_index; VIO_BOOL debugging_on; int n_accesses; int output_every; int n_hits; int n_prev_hits; } VIO_volume_cache_struct; #endif /* VOL_IO_VOLUME_CACHE_H */ libminc-libminc-2-3-00/volume_io/MNI_formats/000077500000000000000000000000001257462267400210155ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/MNI_formats/gen_xf_io.c000066400000000000000000000577001257462267400231270ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /*--------------------- file format keywords ------------------------------ */ static const VIO_STR TRANSFORM_FILE_HEADER = "MNI Transform File"; static const VIO_STR TYPE_STRING = "Transform_Type"; static const VIO_STR LINEAR_TRANSFORM_STRING = "Linear_Transform"; static const VIO_STR LINEAR_TYPE = "Linear"; static const VIO_STR THIN_PLATE_SPLINE_STRING ="Thin_Plate_Spline_Transform"; static const VIO_STR INVERT_FLAG_STRING = "Invert_Flag"; static const VIO_STR TRUE_STRING = "True"; static const VIO_STR FALSE_STRING = "False"; static const VIO_STR N_DIMENSIONS_STRING = "Number_Dimensions"; static const VIO_STR POINTS_STRING = "Points"; static const VIO_STR DISPLACEMENTS_STRING = "Displacements"; static const VIO_STR GRID_TRANSFORM_STRING = "Grid_Transform"; static const VIO_STR DISPLACEMENT_VOLUME = "Displacement_Volume"; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_transform_file_suffix @INPUT : @OUTPUT : @RETURNS : "xfm" @DESCRIPTION: Returns the default transform file suffix. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_default_transform_file_suffix( void ) { return( "xfm" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_one_transform @INPUT : file filename \ these two used to manufacture unique filenames volume_count / for grid transform volume files. invert - whether to invert the transform transform @OUTPUT : @RETURNS : @DESCRIPTION: Outputs a transform to the MNI transform file. : Increment *volume_count. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb 21, 1995 David MacDonald : added grid transforms ---------------------------------------------------------------------------- */ static void output_one_transform( FILE *file, const char *filename, int *volume_count, VIO_BOOL invert, VIO_General_transform *transform ) { int i, c, trans; VIO_Transform *lin_transform; VIO_STR volume_filename, base_filename, prefix_filename; switch( transform->type ) { case LINEAR: (void) fprintf( file, "%s = %s;\n", TYPE_STRING, LINEAR_TYPE ); (void) fprintf( file, "%s =\n", LINEAR_TRANSFORM_STRING ); if( invert ) lin_transform = get_inverse_linear_transform_ptr( transform ); else lin_transform = get_linear_transform_ptr( transform ); for_less( i, 0, 3 ) { (void) fprintf( file, " %.15g %.15g %.15g %.15g", Transform_elem(*lin_transform,i,0), Transform_elem(*lin_transform,i,1), Transform_elem(*lin_transform,i,2), Transform_elem(*lin_transform,i,3) ); if( i == 2 ) (void) fprintf( file, ";" ); (void) fprintf( file, "\n" ); } break; case THIN_PLATE_SPLINE: (void) fprintf( file, "%s = %s;\n", TYPE_STRING, THIN_PLATE_SPLINE_STRING); if( transform->inverse_flag ) invert = !invert; if( invert ) (void) fprintf( file, "%s = %s;\n", INVERT_FLAG_STRING,TRUE_STRING); (void) fprintf( file, "%s = %d;\n", N_DIMENSIONS_STRING, transform->n_dimensions ); (void) fprintf( file, "%s =\n", POINTS_STRING ); for_less( i, 0, transform->n_points ) { for_less( c, 0, transform->n_dimensions ) (void) fprintf( file, " %.15g", transform->points[i][c] ); if( i == transform->n_points-1 ) (void) fprintf( file, ";" ); (void) fprintf( file, "\n" ); } (void) fprintf( file, "%s =\n", DISPLACEMENTS_STRING ); for_less( i, 0, transform->n_points + transform->n_dimensions + 1 ) { for_less( c, 0, transform->n_dimensions ) (void) fprintf( file, " %.15g", transform->displacements[i][c]); if( i == transform->n_points + transform->n_dimensions + 1 - 1 ) (void) fprintf( file, ";" ); (void) fprintf( file, "\n" ); } break; case GRID_TRANSFORM: (void) fprintf( file, "%s = %s;\n", TYPE_STRING, GRID_TRANSFORM_STRING ); if( transform->inverse_flag ) invert = !invert; if( invert ) (void) fprintf( file, "%s = %s;\n", INVERT_FLAG_STRING,TRUE_STRING); /*--- the volume will be stored in a file of the same prefix as this transform, but ending in _grid.mnc */ if( filename == NULL || string_length(filename) == 0 ) prefix_filename = create_string( "grid" ); else { prefix_filename = create_string( filename ); i = string_length( prefix_filename ) - 1; while( i > 0 && prefix_filename[i] != '.' && prefix_filename[i] != '/' ) --i; if( i >= 0 && prefix_filename[i] == '.' ) prefix_filename[i] = VIO_END_OF_STRING; } if( transform->displacement_volume_file ) { delete_string( transform->displacement_volume_file ); transform->displacement_volume_file = NULL; } /*--- write out the volume filename to the transform file */ /* if( ! transform->displacement_volume_file ) {*/ volume_filename = alloc_string( string_length(prefix_filename) + 100 ); sprintf( volume_filename, "%s_grid_%d.mnc", prefix_filename, *volume_count ); transform->displacement_volume_file = volume_filename; /* }*/ /* Increment the volume counter as a side-effect to ensure that grid * files have different names. */ (*volume_count)++; /*--- decide where to write the volume file */ base_filename = remove_directories_from_filename( transform->displacement_volume_file ); fprintf( file, "%s = %s;\n", DISPLACEMENT_VOLUME, base_filename); /*--- write the volume file */ if( transform->displacement_volume ) output_volume( transform->displacement_volume_file, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, (VIO_Volume) transform->displacement_volume, NULL, NULL ); delete_string( prefix_filename ); /*delete_string( volume_filename );*/ delete_string( base_filename ); break; case USER_TRANSFORM: print_error( "Cannot output user transformation.\n" ); output_comments( file, "User transform goes here." ); break; case CONCATENATED_TRANSFORM: if( transform->inverse_flag ) invert = !invert; if( invert ) { for( trans = get_n_concated_transforms(transform)-1; trans >= 0; --trans ) { output_one_transform( file, filename, volume_count, invert, get_nth_general_transform(transform,trans) ); } } else { for_less( trans, 0, get_n_concated_transforms(transform) ) { output_one_transform( file, filename, volume_count, invert, get_nth_general_transform(transform,trans) ); } } break; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_transform file filename \ these two args used to manufacture unique volume_count_ptr / filenames for grid transform volumes comments - can be null transform @INPUT : @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the transform to the file in MNI transform format. The filename is used to define the prefix for writing out volumes that are part of grid transforms. The volume_count_ptr is used to give each volume written in a given file a unique index, and therefore a unique filename. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 21, 1995 D. MacDonald ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_transform( FILE *file, const char *filename, int *volume_count_ptr, const char *comments, VIO_General_transform *transform ) { int volume_count; /* --- parameter checking */ if( file == NULL ) { print_error( "output_transform(): passed NULL FILE ptr.\n" ); return( VIO_ERROR ); } /* --- okay write the file */ (void) fprintf( file, "%s\n", TRANSFORM_FILE_HEADER ); output_comments( file, comments ); (void) fprintf( file, "\n" ); /*--- if the user has not initialized the volume count, then do so */ if( volume_count_ptr == NULL ) { volume_count = 0; volume_count_ptr = &volume_count; } output_one_transform( file, filename, volume_count_ptr, FALSE, transform ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_one_transform @INPUT : file filename - used to get relative paths @OUTPUT : transform @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs a transform from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 21, 1995 David MacDonald - added grid transforms ---------------------------------------------------------------------------- */ static VIO_Status input_one_transform( FILE *file, const char *filename, VIO_General_transform *transform ) { VIO_Status status; int i, j, n_points, n_dimensions; VIO_Real **points, **displacements; VIO_Real value, *points_1d; VIO_STR type_name, str, volume_filename, directory, tmp_filename; VIO_Volume volume; VIO_Transform linear_transform; VIO_Transform_types transform_type; VIO_BOOL inverse_flag; VIO_General_transform inverse; minc_input_options options; inverse_flag = FALSE; /* --- read the type of transform */ status = mni_input_keyword_and_equal_sign( file, TYPE_STRING, FALSE ); if( status != VIO_OK ) return( status ); if( mni_input_string( file, &type_name, (char) ';', (char) 0 ) != VIO_OK ) { print_error( "input_transform(): missing transform type.\n"); return( VIO_ERROR ); } if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) return( VIO_ERROR ); if( equal_strings( type_name, LINEAR_TYPE ) ) transform_type = LINEAR; else if( equal_strings( type_name, THIN_PLATE_SPLINE_STRING ) ) transform_type = THIN_PLATE_SPLINE; else if( equal_strings( type_name, GRID_TRANSFORM_STRING ) ) transform_type = GRID_TRANSFORM; else { delete_string( type_name ); print_error( "input_transform(): invalid transform type.\n"); return( VIO_ERROR ); } delete_string( type_name ); /* --- read the next string */ if( mni_input_string( file, &str, (char) '=', (char) 0 ) != VIO_OK ) return( VIO_ERROR ); if( equal_strings( str, INVERT_FLAG_STRING ) ) { delete_string( str ); if( mni_skip_expected_character( file, (char) '=' ) != VIO_OK ) return( VIO_ERROR ); if( mni_input_string( file, &str, (char) ';', (char) 0 ) != VIO_OK ) return( VIO_ERROR ); if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) { delete_string( str ); return( VIO_ERROR ); } if( equal_strings( str, TRUE_STRING ) ) inverse_flag = TRUE; else if( equal_strings( str, FALSE_STRING ) ) inverse_flag = FALSE; else { delete_string( str ); print_error( "Expected %s or %s after %s =\n", TRUE_STRING, FALSE_STRING, INVERT_FLAG_STRING ); return( VIO_ERROR ); } delete_string( str ); if( mni_input_string( file, &str, (char) '=', (char) 0 ) != VIO_OK ) return( VIO_ERROR ); } switch( transform_type ) { default: print_error( "Unsupported transform type %d \n", transform_type ); delete_string( str ); return( VIO_ERROR ); case LINEAR: if( !equal_strings( str, LINEAR_TRANSFORM_STRING ) ) { print_error( "Expected %s =\n", LINEAR_TRANSFORM_STRING ); delete_string( str ); return( VIO_ERROR ); } delete_string( str ); if( mni_skip_expected_character( file, (char) '=' ) != VIO_OK ) return( VIO_ERROR ); make_identity_transform( &linear_transform ); /* now read the 3 lines of transforms */ for_less( i, 0, 3 ) { for_less( j, 0, 4 ) { if( mni_input_real( file, &value ) != VIO_OK ) { print_error( "input_transform(): error reading transform elem [%d,%d]\n", i+1, j+1 ); return( VIO_ERROR ); } Transform_elem(linear_transform,i,j) = value; } } if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) return( VIO_ERROR ); create_linear_transform( transform, &linear_transform ); break; case THIN_PLATE_SPLINE: /* --- read Number_Dimensions = 3; */ if( !equal_strings( str, N_DIMENSIONS_STRING ) ) { print_error( "Expected %s =\n", N_DIMENSIONS_STRING ); delete_string( str ); return( VIO_ERROR ); } delete_string( str ); if( mni_skip_expected_character( file, (char) '=' ) != VIO_OK ) return( VIO_ERROR ); if( mni_input_int( file, &n_dimensions ) != VIO_OK ) return( VIO_ERROR ); if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) return( VIO_ERROR ); /* --- read Points = x y z x y z .... ; */ if( mni_input_keyword_and_equal_sign( file, POINTS_STRING, TRUE ) != VIO_OK) return( VIO_ERROR ); if( mni_input_reals( file, &n_points, &points_1d ) != VIO_OK ) return( VIO_ERROR ); if( n_points % n_dimensions != 0 ) { print_error( "Number of points (%d) must be multiple of number of dimensions (%d)\n", n_points, n_dimensions ); return( VIO_ERROR ); } n_points = n_points / n_dimensions; VIO_ALLOC2D( points, n_points, n_dimensions ); for_less( i, 0, n_points ) { for_less( j, 0, n_dimensions ) { points[i][j] = points_1d[VIO_IJ(i,j,n_dimensions)]; } } FREE( points_1d ); /* --- allocate and input the displacements */ VIO_ALLOC2D( displacements, n_points + n_dimensions + 1, n_dimensions ); if( mni_input_keyword_and_equal_sign( file, DISPLACEMENTS_STRING, TRUE ) != VIO_OK ) return( VIO_ERROR ); for_less( i, 0, n_points + n_dimensions + 1 ) { for_less( j, 0, n_dimensions ) { if( mni_input_real( file, &value ) != VIO_OK ) { print_error( "Expected more displacements.\n" ); return( VIO_ERROR ); } displacements[i][j] = value; } } if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) return( VIO_ERROR ); create_thin_plate_transform_real( transform, n_dimensions, n_points, points, displacements ); VIO_FREE2D( points ); VIO_FREE2D( displacements ); break; case GRID_TRANSFORM: /*--- read the displacement volume filename */ if( !equal_strings( str, DISPLACEMENT_VOLUME ) ) { print_error( "Expected %s =\n", DISPLACEMENT_VOLUME ); delete_string( str ); return( VIO_ERROR ); } delete_string( str ); if( mni_skip_expected_character( file, (char) '=' ) != VIO_OK ) return( VIO_ERROR ); if( mni_input_string( file, &volume_filename, (char) ';', (char) 0 ) != VIO_OK ) return( VIO_ERROR ); if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) { delete_string( volume_filename ); return( VIO_ERROR ); } /*--- if the volume filename is relative, add the required directory */ if( volume_filename[0] != '/' && filename != NULL ) { directory = extract_directory( filename ); if( string_length(directory) > 0 ) { tmp_filename = concat_strings( directory, "/" ); concat_to_string( &tmp_filename, volume_filename ); replace_string( &volume_filename, tmp_filename ); } delete_string( directory ); } /*--- input the displacement volume */ /*TODO: initialize strings*/ set_default_minc_input_options( &options ); set_minc_input_vector_to_scalar_flag( &options, FALSE ); if( input_volume( volume_filename, 4, NULL, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &volume, &options ) != VIO_OK ) { delete_string( volume_filename ); return( VIO_ERROR ); } create_grid_transform_no_copy( transform, volume, volume_filename ); delete_string( volume_filename ); /*--- create the transform */ break; } if( inverse_flag ) { create_inverse_general_transform( transform, &inverse ); delete_general_transform( transform ); *transform = inverse; } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_transform @INPUT : file filename - used to define directory for relative filename @OUTPUT : transform @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs the transform from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 21, 1995 D. MacDonald ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_transform( FILE *file, const char *filename, VIO_General_transform *transform ) { VIO_Status status; int n_transforms; VIO_STR line; VIO_General_transform next, concated; /* parameter checking */ if( file == (FILE *) 0 ) { print_error( "input_transform(): passed NULL FILE ptr.\n"); return( VIO_ERROR ); } /* okay read the header */ if( mni_input_string( file, &line, (char) 0, (char) 0 ) != VIO_OK ) { delete_string( line ); print_error( "input_transform(): could not read header in file.\n"); return( VIO_ERROR ); } if( !equal_strings( line, TRANSFORM_FILE_HEADER ) ) { delete_string( line ); print_error( "input_transform(): invalid header in file.\n"); return( VIO_ERROR ); } delete_string( line ); n_transforms = 0; while( (status = input_one_transform( file, filename, &next )) == VIO_OK ) { if( n_transforms == 0 ) *transform = next; else { concat_general_transforms( transform, &next, &concated ); delete_general_transform( transform ); delete_general_transform( &next ); *transform = concated; } ++n_transforms; } if( status == VIO_ERROR ) { print_error( "input_transform: error reading transform.\n" ); return( VIO_ERROR ); } else if( n_transforms == 0 ) { print_error( "input_transform: no transform present.\n" ); return( VIO_ERROR ); } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_transform_file @INPUT : filename comments transform @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Opens the file, outputs the transform, and closes the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_transform_file( const char *filename, const char *comments, VIO_General_transform *transform ) { VIO_Status status; FILE *file; int volume_count; status = open_file_with_default_suffix( filename, get_default_transform_file_suffix(), WRITE_FILE, ASCII_FORMAT, &file ); volume_count = 0; if( status == VIO_OK ) status = output_transform( file, filename, &volume_count, comments, transform ); if( status == VIO_OK ) status = close_file( file ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_transform_file @INPUT : filename @OUTPUT : transform @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Opens the file, inputs the transform, and closes the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_transform_file( const char *filename, VIO_General_transform *transform ) { VIO_Status status; FILE *file; status = open_file_with_default_suffix( filename, get_default_transform_file_suffix(), READ_FILE, ASCII_FORMAT, &file ); if( status == VIO_OK ) status = input_transform( file, filename, transform ); if( status == VIO_OK ) status = close_file( file ); return( status ); } libminc-libminc-2-3-00/volume_io/MNI_formats/gen_xfs.c000066400000000000000000001156431257462267400226240ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_linear_transform @INPUT : transform @OUTPUT : @RETURNS : @DESCRIPTION: Allocates memory for the linear transform and its inverse. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void alloc_linear_transform( VIO_General_transform *transform ) { transform->type = LINEAR; transform->inverse_flag = FALSE; ALLOC( transform->linear_transform, 1 ); ALLOC( transform->inverse_linear_transform, 1 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_linear_transform @INPUT : linear_transform @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a general transform of type linear, copying the linear_transform and computing its inverse. If the linear transform is NULL, the identity transform is created. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_linear_transform( VIO_General_transform *transform, VIO_Transform *linear_transform ) { alloc_linear_transform( transform ); if( linear_transform != (VIO_Transform *) NULL && compute_transform_inverse( linear_transform, transform->inverse_linear_transform ) ) { *(transform->linear_transform) = *linear_transform; } else { make_identity_transform( transform->linear_transform ); make_identity_transform( transform->inverse_linear_transform ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_thin_plate_transform @INPUT : transform n_dimensions n_points @OUTPUT : @RETURNS : @DESCRIPTION: Initializes a VIO_General_transform structure for thin plate transforms. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void initialize_thin_plate_transform( VIO_General_transform *transform, int n_dimensions, int n_points ) { transform->type = THIN_PLATE_SPLINE; transform->inverse_flag = FALSE; transform->n_dimensions = n_dimensions; transform->n_points = n_points; transform->displacement_volume = NULL; VIO_ALLOC2D( transform->points, n_points, n_dimensions ); VIO_ALLOC2D( transform->displacements, n_points + n_dimensions + 1, n_dimensions ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_thin_plate_transform_real @INPUT : n_dimensions n_points points displacements @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a general transform of type thin plate spline, given VIO_Real-type points and displacements. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 21, 1995 David MacDonald - make a real and float version ---------------------------------------------------------------------------- */ VIOAPI void create_thin_plate_transform_real( VIO_General_transform *transform, int n_dimensions, int n_points, VIO_Real **points, VIO_Real **displacements ) { int p, d; initialize_thin_plate_transform( transform, n_dimensions, n_points ); for_less( p, 0, n_points ) { for_less( d, 0, n_dimensions ) transform->points[p][d] = points[p][d]; } for_less( p, 0, n_points + n_dimensions + 1 ) { for_less( d, 0, n_dimensions ) transform->displacements[p][d] = displacements[p][d]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_thin_plate_transform @INPUT : n_dimensions n_points points displacements @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a general transform of type thin plate spline. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 21, 1995 David MacDonald - make a real and float version ---------------------------------------------------------------------------- */ VIOAPI void create_thin_plate_transform( VIO_General_transform *transform, int n_dimensions, int n_points, float **points, float **displacements ) { int p, d; initialize_thin_plate_transform( transform, n_dimensions, n_points ); for_less( p, 0, n_points ) { for_less( d, 0, n_dimensions ) transform->points[p][d] = (VIO_Real) points[p][d]; } for_less( p, 0, n_points + n_dimensions + 1 ) { for_less( d, 0, n_dimensions ) transform->displacements[p][d] = (VIO_Real) displacements[p][d]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : internal_create_grid_transform @INPUT : displacement_volume @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a general transform of type grid. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void internal_create_grid_transform( VIO_General_transform *transform, VIO_Volume displacement_volume, VIO_BOOL copy_flag, VIO_STR displacement_volume_file ) { int dim, sizes[VIO_MAX_DIMENSIONS], vector_dim = -1; VIO_STR *dim_names; VIO_Volume copy; VIO_BOOL volume_ok, dim_found[VIO_N_DIMENSIONS]; volume_ok = TRUE; /*TODO: initialize strings?*/ if( displacement_volume ) { if( get_volume_n_dimensions(displacement_volume) != 4 ) { volume_ok = FALSE; print_error( "Grid transform must be 4 dimensional.\n" ); } else { dim_names = get_volume_dimension_names( displacement_volume ); get_volume_sizes( displacement_volume, sizes ); dim_found[VIO_X] = FALSE; dim_found[VIO_Y] = FALSE; dim_found[VIO_Z] = FALSE; vector_dim = -1; for_less( dim, 0, 4 ) { if( equal_strings( dim_names[dim], MIxspace ) ) dim_found[VIO_X] = TRUE; else if( equal_strings( dim_names[dim], MIyspace ) ) dim_found[VIO_Y] = TRUE; else if( equal_strings( dim_names[dim], MIzspace ) ) dim_found[VIO_Z] = TRUE; else { if( sizes[dim] != 3 ) { print_error( "displacement_volume must have 3 components on " ); print_error( "the non-spatial axis.\n" ); volume_ok = FALSE; } vector_dim = dim; } } if( !dim_found[VIO_X] || !dim_found[VIO_Y] || !dim_found[VIO_Z] ) { print_error( "Must have an x, y, and z dimension in displacement volume.\n" ); volume_ok = FALSE; } delete_dimension_names( displacement_volume, dim_names ); } if( !volume_ok ) { create_linear_transform( transform, NULL ); /*--- make identity */ return; } if( copy_flag ) copy = copy_volume( displacement_volume ); else copy = displacement_volume; transform->type = GRID_TRANSFORM; transform->inverse_flag = FALSE; /* --- force 4th dimension to be vector dimension */ replace_string( ©->dimension_names[vector_dim], create_string(MIvector_dimension) ); transform->displacement_volume = (void *) copy; } else { /*we are probably dealing with a case when only a placeholder is needed*/ transform->type = GRID_TRANSFORM; transform->inverse_flag = FALSE; transform->displacement_volume = NULL; } /*Will be initialized on save*/ if(displacement_volume_file) transform->displacement_volume_file = create_string( displacement_volume_file ); else transform->displacement_volume_file = NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_grid_transform @INPUT : displacement_volume @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a grid transform VIO_General_transform. Makes a copy of the displacement volume and puts it in the VIO_General_transform. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_grid_transform( VIO_General_transform *transform, VIO_Volume displacement_volume, VIO_STR displacement_volume_file) { internal_create_grid_transform( transform, displacement_volume, TRUE, displacement_volume_file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_grid_transform_no_copy @INPUT : displacement_volume @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a grid transform VIO_General_transform. Places the displacement volume into the VIO_General_transform; therefore the calling program should not delete the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_grid_transform_no_copy( VIO_General_transform *transform, VIO_Volume displacement_volume, VIO_STR displacement_volume_file ) { internal_create_grid_transform( transform, displacement_volume, FALSE,displacement_volume_file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_user_transform @INPUT : user_data size_user_data transform_function inverse_transform_function @OUTPUT : transform @RETURNS : @DESCRIPTION: Creates a general transform of type user transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_user_transform( VIO_General_transform *transform, void *user_data, size_t size_user_data, VIO_User_transform_function transform_function, VIO_User_transform_function inverse_transform_function ) { unsigned char *byte_ptr; transform->type = USER_TRANSFORM; transform->inverse_flag = FALSE; transform->size_user_data = size_user_data; ALLOC( byte_ptr, size_user_data ); transform->user_data = byte_ptr; (void) memcpy( transform->user_data, user_data, size_user_data ); transform->user_transform_function = transform_function; transform->user_inverse_transform_function = inverse_transform_function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_transform_type @INPUT : transform @OUTPUT : @RETURNS : type @DESCRIPTION: Returns the type of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Transform_types get_transform_type( VIO_General_transform *transform ) { return( transform->type ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_n_concated_transforms @INPUT : transform @OUTPUT : @RETURNS : # transforms @DESCRIPTION: Returns the number of concatenated transforms if the transform type is concatenated, otherwise, 1. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_n_concated_transforms( VIO_General_transform *transform ) { if( transform->type == CONCATENATED_TRANSFORM ) return( transform->n_transforms ); else return( 1 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_nth_general_transform @INPUT : transform n @OUTPUT : @RETURNS : pointer to nth transform @DESCRIPTION: Returns a pointer to the nth transform of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_General_transform *get_nth_general_transform( VIO_General_transform *transform, int n ) { if( n < 0 || n >= get_n_concated_transforms( transform ) ) { handle_internal_error( "get_nth_general_transform" ); return( (VIO_General_transform *) NULL ); } else if( transform->type == CONCATENATED_TRANSFORM ) return( &transform->transforms[n] ); else return( transform ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_linear_transform_ptr @INPUT : transform @OUTPUT : @RETURNS : pointer to linear transform @DESCRIPTION: Returns a pointer to the linear transform of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Transform *get_linear_transform_ptr( VIO_General_transform *transform ) { if( transform->type == LINEAR ) { if( transform->inverse_flag ) return( transform->inverse_linear_transform ); else return( transform->linear_transform ); } else { handle_internal_error( "get_linear_transform_ptr" ); return( (VIO_Transform *) NULL ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_inverse_linear_transform_ptr @INPUT : transform @OUTPUT : @RETURNS : pointer to inverse linear transform @DESCRIPTION: Returns a pointer to the inverse linear transform of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Transform *get_inverse_linear_transform_ptr( VIO_General_transform *transform ) { if( transform->type == LINEAR ) { if( transform->inverse_flag ) return( transform->linear_transform ); else return( transform->inverse_linear_transform ); } else { handle_internal_error( "get_inverse_linear_transform_ptr" ); return( (VIO_Transform *) NULL ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : transform_or_invert_point_with_input_steps @INPUT : transform inverse_flag x y z input_volume_steps @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms a point by the general transform or its inverse, depending on inverse_flag. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Feb. 27, 1995 D. MacDonald - added grid transforms @MODIFIED : 2013, June 10, Matthijs van Eede, added the possibility to pass along the step sizes of the input file which is being resampled to determine the appropriate error margin (ftol) in grid_inverse_transform_point_with_input_steps ---------------------------------------------------------------------------- */ static VIO_Status transform_or_invert_point_with_input_steps( VIO_General_transform *transform, VIO_BOOL inverse_flag, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { int trans; VIO_Status status=VIO_ERROR; switch( transform->type ) { case LINEAR: if( inverse_flag ) return transform_point( transform->inverse_linear_transform, x, y, z, x_transformed, y_transformed, z_transformed ); else return transform_point( transform->linear_transform, x, y, z, x_transformed, y_transformed, z_transformed ); break; case THIN_PLATE_SPLINE: if( inverse_flag ) { return thin_plate_spline_inverse_transform( transform->n_dimensions, transform->n_points, transform->points, transform->displacements, x, y, z, x_transformed, y_transformed, z_transformed ); } else { return thin_plate_spline_transform( transform->n_dimensions, transform->n_points, transform->points, transform->displacements, x, y, z, x_transformed, y_transformed, z_transformed ); } break; case GRID_TRANSFORM: if( !transform->displacement_volume ) { handle_internal_error( "Not initialized grid transform, make sure you have MINC1" ); return VIO_ERROR; break; } if( inverse_flag ) { return grid_inverse_transform_point_with_input_steps( transform, x, y, z, input_volume_steps, x_transformed, y_transformed, z_transformed ); } else { return grid_transform_point( transform, x, y, z, x_transformed, y_transformed, z_transformed ); } break; case USER_TRANSFORM: if( inverse_flag ) { transform->user_inverse_transform_function( transform->user_data, x, y, z, x_transformed, y_transformed, z_transformed ); /*TODO: add error handling?*/ } else { transform->user_transform_function( transform->user_data, x, y, z, x_transformed, y_transformed, z_transformed ); /*TODO: add error handling?*/ } return VIO_OK; break; case CONCATENATED_TRANSFORM: *x_transformed = x; *y_transformed = y; *z_transformed = z; if( inverse_flag ) { for( trans = transform->n_transforms-1; trans >= 0; --trans ) { if( (status=general_inverse_transform_point_with_input_steps( &transform->transforms[trans], *x_transformed, *y_transformed, *z_transformed, input_volume_steps, x_transformed, y_transformed, z_transformed )) != VIO_OK) return status; } } else { for_less( trans, 0, transform->n_transforms ) { if((status=general_transform_point_with_input_steps( &transform->transforms[trans], *x_transformed, *y_transformed, *z_transformed, input_volume_steps, x_transformed, y_transformed, z_transformed )) != VIO_OK) return status; } } break; default: handle_internal_error( "transform_or_invert_point_with_input_steps" ); return VIO_ERROR; break; } return status; } /* ----------------------------- MNI Header ----------------------------------- @NAME : general_transform_point_with_input_steps @INPUT : transform x y z input_volume_steps @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms a point by the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : @MODIFIED : 2013, June 10, Matthijs van Eede, added the possibility to pass along the step sizes of the input file which is being resampled to determine the appropriate error margin (ftol) in grid_inverse_transform_point_with_input_steps ---------------------------------------------------------------------------- */ VIOAPI VIO_Status general_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { return transform_or_invert_point_with_input_steps( transform, transform->inverse_flag, x, y, z, input_volume_steps, x_transformed, y_transformed, z_transformed ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : general_inverse_transform_point_with_input_steps @INPUT : transform x y z input_volume_steps @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms a point by the inverse of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status general_inverse_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { return transform_or_invert_point_with_input_steps( transform, !transform->inverse_flag, x, y, z, input_volume_steps, x_transformed, y_transformed, z_transformed ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : general_transform_point @INPUT : transform x y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms a point by the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status general_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { return general_transform_point_with_input_steps( transform, x, y, z, NULL, x_transformed, y_transformed, z_transformed ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : general_inverse_transform_point @INPUT : transform x y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms a point by the inverse of the general transform. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status general_inverse_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { return general_inverse_transform_point_with_input_steps( transform, x, y, z, NULL, x_transformed, y_transformed, z_transformed ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_and_invert_transform @INPUT : transform invert_it @OUTPUT : copy @RETURNS : @DESCRIPTION: Copies the transform or its inverse to copy. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : @MODIFIED : Feb. 27, 1995 D. MacDonald - added grid transforms ---------------------------------------------------------------------------- */ static void copy_and_invert_transform( VIO_General_transform *transform, VIO_BOOL invert_it, VIO_General_transform *copy ) { unsigned char *byte_ptr; VIO_Transform *swap; int i, j, trans; *copy = *transform; switch( transform->type ) { case LINEAR: alloc_linear_transform( copy ); *(copy->linear_transform) = *(transform->linear_transform); *(copy->inverse_linear_transform) = *(transform->inverse_linear_transform); if( transform->inverse_flag ) invert_it = !invert_it; if( invert_it ) { swap = copy->linear_transform; copy->linear_transform = copy->inverse_linear_transform; copy->inverse_linear_transform = swap; } copy->inverse_flag = FALSE; break; case THIN_PLATE_SPLINE: VIO_ALLOC2D( copy->points, copy->n_points, copy->n_dimensions); VIO_ALLOC2D( copy->displacements, copy->n_points + copy->n_dimensions + 1, copy->n_dimensions); for_less( i, 0, copy->n_points ) for_less( j, 0, copy->n_dimensions ) copy->points[i][j] = transform->points[i][j]; for_less( i, 0, copy->n_points + copy->n_dimensions + 1 ) for_less( j, 0, copy->n_dimensions ) copy->displacements[i][j] = transform->displacements[i][j]; if( invert_it ) copy->inverse_flag = !copy->inverse_flag; break; case GRID_TRANSFORM: if( transform->displacement_volume ) copy->displacement_volume = (void *) copy_volume( (VIO_Volume) transform->displacement_volume ); if( transform->displacement_volume_file ) copy->displacement_volume_file = create_string( transform->displacement_volume_file ); if( invert_it ) copy->inverse_flag = !copy->inverse_flag; break; case USER_TRANSFORM: ALLOC( byte_ptr, copy->size_user_data ); copy->user_data = byte_ptr; (void) memcpy( copy->user_data, transform->user_data, copy->size_user_data ); if( invert_it ) copy->inverse_flag = !copy->inverse_flag; break; case CONCATENATED_TRANSFORM: ALLOC( copy->transforms, copy->n_transforms ); for_less( trans, 0, copy->n_transforms ) { copy_general_transform( &transform->transforms[trans], ©->transforms[trans] ); } if( invert_it ) copy->inverse_flag = !copy->inverse_flag; break; default: handle_internal_error( "copy_and_invert_transform" ); break; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_general_transform @INPUT : transform @OUTPUT : copy @RETURNS : @DESCRIPTION: Copies the general transform to copy. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void copy_general_transform( VIO_General_transform *transform, VIO_General_transform *copy ) { copy_and_invert_transform( transform, FALSE, copy ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : invert_general_transform @INPUT : transform @OUTPUT : transform @RETURNS : @DESCRIPTION: Inverts a general transform in place. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void invert_general_transform( VIO_General_transform *transform ) { transform->inverse_flag = !transform->inverse_flag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_inverse_general_transform @INPUT : transform @OUTPUT : inverse @RETURNS : @DESCRIPTION: Creates a general transform that is the inverse of the given one. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_inverse_general_transform( VIO_General_transform *transform, VIO_General_transform *inverse ) { copy_and_invert_transform( transform, TRUE, inverse ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : concat_general_transforms @INPUT : first second @OUTPUT : result @RETURNS : @DESCRIPTION: Concatenates two general transforms into result. Transforming a point by result is the same as transforming it by 'first', then transforming by 'second'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void concat_general_transforms( VIO_General_transform *first, VIO_General_transform *second, VIO_General_transform *result ) { int first_start, first_end, first_step; int second_start, second_end, second_step; int i, trans; VIO_BOOL crunching_linear; VIO_BOOL first_inverted_concat, second_inverted_concat; VIO_Transform *first_transform, *first_inverse; VIO_Transform *second_transform, *second_inverse; VIO_General_transform result_tmp, *result_ptr; VIO_General_transform *transform; if( result == first || result == second ) result_ptr = &result_tmp; else result_ptr = result; first_inverted_concat = first->type == CONCATENATED_TRANSFORM && first->inverse_flag; second_inverted_concat = second->type == CONCATENATED_TRANSFORM && second->inverse_flag; if( first->inverse_flag ) { first_start = get_n_concated_transforms( first ) - 1; first_end = 0; first_step = -1; } else { first_start = 0; first_end = get_n_concated_transforms( first ) - 1; first_step = 1; } if( second->inverse_flag ) { second_start = get_n_concated_transforms( second ) - 1; second_end = 0; second_step = -1; } else { second_start = 0; second_end = get_n_concated_transforms( second ) - 1; second_step = 1; } result_ptr->n_transforms = VIO_ABS( first_end - first_start ) + 1 + VIO_ABS( second_end - second_start ) + 1; crunching_linear = FALSE; if( get_nth_general_transform( first, first_end )->type == LINEAR && get_nth_general_transform( second, second_start )->type == LINEAR ) { --result_ptr->n_transforms; crunching_linear = TRUE; first_end -= first_step; second_start += second_step; } if( result_ptr->n_transforms == 1 ) result_ptr->type = LINEAR; else { result_ptr->type = CONCATENATED_TRANSFORM; ALLOC( result_ptr->transforms, result_ptr->n_transforms ); } result_ptr->inverse_flag = FALSE; trans = 0; for( i = first_start; i != first_end + first_step; i += first_step ) { copy_and_invert_transform( get_nth_general_transform( first, i ), first_inverted_concat, get_nth_general_transform(result_ptr,trans)); ++trans; } if( crunching_linear ) { transform = get_nth_general_transform( result_ptr, trans ); alloc_linear_transform( transform ); if( first_inverted_concat ) { first_inverse = get_linear_transform_ptr( get_nth_general_transform(first,first_end+first_step)); first_transform = get_inverse_linear_transform_ptr( get_nth_general_transform(first,first_end+first_step)); } else { first_transform = get_linear_transform_ptr( get_nth_general_transform(first,first_end+first_step)); first_inverse = get_inverse_linear_transform_ptr( get_nth_general_transform(first,first_end+first_step)); } if( second_inverted_concat ) { second_inverse = get_linear_transform_ptr( get_nth_general_transform(second,second_start-second_step)); second_transform = get_inverse_linear_transform_ptr( get_nth_general_transform(second,second_start-second_step)); } else { second_transform = get_linear_transform_ptr( get_nth_general_transform(second,second_start-second_step)); second_inverse = get_inverse_linear_transform_ptr( get_nth_general_transform(second,second_start-second_step)); } concat_transforms( get_linear_transform_ptr(transform), first_transform, second_transform ); concat_transforms( get_inverse_linear_transform_ptr(transform), second_inverse, first_inverse ); ++trans; } for( i = second_start; i != second_end + second_step; i += second_step ) { copy_and_invert_transform( get_nth_general_transform( second, i ), second_inverted_concat, get_nth_general_transform(result_ptr,trans)); ++trans; } if( result == first || result == second ) *result = *result_ptr; } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_general_transform @INPUT : transform @OUTPUT : @RETURNS : @DESCRIPTION: Deletes the transform, freeing up memory. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : @MODIFIED : Feb. 27, 1995 D. MacDonald - added grid transforms ---------------------------------------------------------------------------- */ VIOAPI void delete_general_transform( VIO_General_transform *transform ) { int trans; switch( transform->type ) { case LINEAR: FREE( transform->linear_transform ); FREE( transform->inverse_linear_transform ); break; case THIN_PLATE_SPLINE: if( transform->n_points > 0 && transform->n_dimensions > 0 ) { VIO_FREE2D( transform->points ); VIO_FREE2D( transform->displacements ); } break; case GRID_TRANSFORM: if( transform->displacement_volume ) delete_volume( (VIO_Volume) transform->displacement_volume ); if( transform->displacement_volume_file ) delete_string(transform->displacement_volume_file); transform->displacement_volume_file=NULL; break; case USER_TRANSFORM: if( transform->size_user_data ) FREE( transform->user_data ); break; case CONCATENATED_TRANSFORM: for_less( trans, 0, transform->n_transforms ) delete_general_transform( &transform->transforms[trans] ); if( transform->n_transforms > 0 ) FREE( transform->transforms ); break; default: handle_internal_error( "delete_general_transform" ); break; } } libminc-libminc-2-3-00/volume_io/MNI_formats/grid_transforms.c000066400000000000000000000511451257462267400243720ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #define DEGREES_CONTINUITY 2 /* -1 = Nearest; 0 = Linear; 1 = Quadratic; 2 = Cubic interpolation */ #define SPLINE_DEGREE ((DEGREES_CONTINUITY) + 2) #define N_COMPONENTS VIO_N_DIMENSIONS /* displacement vector has 3 components */ #define FOUR_DIMS 4 #ifdef USE_NEWTONS_METHOD #define INVERSE_FUNCTION_TOLERANCE 0.01 #define INVERSE_DELTA_TOLERANCE 1.0e-5 #define MAX_INVERSE_ITERATIONS 20 #endif static void evaluate_grid_volume( VIO_Volume volume, VIO_Real x, VIO_Real y, VIO_Real z, int degrees_continuity, VIO_Real values[], VIO_Real deriv_x[], VIO_Real deriv_y[], VIO_Real deriv_z[] ); /* ----------------------------- MNI Header ----------------------------------- @NAME : grid_transform_point @INPUT : transform x y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Applies a grid transform to the point @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status grid_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { VIO_Real displacements[N_COMPONENTS]; VIO_Volume volume; /* --- the volume that defines the transform is an offset vector, so evaluate the volume at the given position and add the resulting offset to the given position */ if(!transform->displacement_volume) return VIO_ERROR; volume = (VIO_Volume) transform->displacement_volume; evaluate_grid_volume( volume, x, y, z, DEGREES_CONTINUITY, displacements, NULL, NULL, NULL ); *x_transformed = x + displacements[VIO_X]; *y_transformed = y + displacements[VIO_Y]; *z_transformed = z + displacements[VIO_Z]; return VIO_OK; } #ifdef USE_NEWTONS_METHOD /* ----------------------------- MNI Header ----------------------------------- @NAME : forward_function @INPUT : function_data - contains transform info parameters - x,y,z position @OUTPUT : values - where x,y,z, maps to derivatives - the 3 by 3 derivatives of the mapping @RETURNS : @DESCRIPTION: This function does the same thing as grid_transform_point(), but also gets derivatives. This function is passed to the newton function solution routine to perform the inverse mapping of the grid transformation. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. , 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void forward_function( void *function_data, VIO_Real parameters[], VIO_Real values[], VIO_Real **derivatives ) { int c; VIO_General_transform *transform; VIO_Real deriv_x[N_COMPONENTS], deriv_y[N_COMPONENTS]; VIO_Real deriv_z[N_COMPONENTS]; VIO_Volume volume; transform = (VIO_General_transform *) function_data; /* --- store the offset vector in values[0-2] */ volume = (VIO_Volume) transform->displacement_volume; evaluate_grid_volume( volume, parameters[X], parameters[Y], parameters[Z], DEGREES_CONTINUITY, values, deriv_x, deriv_y, deriv_z ); for_less( c, 0, N_COMPONENTS ) { values[c] += parameters[c]; /* to get x',y',z', add offset to x,y,z */ /*--- given the derivatives of the offset, compute the derivatives of (x,y,z) + offset, with respect to x,y,z */ derivatives[c][X] = deriv_x[c]; derivatives[c][Y] = deriv_y[c]; derivatives[c][Z] = deriv_z[c]; derivatives[c][c] += 1.0; /* deriv of (x,y,z) w.r.t. x or y or z */ } } /* ----------------------------- MNI Header ----------------------------------- @NAME : grid_inverse_transform_point @INPUT : transform x y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : VIO_Status @DESCRIPTION: Applies the inverse grid transform to the point. This is done by using newton-rhapson steps to find the point which maps to the parameters (x,y,z). @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* --------------------------------------------------------------------------- There are two different versions of the grid inverse function. I would have hoped that my version worked best, since it uses first derivatives and Newton's method. However, Louis' version seems to work better, perhaps since it matches the code he uses in minctracc to generate the grid transforms. - David MacDonald ---------------------------------------------------------------------------- */ VIOAPI VIO_Status grid_inverse_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { VIO_Real solution[VIO_N_DIMENSIONS]; VIO_Real initial_guess[VIO_N_DIMENSIONS]; VIO_Real desired_values[VIO_N_DIMENSIONS]; /* --- fill in the initial guess */ initial_guess[X] = x; initial_guess[Y] = y; initial_guess[Z] = z; /* --- define what the desired function values are */ desired_values[X] = x; desired_values[Y] = y; desired_values[Z] = z; /* --- find the x,y,z that are mapped to the desired values */ if( newton_root_find( VIO_N_DIMENSIONS, forward_function, (void *) transform, initial_guess, desired_values, solution, INVERSE_FUNCTION_TOLERANCE, INVERSE_DELTA_TOLERANCE, MAX_INVERSE_ITERATIONS )) { *x_transformed = solution[X]; *y_transformed = solution[Y]; *z_transformed = solution[Z]; return VIO_OK; } else /* --- if no solution found, not sure what is reasonable to return */ { *x_transformed = x; *y_transformed = y; *z_transformed = z; return VIO_ERROR; } return VIO_ERROR; } #endif /* ----------------------------- MNI Header ----------------------------------- @NAME : grid_inverse_transform_point_with_input_steps @INPUT : transform x y z input_volume_steps @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms the point by the inverse of the grid transform. Approximates the solution using a simple iterative step method. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993? Louis Collins @MODIFIED : 1994 David MacDonald @MODIFIED : 2013 June 10, Matthijs van Eede, added the possibility to pass along the step sizes of the input file which is being resampled to determine the appropriate error margin (ftol) ---------------------------------------------------------------------------- */ VIOAPI VIO_Status grid_inverse_transform_point_with_input_steps( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *input_volume_steps, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { #define NUMBER_TRIES 10 int tries; VIO_Real best_x, best_y, best_z; VIO_Real tx, ty, tz; VIO_Real gx, gy, gz; VIO_Real error_x, error_y, error_z, error, smallest_e; VIO_Real ftol; VIO_Status status=VIO_ERROR; int sizes[VIO_MAX_DIMENSIONS]; VIO_Real steps[VIO_MAX_DIMENSIONS]; VIO_Volume volume; short d, vector_dim = -1; int i; if((status=grid_transform_point( transform, x, y, z, &tx, &ty, &tz ))!=VIO_OK) return status; tx = x - (tx - x); ty = y - (ty - y); tz = z - (tz - z); if((status=grid_transform_point( transform, tx, ty, tz, &gx, &gy, &gz ))!=VIO_OK) return status; error_x = x - gx; error_y = y - gy; error_z = z - gz; tries = 0; smallest_e = VIO_FABS(error_x) + VIO_FABS(error_y) + VIO_FABS(error_z); best_x = tx; best_y = ty; best_z = tz; // Adapt ftol to grid step sizes. For 1mm stx volume with grid 4mm, we // are using ftol=0.05 (=4mm/80). For histology data at grid 0.125mm, // then use ftol=0.125/80=0.0015625, which is fine on 0.01mm volume. // ftol = 0.05; // good for MNI space at 1mm (grid 2mm or 4mm) // ftol = 0.001; // acceptable for big brain slice (grid at 0.125, voxel at 0.01mm) // This is ok too: // Make the error a fraction of the initial residual. // ftol = 0.05 * smallest_e + 0.0001; volume = (VIO_Volume) transform->displacement_volume; get_volume_sizes( volume, sizes ); get_volume_separations( volume, steps ); /*--- find which of 4 dimensions is the vector dimension */ for_less( vector_dim, 0, FOUR_DIMS ) { for_less( d, 0, VIO_N_DIMENSIONS ) { if( volume->spatial_axes[d] == vector_dim ) break; } if( d == VIO_N_DIMENSIONS ) break; } // If we have information about the step sizes of the input volume that is // being resampled, base the tolerance on those instead of the step sizes // of the deformation grid; there can be a significant difference. ftol = -1.0; if( input_volume_steps != NULL){ for_less( i, 0, 3 ) { if( ftol < 0 ) ftol = input_volume_steps[i]; if( input_volume_steps[i] < ftol ) ftol = input_volume_steps[i]; } } else { for_less( d, 0, FOUR_DIMS ) { if( d == vector_dim ) continue; if( sizes[d] == 1 ) continue; if( ftol < 0 ) ftol = steps[d]; if( steps[d] < ftol ) ftol = steps[d]; } } ftol = ftol / 80.0; if( ftol > 0.05 ) ftol = 0.05; // just to be sure for large grids while( ++tries < NUMBER_TRIES && smallest_e > ftol ) { tx += 0.95 * error_x; ty += 0.95 * error_y; tz += 0.95 * error_z; if((status=grid_transform_point( transform, tx, ty, tz, &gx, &gy, &gz ))!=VIO_OK) return status; error_x = x - gx; error_y = y - gy; error_z = z - gz; error = VIO_FABS(error_x) + VIO_FABS(error_y) + VIO_FABS(error_z); if( error < smallest_e ) { smallest_e = error; best_x = tx; best_y = ty; best_z = tz; } } *x_transformed = best_x; *y_transformed = best_y; *z_transformed = best_z; return VIO_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : grid_inverse_transform_point @INPUT : transform x y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : @DESCRIPTION: Transforms the point by the inverse of the grid transform. Approximates the solution using a simple iterative step method. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993? Louis Collins @MODIFIED : 1994 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status grid_inverse_transform_point( VIO_General_transform *transform, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { return grid_inverse_transform_point_with_input_steps(transform, x, y, z, NULL, x_transformed, y_transformed, z_transformed ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_grid_volume @INPUT : volume voxel degrees_continuity @OUTPUT : values derivs (if non-NULL) @RETURNS : @DESCRIPTION: Takes a voxel space position and evaluates the value within the volume by nearest_neighbour, linear, quadratic, or cubic interpolation. Rather than use the generic evaluate_volume function, this special purpose function is a bit faster. @CREATED : Mar. 16, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void evaluate_grid_volume( VIO_Volume volume, VIO_Real x, VIO_Real y, VIO_Real z, int degrees_continuity, VIO_Real values[], VIO_Real deriv_x[], VIO_Real deriv_y[], VIO_Real deriv_z[] ) { VIO_Real voxel[VIO_MAX_DIMENSIONS], voxel_vector[VIO_MAX_DIMENSIONS]; int inc0, inc1, inc2, inc3, inc[VIO_MAX_DIMENSIONS], derivs_per_value; int ind0, vector_dim; int start0, start1, start2, start3, inc_so_far; int end0, end1, end2, end3; int v0, v1, v2, v3; int v, d, id, sizes[VIO_MAX_DIMENSIONS]; int start[VIO_MAX_DIMENSIONS]; int end[VIO_MAX_DIMENSIONS]; VIO_Real fraction[VIO_MAX_DIMENSIONS], bound, pos; VIO_Real coefs[SPLINE_DEGREE*SPLINE_DEGREE*SPLINE_DEGREE*N_COMPONENTS]; VIO_Real values_derivs[N_COMPONENTS + N_COMPONENTS * VIO_N_DIMENSIONS]; int is_2dslice = -1; convert_world_to_voxel( volume, x, y, z, voxel ); if( get_volume_n_dimensions(volume) != FOUR_DIMS ) handle_internal_error( "evaluate_grid_volume" ); /*--- find which of 4 dimensions is the vector dimension */ for_less( vector_dim, 0, FOUR_DIMS ) { for_less( d, 0, VIO_N_DIMENSIONS ) { if( volume->spatial_axes[d] == vector_dim ) break; } if( d == VIO_N_DIMENSIONS ) break; } get_volume_sizes( volume, sizes ); /*--- if a 2-d slice, do best interpolation in the plane */ for_less( d, 0, FOUR_DIMS ) { if( d == vector_dim ) continue; if( sizes[d] == 1 ) { is_2dslice = d; } } bound = (VIO_Real) degrees_continuity / 2.0; /*--- if near the edges, reduce the degrees of continuity. This is very important. Doing cubic (with a shifted stencil) near a boundary will cause trouble because the stencil needs to be centered. This is why quadratic is also disabled (not symmetric stencil). CL. */ for_less( d, 0, FOUR_DIMS ) { if( d == is_2dslice ) continue; if( d == vector_dim ) continue; while( degrees_continuity >= -1 && (voxel[d] < bound || voxel[d] > (VIO_Real) sizes[d] - 1.0 - bound || bound == (VIO_Real) sizes[d] - 1.0 - bound ) ) { --degrees_continuity; if( degrees_continuity == 1 ) degrees_continuity = 0; bound = (VIO_Real) degrees_continuity / 2.0; } } /*--- check to fill in the first derivative */ if( degrees_continuity < 0 && deriv_x != NULL ) { for_less( v, 0, N_COMPONENTS ) { deriv_x[v] = 0.0; deriv_y[v] = 0.0; deriv_z[v] = 0.0; } } /*--- check if outside */ for( d = 0; d < FOUR_DIMS; d++ ) { if( d == vector_dim ) continue; if( voxel[d] < -0.5 || voxel[d] > sizes[d]-0.5 ) { for_less( v, 0, N_COMPONENTS ) { values[v] = 0.0; } return; } } /*--- determine the starting positions in the volume to grab control vertices */ id = 0; for_less( d, 0, FOUR_DIMS ) { if( d == vector_dim ) continue; if( d == is_2dslice ) { start[d] = 0; end[d] = 1; } else { pos = voxel[d] - bound; start[d] = VIO_FLOOR( pos ); if( start[d] < 0 ) { start[d] = 0; } else if( start[d]+degrees_continuity+1 >= sizes[d] ) { start[d] = sizes[d] - degrees_continuity - 2; } end[d] = start[d] + degrees_continuity + 2; fraction[id] = pos - (double) start[d]; ++id; } } /*--- create the strides */ start[vector_dim] = 0; end[vector_dim] = N_COMPONENTS; inc_so_far = N_COMPONENTS; for_down( d, FOUR_DIMS-1, 0 ) { if( d != vector_dim ) { inc[d] = inc_so_far; inc_so_far *= ( end[d] - start[d] ); } } /*--- copy stride arrays to variables for speed */ inc[vector_dim] = 1; start0 = start[0]; start1 = start[1]; start2 = start[2]; start3 = start[3]; end0 = end[0]; end1 = end[1]; end2 = end[2]; end3 = end[3]; inc0 = inc[0] - inc[1] * (end1 - start1); inc1 = inc[1] - inc[2] * (end2 - start2); inc2 = inc[2] - inc[3] * (end3 - start3); inc3 = inc[3]; /*--- extract values from volume */ ind0 = 0; for_less( v0, start0, end0 ) { for_less( v1, start1, end1 ) { for_less( v2, start2, end2 ) { for_less( v3, start3, end3 ) { GET_VALUE_4D_TYPED( coefs[ind0], (VIO_Real), volume, v0, v1, v2, v3 ); ind0 += inc3; } ind0 += inc2; } ind0 += inc1; } ind0 += inc0; } /*--- interpolate values */ if( degrees_continuity == -1 ) { for_less( v, 0, N_COMPONENTS ) values[v] = coefs[v]; } else { if( is_2dslice == -1 ) { evaluate_interpolating_spline( VIO_N_DIMENSIONS, fraction, degrees_continuity + 2, N_COMPONENTS, coefs, 0, values_derivs ); } else { evaluate_interpolating_spline( VIO_N_DIMENSIONS-1, fraction, degrees_continuity + 2, N_COMPONENTS, coefs, 0, values_derivs ); } /*--- extract values and derivatives from values_derivs */ if( deriv_x != NULL ) derivs_per_value = 8; else derivs_per_value = 1; for_less( v, 0, N_COMPONENTS ) { values[v] = values_derivs[v*derivs_per_value]; } if( deriv_x != NULL ) { for_less( v, 0, N_COMPONENTS ) { id = 0; for_less( d, 0, FOUR_DIMS ) { if( d != vector_dim ) { voxel_vector[d] = values_derivs[v*8 + (4>>id)]; ++id; } else voxel_vector[d] = 0.0; } convert_voxel_normal_vector_to_world( volume, voxel_vector, &deriv_x[v], &deriv_y[v], &deriv_z[v] ); } } } } libminc-libminc-2-3-00/volume_io/MNI_formats/mni_io.c000066400000000000000000000277311257462267400224450ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include static const char COMMENT_CHAR1 = '%'; static const char COMMENT_CHAR2 = '#'; /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_get_nonwhite_character @INPUT : file @OUTPUT : ch @RETURNS : VIO_OK or VIO_END_OF_FILE @DESCRIPTION: Gets the next non white space character from the MNI file (i.e., tags or transforms). This routine handles comment characters, and is thus the base routine for all MNI tag or transform file input. Any part of a line starting with a comment character is ignored. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_get_nonwhite_character( FILE *file, char *ch ) { VIO_BOOL in_comment; VIO_Status status; in_comment = FALSE; do { status = input_character( file, ch ); if( status == VIO_OK ) { if( *ch == COMMENT_CHAR1 || *ch == COMMENT_CHAR2 ) in_comment = TRUE; else if( *ch == '\n' ) in_comment = FALSE; } } while( status == VIO_OK && (in_comment || *ch == ' ' || *ch == '\t' || *ch == '\n' || *ch == '\r') ); /* ignore carriage returns */ if( status == VIO_ERROR ) status = VIO_END_OF_FILE; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_skip_expected_character @INPUT : file expected_ch @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Gets the next nonwhite character. If it is the expected character, fine, otherwise print an error message. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_skip_expected_character( FILE *file, char expected_ch ) { char ch; VIO_Status status; status = mni_get_nonwhite_character( file, &ch ); if( status == VIO_OK ) { if( ch != expected_ch ) { print_error( "Expected '%c', found '%c'.\n", expected_ch, ch ); status = VIO_ERROR; } } else { print_error( "Expected '%c', found end of file.\n", expected_ch ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_line @INPUT : file max_length @OUTPUT : string @RETURNS : VIO_OK or VIO_END_OF_FILE @DESCRIPTION: Inputs a line of text from a file. The carriage return is read, but not placed in the string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_line( FILE *file, VIO_STR *string ) { VIO_Status status; char ch; *string = create_string( NULL ); status = input_character( file, &ch ); while( status == VIO_OK && ch != '\n' ) { if (ch != '\r') { /* Always ignore carriage returns */ concat_char_to_string( string, ch ); } status = input_character( file, &ch ); } if( status != VIO_OK ) { delete_string( *string ); *string = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_string @INPUT : file max_length termination_char1 termination_char2 @OUTPUT : string @RETURNS : VIO_OK or VIO_END_OF_FILE @DESCRIPTION: Inputs a string from the file, up to the next occurrence of one of the termination characters or a carriage return. If the first nonwhite character is a '"', then the termination characters become '"'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_string( FILE *file, VIO_STR *string, char termination_char1, char termination_char2 ) { VIO_Status status; char ch; VIO_BOOL quoted; *string = create_string( NULL ); status = mni_get_nonwhite_character( file, &ch ); if( status == VIO_OK && ch == '"' ) { quoted = TRUE; status = mni_get_nonwhite_character( file, &ch ); termination_char1 = '"'; termination_char2 = '"'; } else quoted = FALSE; while( status == VIO_OK && ch != termination_char1 && ch != termination_char2 && ch != '\n' ) { if (ch != '\r') { /* Always ignore carriage returns */ concat_char_to_string( string, ch ); } status = input_character( file, &ch ); } if( !quoted ) (void) unget_character( file, ch ); while( string_length(*string) > 0 && (*string)[string_length(*string)-1] == ' ' ) (*string)[string_length(*string)-1] = VIO_END_OF_STRING; if( status != VIO_OK ) { delete_string( *string ); *string = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_keyword_and_equal_sign @INPUT : file keyword print_error_message - whether to print error messages @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs the desired keyword from the file and an equal sign. If there is no match, then an error message may be printed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_keyword_and_equal_sign( FILE *file, const char keyword[], VIO_BOOL print_error_message ) { VIO_Status status; VIO_STR str; status = mni_input_string( file, &str, (char) '=', (char) 0 ); if( status == VIO_END_OF_FILE ) return( status ); if( status != VIO_OK || !equal_strings( str, keyword ) || mni_skip_expected_character( file, (char) '=' ) != VIO_OK ) { if( print_error_message ) print_error( "Expected \"%s =\"\n", keyword ); status = VIO_ERROR; } delete_string( str ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : unget_string @INPUT : file str @OUTPUT : @RETURNS : @DESCRIPTION: Places the first nonblank character of the string back onto the input stream, as an approximation to pushing the entire string back on the input stream, which only happens in error situations. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void unget_string( FILE *file, VIO_STR str ) { int len; len = 0; while( str[len] == ' ' || str[len] == '\t' ) ++len; if( str[len] != VIO_END_OF_STRING ) (void) unget_character( file, str[len] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_real @INPUT : file @OUTPUT : d @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs an ascii representation of a real value. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_real( FILE *file, VIO_Real *d ) { VIO_Status status; VIO_STR str; status = mni_input_string( file, &str, (char) ' ', (char) ';' ); if( status == VIO_OK && sscanf( str, "%lf", d ) != 1 ) { unget_string( file, str ); status = VIO_ERROR; } delete_string( str ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_reals @INPUT : file @OUTPUT : n reals @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs an arbitrary number of real values, up to the next semicolon. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_reals( FILE *file, int *n, VIO_Real *reals[] ) { VIO_Real d; *n = 0; while( mni_input_real( file, &d ) != VIO_ERROR ) { ADD_ELEMENT_TO_ARRAY( *reals, *n, d, DEFAULT_CHUNK_SIZE ); } return( mni_skip_expected_character( file, (char) ';' ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : mni_input_int @INPUT : file @OUTPUT : i @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs an integer from an ascii file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status mni_input_int( FILE *file, int *i ) { VIO_Status status; VIO_STR str; status = mni_input_string( file, &str, (char) ' ', (char) ';' ); if( status == VIO_OK && sscanf( str, "%d", i ) != 1 ) { unget_string( file, str ); status = VIO_ERROR; } delete_string( str ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_comments @INPUT : file comments @OUTPUT : @RETURNS : @DESCRIPTION: Outputs a string to the file, in comment format, by placing a comment at the beginning of the string, and after each carriage return. An extra carriage return is placed after the comments, if the comments do not end in a carriage return. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void output_comments( FILE *file, const char *comments ) { int i, len; if( comments != NULL ) { len = string_length( comments ); (void) output_character( file, COMMENT_CHAR1 ); for( i = 0; i < len; ++i ) { (void) output_character( file, comments[i] ); if( comments[i] == '\n' && i < len - 1 ) (void) output_character( file, COMMENT_CHAR1 ); } if( len > 0 && comments[len-1] != '\n' ) (void) output_character( file, (char) '\n' ); } } libminc-libminc-2-3-00/volume_io/MNI_formats/tag_points.c000066400000000000000000000625471257462267400233460ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include static const char * const TAG_FILE_HEADER = "MNI Tag Point File"; static const char * const VOLUMES_STRING = "Volumes"; static const char * const TAG_POINTS_STRING = "Points"; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_tag_file_suffix @INPUT : @OUTPUT : @RETURNS : "tag" @DESCRIPTION: Returns the default tag file suffix. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_default_tag_file_suffix( void ) { return( "tag" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_tag_file_output @INPUT : file comments n_volumes @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Writes the header and first part of a tag file. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status initialize_tag_file_output( FILE *file, VIO_STR comments, int n_volumes ) { VIO_Status status; /* parameter checking */ status = VIO_OK; if( file == NULL ) { print_error( "start_writing_tags(): passed NULL FILE ptr.\n"); status = VIO_ERROR; } if( n_volumes != 1 && n_volumes != 2 ) { print_error( "output_tag_points():" ); print_error( " can only support 1 or 2 volumes;\n" ); print_error( " you've supplied %d.\n", n_volumes ); status = VIO_ERROR; } if( status == VIO_OK ) { /* okay write the file header and tag points header */ (void) fprintf( file, "%s\n", TAG_FILE_HEADER ); (void) fprintf( file, "%s = %d;\n", VOLUMES_STRING, n_volumes ); output_comments( file, comments ); (void) fprintf( file, "\n" ); (void) fprintf( file, "%s =", TAG_POINTS_STRING ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_one_tag @INPUT : file n_volumes tag_volume1 tag_volume2 weight - NULL if not desired to specify structure_id - NULL if not desired to specify patient_id - NULL if not desired to specify label - NULL if not desired to specify @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Writes one tag to the output. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_one_tag( FILE *file, int n_volumes, VIO_Real tag_volume1[], VIO_Real tag_volume2[], VIO_Real *weight, int *structure_id, int *patient_id, VIO_STR label ) { VIO_Status status; VIO_BOOL aux_present; /* parameter checking */ status = VIO_OK; (void) fprintf( file, "\n %.15g %.15g %.15g", tag_volume1[0], tag_volume1[1], tag_volume1[2] ); if( n_volumes >= 2 ) { (void) fprintf( file, " %.15g %.15g %.15g", tag_volume2[0], tag_volume2[1], tag_volume2[2] ); } /*--- decide whether to output the 3 numerical information */ aux_present = (weight != NULL || structure_id != NULL || patient_id != NULL); if( aux_present ) { if( weight != (VIO_Real *) NULL ) (void) fprintf( file, " %.15g", *weight ); else (void) fprintf( file, " %.15g", 0.0 ); if( structure_id != NULL ) (void) fprintf( file, " %d", *structure_id ); else (void) fprintf( file, " %d", -1 ); if( patient_id != NULL ) (void) fprintf( file, " %d", *patient_id ); else (void) fprintf( file, " %d", -1 ); } if( label != NULL ) (void) fprintf( file, " \"%s\"", label ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : terminate_tag_file_output @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Finishes writing the tag file, by placing the closing semicolon. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void terminate_tag_file_output( FILE *file ) { (void) fprintf( file, ";\n" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_tag_points @INPUT : file comments - may be null n_volumes n_tag_points tags_volume1 tags_volume2 weights structure_ids patient_ids labels @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the tag points in MNI tag point format. If weights, structure_ids, and patient_ids are all NULL, they are not written to the file. If labels is NULL, it is not written. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 19, 1995 D. MacDonald, now calls the 1 at a time routine @MODIFIED : Apr. 1, 1996 D. MacDonald, fixed bug of passing non-null tags_volume2 with n_volumes==1 ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_tag_points( FILE *file, VIO_STR comments, int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], VIO_STR *labels ) { VIO_Status status; int i; status = initialize_tag_file_output( file, comments, n_volumes ); if( status == VIO_OK ) { for( i = 0; i < n_tag_points; ++i ) { status = output_one_tag( file, n_volumes, tags_volume1[i], (n_volumes == 1) ? NULL : tags_volume2[i], weights == NULL ? NULL : &weights[i], structure_ids == NULL ? NULL : &structure_ids[i], patient_ids == NULL ? NULL : &patient_ids[i], labels == NULL ? NULL : labels[i] ); if( status != VIO_OK ) break; } } if( status == VIO_OK ) terminate_tag_file_output( file ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_tags @INPUT : tags n_tag_points @OUTPUT : @RETURNS : @DESCRIPTION: Frees the tag x,y,z positions. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void free_tags( VIO_Real **tags, int n_tag_points ) { int i; for( i = 0; i < n_tag_points; ++i ) FREE( tags[i] ); if( n_tag_points > 0 ) FREE( tags ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_tag_points @INPUT : n_volumes n_tag_points tags_volume1 tags_volume2 weights structure_ids patient_ids labels @OUTPUT : @RETURNS : @DESCRIPTION: Frees the tag point data. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_tag_points( int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], char **labels ) { int i; if( n_tag_points > 0 ) { free_tags( tags_volume1, n_tag_points ); if( n_volumes == 2 ) free_tags( tags_volume2, n_tag_points ); if( weights != (VIO_Real *) NULL ) FREE( weights ); if( structure_ids != (int *) NULL ) FREE( structure_ids ); if( patient_ids != (int *) NULL ) FREE( patient_ids ); if( labels != (char **) NULL ) { for( i = 0; i < n_tag_points; ++i ) delete_string( labels[i] ); if( n_tag_points > 0 ) FREE( labels ); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : extract_label @INPUT : str @OUTPUT : label @RETURNS : @DESCRIPTION: Extracts the label from the string, by either taking the first space delimited word, or first quoted string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_STR extract_label( VIO_STR str ) { VIO_BOOL quoted; int i; VIO_STR label; i = 0; /* --- skip leading space */ while( str[i] == ' ' || str[i] == '\t' ) ++i; if( str[i] == '"' ) { quoted = TRUE; ++i; } else quoted = FALSE; /* --- copy characters until either closing quote is found (if quoted), or white space or end of string is found */ label = create_string( NULL ); while( str[i] != VIO_END_OF_STRING && ( (quoted && str[i] != '"') || (!quoted && str[i] != ' ' && str[i] != '\t') ) ) { concat_char_to_string( &label, str[i] ); ++i; } return( label ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_tag_file_input @INPUT : file @OUTPUT : n_volumes @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Reads the tag file header and first part of file. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status initialize_tag_file_input( FILE *file, int *n_volumes_ptr ) { VIO_STR line; int n_volumes; /* parameter checking */ if( file == NULL ) { print_error( "initialize_tag_file_input(): passed NULL FILE ptr.\n"); return( VIO_ERROR ); } /* okay read the header */ if( mni_input_string( file, &line, (char) 0, (char) 0 ) != VIO_OK || !equal_strings( line, TAG_FILE_HEADER ) ) { print_error( "input_tag_points(): invalid header in file.\n"); delete_string( line ); return( VIO_ERROR ); } delete_string( line ); /* now read the number of volumes */ if( mni_input_keyword_and_equal_sign( file, VOLUMES_STRING, TRUE ) != VIO_OK ) return( VIO_ERROR ); if( mni_input_int( file, &n_volumes ) != VIO_OK ) { print_error( "input_tag_points(): expected # volumes after %s.\n", VOLUMES_STRING ); return( VIO_ERROR ); } if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) return( VIO_ERROR ); if( n_volumes != 1 && n_volumes != 2 ) { print_error( "input_tag_points(): invalid # volumes: %d \n", n_volumes ); return( VIO_ERROR ); } /* now read the tag points header */ if( mni_input_keyword_and_equal_sign( file, TAG_POINTS_STRING, TRUE ) != VIO_OK) return( VIO_ERROR ); if( n_volumes_ptr != NULL ) *n_volumes_ptr = n_volumes; return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_one_tag @INPUT : file n_volumes @OUTPUT : tags_volume1_ptr tags_volume2_ptr weight_ptr structure_id_ptr patient_id_ptr label_ptr @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs the file and passes back the data. The last four arguments can each be set to NULL if the corresponding information is not desired. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status read_one_tag( FILE *file, int n_volumes, VIO_Real tags_volume1_ptr[], VIO_Real tags_volume2_ptr[], VIO_Real *weight_ptr, int *structure_id_ptr, int *patient_id_ptr, VIO_STR *label_ptr ) { VIO_Status status; VIO_STR line; VIO_BOOL last_was_blank, in_quotes; int n_strings, pos, i; VIO_Real x1 = 0.0, y1 = 0.0, z1 = 0.0, x2 = 0.0, y2 = 0.0, z2 = 0.0; int structure_id, patient_id; VIO_Real weight; VIO_STR label; /* parameter checking */ if( file == NULL ) { print_error( "read_one_tag(): passed NULL FILE ptr.\n"); return( VIO_ERROR ); } status = mni_input_real( file, &x1 ); if( status == VIO_OK ) { if( mni_input_real( file, &y1 ) != VIO_OK || mni_input_real( file, &z1 ) != VIO_OK || (n_volumes == 2 && (mni_input_real( file, &x2 ) != VIO_OK || mni_input_real( file, &y2 ) != VIO_OK || mni_input_real( file, &z2 ) != VIO_OK)) ) { print_error( "read_one_tag(): error reading tag point\n" ); return( VIO_ERROR ); } if( tags_volume1_ptr != NULL ) { tags_volume1_ptr[VIO_X] = x1; tags_volume1_ptr[VIO_Y] = y1; tags_volume1_ptr[VIO_Z] = z1; } if( n_volumes == 2 && tags_volume2_ptr != NULL ) { tags_volume2_ptr[VIO_X] = x2; tags_volume2_ptr[VIO_Y] = y2; tags_volume2_ptr[VIO_Z] = z2; } label = NULL; weight = 0.0; structure_id = -1; patient_id = -1; n_strings = 0; if( mni_input_line( file, &line ) == VIO_OK ) { i = 0; last_was_blank = TRUE; in_quotes = FALSE; while( line[i] != VIO_END_OF_STRING ) { if( line[i] == ' ' || line[i] == '\t' ) { last_was_blank = TRUE; } else { if( last_was_blank && !in_quotes ) ++n_strings; last_was_blank = FALSE; if( line[i] == '\"' ) in_quotes = !in_quotes; } ++i; } while( i > 0 && (line[i] == ' ' || line[i] == '\t' || line[i] == VIO_END_OF_STRING) ) --i; if( line[i] == ';' ) { (void) unget_character( file, (char) ';' ); line[i] = VIO_END_OF_STRING; } } if( n_strings != 0 ) { if( n_strings == 1 ) { label = extract_label( line ); } else if( n_strings < 3 || n_strings > 4 || sscanf( line, "%lf %d %d %n", &weight, &structure_id, &patient_id, &pos ) != 3 ) { print_error( "input_tag_points(): error reading tag point\n" ); return( VIO_ERROR ); } else if( n_strings == 4 ) { label = extract_label( &line[pos] ); } } delete_string( line ); if( weight_ptr != NULL ) *weight_ptr = weight; if( structure_id_ptr != NULL ) *structure_id_ptr = structure_id; if( patient_id_ptr != NULL ) *patient_id_ptr = patient_id; if( label_ptr != NULL ) *label_ptr = label; else delete_string( label ); } if( status == VIO_ERROR ) /* --- found no more tag points, should now find ; */ { if( mni_skip_expected_character( file, (char) ';' ) != VIO_OK ) status = VIO_ERROR; else status = VIO_END_OF_FILE; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_tag_file @INPUT : filename comments n_volumes n_tag_points tags_volume1 tags_volume2 weights structure_ids patient_ids labels @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Opens the file, outputs the tag points, and closes the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 19, 1995 D. MacDonald - now calls the 1 at a time funcs ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_tag_file( VIO_STR filename, VIO_STR comments, int n_volumes, int n_tag_points, VIO_Real **tags_volume1, VIO_Real **tags_volume2, VIO_Real weights[], int structure_ids[], int patient_ids[], VIO_STR labels[] ) { VIO_Status status; FILE *file; status = open_file_with_default_suffix( filename, get_default_tag_file_suffix(), WRITE_FILE, ASCII_FORMAT, &file ); if( status == VIO_OK ) status = output_tag_points( file, comments, n_volumes, n_tag_points, tags_volume1, tags_volume2, weights, structure_ids, patient_ids, labels ); if( status == VIO_OK ) status = close_file( file ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_tag_file @INPUT : filename @OUTPUT : n_volumes n_tag_points tags_volume1 tags_volume2 weights structure_ids patient_ids labels @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Opens the file, inputs the tag points, and closes the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_tag_file( VIO_STR filename, int *n_volumes, int *n_tag_points, VIO_Real ***tags_volume1, VIO_Real ***tags_volume2, VIO_Real **weights, int **structure_ids, int **patient_ids, VIO_STR *labels[] ) { VIO_Status status; FILE *file; status = open_file_with_default_suffix( filename, get_default_tag_file_suffix(), READ_FILE, ASCII_FORMAT, &file ); if( status == VIO_OK ) status = input_tag_points( file, n_volumes, n_tag_points, tags_volume1, tags_volume2, weights, structure_ids, patient_ids, labels ); if( status == VIO_OK ) status = close_file( file ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_one_tag @INPUT : file n_volumes @OUTPUT : tag_volume1 tag_volume2 weight structure_id patient_id label status @RETURNS : TRUE if successful. @DESCRIPTION: Reads one tag point line from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL input_one_tag( FILE *file, int n_volumes, VIO_Real tag_volume1[], VIO_Real tag_volume2[], VIO_Real *weight, int *structure_id, int *patient_id, VIO_STR *label, VIO_Status *status ) { VIO_BOOL read_one; VIO_Status read_status; read_status = read_one_tag( file, n_volumes, tag_volume1, tag_volume2, weight, structure_id, patient_id, label ); read_one = (read_status == VIO_OK); if( read_status == VIO_END_OF_FILE ) read_status = VIO_OK; if( status != NULL ) *status = read_status; return( read_one ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_tag_points @INPUT : file @OUTPUT : n_volumes n_tag_points tags_volume1 tags_volume2 weights structure_ids patient_ids labels @RETURNS : OR or VIO_ERROR @DESCRIPTION: Inputs an entire tag point file into a set of arrays. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_tag_points( FILE *file, int *n_volumes_ptr, int *n_tag_points, VIO_Real ***tags_volume1, VIO_Real ***tags_volume2, VIO_Real **weights, int **structure_ids, int **patient_ids, VIO_STR *labels[] ) { VIO_Status status; VIO_Real tags1[VIO_N_DIMENSIONS]; VIO_Real tags2[VIO_N_DIMENSIONS]; VIO_Real weight; int structure_id, patient_id, n_volumes; VIO_STR label; status = initialize_tag_file_input( file, &n_volumes ); if( n_volumes_ptr != NULL ) *n_volumes_ptr = n_volumes; *n_tag_points = 0; while( status == VIO_OK && input_one_tag( file, n_volumes, tags1, tags2, &weight, &structure_id, &patient_id, &label, &status ) ) { if( tags_volume1 != NULL ) { SET_ARRAY_SIZE( *tags_volume1, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE ); ALLOC( (*tags_volume1)[*n_tag_points], 3 ); (*tags_volume1)[*n_tag_points][VIO_X] = tags1[VIO_X]; (*tags_volume1)[*n_tag_points][VIO_Y] = tags1[VIO_Y]; (*tags_volume1)[*n_tag_points][VIO_Z] = tags1[VIO_Z]; } if( n_volumes == 2 && tags_volume2 != NULL ) { SET_ARRAY_SIZE( *tags_volume2, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE ); ALLOC( (*tags_volume2)[*n_tag_points], 3 ); (*tags_volume2)[*n_tag_points][VIO_X] = tags2[VIO_X]; (*tags_volume2)[*n_tag_points][VIO_Y] = tags2[VIO_Y]; (*tags_volume2)[*n_tag_points][VIO_Z] = tags2[VIO_Z]; } if( weights != NULL ) { SET_ARRAY_SIZE( *weights, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE); (*weights)[*n_tag_points] = weight; } if( structure_ids != NULL ) { SET_ARRAY_SIZE( *structure_ids, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE); (*structure_ids)[*n_tag_points] = structure_id; } if( patient_ids != NULL ) { SET_ARRAY_SIZE( *patient_ids, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE); (*patient_ids)[*n_tag_points] = patient_id; } if( labels != NULL ) { SET_ARRAY_SIZE( *labels, *n_tag_points, *n_tag_points+1, DEFAULT_CHUNK_SIZE); (*labels)[*n_tag_points] = label; } else delete_string( label ); ++(*n_tag_points); } return( status ); } libminc-libminc-2-3-00/volume_io/MNI_formats/thin_plate_spline.c000066400000000000000000000357441257462267400246770ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #define INVERSE_FUNCTION_TOLERANCE 0.01 #define INVERSE_DELTA_TOLERANCE 0.01 #define MAX_INVERSE_ITERATIONS 20 /* ----------------------------- MNI Header ----------------------------------- @NAME : thin_plate_spline.c @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: library of routines for warping/mapping transformations. @METHOD : The original source code for these routines are taken from program VOI, written by Weiqian Dai. @GLOBALS : @CALLS : @CREATED : Dec 2, 1991 LC @MODIFIED : Mon Apr 5 09:00:54 EST 1993 louis - building new library routines, with prototypes @MODIFIED : Wed Jul 14 1993 david - incorporated into libmni.c Feb. 28, 1995 D. MacDonald - rewrote to get rid of mnewt and floats ---------------------------------------------------------------------------- */ /* ----- structure used by newton root finding ---- */ typedef struct { VIO_Real **points; VIO_Real **weights; int n_points; int n_dims; } spline_data_struct; /*------------ static functions -----------------*/ static void newton_function( void *function_data, VIO_Real parameters[], VIO_Real values[], VIO_Real **first_derivs ); static VIO_Real thin_plate_spline_U_deriv( VIO_Real pos[], VIO_Real landmark[], int n_dims, int deriv_dim ); /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_thin_plate_spline @INPUT : n_dims - dimensionality of the function n_values - number of values of the function n_points - number of defining landmarks points[n_points][n_dims] - landmarks weights[n_points+1+n_dims][n_values] - weights for the points pos[n_dims] - position at which to evaluate @OUTPUT : values[n_values] - function values at this position deriv[n_values][n_dims] - function derivatives at this point @RETURNS : @DESCRIPTION: Evaluates the thin plate spline at the given point, and, if the argument is non-null, the derivatives also. The thin-plate spline takes a point in n_dims dimensional space and returns a point in n_values dimensional space. When used for transforms, as in this file, n_values == n_dims, but the code will work for the general case where n_values != n_dims. @METHOD : @GLOBALS : @CALLS : @CREATED : @MODIFIED : Feb. 27, 1995 David MacDonald modified from some code written by WeiQian Dai later modified by Louis Collins ---------------------------------------------------------------------------- */ VIOAPI void evaluate_thin_plate_spline( int n_dims, int n_values, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real pos[], VIO_Real values[], VIO_Real **derivs ) { int v, d, p; VIO_Real dist, dist_deriv; /* f(x,y[,z]) =a_{n} + a_{n+1}x + a_{n+1}y + sum_{0}^{n-1} * w_{i}U(|P_{i} - (x,y)|) */ /* --- initialize derivatives, if desired */ if( derivs != NULL ) { for_less( v, 0, n_values ) for_less( d, 0, n_dims ) derivs[v][d] = 0.0; } /* --- initialize value of thin plate spline to 0 */ for_less( v, 0, n_values ) values[v] = 0.0; /* --- for each point, add its contribution to the values and derivs */ for_less( p, 0, n_points ) { /* --- the thin plate spline weighting function for this point */ dist = thin_plate_spline_U( pos, points[p], n_dims ); /* --- add the weighted component to the values */ for_less( v, 0, n_values ) values[v] = values[v] + (VIO_Real) weights[p][v] * dist; /* --- add the weighted component to the derivatives */ if( derivs != NULL ) { for_less( v, 0, n_values ) { for_less( d, 0, n_dims ) { dist_deriv = thin_plate_spline_U_deriv( pos, points[p], n_dims, d ); derivs[v][d] += (VIO_Real) weights[p][v] * dist_deriv; } } } } /* --- add the constant component to the values */ for_less( v, 0, n_values ) values[v] += (VIO_Real) weights[n_points][v]; /* --- add the linear components to the values and derivatives */ for_less( v, 0, n_values ) { for_less( d, 0, n_dims ) { values[v] += (VIO_Real) weights[n_points+1+d][v] * pos[d]; if( derivs != NULL ) derivs[v][d] += (VIO_Real) weights[n_points+1+d][v]; } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : thin_plate_spline_transform @INPUT : n_dims - number of dimensions (either 2 or 3). n_points - number of points points - array with 'n_points' rows, and 'n_dims' cols, the list of landmarks points in the 'source' volume weights - array with 'n_points+n_dims+1' rows, and 'n_dims' cols, the deformation weights that define the thin plate spline n_points - number of landmark points x - coordinate to transform y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : nothing @DESCRIPTION: Transforms the 1,2, or 3D point given the thin plate spline transform @METHOD : @GLOBALS : none @CALLS : @CREATED : Mon Apr 5 09:00:54 EST 1993 @MODIFIED : Feb. 27, 1995 D. MacDonald - reorganized to call evaluate_thin_plane_spline() ---------------------------------------------------------------------------- */ VIOAPI VIO_Status thin_plate_spline_transform( int n_dims, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { VIO_Real input_point[VIO_N_DIMENSIONS], output_point[VIO_N_DIMENSIONS]; input_point[0] = x; input_point[1] = y; input_point[2] = z; evaluate_thin_plate_spline( n_dims, n_dims, n_points, points, weights, input_point, output_point, NULL ); *x_transformed = output_point[0]; if( n_dims >= 2 ) *y_transformed = output_point[1]; if( n_dims >= 3 ) *z_transformed = output_point[2]; return VIO_OK; } /* ----------------------------- MNI Header ----------------------------------- @NAME : thin_plate_spline_inverse_transform @INPUT : n_dims - number of dimensions (either 2 or 3). n_points - number of points points - array with 'n_points' rows, and 'n_dims' cols, the list of landmarks points in the 'source' volume weights - array with 'n_points+n_dims+1' rows, and 'n_dims' cols, the deformation weights that define the thin plate spline n_points - number of landmark points x - coordinate to inverse transform y z @OUTPUT : x_transformed y_transformed z_transformed @RETURNS : nothing @DESCRIPTION: Inverse transforms the 1,2, or 3D point given the thin plate spline transform. @METHOD : @GLOBALS : none @CALLS : @CREATED : Mon Apr 5 09:00:54 EST 1993 @MODIFIED : Feb. 27, 1995 D. MacDonald - reorganized to call evaluate_thin_plane_spline() ---------------------------------------------------------------------------- */ VIOAPI VIO_Status thin_plate_spline_inverse_transform( int n_dims, int n_points, VIO_Real **points, VIO_Real **weights, VIO_Real x, VIO_Real y, VIO_Real z, VIO_Real *x_transformed, VIO_Real *y_transformed, VIO_Real *z_transformed ) { VIO_Real x_in[VIO_N_DIMENSIONS], solution[VIO_N_DIMENSIONS]; spline_data_struct data; x_in[VIO_X] = x; if( n_dims >= 2 ) x_in[VIO_Y] = y; else x_in[VIO_Y] = 0.0; if( n_dims >= 3 ) x_in[VIO_Z] = z; else x_in[VIO_Z] = 0.0; data.points = points; data.weights = weights; data.n_points = n_points; data.n_dims = n_dims; /* --- solve for the root of the function using Newton steps, which require a function (newton_function) that evaluates the thin plate spline and its derivative at an arbitrary point */ if( newton_root_find( n_dims, newton_function, (void *) &data, x_in, x_in, solution, INVERSE_FUNCTION_TOLERANCE, INVERSE_DELTA_TOLERANCE, MAX_INVERSE_ITERATIONS ) ) { *x_transformed = solution[0]; *y_transformed = solution[1]; *z_transformed = solution[2]; return VIO_OK; } else { *x_transformed = x_in[0]; *y_transformed = x_in[1]; *z_transformed = x_in[2]; return VIO_ERROR; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : newton_function @INPUT : function_data parameters @OUTPUT : values first_derivs @RETURNS : @DESCRIPTION: This function is passed to the newton function root finding routine, and evaluates the values and derivatives of the thin plate spline. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 27, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void newton_function( void *function_data, VIO_Real parameters[], VIO_Real values[], VIO_Real **first_derivs ) { spline_data_struct *spline_data; spline_data = (spline_data_struct *) function_data; evaluate_thin_plate_spline( spline_data->n_dims, spline_data->n_dims, spline_data->n_points, spline_data->points, spline_data->weights, parameters, values, first_derivs ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : thin_plate_spline_U @INPUT : pos - position at which to evaluate landmark - landmark n_dims number of dimensions (1,2, or 3) @OUTPUT : @RETURNS : U interpolation function of distance between the two args @DESCRIPTION: Returns the U interpolation function of the distance between points. In order to correspond to a thin-plate spline, this function has a different form in each of the 3 dimensions. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. , 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real thin_plate_spline_U( VIO_Real pos[], VIO_Real landmark[], int n_dims ) { VIO_Real r, fu, dx, dy, dz; switch( n_dims ) { case 1: dx = pos[VIO_X] - landmark[VIO_X]; r = VIO_FABS( dx ); fu = r * r * r; break; case 2: /* r is actually r^2 */ dx = pos[VIO_X] - landmark[VIO_X]; dy = pos[VIO_Y] - landmark[VIO_Y]; r = dx * dx + dy * dy; if( r == 0.0 ) fu = 0.0; else fu = r * log( r ); break; case 3: dx = pos[VIO_X] - landmark[VIO_X]; dy = pos[VIO_Y] - landmark[VIO_Y]; dz = pos[VIO_Z] - landmark[VIO_Z]; r = sqrt( dx * dx + dy * dy + dz * dz ); fu = r; break; default: handle_internal_error( " impossible error in FU" ); fu = 0.0; break; } return( fu ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : thin_plate_spline_U_deriv @INPUT : pos - position at which to evaluate landmark - landmark n_dims - number of dimensions (1,2, or 3) deriv_dim - dimension to differentiate @OUTPUT : @RETURNS : derivative of U interpolation function @DESCRIPTION: Returns the derivative of the U interpolation function of the distance between points (as specified by thin_plate_spline_U() above). @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. , 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Real thin_plate_spline_U_deriv( VIO_Real pos[], VIO_Real landmark[], int n_dims, int deriv_dim ) { VIO_Real r, r2, deriv, delta[VIO_N_DIMENSIONS]; switch( n_dims ) { case 1: delta[VIO_X] = pos[VIO_X] - landmark[VIO_X]; r = delta[VIO_X]; deriv = 3.0 * r * r; break; case 2: /* r2 is r^2 */ delta[VIO_X] = pos[VIO_X] - landmark[VIO_X]; delta[VIO_Y] = pos[VIO_Y] - landmark[VIO_Y]; r2 = delta[VIO_X] * delta[VIO_X] + delta[VIO_Y] * delta[VIO_Y]; if( r2 == 0.0 ) deriv = 0.0; else deriv = (1.0 + log( r2 )) * 2.0 * delta[deriv_dim]; break; case 3: delta[VIO_X] = pos[VIO_X] - landmark[VIO_X]; delta[VIO_Y] = pos[VIO_Y] - landmark[VIO_Y]; delta[VIO_Z] = pos[VIO_Z] - landmark[VIO_Z]; r = sqrt( delta[VIO_X] * delta[VIO_X] + delta[VIO_Y] * delta[VIO_Y] + delta[VIO_Z] * delta[VIO_Z] ); if( r == 0.0 ) deriv = 0.0; else deriv = delta[deriv_dim] / r; break; default: handle_internal_error( " invalid dimensions error in FU" ); deriv = 0.0; break; } return( deriv ); } libminc-libminc-2-3-00/volume_io/ProgFiles000066400000000000000000000001771257462267400204610ustar00rootroot00000000000000PROGSRCS = \ Testing/example_modify.c \ Testing/example_tags.c \ Testing/example_volume_io.c \ Testing/test-xfm.c libminc-libminc-2-3-00/volume_io/Prog_utils/000077500000000000000000000000001257462267400207665ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Prog_utils/alloc.c000066400000000000000000000544301257462267400222320ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : set_up_array_pointers_2D @INPUT : ptr n1 n2 type_size @OUTPUT : @RETURNS : @DESCRIPTION: Given a pointer allocated for 2D, creates the 1st level pointers. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void set_up_array_pointers_2D( void **ptr, size_t n1, size_t n2, size_t type_size ) { size_t i; for_less( i, 1, n1 ) ptr[i] = (void *) ((long) ptr[i-1] + (long) n2* (long) type_size); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_alloc_memory @INPUT : n_bytes @OUTPUT : ptr @RETURNS : @DESCRIPTION: Allocates the specified number of bytes. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status private_alloc_memory( void **ptr, size_t n_bytes ) { if( n_bytes != 0 ) { *ptr = (void *) malloc( n_bytes ); if( *ptr == NULL ) return( VIO_ERROR ); } else *ptr = NULL; return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_alloc_memory_2d @INPUT : n1 n2 type_size @OUTPUT : ptr @RETURNS : @DESCRIPTION: Allocates the specified number of 2d elements. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status private_alloc_memory_2d( void ***ptr, size_t n1, size_t n2, size_t type_size ) { if( private_alloc_memory( (void **) ptr, n1 * sizeof(**ptr) ) != VIO_OK || private_alloc_memory( *ptr, n1 * n2 * type_size ) != VIO_OK ) { return( VIO_ERROR ); } set_up_array_pointers_2D( *ptr, n1, n2, type_size ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_alloc_memory_3d @INPUT : n1 n2 n3 type_size @OUTPUT : ptr @RETURNS : @DESCRIPTION: Allocates the specified number of 3d elements. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status private_alloc_memory_3d( void ****ptr, size_t n1, size_t n2, size_t n3, size_t type_size ) { if( private_alloc_memory_2d( (void ***) ptr, n1, n2, sizeof(***ptr) )!= VIO_OK|| private_alloc_memory( **ptr, n1 * n2 * n3 * type_size ) != VIO_OK) { return( VIO_ERROR ); } set_up_array_pointers_2D( **ptr, n1 * n2, n3, type_size ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_alloc_memory_4d @INPUT : n1 n2 n3 n4 type_size @OUTPUT : ptr @RETURNS : @DESCRIPTION: Allocates the specified number of 4d elements. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status private_alloc_memory_4d( void *****ptr, size_t n1, size_t n2, size_t n3, size_t n4, size_t type_size ) { if( private_alloc_memory_3d( (void ****) ptr, n1, n2, n3, sizeof(****ptr) )!= VIO_OK || private_alloc_memory( ***ptr, n1 * n2 * n3 * n4 * type_size ) != VIO_OK ) { return( VIO_ERROR ); } set_up_array_pointers_2D( ***ptr, n1 * n2 * n3, n4, type_size ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_alloc_memory_5d @INPUT : n1 n2 n3 n4 n5 type_size @OUTPUT : ptr @RETURNS : @DESCRIPTION: Allocates the specified number of 5d elements. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status private_alloc_memory_5d( void ******ptr, size_t n1, size_t n2, size_t n3, size_t n4, size_t n5, size_t type_size ) { if( private_alloc_memory_4d( (void *****) ptr, n1, n2, n3, n4, sizeof(*****ptr) )!= VIO_OK || private_alloc_memory( ****ptr, n1 * n2 * n3 * n4 * n5 * type_size )!=VIO_OK) { return( VIO_ERROR ); } set_up_array_pointers_2D( ****ptr, n1 * n2 * n3 * n4, n5, type_size ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_in_bytes @INPUT : n_bytes filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates the specified amount of memory, and if successful, calls the routine to record the memory allocated, and returns the pointer. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_in_bytes( size_t n_bytes _ALLOC_SOURCE_LINE_ARG_DEF ) { void *ptr; if( private_alloc_memory( &ptr, n_bytes ) != VIO_OK ) { print_error( "Cannot alloc 1D array of %lu bytes.\n", n_bytes ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else record_ptr_alloc_check( ptr, n_bytes, filename, line_number ); #endif return( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_1d @INPUT : n_elements type_size filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates a 1D array and returns it. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : Apr. 16, 1996 D. MacDonald : returns the pointer ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_1d( size_t n_elements, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { void *ptr; if( private_alloc_memory( &ptr, n_elements * type_size ) != VIO_OK ) { print_error( "Cannot alloc 1D array of %lu elements of %lu bytes.\n", n_elements, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else record_ptr_alloc_check( ptr, n_elements * type_size, filename, line_number ); #endif return( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_2d @INPUT : n1 n2 type_size filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates a 2D array and returns a pointer to it. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_2d( size_t n1, size_t n2, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { void **ptr; if( private_alloc_memory_2d( &ptr, n1, n2, type_size ) != VIO_OK ) { print_error( "Cannot alloc 2D array of %lu by %lu elements of %lu bytes.\n", n1, n2, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else { record_ptr_alloc_check( ptr, n1 * sizeof(*ptr), filename, line_number ); record_ptr_alloc_check( *ptr, n1 * n2 * type_size, filename, line_number ); } #endif return( (void *) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_3d @INPUT : n1 n2 n3 type_size filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates a 3D array. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_3d( size_t n1, size_t n2, size_t n3, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { void ***ptr; if( private_alloc_memory_3d( &ptr, n1, n2, n3, type_size ) != VIO_OK ) { print_error( "Cannot alloc 3D array of %lu by %lu by %lu elements of %lu bytes.\n", n1, n2, n3, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else { record_ptr_alloc_check( ptr, n1 * sizeof(*ptr), filename, line_number ); record_ptr_alloc_check( *ptr, n1 * n2 * sizeof(**ptr), filename, line_number ); record_ptr_alloc_check( **ptr, n1 * n2 * n3 * type_size, filename, line_number ); } #endif return( (void *) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_4d @INPUT : n1 n2 n3 n4 type_size filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates a 4D array. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_4d( size_t n1, size_t n2, size_t n3, size_t n4, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { void ****ptr; if( private_alloc_memory_4d( &ptr, n1, n2, n3, n4, type_size ) != VIO_OK ) { print_error( "Cannot alloc 4D array of %lu by %lu by %lu by %lu elements of %lu bytes.\n", n1, n2, n3, n4, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else { record_ptr_alloc_check( ptr, n1 * sizeof(*ptr), filename, line_number ); record_ptr_alloc_check( *ptr, n1 * n2 * sizeof(**ptr), filename, line_number ); record_ptr_alloc_check( **ptr, n1 * n2 * n3 * sizeof(***ptr), filename, line_number ); record_ptr_alloc_check( ***ptr, n1 * n2 * n3 * n4 * type_size, filename, line_number ); } #endif return( (void *) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_memory_5d @INPUT : n1 n2 n3 n4 n5 type_size filename line_number @OUTPUT : @RETURNS : void * @DESCRIPTION: Allocates a 5D array. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void *alloc_memory_5d( size_t n1, size_t n2, size_t n3, size_t n4, size_t n5, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { void *****ptr; if( private_alloc_memory_5d( &ptr, n1, n2, n3, n4, n5, type_size ) != VIO_OK ) { print_error( "Cannot alloc 4D array of %lu by %lu by %lu by %lu by %lu elements of %lu bytes.\n", n1, n2, n3, n4, n5, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC else { record_ptr_alloc_check( ptr, n1 * sizeof(*ptr), filename, line_number ); record_ptr_alloc_check( *ptr, n1 * n2 * sizeof(**ptr), filename, line_number ); record_ptr_alloc_check( **ptr, n1 * n2 * n3 * sizeof(***ptr), filename, line_number ); record_ptr_alloc_check( ***ptr, n1 * n2 * n3 * n4 * sizeof(****ptr), filename, line_number ); record_ptr_alloc_check( ****ptr, n1 * n2 * n3 * n4 * n5 * type_size, filename, line_number ); } #endif return( (void *) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : realloc_memory @INPUT : ptr n_elements type_size filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: VIO_Reallocates the ptr. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void realloc_memory( void **ptr, size_t n_elements, size_t type_size _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC void *old_ptr = *ptr; #endif if( n_elements != 0 ) { *ptr = (void *) realloc( *ptr, n_elements * type_size ); if( *ptr == NULL ) { print_error( "Error reallocing %lu elements of size %lu.\n", n_elements, type_size ); PRINT_ALLOC_SOURCE_LINE abort_if_allowed(); } #ifndef NO_DEBUG_ALLOC change_ptr_alloc_check( old_ptr, *ptr, n_elements * type_size, filename, line_number ); #endif } else { print_error("Error: tried to realloc invalid number of elements, %lu.\n", n_elements ); PRINT_ALLOC_SOURCE_LINE } } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_free_memory_1d @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees the array and assigns the pointer to NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void private_free_memory_1d( void **ptr) { if( *ptr != NULL ) { free( *ptr ); *ptr = NULL; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_free_memory_2d @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees the array and assigns the pointer to NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void private_free_memory_2d( void ***ptr) { private_free_memory_1d( *ptr ); private_free_memory_1d( (void **) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_free_memory_3d @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees the array and assigns the pointer to NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void private_free_memory_3d( void ****ptr) { private_free_memory_1d( **ptr ); private_free_memory_2d( (void ***) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_free_memory_4d @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees the array and assigns the pointer to NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void private_free_memory_4d( void *****ptr) { private_free_memory_1d( ***ptr ); private_free_memory_3d( (void ****) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : private_free_memory_5d @INPUT : ptr @OUTPUT : @RETURNS : @DESCRIPTION: Frees the array and assigns the pointer to NULL. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void private_free_memory_5d( void ******ptr) { private_free_memory_1d( ****ptr ); private_free_memory_4d( (void *****) ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_memory_1d @INPUT : ptr filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Frees the pointer, and sets it to NIL. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_memory_1d( void **ptr _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC if( unrecord_ptr_alloc_check( *ptr, filename, line_number ) ) #endif private_free_memory_1d( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_memory_2d @INPUT : ptr filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Frees the pointer, and sets it to NIL. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_memory_2d( void ***ptr _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC if( unrecord_ptr_alloc_check( **ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) *ptr, filename, line_number ) ) #endif private_free_memory_2d( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_memory_3d @INPUT : ptr filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Frees the pointer, and sets it to NIL. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_memory_3d( void ****ptr _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC if( unrecord_ptr_alloc_check( ***ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) **ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) *ptr, filename, line_number ) ) #endif private_free_memory_3d( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_memory_4d @INPUT : ptr filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Frees the pointer, and sets it to NIL. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_memory_4d( void *****ptr _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC if( unrecord_ptr_alloc_check( ****ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) ***ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) **ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) *ptr, filename, line_number ) ) #endif private_free_memory_4d( ptr ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_memory_5d @INPUT : ptr filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Frees the pointer, and sets it to NIL. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_memory_5d( void ******ptr _ALLOC_SOURCE_LINE_ARG_DEF ) { #ifndef NO_DEBUG_ALLOC if( unrecord_ptr_alloc_check( *****ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) ****ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) ***ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) **ptr, filename, line_number ) && unrecord_ptr_alloc_check( (void *) *ptr, filename, line_number ) ) #endif private_free_memory_5d( ptr ); } libminc-libminc-2-3-00/volume_io/Prog_utils/alloc_check.c000066400000000000000000000667401257462267400233760ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_check.c @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Maintains a skiplist structure to list all memory allocated, : and check for errors such as freeing a pointer twice or : overlapping allocations. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define MAX_SKIP_LEVELS 50 #define SKIP_P 0.5 #define MEMORY_DIFFERENCE 1000000 typedef struct skip_entry { void *ptr; size_t n_bytes; VIO_STR source_file; int line_number; int sequence_number; struct skip_entry *forward[1]; } skip_entry; typedef struct { size_t next_memory_threshold; size_t total_memory_allocated; skip_entry *header; int level; } alloc_struct; typedef struct { skip_entry *update[MAX_SKIP_LEVELS]; } update_struct; static void update_total_memory( alloc_struct *, size_t ); static int get_random_level( void ); static void output_entry( FILE *, skip_entry * ); static VIO_BOOL size_display_enabled( void ); static size_t skip_alloc_size = 0; typedef void *alloc_ptr; #define ALLOC_SKIP_STRUCT( ptr, n_level ) \ (ptr) = (skip_entry *) malloc( \ (sizeof(skip_entry)+((size_t)(n_level)-1) * sizeof(skip_entry *)) ); /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_alloc_list @INPUT : alloc_list @OUTPUT : @RETURNS : @DESCRIPTION: Initializes the allocation list to empty. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void initialize_alloc_list( alloc_struct *alloc_list ) { int i; alloc_list->next_memory_threshold = MEMORY_DIFFERENCE; alloc_list->total_memory_allocated = 0; ALLOC_SKIP_STRUCT( alloc_list->header, MAX_SKIP_LEVELS ); skip_alloc_size += sizeof(skip_entry)+(MAX_SKIP_LEVELS-1) * sizeof(skip_entry *); alloc_list->level = 1; for_less( i, 0, MAX_SKIP_LEVELS ) alloc_list->header->forward[i] = (skip_entry *) 0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_initialized_alloc_list @INPUT : alloc_list @OUTPUT : @RETURNS : @DESCRIPTION: Checks to make sure the allocation list is initialized. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void check_initialized_alloc_list( alloc_struct *alloc_list ) { static VIO_BOOL first = TRUE; if( first ) { first = FALSE; initialize_alloc_list( alloc_list ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : find_pointer_position @INPUT : alloc_list : ptr @OUTPUT : update @RETURNS : TRUE if found @DESCRIPTION: Searches the alloc_list for the given ptr, and sets the update : struct so that it can provide an insert. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL find_pointer_position( alloc_struct *alloc_list, void *ptr, update_struct *update ) { int i; skip_entry *x; VIO_BOOL found; x = alloc_list->header; i = alloc_list->level-1; if( i < 0 ) return FALSE; for( ; i >= 0; --i ) { while( x->forward[i] != NULL && (void *) x->forward[i]->ptr < ptr ) { x = x->forward[i]; } update->update[i] = x; } x = update->update[0]->forward[0]; found = (x != NULL) && (x->ptr == ptr); return( found ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : insert_ptr_in_alloc_list @INPUT : alloc_list : update - the set of pointers indicating where to insert : ptr } : n_bytes }} : source_file }}} these are recorded in the list : line_number }} : sequence_number } @OUTPUT : @RETURNS : @DESCRIPTION: Records the allocated pointer in the allocation list. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void insert_ptr_in_alloc_list( alloc_struct *alloc_list, update_struct *update, void *ptr, size_t n_bytes, VIO_STR source_file, int line_number, int sequence_number ) { int i, new_level; skip_entry *x; new_level = get_random_level(); if( new_level > alloc_list->level ) { for( i = alloc_list->level; i < new_level; ++i ) update->update[i] = alloc_list->header; alloc_list->level = new_level; } ALLOC_SKIP_STRUCT( x, new_level ); skip_alloc_size += sizeof(skip_entry)+((size_t)new_level-1) * sizeof(skip_entry *); x->ptr = ptr; x->n_bytes = n_bytes; x->source_file = source_file; x->line_number = line_number; x->sequence_number = sequence_number; update_total_memory( alloc_list, n_bytes ); for( i = 0; i < new_level; ++i ) { x->forward[i] = update->update[i]->forward[i]; update->update[i]->forward[i] = x; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_overlap @INPUT : update : ptr : n_bytes @OUTPUT : entry @RETURNS : TRUE if an overlap @DESCRIPTION: Checks the new ptr to see if it overlaps with the previous and : following memory allocations in the list, and returns the result. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL check_overlap( alloc_struct *alloc_list, update_struct *update, void *ptr, size_t n_bytes, skip_entry **entry ) { VIO_BOOL overlap; overlap = FALSE; *entry = update->update[0]; if( *entry != alloc_list->header && *entry != (skip_entry *) 0 ) { if( (void *) ((char *) (*entry)->ptr + (*entry)->n_bytes) > ptr ) overlap = TRUE; else { (*entry) = (*entry)->forward[0]; if( *entry != (skip_entry *) 0 && (void *) ((char*)ptr + n_bytes) > (*entry)->ptr ) overlap = TRUE; } } return( overlap ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : remove_ptr_from_alloc_list @INPUT : alloc_list : ptr @OUTPUT : source_file : line_number : sequence_number @RETURNS : TRUE if it existed @DESCRIPTION: Finds and deletes the entry in the skip list associated with : ptr, and returns the information associated with the entry : (source_file, line_number, sequence_number). @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL remove_ptr_from_alloc_list( alloc_struct *alloc_list, void *ptr, VIO_STR *source_file, int *line_number, int *sequence_number ) { int i; VIO_BOOL found; skip_entry *x; update_struct update; found = find_pointer_position( alloc_list, ptr, &update ); if( found ) { x = update.update[0]->forward[0]; *source_file = x->source_file; *line_number = x->line_number; *sequence_number = x->sequence_number; update_total_memory( alloc_list, -x->n_bytes ); for( i = 0; i < alloc_list->level; ++i ) { if( update.update[i]->forward[i] != x ) break; update.update[i]->forward[i] = x->forward[i]; } skip_alloc_size -= sizeof(skip_entry) + (size_t) (i-1) * sizeof(skip_entry *); free( (alloc_ptr) x ); while( alloc_list->level > 1 && alloc_list->header->forward[alloc_list->level-1] == (skip_entry *) 0 ) { --alloc_list->level; } } return( found ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_random_0_to_1 @INPUT : @OUTPUT : @RETURNS : random number @DESCRIPTION: Returns a random number >= 0 and < 1. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Real get_random_0_to_1( void ) { return( (VIO_Real) rand() ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_random_level @INPUT : @OUTPUT : @RETURNS : a random level between 1 and MAX_LEVELS @DESCRIPTION: Determines a random level with exponential probability of higher : levels. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static int get_random_level( void ) { int level; level = 1; while( get_random_0_to_1() < SKIP_P && level < MAX_SKIP_LEVELS ) ++level; return( level ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : memory_still_alloced @INPUT : alloc_list @OUTPUT : @RETURNS : TRUE or FALSE @DESCRIPTION: Decides if any memory is still alloced, thus checking for memory leaks. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL memory_still_alloced( alloc_struct *alloc_list ) { return( alloc_list->header->forward[0] != (skip_entry *) NULL ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_alloc_list @INPUT : file : alloc_list @OUTPUT : @RETURNS : @DESCRIPTION: Outputs the list of allocated memory to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void output_alloc_list( FILE *file, alloc_struct *alloc_list ) { skip_entry *ptr; ptr = alloc_list->header->forward[0]; while( ptr != (skip_entry *) 0 ) { output_entry( file, ptr ); ptr = ptr->forward[0]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_total_memory @INPUT : alloc_list : n_bytes @OUTPUT : @RETURNS : @DESCRIPTION: Adds n_bytes to the size of memory recorded. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void update_total_memory( alloc_struct *alloc_list, size_t n_bytes ) { alloc_list->total_memory_allocated += n_bytes; if( size_display_enabled() && alloc_list->total_memory_allocated > alloc_list->next_memory_threshold ) { alloc_list->next_memory_threshold = MEMORY_DIFFERENCE * (alloc_list->total_memory_allocated / MEMORY_DIFFERENCE + 1); print( "Memory allocated =%5.1f Megabytes (Overhead = %5.1f Mb)\n", (VIO_Real) alloc_list->total_memory_allocated / 1000000.0, (VIO_Real) skip_alloc_size / 1000000.0 ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : print_source_location @INPUT : source_file : line_number : sequence_number @OUTPUT : @RETURNS : @DESCRIPTION: Prints the information about a particular allocation. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void print_source_location( VIO_STR source_file, int line_number, int sequence_number ) { print_error( "%s:%d\t%d'th alloc", source_file, line_number, sequence_number ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_entry @INPUT : file : entry @OUTPUT : @RETURNS : @DESCRIPTION: Outputs the information about an allocation entry to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void output_entry( FILE *file, skip_entry *entry ) { (void) fprintf( file, "%s:%d\t%d'th alloc\n", entry->source_file, entry->line_number, entry->sequence_number ); } /* -------------------------------------------------------------------------- Routines that are to be called from outside this file -------------------------------------------------------------------------- */ static alloc_struct alloc_list; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_total_memory_alloced @INPUT : @OUTPUT : @RETURNS : size_t - the number of bytes allocated @DESCRIPTION: Returns the total amount of memory allocated by the program, : not counting that used by the skip list. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI size_t get_total_memory_alloced( void ) { return( alloc_list.total_memory_allocated ); } static VIO_BOOL checking_enabled; static VIO_BOOL enabled_initialized = FALSE; /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_checking_enabled @INPUT : @OUTPUT : @RETURNS : TRUE if alloc checking is turned on @DESCRIPTION: Checks an environment variable to see if alloc checking is : not disabled. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL alloc_checking_enabled( void ) { #ifdef NO_DEBUG_ALLOC return( FALSE ); #else if( !enabled_initialized ) { set_alloc_checking( VIO_ENV_EXISTS( "DEBUG_ALLOC" ) ); } return( checking_enabled ); #endif } VIOAPI void set_alloc_checking( VIO_BOOL state ) { enabled_initialized = TRUE; checking_enabled = state; } /* ----------------------------- MNI Header ----------------------------------- @NAME : size_display_enabled @INPUT : @OUTPUT : @RETURNS : TRUE if size displaying is turned on @DESCRIPTION: Checks an environment variable to see if memory size display : is disabled. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL size_display_enabled( void ) { #ifdef NO_DEBUG_ALLOC return( FALSE ); #else static VIO_BOOL first = TRUE; static VIO_BOOL enabled; if( first ) { enabled = VIO_ENV_EXISTS( "ALLOC_SIZE" ); first = FALSE; } return( enabled ); #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_stop_sequence_number @INPUT : @OUTPUT : @RETURNS : which allocation number @DESCRIPTION: Returns the number at which allocation should stop. This is used for debugging. For instance, if an error message indicates a problem with the 100'th alloc of the program, then do a SETENV STOP_ALLOC_AT 100 and run the program from the debugger. It will stop at the requested allocation. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static int get_stop_sequence_number( void ) { static int first = TRUE; static int stop_sequence_number = -1; VIO_STR str; if( first ) { first = FALSE; str = getenv( "STOP_ALLOC_AT" ); if( str == NULL || sscanf( str, "%d", &stop_sequence_number ) != 1 ) stop_sequence_number = -1; } return( stop_sequence_number ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_current_sequence_number @INPUT : @OUTPUT : @RETURNS : the index of this alloc @DESCRIPTION: Returns the count of how many allocations have been done, so that each allocation can be assigned a value equal to its cardinality in the set of allocations over the life of the program. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static int get_current_sequence_number( void ) { static int current_sequence_number = 0; ++current_sequence_number; if( current_sequence_number == get_stop_sequence_number() ) handle_internal_error( "get_current_sequence_number" ); return( current_sequence_number ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : record_ptr @INPUT : ptr : n_bytes : source_file : line_number @OUTPUT : @RETURNS : @DESCRIPTION: Records the information about a single allocation in the list. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void record_ptr_alloc_check( void *ptr, size_t n_bytes, VIO_STR source_file, int line_number ) { update_struct update_ptrs; skip_entry *entry; if( alloc_checking_enabled() ) { check_initialized_alloc_list( &alloc_list ); if( n_bytes == 0 ) { print_source_location( source_file, line_number, -1 ); print_error( ": Alloc called with zero size.\n" ); abort_if_allowed(); } else if( ptr == (void *) 0 ) { print_source_location( source_file, line_number, -1 ); print_error( ": Alloc returned a NIL pointer.\n" ); abort_if_allowed(); } else { (void) find_pointer_position( &alloc_list, ptr, &update_ptrs ); if( check_overlap( &alloc_list, &update_ptrs, ptr, n_bytes, &entry)) { print_source_location( source_file, line_number, -1 ); print_error( ": Alloc returned a pointer overlapping an existing block:\n" ); print_source_location( entry->source_file, entry->line_number, entry->sequence_number ); print_error( "\n" ); abort_if_allowed(); } else insert_ptr_in_alloc_list( &alloc_list, &update_ptrs, ptr, n_bytes, source_file, line_number, get_current_sequence_number() ); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : change_ptr @INPUT : old_ptr : new_ptr : n_bytes : source_file : line_number @OUTPUT : @RETURNS : @DESCRIPTION: Changes the information (mainly the n_bytes) associated with a : given pointer. This function is called from the def_alloc : macros after a realloc(). @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void change_ptr_alloc_check( void *old_ptr, void *new_ptr, size_t n_bytes, VIO_STR source_file, int line_number ) { VIO_STR orig_source; int orig_line; int sequence_number; skip_entry *entry; update_struct update_ptrs; if( alloc_checking_enabled() ) { check_initialized_alloc_list( &alloc_list ); if( n_bytes == 0 ) { print_source_location( source_file, line_number, -1 ); print_error( ": VIO_Realloc called with zero size.\n" ); abort_if_allowed(); } else if( !remove_ptr_from_alloc_list( &alloc_list, old_ptr, &orig_source, &orig_line, &sequence_number ) ) { print_source_location( source_file, line_number, -1 ); print_error( ": Tried to realloc a pointer not already alloced.\n"); abort_if_allowed(); } else { (void) find_pointer_position( &alloc_list, new_ptr, &update_ptrs ); if( check_overlap( &alloc_list, &update_ptrs, new_ptr, n_bytes, &entry ) ) { print_source_location( source_file, line_number, -1 ); print_error( ": VIO_Realloc returned a pointer overlapping an existing block:\n"); print_source_location( entry->source_file, entry->line_number, entry->sequence_number ); print_error( "\n" ); abort_if_allowed(); } else insert_ptr_in_alloc_list( &alloc_list, &update_ptrs, new_ptr, n_bytes, orig_source, orig_line, sequence_number ); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : unrecord_ptr @INPUT : ptr : source_file : line_number @OUTPUT : @RETURNS : TRUE if ptr was in list @DESCRIPTION: Removes the entry for the given ptr from the list. Called by : the macros during a FREE. Returns TRUE if the pointer was : in the list. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL unrecord_ptr_alloc_check( void *ptr, VIO_STR source_file, int line_number ) { VIO_BOOL was_previously_alloced; VIO_STR orig_source; int orig_line; int sequence_number; was_previously_alloced = TRUE; if( alloc_checking_enabled() ) { check_initialized_alloc_list( &alloc_list ); if( ptr == (void *) 0 ) { print_source_location( source_file, line_number, -1 ); print_error( ": Tried to free a NIL pointer.\n" ); abort_if_allowed(); was_previously_alloced = FALSE; } else if( !remove_ptr_from_alloc_list( &alloc_list, ptr, &orig_source, &orig_line, &sequence_number ) ) { print_source_location( source_file, line_number, -1 ); print_error( ": Tried to free a pointer not alloced.\n" ); abort_if_allowed(); was_previously_alloced = FALSE; } } return( was_previously_alloced ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_alloc_to_file @INPUT : filename @OUTPUT : @RETURNS : @DESCRIPTION: Outputs a list of all memory allocated to the given file. Usually : done at the end of the program to see if there is any memory that : was orphaned. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void output_alloc_to_file( VIO_STR filename ) { FILE *file; VIO_STR date_str; if( alloc_checking_enabled() ) { check_initialized_alloc_list( &alloc_list ); if( memory_still_alloced( &alloc_list ) ) { print_error( "\n" ); print_error( "\n" ); print_error( "A memory leak was found in this program.\n" ); if( filename != NULL ) print_error( "A description has been recorded in the file %s.\n", filename ); print_error( "Please report this file to the author of the program.\n" ); print_error( "\n" ); if( filename != NULL && filename[0] != (char) 0 ) file = fopen( filename, "w" ); else file = stdout; if( file != NULL ) { date_str = get_date(); (void) fprintf( file, "Alloc table at %s\n", date_str ); delete_string( date_str ); output_alloc_list( file, &alloc_list ); if( file != stdout ) (void) fclose( file ); } } } } #ifndef NO_DEBUG_ALLOC VIOAPI void print_alloc_source_line( VIO_STR filename, int line_number ) { print_error( " Source position: %s:%d\n", filename, line_number ); } #endif libminc-libminc-2-3-00/volume_io/Prog_utils/arrays.c000066400000000000000000000047551257462267400224460ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : set_array_size @INPUT : array type_size previous_n_elems new_n_elems - desired new array size chunk_size filename line_number @OUTPUT : @RETURNS : @DESCRIPTION: Sets the number of items allocated in the array to a multiple of : chunk_size larger than new_n_elems @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_array_size( void **array, size_t type_size, size_t previous_n_elems, size_t new_n_elems, size_t chunk_size _ALLOC_SOURCE_LINE_ARG_DEF ) { size_t new_chunk, previous_chunk; if( new_n_elems != 0 ) { new_chunk = ((new_n_elems+chunk_size-1) / chunk_size) * chunk_size; if( previous_n_elems == 0 ) { *array = alloc_memory_1d( new_chunk, type_size _ALLOC_SOURCE_LINE_ARGUMENTS ); } else { previous_chunk = ((previous_n_elems+chunk_size-1) / chunk_size) * chunk_size; if( new_chunk != previous_chunk ) realloc_memory( array, new_chunk, type_size _ALLOC_SOURCE_LINE_ARGUMENTS ); } } else if( previous_n_elems != 0 ) free_memory_1d( array _ALLOC_SOURCE_LINE_ARGUMENTS ); } libminc-libminc-2-3-00/volume_io/Prog_utils/files.c000066400000000000000000002256201257462267400222430ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #if HAVE_SYS_TYPES_H #include #endif /* HAVE_SYS_TYPES_H */ #if HAVE_SYS_STAT_H #include #endif /* HAVE_SYS_STAT_H */ #if HAVE_PWD_H #include #endif /* HAVE_PWD_H */ #include #if HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #if HAVE_FCNTL_H #include #endif /* HAVE_FCNTL_H */ #include /* hack for compiling on Windows*/ #if !defined(S_IREAD) && defined(_S_IREAD) #define S_IREAD _S_IREAD #endif #if !defined(S_IWRITE) && defined(_S_IWRITE) #define S_IWRITE _S_IWRITE #endif #if !defined(O_CREAT) && defined(_O_CREAT) #define O_CREAT _O_CREAT #endif #if !defined(O_EXCL) && defined(_O_EXCL) #define O_EXCL _O_EXCL #endif #if !defined(O_RDWR) && defined(_O_RDWR) #define O_RDWR _O_RDWR #endif static VIO_BOOL has_no_extension( VIO_STR ); static VIO_STR compressed_endings[] = { ".z", ".Z", ".gz" }; #if !HAVE_STRERROR static char *strerror(int errnum) { extern int sys_nerr; extern char *sys_errlist[]; if( errnum < 0 || errnum >= sys_nerr ) { return( "" ); } return( sys_errlist[errnum] ); } #endif /* !HAVE_STRERROR */ /* ----------------------------- MNI Header ----------------------------------- @NAME : print_system_error @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Prints the most recent system error. @METHOD : @GLOBALS : @CALLS : @CREATED : , 1996 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void print_system_error( void ) { char *error; error = strerror( errno ); print_error( "\nSystem message: %s\n", error ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : real_is_double @INPUT : @OUTPUT : @RETURNS : TRUE if real is defined to be type double @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL real_is_double( void ) { static const size_t constant_8 = sizeof(double); return( sizeof(VIO_Real) == constant_8 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : file_exists @INPUT : filename @OUTPUT : @RETURNS : TRUE or FALSE if file exists @DESCRIPTION: Checks if the file of the given name exists @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL file_exists( VIO_STR filename ) { VIO_BOOL exists; FILE *file; VIO_STR expanded; expanded = expand_filename( filename ); file = fopen( expanded, "r" ); if( file != NULL ) { (void) fclose( file ); exists = TRUE; } else exists = FALSE; delete_string( expanded ); return( exists ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : file_directory_exists @INPUT : filename @OUTPUT : @RETURNS : TRUE if directory containing file exists. @DESCRIPTION: Checks if the directory contained in the path name exists. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 2, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL file_directory_exists( VIO_STR filename ) { VIO_BOOL exists; VIO_STR dir; dir = extract_directory( filename ); if( string_length( dir ) != 0 ) exists = file_exists( dir ); else exists = TRUE; delete_string( dir ); return( exists ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_clobber_file @INPUT : filename @OUTPUT : @RETURNS : TRUE if can write file @DESCRIPTION: Checks if the file exists. If so, asks the user for permission to overwrite the file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL check_clobber_file( VIO_STR filename ) { char ch; VIO_BOOL okay; VIO_STR expanded; okay = TRUE; if( file_exists( filename ) ) { expanded = expand_filename( filename ); print( "File <%s> exists, do you wish to overwrite (y or n): ", expanded ); delete_string( expanded ); while( input_character( stdin, &ch ) == VIO_OK && ch != 'y' && ch != 'n' && ch != 'N' && ch != 'Y' ) { if( ch == '\n' ) print( " Please type y or n: " ); } (void) input_newline( stdin ); okay = (ch == 'y' || ch == 'Y'); } return( okay ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_clobber_file_default_suffix @INPUT : filename default_suffix @OUTPUT : @RETURNS : TRUE if can write file @DESCRIPTION: Checks if the file exists (adding the default suffix if necessary). If the file exists, asks the user for permission to overwrite the file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL check_clobber_file_default_suffix( VIO_STR filename, VIO_STR default_suffix ) { VIO_STR expanded; VIO_BOOL can_write; expanded = expand_filename( filename ); if( has_no_extension( expanded ) ) { concat_to_string( &expanded, "." ); concat_to_string( &expanded, default_suffix ); } can_write = check_clobber_file( expanded ); delete_string( expanded ); return( can_write ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_backup_filename @INPUT : filename @OUTPUT : @RETURNS : VIO_STR - a backup filename @DESCRIPTION: Creates a backup filename that is filename.{date}.bkp If this already exists (not very likely), then it tries appending _1, _2, ... @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 3, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_STR create_backup_filename( VIO_STR filename ) { int i, len, count; VIO_STR expanded, backup_filename, date; expanded = expand_filename( filename ); date = get_date(); len = string_length( expanded ) + string_length( date ) + 100; ALLOC( backup_filename, len ); count = 0; do { if( count == 0 ) { (void) sprintf( backup_filename, "%s.%s.bkp", expanded, date ); } else { (void) sprintf( backup_filename, "%s.%s.bkp_%d", expanded, date, count ); } len = string_length( backup_filename ); while( len > 0 && (backup_filename[len-1] == ' ' || backup_filename[len-1] == '\t' || backup_filename[len-1] == '\n') ) { --len; } backup_filename[len] = (char) 0; for_less( i, 0, len ) { if( backup_filename[i] == ' ' || backup_filename[i] == '\t' || backup_filename[i] == '\n' ) backup_filename[i] = '_'; /* remove ':' for windows */ if( backup_filename[i] == ':'){ backup_filename[i] = '-'; } } ++count; } while( file_exists( backup_filename ) ); delete_string( expanded ); delete_string( date ); return( backup_filename ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_backup_file @INPUT : filename @OUTPUT : backup_filename @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: If the file exists, creates a backup of the file, and passes back the name of the backup file, which must be passed to cleanup_backup_file after the write of filename is performed. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 3, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status make_backup_file( VIO_STR filename, VIO_STR *backup_filename ) { VIO_Status status; status = VIO_OK; if( file_exists( filename ) ) { *backup_filename = create_backup_filename( filename ); status = copy_file( filename, *backup_filename ); if( status != VIO_OK ) { print_error( "Error making backup file for: %s\n", filename ); *backup_filename = NULL; } } else *backup_filename = NULL; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cleanup_backup_file @INPUT : filename backup_filename status_of_write @OUTPUT : @RETURNS : @DESCRIPTION: This function is called after writing a file. If a backup file was made before the write, then it is deleted, if the write was successful, or copied to the original file, otherwise. @METHOD : @GLOBALS : @CALLS : @CREATED : Feb. 3, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void cleanup_backup_file( VIO_STR filename, VIO_STR backup_filename, VIO_Status status_of_write ) { VIO_BOOL can_remove; if( backup_filename != NULL ) { can_remove = TRUE; if( status_of_write != VIO_OK ) { if( copy_file( backup_filename, filename ) != VIO_OK ) { print_error( "File %s was corrupted during a failed write,\n", filename ); print_error( "File %s contains the state prior to the write attempt.\n", backup_filename ); can_remove = FALSE; } } if( can_remove ) remove_file( backup_filename ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : remove_file @INPUT : filename @OUTPUT : @RETURNS : @DESCRIPTION: Deletes the given file. @METHOD : Makes a system call to unlink(). @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void remove_file( VIO_STR filename ) { VIO_STR expanded; expanded = expand_filename( filename ); if( unlink( expanded ) != 0 ) { print_error( "Error removing %s. ", expanded ); print_system_error(); } delete_string( expanded ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_file @INPUT : src dest @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Copies the src file to the dest file. @METHOD : Makes a UNIX system call, using /bin/cp @GLOBALS : @CALLS : @CREATED : Feb. 3, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_file( VIO_STR src, VIO_STR dest ) { VIO_Status status; VIO_STR src_expanded, dest_expanded, command; src_expanded = expand_filename( src ); dest_expanded = expand_filename( dest ); command = concat_strings( "/bin/cp ", src_expanded ); concat_to_string( &command, " " ); concat_to_string( &command, dest_expanded ); if( system( command ) != 0 ) { print_error( "Error copying file %s to %s: ", src_expanded, dest_expanded ); print_system_error(); status = VIO_ERROR; } else status = VIO_OK; delete_string( src_expanded ); delete_string( dest_expanded ); delete_string( command ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : move_file @INPUT : src dest @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Move the src file to the dest file. @METHOD : Makes a UNIX system call, using /bin/mv @GLOBALS : @CALLS : @CREATED : Feb. 3, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status move_file( VIO_STR src, VIO_STR dest ) { VIO_Status status; VIO_STR src_expanded, dest_expanded, command; src_expanded = expand_filename( src ); dest_expanded = expand_filename( dest ); command = concat_strings( "/bin/cp -f ", src_expanded ); concat_to_string( &command, " " ); concat_to_string( &command, dest_expanded ); if( system( command ) != 0 ) { print_error( "Error moving file %s to %s: ", src_expanded, dest_expanded ); print_system_error(); status = VIO_ERROR; } else status = VIO_OK; delete_string( src_expanded ); delete_string( dest_expanded ); delete_string( command ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_user_home_directory @INPUT : user_name @OUTPUT : @RETURNS : Pointer to home directory string. @DESCRIPTION: Returns the home directory of the specified user. @METHOD : UNIX password file utilities @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_STR get_user_home_directory( VIO_STR user_name ) { #if HAVE_GETPWNAM struct passwd *p; p = getpwnam( user_name ); if( p == NULL ) return( NULL ); else return( p->pw_dir ); #else return ("."); #endif /* HAVE_GETPWNAM */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : expand_filename @INPUT : filename @OUTPUT : expanded_filename @RETURNS : @DESCRIPTION: Expands certain strings in the filename, if present: environment variables, e.g. "$DATA_DIR/filename.txt" ~ e.g. "~david/filename.txt" If a dollar sign or backslash is desired, it must be preceded by a backslash. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR expand_filename( const char *filename ) { int i, new_i, dest, len, env_index; VIO_BOOL tilde_found, prev_was_backslash; char *expand_value; int n_alloced, n_env_alloced; VIO_STR env, expanded; /* --- copy from filename to expanded_filename, changing environment variables and home directories */ len = string_length( filename ); prev_was_backslash = FALSE; i = 0; dest = 0; n_alloced = 0; n_env_alloced = 0; env = NULL; expanded = NULL; while( i < len+1 ) { /* --- if not escaped by backslash, and is either a '~' at the beginning or a '$' anywhere, expand it */ if( !prev_was_backslash && ((i == 0 && filename[i] == '~') || filename[i] == '$') ) { /* --- pick up the environment variable name or user name, by searching until the next '/' or a '.' or end of string */ new_i = i; tilde_found = (filename[new_i] == '~'); ++new_i; env_index = 0; while( filename[new_i] != '/' && filename[new_i] != '.' && filename[new_i] != VIO_END_OF_STRING ) { ADD_ELEMENT_TO_ARRAY_WITH_SIZE( env, n_env_alloced, env_index, filename[new_i], DEFAULT_CHUNK_SIZE ); ++new_i; } ADD_ELEMENT_TO_ARRAY_WITH_SIZE( env, n_env_alloced, env_index, VIO_END_OF_STRING, DEFAULT_CHUNK_SIZE ); /* --- if expanding a '~', find the corresponding home directory */ if( tilde_found ) { if( string_length( env ) == 0 ) expand_value = getenv( "HOME" ); else expand_value = get_user_home_directory( env ); } else /* --- get the environment variable value */ expand_value = getenv( env ); /* --- if an expansion is found, copy it, otherwise just copy char*/ if( expand_value != NULL ) { SET_ARRAY_SIZE( expanded, n_alloced, n_alloced + string_length(expand_value), DEFAULT_CHUNK_SIZE ); n_alloced += string_length(expand_value); (void) strcpy( &expanded[dest], expand_value ); dest += string_length( expand_value ); i = new_i; } else { SET_ARRAY_SIZE( expanded, n_alloced, n_alloced + 1, DEFAULT_CHUNK_SIZE ); ++n_alloced; expanded[dest] = filename[i]; ++dest; ++i; } prev_was_backslash = FALSE; } else { /* --- if not a backslash or if it is escaped, add character */ if( filename[i] != '\\' || prev_was_backslash ) { SET_ARRAY_SIZE( expanded, n_alloced, n_alloced + 1, DEFAULT_CHUNK_SIZE ); ++n_alloced; expanded[dest] = filename[i]; ++dest; prev_was_backslash = FALSE; } else prev_was_backslash = TRUE; ++i; } } if( n_env_alloced > 0 ) delete_string( env ); return( expanded ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : filename_extension_matches @INPUT : filename extension @OUTPUT : @RETURNS : TRUE if filename extension matches @DESCRIPTION: Checks if the filename ends in a period, then the given extension. Note that the filename first undergoes expansion for home directories and environment variables, and any ending of ".z", ".Z", or ".gz" is first removed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL filename_extension_matches( VIO_STR filename, VIO_STR extension ) { int len, i; VIO_STR filename_no_z, ending; VIO_BOOL matches; filename_no_z = expand_filename( filename ); len = string_length( filename_no_z ); for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY(compressed_endings) ) { if( string_ends_in( filename_no_z, compressed_endings[i] ) ) { filename_no_z[len-string_length(compressed_endings[i])] = VIO_END_OF_STRING; } } ending = concat_strings( ".", extension ); matches = string_ends_in( filename_no_z, ending ); delete_string( filename_no_z ); delete_string( ending ); return( matches ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : remove_directories_from_filename @INPUT : filename @OUTPUT : filename_no_directories @RETURNS : @DESCRIPTION: Creates a new filename with no directories in it. E.G. if filename equals "/usr/people/david/test.c" filename_no_directories will be set to "test.c" @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR remove_directories_from_filename( VIO_STR filename ) { VIO_STR expanded, no_directories; int i; expanded = expand_filename( filename ); i = string_length( expanded ); while( i >= 0 && expanded[i] != '/' ) --i; ++i; no_directories = create_string( &expanded[i] ); delete_string( expanded ); return( no_directories ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : file_exists_as_compressed @INPUT : filename @OUTPUT : compressed_filename @RETURNS : TRUE if a compressed file exists @DESCRIPTION: Checks to see if a compressed version of the file exists. If so, passes back the name of the compressed file. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL file_exists_as_compressed( VIO_STR filename, VIO_STR *compressed_filename ) { int i; VIO_STR compressed, expanded; VIO_BOOL gzipped; gzipped = FALSE; expanded = expand_filename( filename ); /* --- check to see if file.z or file.Z, etc, exists */ for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY( compressed_endings ) ) { compressed = concat_strings( expanded, compressed_endings[i] ); if( file_exists( compressed ) ) { if( *compressed_filename == filename ) delete_string( filename ); *compressed_filename = compressed; gzipped = TRUE; break; } delete_string( compressed ); } delete_string( expanded ); return( gzipped ); } VIOAPI VIO_STR get_temporary_filename( void ) { int tmp_fd; char *tmpfile_ptr; #if defined (HAVE_MKSTEMP) /* Best-case scenario (so far...) * mkstemp() creates a file immediately, minimizing the race * conditions that exist when using the other functions. These race * conditions can lead to small security holes (and large, annoying * GNU linker messages). * * The only catch is that mkstemp() does not automatically put the * file in the TMPDIR directory (or some other appropriate place). * So I more-or-less emulate that behavior here. */ const char pat_str[] = "/minc-XXXXXX"; char *tmpdir_ptr; if ((tmpdir_ptr = getenv("TMPDIR")) == NULL) { tmpdir_ptr = P_tmpdir; } tmpfile_ptr = malloc(strlen(tmpdir_ptr) + sizeof (pat_str)); if (tmpfile_ptr == NULL) { return (NULL); } strcpy(tmpfile_ptr, tmpdir_ptr); strcat(tmpfile_ptr, pat_str); tmp_fd = mkstemp(tmpfile_ptr); /* Creates the file if possible. */ #elif defined (HAVE_TEMPNAM) /* Second-best case. While not completely avoiding the race condition, * this approach should at least have the nice property of putting the * tempfile in the right directory (on IRIX and Linux, at least - on * some systems tempnam() may not consult the TMPDIR environment variable). */ tmpfile_ptr = tempnam(NULL, "minc-"); if (tmpfile_ptr == NULL) { return (NULL); } tmp_fd = open(tmpfile_ptr, O_CREAT | O_EXCL | O_RDWR, S_IWRITE | S_IREAD); #elif defined (HAVE_TMPNAM) /* Worst case. tmpnam() is apparently the worst of all possible worlds * here. It doesn't allow any way to force a particular directory, * and it doesn't avoid the race condition. But volume_io used it for * years, so I see no reason to disallow this case for systems that * might not define the above two functions (whether any such systems * exist is unclear to me). */ tmpfile_ptr = malloc(L_tmpnam + 1); if (tmpfile_ptr == NULL) { return (NULL); } if (tmpnam(tmpfile_ptr) == NULL) { free(tmpfile_ptr); return (NULL); } tmp_fd = open(tmpfile_ptr, O_CREAT | O_EXCL | O_RDWR, S_IWRITE | S_IREAD); #else #error "System defines neither mkstemp(), tempnam(), nor tmpnam()" #endif /* Neither HAVE_MKSTEMP, HAVE_TEMPNAM, or HAVE_TMPNAM defined. */ /* If we get here, tmp_fd should have been opened and the file * created. Now go ahead and close the file. */ if (tmp_fd >= 0) { close(tmp_fd); } else { free(tmpfile_ptr); tmpfile_ptr = NULL; } return (tmpfile_ptr); } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_file @INPUT : filename : io_type READ_FILE or WRITE_FILE : file_format ASCII_FORMAT or BINARY_FORMAT @OUTPUT : file @RETURNS : @DESCRIPTION: Opens the given filename for ascii or binary input or output. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status open_file( VIO_STR filename, VIO_IO_types io_type, VIO_File_formats file_format, FILE **file ) { VIO_Status status; int i; char *tmp_name; char command[VIO_EXTREMELY_LARGE_STRING_SIZE]; VIO_STR access_str, expanded; VIO_BOOL gzipped; int command_status; /* --- determine what mode of file access */ switch( io_type ) { case APPEND_FILE: access_str = create_string( "a" ); break; case WRITE_FILE: access_str = create_string( "w" ); break; case READ_FILE: default: access_str = create_string( "r" ); break; } /* --- check if ascii or binary */ if( file_format == BINARY_FORMAT ) concat_to_string( &access_str, "b" ); /* --- expand ~ and $ in filename */ expanded = expand_filename( filename ); gzipped = FALSE; /* --- if reading the file, check if it is in compressed format */ if( io_type == READ_FILE ) { /* --- check if the filename ends in one of the compressed suffixes */ for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY( compressed_endings ) ) { if( string_ends_in( expanded, compressed_endings[i] ) ) { gzipped = TRUE; break; } } /* --- if the filename does not have a compressed suffix and the file does not exist, check to see if file.z or file.Z, etc, exists */ if( !gzipped && !file_exists( expanded ) ) gzipped = file_exists_as_compressed( expanded, &expanded ); } /* --- if reading from a compressed file, decompress it to a temp file */ status = VIO_OK; if( gzipped ) { /* --- uncompress to a temporary file */ tmp_name = get_temporary_filename(); (void) sprintf( command, "gunzip -c %s > %s", expanded, tmp_name ); command_status = system( command ); /* Try again, using bzip2 */ if( command_status != 0 ) { (void) sprintf( command, "bunzip2 -c %s > %s", expanded, tmp_name ); command_status = system( command ); } /* Check for failure */ if( command_status != 0 ) { print_error( "Error uncompressing %s into %s using gunzip and bunzip2\n", expanded, tmp_name ); status = VIO_ERROR; } else replace_string( &expanded, create_string(tmp_name) ); free(tmp_name); } /* --- finally, open the file */ if( status == VIO_OK ) { *file = fopen( expanded, access_str ); if( *file == NULL ) /* --- print error message if needed */ { print_error( "Error: could not open file \"%s\". ", expanded ); print_system_error(); status = VIO_ERROR; } else if( gzipped ) /* if reading a decompressed temp file, */ remove_file( expanded ); /* unlink it, so that when the program */ /* closes the file or dies, the file is */ /* removed */ } delete_string( access_str ); delete_string( expanded ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_file_with_default_suffix @INPUT : filename : default_suffix - e.g. ".obj" : io_type READ_FILE or WRITE_FILE : file_format ASCII_FORMAT or BINARY_FORMAT @OUTPUT : file @RETURNS : @DESCRIPTION: Opens the given filename for ascii or binary input or output. : On output, if the file has no suffix, it adds the default suffix. : On input, if the file does not exist as given, then it tries to : find the file with the default_suffix. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status open_file_with_default_suffix( const char *filename, VIO_STR default_suffix, VIO_IO_types io_type, VIO_File_formats file_format, FILE **file ) { VIO_Status status; VIO_BOOL suffix_added; VIO_STR used_filename = NULL, expanded; expanded = expand_filename( filename ); if( io_type == READ_FILE ) { suffix_added = FALSE; if( !file_exists(expanded) && has_no_extension( expanded ) ) { used_filename = concat_strings( expanded, "." ); concat_to_string( &used_filename, default_suffix ); if( file_exists( used_filename ) ) suffix_added = TRUE; else delete_string( used_filename ); } if( !suffix_added ) used_filename = create_string( expanded ); } else if( has_no_extension( expanded ) ) { used_filename = concat_strings( expanded, "." ); concat_to_string( &used_filename, default_suffix ); } else { used_filename = create_string( expanded ); } status = open_file( used_filename, io_type, file_format, file ); delete_string( expanded ); delete_string( used_filename ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : has_no_extension @INPUT : filename @OUTPUT : @RETURNS : TRUE if there is no . extension @DESCRIPTION: Checks if there is an extension on the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL has_no_extension( VIO_STR filename ) { VIO_STR base_name; VIO_BOOL dot_found; base_name = remove_directories_from_filename( filename ); dot_found = (find_character( base_name, '.' ) >= 0); delete_string( base_name ); return( !dot_found ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_file_position @INPUT : file : byte_position @OUTPUT : @RETURNS : @DESCRIPTION: Sets the file position to the given offset from the start. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status set_file_position( FILE *file, long byte_position ) { VIO_Status status; if( fseek( file, byte_position, 0 ) == 0 ) { status = VIO_OK; } else { print_error( "Error setting the file position. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_file @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Closes the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status close_file( FILE *file ) { if( file != NULL ) { (void) fclose( file ); return( VIO_OK ); } else return( VIO_ERROR ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : extract_directory @INPUT : filename @OUTPUT : directory @RETURNS : @DESCRIPTION: Extracts the directory from the filename by copying the string : from the beginning up to the last '/'. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR extract_directory( const char *filename ) { int i, slash_index; VIO_STR expanded, directory; expanded = expand_filename( filename ); slash_index = string_length(expanded) - 1; while( slash_index >= 0 && expanded[slash_index] != '/' ) --slash_index; if( slash_index < 0 ) directory = create_string( "." ); else { ++slash_index; directory = alloc_string( slash_index ); for_less( i, 0, slash_index ) directory[i] = expanded[i]; directory[slash_index] = VIO_END_OF_STRING; } delete_string( expanded ); return( directory ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_absolute_filename @INPUT : filename : directory @OUTPUT : @RETURNS : @DESCRIPTION: Given a filename and a default directory, determines the correct : filename by checking if the filename is a relative or absolute : pathname, and prepending the directory, if the former. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_absolute_filename( VIO_STR filename, VIO_STR directory ) { VIO_STR abs_filename, expanded; /* if the directory is non-null and the filename is not already absolute (begins with '/'), then prefix the directory to the filename */ expanded = expand_filename( filename ); if( string_length( directory ) > 0 && expanded[0] != '/' ) { if( directory[string_length(directory)-1] == '/' ) abs_filename = create_string( directory ); else abs_filename = concat_strings( directory, "/" ); } else { abs_filename = create_string( NULL ); } concat_to_string( &abs_filename, expanded ); delete_string( expanded ); return( abs_filename ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : flush_file @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Flushes the output buffer for the given file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status flush_file( FILE *file ) { VIO_Status status; if( fflush( file ) == 0 ) { status = VIO_OK; } else { print_error( "Error flushing file. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_character @INPUT : file @OUTPUT : ch @RETURNS : VIO_Status @DESCRIPTION: Inputs one character from the file, returning VIO_ERROR if eof. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_character( FILE *file, char *ch ) { VIO_Status status; int c; c = fgetc( file ); if( c == EOF ) { *ch = 0; status = VIO_ERROR; } else { *ch = (char) c; status = VIO_OK; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : unget_character @INPUT : file @OUTPUT : ch @RETURNS : VIO_Status @DESCRIPTION: Ungets one character back to the file, returning status. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status unget_character( FILE *file, char ch ) { VIO_Status status; int c; c = ungetc( (int) ch, file ); if( c == EOF ) status = VIO_ERROR; else status = VIO_OK; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_nonwhite_character @INPUT : file @OUTPUT : ch @RETURNS : VIO_Status @DESCRIPTION: Inputs the next nonwhite (tab, space, newline) character. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_nonwhite_character( FILE *file, char *ch ) { VIO_Status status; do { status = input_character( file, ch ); } while( status == VIO_OK && (*ch == ' ' || *ch == '\t' || *ch == '\n') ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_character @INPUT : file : ch @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs the character to the file, returning the status. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_character( FILE *file, char ch ) { VIO_Status status; if( fputc( (int) ch, file ) != ch ) { status = VIO_ERROR; } else { status = VIO_OK; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : skip_input_until @INPUT : file : search_char @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Skips characters in the file, up to and including the first match : of the search_char; @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status skip_input_until( FILE *file, char search_char ) { VIO_Status status; char ch; do { status = input_character( file, &ch ); } while( status == VIO_OK && ch != search_char ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_string @INPUT : file : str @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs the string to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_string( FILE *file, VIO_STR str ) { VIO_Status status; if( fprintf( file, "%s", str ) == string_length(str) ) status = VIO_OK; else { print_error( "Error outputting string. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_string @INPUT : file : termination_char @OUTPUT : str @RETURNS : VIO_Status @DESCRIPTION: Inputs a string from the file. First it skips white space, then : inputs all characters until the termination_char is found. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_string( FILE *file, VIO_STR *str, char termination_char ) { char ch = 0; VIO_Status status; status = input_nonwhite_character( file, &ch ); *str = create_string( NULL ); while( status == VIO_OK && ch != termination_char && ch != '\n' ) { concat_char_to_string( str, ch ); status = input_character( file, &ch ); } if( termination_char != '\n' && ch == '\n' ) (void) unget_character( file, ch ); if( status != VIO_OK ) { delete_string( *str ); *str = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_quoted_string @INPUT : file @OUTPUT : str @RETURNS : VIO_Status @DESCRIPTION: Skips to the next nonwhitespace character, checks if it is a : quotation mark ( ", ', or ` ), then reads characters into the : string until the : next quotation mark. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_quoted_string( FILE *file, VIO_STR *str ) { char ch, quote; VIO_Status status; status = input_nonwhite_character( file, "e ); if( status == VIO_OK && quote != '"' && quote != '\'' && quote != '`' ) status = VIO_ERROR; if( status == VIO_OK ) status = input_character( file, &ch ); *str = create_string( NULL ); while( status == VIO_OK && ch != quote ) { concat_char_to_string( str, ch ); status = input_character( file, &ch ); } if( status != VIO_OK ) { delete_string( *str ); *str = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_possibly_quoted_string @INPUT : file : str : str_length - size of string storage @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Skips to the next nonwhitespace character, checks if it is a : quotation mark, then reads characters into the string until the : next quotation mark. If it is not a quotation mark, reads to : the next white space. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_possibly_quoted_string( FILE *file, VIO_STR *str ) { VIO_BOOL quoted; char ch, quote; VIO_Status status; status = input_nonwhite_character( file, "e ); if( status == VIO_OK ) { if( quote == '"' || quote == '\'' || quote == '`' ) { quoted = TRUE; status = input_character( file, &ch ); } else { quoted = FALSE; ch = quote; } } *str = create_string( NULL ); while( status == VIO_OK && ((quoted && ch != quote) || (!quoted && ch != ' ' && ch != '\t' && ch != '\n')) ) { concat_char_to_string( str, ch ); status = input_character( file, &ch ); } if( !quoted ) (void) unget_character( file, ch ); if( status != VIO_OK ) { delete_string( *str ); *str = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_quoted_string @INPUT : file : str @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs the given string, with quotation marks around it. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_quoted_string( FILE *file, VIO_STR str ) { VIO_Status status; if( fprintf( file, " \"%s\"", str ) > 0 ) status = VIO_OK; else status = VIO_ERROR; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_binary_data @INPUT : file : element_size size of each element : n number of elements @OUTPUT : data array of elements to input @RETURNS : VIO_Status @DESCRIPTION: Inputs the data in binary format. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_binary_data( FILE *file, void *data, size_t element_size, int n ) { VIO_Status status; int n_done; status = VIO_OK; n_done = (int) fread( data, element_size, (size_t) n, file ); if( n_done != n ) { print_error( "Error inputting binary data.\n" ); print_error( " (%d out of %d items of size %ld). ", n_done, n, element_size ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_binary_data @INPUT : file : data array of elements to output : element_size size of each element : n number of elements @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs the data in binary format. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_binary_data( FILE *file, void *data, size_t element_size, int n ) { VIO_Status status; int n_done; status = VIO_OK; n_done = (int) fwrite( data, element_size, (size_t) n, file ); if( n_done != n ) { print_error( "Error outputting binary data.\n" ); print_error( " (%d out of %d items of size %ld). ", n_done, n, element_size ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_newline @INPUT : file @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Skips to after the next newline in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_newline( FILE *file ) { VIO_Status status; status = skip_input_until( file, '\n' ); if( status != VIO_OK ) { print_error( "Error inputting newline. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_newline @INPUT : file @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs a newline to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_newline( FILE *file ) { VIO_Status status; if( fprintf( file, "\n" ) > 0 ) status = VIO_OK; else { print_error( "Error outputting newline. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_line @INPUT : line - string to input to : str_length - storage allocated to the string @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs all characters upto the next newline. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_line( FILE *file, VIO_STR *line ) { VIO_Status status; char ch; *line = create_string( NULL ); status = input_character( file, &ch ); while( status == VIO_OK && ch != '\n' ) { concat_char_to_string( line, ch ); status = input_character( file, &ch ); } if( status != VIO_OK ) { delete_string( *line ); *line = NULL; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_boolean @INPUT : file @OUTPUT : b @RETURNS : VIO_Status @DESCRIPTION: Inputs a VIO_BOOL value from a file, by looking for an 'f' or 't'. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_boolean( FILE *file, VIO_BOOL *b ) { VIO_Status status; char ch; status = input_nonwhite_character( file, &ch ); if( status == VIO_OK ) { if( ch == 'f' || ch == 'F' ) *b = FALSE; else if( ch == 't' || ch == 'T' ) *b = TRUE; else status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_boolean @INPUT : file : b @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs a T or F to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_boolean( FILE *file, VIO_BOOL b ) { VIO_Status status; VIO_STR str; status = VIO_OK; if( b ) str = "T"; else str = "F"; if( fprintf( file, " %s", str ) <= 0 ) { print_error( "Error outputting VIO_BOOL. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_short @INPUT : file @OUTPUT : s @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii short. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_short( FILE *file, short *s ) { VIO_Status status; if( fscanf( file, "%hd", s ) == 1 ) status = VIO_OK; else status = VIO_ERROR; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_short @INPUT : file : s @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii short. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_short( FILE *file, short s ) { VIO_Status status; if( fprintf( file, " %d", s ) > 0 ) status = VIO_OK; else { print_error( "Error outputting short. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_unsigned_short @INPUT : file @OUTPUT : s @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii unsigned short. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_unsigned_short( FILE *file, unsigned short *s ) { int i; VIO_Status status; if( fscanf( file, "%d", &i ) == 1 ) { *s = (unsigned short) i; status = VIO_OK; } else status = VIO_ERROR; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_unsigned_short @INPUT : file : s @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii unsigned short. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_unsigned_short( FILE *file, unsigned short s ) { VIO_Status status; if( fprintf( file, " %d", (int) s ) > 0 ) status = VIO_OK; else { print_error( "Error outputting unsigned short. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_int @INPUT : file @OUTPUT : i @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii integer. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_int( FILE *file, int *i ) { VIO_Status status; if( fscanf( file, "%d", i ) == 1 ) status = VIO_OK; else status = VIO_ERROR; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_int @INPUT : file : i @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii integer. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_int( FILE *file, int i ) { VIO_Status status; if( fprintf( file, " %d", i ) > 0 ) status = VIO_OK; else { print_error( "Error outputting int. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_real @INPUT : file @OUTPUT : r @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii real value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_real( FILE *file, VIO_Real *r ) { VIO_Status status; if( real_is_double() ) { status = input_double( file, (double *) r ); } else { status = input_float( file, (float *) r ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_real @INPUT : file : i @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii real value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_real( FILE *file, VIO_Real r ) { VIO_Status status; if( real_is_double() ) { status = output_double( file, (double) r ); } else { status = output_float( file, (float) r ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_float @INPUT : file @OUTPUT : f @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii float. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_float( FILE *file, float *f ) { VIO_Status status; if( fscanf( file, "%f", f ) == 1 ) status = VIO_OK; else { status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_float @INPUT : file : f @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii float value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_float( FILE *file, float f ) { VIO_Status status; if( fprintf( file, " %g", f ) > 0 ) status = VIO_OK; else { print_error( "Error outputting float. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_double @INPUT : file @OUTPUT : d @RETURNS : VIO_Status @DESCRIPTION: Inputs an ascii double. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_double( FILE *file, double *d ) { VIO_Status status; if( fscanf( file, "%lf", d ) == 1 ) status = VIO_OK; else { status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_double @INPUT : file : d @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Outputs an ascii double value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_double( FILE *file, double d ) { VIO_Status status; if( fprintf( file, " %g", d ) > 0 ) status = VIO_OK; else { print_error( "Error outputting double. " ); print_system_error(); status = VIO_ERROR; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_binary_data @INPUT : file : io_flag : data : element_size : n @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs binary data, depending on io_flag. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_binary_data( FILE *file, VIO_IO_types io_flag, void *data, size_t element_size, int n ) { VIO_Status status; if( io_flag == READ_FILE ) status = input_binary_data( file, data, element_size, n ); else status = output_binary_data( file, data, element_size, n ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_newline @INPUT : file : io_flag : data @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary newline char, as appropriate. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_newline( FILE *file, VIO_IO_types io_flag, VIO_File_formats format ) { VIO_Status status; status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = VIO_OK; else status = output_newline( file ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_quoted_string @INPUT : file : io_flag : format : str : str_length @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary quoted string. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_quoted_string( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_STR *str ) { int length; VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_quoted_string( file, str ); else status = output_quoted_string( file, *str ); } else { if( io_flag == WRITE_FILE ) length = string_length( *str ); status = io_int( file, io_flag, format, &length ); if( io_flag == READ_FILE ) *str = alloc_string( length ); if( status == VIO_OK ) { status = io_binary_data( file, io_flag, (void *) (*str), sizeof((*str)[0]), length ); } str[length] = NULL; } if( status != VIO_OK ) print_error( "Error in quoted string in file.\n" ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_boolean @INPUT : file : io_flag : format : b boolean value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary boolean value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_boolean( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_BOOL *b ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_boolean( file, b ); else status = output_boolean( file, *b ); } else status = io_binary_data( file, io_flag, (void *) b, sizeof(*b), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_short @INPUT : file : io_flag : format : short_int short value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary short value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_short( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, short *short_int ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_short( file, short_int ); else status = output_short( file, *short_int ); } else status = io_binary_data( file, io_flag, (void *) short_int, sizeof(*short_int), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_unsigned_short @INPUT : file : io_flag : format : unsigned_short short value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary unsigned short value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_unsigned_short( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, unsigned short *unsigned_short ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_unsigned_short( file, unsigned_short ); else status = output_unsigned_short( file, *unsigned_short ); } else status = io_binary_data( file, io_flag, (void *) unsigned_short, sizeof(*unsigned_short), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_unsigned_char @INPUT : file : io_flag : format : c unsigned char value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary unsigned char. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_unsigned_char( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, unsigned char *c ) { int i; VIO_Status status; status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) { if( fscanf( file, "%d", &i ) == 1 ) *c = (unsigned char) i; else { print_error( "Error inputting unsigned char. " ); print_system_error(); status = VIO_ERROR; } } else { if( fprintf( file, " %d", (int) *c ) < 0 ) { print_error( "Error outputting unsigned char. " ); print_system_error(); status = VIO_ERROR; } } } else status = io_binary_data( file, io_flag, (void *) c, sizeof(*c), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_int @INPUT : file : io_flag : format : i integer value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary integer value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_int( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int *i ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_int( file, i ); else status = output_int( file, *i ); } else status = io_binary_data( file, io_flag, (void *) i, sizeof(*i), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_real @INPUT : file : io_flag : format : r real value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary real value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_real( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, VIO_Real *r ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_real( file, r ); else status = output_real( file, *r ); } else status = io_binary_data( file, io_flag, (void *) r, sizeof(*r), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_float @INPUT : file : io_flag : format : f float value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary double value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_float( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, float *f ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_float( file, f ); else status = output_float( file, *f ); } else status = io_binary_data( file, io_flag, (void *) f, sizeof(*f), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_double @INPUT : file : io_flag : format : d double value @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs an ascii or binary double value. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_double( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, double *d ) { VIO_Status status = VIO_OK; if( format == ASCII_FORMAT ) { if( io_flag == READ_FILE ) status = input_double( file, d ); else status = output_double( file, *d ); } else status = io_binary_data( file, io_flag, (void *) d, sizeof(*d), 1 ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_ints @INPUT : file : io_flag : format : n number of ints : ints array of ints @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs a list of ascii or binary integers. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_ints( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int n, int *ints[] ) { VIO_Status status; int i; #define INTS_PER_LINE 8 status = VIO_OK; if( io_flag == READ_FILE ) { ALLOC( *ints, n ); } if( format == ASCII_FORMAT ) { for_less( i, 0, n ) { status = io_int( file, io_flag, format, &(*ints)[i] ); if( status == VIO_OK ) { if( i == n - 1 || (i+1) % INTS_PER_LINE == 0 ) status = io_newline( file, io_flag, format ); } if( status == VIO_ERROR ) break; } } else { status = io_binary_data( file, io_flag, (void *) *ints, sizeof((*ints)[0]), n ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : io_unsigned_chars @INPUT : file : io_flag : format : n number of unsigned chars : unsigned_chars array of unsigned chars @OUTPUT : @RETURNS : VIO_Status @DESCRIPTION: Inputs or outputs a list of ascii or binary unsigned chars. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status io_unsigned_chars( FILE *file, VIO_IO_types io_flag, VIO_File_formats format, int n, unsigned char *unsigned_chars[] ) { VIO_Status status; int i; status = VIO_OK; if( io_flag == READ_FILE ) ALLOC( *unsigned_chars, n ); if( format == ASCII_FORMAT ) { for_less( i, 0, n ) { status = io_unsigned_char( file, io_flag, format, &(*unsigned_chars)[i] ); if( status == VIO_OK ) { if( i == n - 1 || (i+1) % INTS_PER_LINE == 0 ) status = io_newline( file, io_flag, format ); } if( status == VIO_ERROR ) break; } } else { status = io_binary_data( file, io_flag, (void *) (*unsigned_chars), sizeof((*unsigned_chars)[0]), n ); } return( status ); } libminc-libminc-2-3-00/volume_io/Prog_utils/print.c000066400000000000000000000212671257462267400222760ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #define MAX_PRINT_STACK 100 typedef void (*print_function_type) ( VIO_STR ); static print_function_type print_function[MAX_PRINT_STACK] = { NULL }; static int top_of_stack = 0; static print_function_type print_error_function[MAX_PRINT_STACK] = { NULL }; static int top_of_error_stack = 0; /* ----------------------------- MNI Header ----------------------------------- @NAME : set_print_function @INPUT : function @OUTPUT : @RETURNS : @DESCRIPTION: Sets the output function. If you use the function print() everywhere, in place of printf, then by default it uses printf to send output to stdout. However, you can call the set_print_function() to tell it to use a different output function, e.g. output to a GL or X window. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_print_function( void (*function) ( VIO_STR ) ) { print_function[top_of_stack] = function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : push_print_function @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Save the current print function, so, for instance, you can print to stdout temporarily. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void push_print_function( void ) { if( top_of_stack < MAX_PRINT_STACK - 1 ) { ++top_of_stack; print_function[top_of_stack] = NULL; } else handle_internal_error( "Stack overflow in push_print_function" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : pop_print_function @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Restore the print function. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void pop_print_function( void ) { if( top_of_stack > 0 ) --top_of_stack; else handle_internal_error( "Stack underflow in pop_print_function" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : print @INPUT : exactly same arguments as printf @OUTPUT : @RETURNS : @DESCRIPTION: prints the arguments to a temporary string buffer, then either printf's the or calls the user installed function to output the string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* VARARGS */ VIOAPI void print( VIO_STR format, ... ) { va_list ap; char print_buffer[VIO_EXTREMELY_LARGE_STRING_SIZE]; va_start( ap, format ); (void) vsprintf( print_buffer, format, ap ); va_end( ap ); if( print_function[top_of_stack] == NULL ) (void) printf( "%s", print_buffer ); else (*(print_function[top_of_stack])) ( print_buffer ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_print_error_function @INPUT : function @OUTPUT : @RETURNS : @DESCRIPTION: Sets the output function. If you use the function print_error() everywhere, in place of printf, then by default it uses printf to send output to stderr. However, you can call the set_print_error_function() to tell it to use a different output function, e.g. output to a GL or X window. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_print_error_function( void (*function) ( char [] ) ) { print_error_function[top_of_error_stack] = function; } /* ----------------------------- MNI Header ----------------------------------- @NAME : push_print_error_function @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Save the current print error function, so, for instance, you can print to stdout temporarily. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void push_print_error_function( void ) { if( top_of_error_stack < MAX_PRINT_STACK - 1 ) { ++top_of_error_stack; print_error_function[top_of_error_stack] = NULL; } else handle_internal_error( "Stack overflow in push_print_error_function" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : pop_print_error_function @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Restore the print_error function. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void pop_print_error_function( void ) { if( top_of_error_stack > 0 ) --top_of_error_stack; else handle_internal_error( "Stack underflow in pop_print_error_function" ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : print_error @INPUT : exactly same arguments as printf @OUTPUT : @RETURNS : @DESCRIPTION: prints the arguments to a temporary string buffer, then either fprintf's to stderr or calls the user installed function to output the string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ /* VARARGS */ VIOAPI void print_error( char format[], ... ) { va_list ap; char print_buffer[VIO_EXTREMELY_LARGE_STRING_SIZE]; va_start( ap, format ); (void) vsprintf( print_buffer, format, ap ); va_end( ap ); if( print_error_function[top_of_error_stack] == NULL ) (void) fprintf( stderr, "%s", print_buffer ); else (*(print_error_function[top_of_error_stack])) ( print_buffer ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : handle_internal_error @INPUT : str @OUTPUT : @RETURNS : @DESCRIPTION: Prints the error string and tries to get users permission to abort. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void handle_internal_error( char str[] ) { print_error( "Internal error: %s\n", str ); abort_if_allowed(); } /* ----------------------------- MNI Header ----------------------------------- @NAME : abort_if_allowed @INPUT : @OUTPUT : @RETURNS : @DESCRIPTION: Checks if the user wants to abort. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void abort_if_allowed( void ) { char ch; if( VIO_ENV_EXISTS( "ABORT_FLAG" ) ) { print_error( "Do you wish to abort (y/n): " ); do { ch = (char) getchar(); } while( ch != 'y' && ch != 'n' ); while( getchar() != '\n' ) { } if( ch == 'y' ) { abort(); } } } libminc-libminc-2-3-00/volume_io/Prog_utils/progress.c000066400000000000000000000245321257462267400230040ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #include #define FIRST_MESSAGE_THRESHOLD 5.0 #define ONE_LINE_THRESHOLD 160.0 #define LINE_LENGTH 77 #define MIN_UPDATE_RATE 20.0 /* seconds */ #define UPDATE_RATE_FACTOR 0.05 #define RATIO_FOR_LINEAR 0.5 #define DOUBLE_THRESHOLD 0.01 #define HALF_THRESHOLD 0.5 static void show_one_line_progress( VIO_progress_struct *progress, int current_step ); static void show_multi_line_progress( VIO_progress_struct *progress, int current_step, VIO_Real time_so_far, VIO_Real est_total_time ); /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_progress_report @INPUT : one_line_only - whether line of dots is desired : n_steps : title @OUTPUT : progress - structure is filled in @RETURNS : @DESCRIPTION: Initializes the progress report, which is either a line of dots : crossing the screen, or if the progress is too slow, a line : every 20 seconds or so indicating the amount of time left. : If one_line_only is true, then it is always a single line of dots. : n_steps is the total number of items or times through the loop. : If it is really fast, no messages at all are displayed. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void initialize_progress_report( VIO_progress_struct *progress, VIO_BOOL one_line_only, int n_steps, VIO_STR title ) { progress->force_one_line = one_line_only; progress->first_msg_displayed = FALSE; progress->one_line_flag = TRUE; progress->n_steps = n_steps; progress->title = create_string( title ); progress->start_time = current_realtime_seconds(); progress->previous_time = progress->start_time; progress->last_check_time = progress->start_time; progress->last_check_step = 0; progress->next_check_step = 1; progress->check_every = 1; progress->update_rate = MIN_UPDATE_RATE; progress->sum_xy = 0.0; progress->sum_xx = 0.0; progress->n_dots_so_far = 0; progress->total_n_dots = LINE_LENGTH - string_length( progress->title ); if( progress->total_n_dots < 1 ) progress->total_n_dots = 2; } /* ----------------------------- MNI Header ----------------------------------- @NAME : update_progress_report @INPUT : progress : current_step (an integer between 1 and n_steps) @OUTPUT : @RETURNS : @DESCRIPTION: Checks the current time and determines if it is time to output : a progress message. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : Sep. 1, 1995 D. MacDonald - changed update rate to be relative to time so far ---------------------------------------------------------------------------- */ VIOAPI void update_progress_report( VIO_progress_struct *progress, int current_step ) { VIO_Real current_time, constant, n_seconds_per; VIO_Real time_so_far, est_total_time; if( current_step < 1 || current_step < progress->next_check_step ) return; if( current_step > progress->n_steps ) current_step = progress->n_steps; current_time = current_realtime_seconds(); n_seconds_per = (VIO_Real) progress->check_every * (current_time - progress->last_check_time) / (VIO_Real) (current_step - progress->last_check_step); if( n_seconds_per < DOUBLE_THRESHOLD ) progress->check_every *= 2; else if( n_seconds_per > HALF_THRESHOLD && progress->check_every > 1 ) progress->check_every /= 2; progress->last_check_time = current_time; progress->last_check_step = current_step; progress->next_check_step = current_step + progress->check_every; if( progress->next_check_step > progress->n_steps ) progress->next_check_step = progress->n_steps; time_so_far = current_time - progress->start_time; progress->sum_xy = RATIO_FOR_LINEAR * progress->sum_xy + (VIO_Real) current_step * time_so_far; progress->sum_xx = RATIO_FOR_LINEAR * progress->sum_xx + (VIO_Real) current_step * (VIO_Real) current_step; if( time_so_far > FIRST_MESSAGE_THRESHOLD ) { constant = progress->sum_xy / progress->sum_xx; est_total_time = (VIO_Real) progress->n_steps * constant; if( est_total_time <= time_so_far ) { est_total_time = time_so_far * (VIO_Real) progress->n_steps / (VIO_Real) current_step; } if( progress->force_one_line || (progress->one_line_flag && est_total_time < ONE_LINE_THRESHOLD) ) { show_one_line_progress( progress, current_step ); progress->first_msg_displayed = TRUE; } else { if( progress->first_msg_displayed && progress->one_line_flag ) print( "\n" ); progress->one_line_flag = FALSE; if( current_time - progress->previous_time >= progress->update_rate) { show_multi_line_progress( progress, current_step, time_so_far, est_total_time ); progress->first_msg_displayed = TRUE; progress->previous_time = current_time; progress->update_rate = (current_time - progress->start_time) * UPDATE_RATE_FACTOR; if( progress->update_rate < MIN_UPDATE_RATE ) progress->update_rate = MIN_UPDATE_RATE; } } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : show_one_line_progress @INPUT : progress : current_step @OUTPUT : @RETURNS : @DESCRIPTION: Given the current_step, and the total number, ensures that the : number of dots on the line is representative. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void show_one_line_progress( VIO_progress_struct *progress, int current_step ) { long i, n_dots; n_dots = VIO_ROUND( (VIO_Real) current_step / (VIO_Real) progress->n_steps * (VIO_Real) progress->total_n_dots ); if( n_dots > progress->total_n_dots ) handle_internal_error( "show_one_line_progress" ); if( n_dots > progress->n_dots_so_far ) { if( progress->n_dots_so_far == 0 ) { print( "%s: ", progress->title ); } for_less( i, progress->n_dots_so_far, n_dots ) { print( "." ); } (void) flush_file( stdout ); progress->n_dots_so_far = n_dots; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : show_multi_line_progress @INPUT : progress : current_step : time_so_far : est_total_time @OUTPUT : @RETURNS : @DESCRIPTION: Displays report about time so far, estimated time left, etc. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void show_multi_line_progress( VIO_progress_struct *progress, int current_step, VIO_Real time_so_far, VIO_Real est_total_time ) { long percent_done; VIO_STR time_so_far_str, est_total_time_str; percent_done = VIO_ROUND( 100.0 * (VIO_Real) current_step / (VIO_Real) progress->n_steps ); time_so_far_str = format_time( "%g %s", time_so_far ); est_total_time_str = format_time( "%g %s", est_total_time ); print( "%s: %3ld%% done. (%d/%d) Time: %s out of approx %s\n", progress->title, percent_done, current_step, progress->n_steps, time_so_far_str, est_total_time_str ); delete_string( time_so_far_str ); delete_string( est_total_time_str ); (void) flush_file( stdout ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : terminate_progress_report @INPUT : progress @OUTPUT : @RETURNS : @DESCRIPTION: Terminates the progress report. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void terminate_progress_report( VIO_progress_struct *progress ) { VIO_Real total_time; VIO_STR time_str; if( progress->first_msg_displayed ) { if( progress->one_line_flag ) { show_one_line_progress( progress, progress->n_steps ); print( "\n" ); } else { total_time = current_realtime_seconds() - progress->start_time; time_str = format_time( "%g %s", total_time ); print( "%s: DONE in %s\n", progress->title, time_str ); delete_string( time_str ); } } delete_string( progress->title ); } libminc-libminc-2-3-00/volume_io/Prog_utils/string.c000066400000000000000000000175701257462267400224520ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include static const VIO_STR empty_string = ""; VIOAPI VIO_STR alloc_string( size_t length ) { VIO_STR str; ALLOC( str, length+1 ); return( str ); } VIOAPI VIO_STR create_string( const char *initial ) { VIO_STR str; if( initial == NULL ) initial = empty_string; str = alloc_string( string_length(initial) ); (void) strcpy( str, initial ); return( str ); } VIOAPI void delete_string( VIO_STR string ) { if( string != NULL ) FREE( string ); } VIOAPI VIO_STR concat_strings( VIO_STR str1, VIO_STR str2 ) { VIO_STR str; if( str1 == NULL ) str1 = empty_string; if( str2 == NULL ) str2 = empty_string; ALLOC( str, string_length(str1) + string_length(str2) + 1 ); (void) strcpy( str, str1 ); (void) strcat( str, str2 ); return( str ); } VIOAPI void replace_string( VIO_STR *string, VIO_STR new_string ) { delete_string( *string ); *string = new_string; } VIOAPI void concat_char_to_string( VIO_STR *string, char ch ) { int len; len = string_length( *string ); if( *string == NULL ) *string = alloc_string( 1 ); else SET_ARRAY_SIZE( *string, len+1, len+2, 1 ); (*string)[len] = ch; (*string)[len+1] = VIO_END_OF_STRING; } VIOAPI void concat_to_string( VIO_STR *string, VIO_STR str2 ) { VIO_STR new_string; new_string = concat_strings( *string, str2 ); replace_string( string, new_string ); } VIOAPI int string_length( const char *string ) { if( string == NULL ) return( 0 ); else return( (int) strlen( string ) ); } VIOAPI VIO_BOOL equal_strings( const char *str1, const char *str2 ) { const char *strA = str1 ? str1 : empty_string; const char *strB = str2 ? str2 : empty_string; return( strcmp( strA, strB ) == 0 ); } VIOAPI VIO_BOOL is_lower_case( char ch ) { return( ch >= 'a' && ch <= 'z' ); } VIOAPI VIO_BOOL is_upper_case( char ch ) { return( ch >= 'A' && ch <= 'Z' ); } VIOAPI char get_lower_case( char ch ) { if( is_upper_case( ch ) ) return( (char) ((ch)+'a'-'A') ); else return( ch ); } VIOAPI char get_upper_case( char ch ) { if( is_lower_case( ch ) ) return( (char) ((ch)+'A'-'a') ); else return( ch ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : string_ends_in @INPUT : string : ending @OUTPUT : @RETURNS : TRUE if string ends in "ending" @DESCRIPTION: Checks if the string ends in ending, e.g., : string_ends_in( "main.c", ".c" ) returns true. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL string_ends_in( VIO_STR string, VIO_STR ending ) { int len_string, len_ending; VIO_BOOL ending_present; len_string = string_length( string ); len_ending = string_length( ending ); if( len_ending > len_string ) ending_present = FALSE; else ending_present = equal_strings( &string[len_string-len_ending], ending); return( ending_present ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : strip_outer_blanks @INPUT : str @OUTPUT : stripped @RETURNS : @DESCRIPTION: Creates a new string which is the original str without any : leading or trailing blanks. Output argument may be the same pointer as input. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR strip_outer_blanks( VIO_STR str ) { VIO_STR stripped; int i, first_non_blank, last_non_blank, len; len = string_length( str ); /* --- skip leading blanks */ first_non_blank = 0; while( first_non_blank < len && str[first_non_blank] == ' ' ) { ++first_non_blank; } /* --- skip trailing blanks */ last_non_blank = len-1; while( last_non_blank >= 0 && str[last_non_blank] == ' ' ) { --last_non_blank; } /* --- now copy string, without leading or trailing blanks */ if( first_non_blank > last_non_blank ) last_non_blank = first_non_blank - 1; stripped = alloc_string( last_non_blank - first_non_blank + 1 ); for_inclusive( i, first_non_blank, last_non_blank ) stripped[i-first_non_blank] = str[i]; stripped[last_non_blank - first_non_blank + 1] = VIO_END_OF_STRING; return( stripped ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : find_character @INPUT : string : ch @OUTPUT : @RETURNS : index of ch within string or -1 @DESCRIPTION: Finds the index of the given character within the string. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int find_character( VIO_STR string, char ch ) { int i; if( string == NULL ) return( -1 ); i = 0; while( string[i] != VIO_END_OF_STRING ) { if( string[i] == ch ) return( i ); ++i; } return( -1 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : make_string_upper_case @INPUT : string @OUTPUT : string @RETURNS : @DESCRIPTION: Converts every lower case character in string to upper case. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void make_string_upper_case( VIO_STR string ) { int i, len; len = string_length( string ); for_less( i, 0, len ) { string[i] = get_upper_case( string[i] ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : blank_string @INPUT : string @OUTPUT : @RETURNS : TRUE if string is blank @DESCRIPTION: Checks to see if the string is blank; only contains space, tabs, and newlines. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL blank_string( VIO_STR string ) { int i; VIO_BOOL blank; if( string == NULL ) string = empty_string; blank = TRUE; i = 0; while( string[i] != VIO_END_OF_STRING ) { if( string[i] != ' ' && string[i] != '\t' && string[i] != '\n' ) { blank = FALSE; break; } ++i; } return( blank ); } libminc-libminc-2-3-00/volume_io/Prog_utils/time.c000066400000000000000000000212111257462267400220650ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #if HAVE_CLOCK_GETTIME # include #elif HAVE_GETTIMEOFDAY && HAVE_SYS_TIME_H # include #else # include #endif #if HAVE_UNISTD_H #include #endif #ifndef CLK_TCK #define CLK_TCK CLOCKS_PER_SEC #endif #if !defined(HAVE_SLEEP) void sleep(unsigned milliseconds) { fprintf(stderr,"Unfortunately sleep is not implemented!\n"); } #endif /*HAVE_SLEEP*/ /* ----------------------------- MNI Header ----------------------------------- @NAME : get_clock_ticks_per_second @INPUT : @OUTPUT : @RETURNS : number clock ticks per second @DESCRIPTION: Returns the number of clock ticks per second in a system independent fashion @METHOD : @GLOBALS : @CALLS : @CREATED : Jul 3, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Real get_clock_ticks_per_second( void ) { static VIO_BOOL initialized = FALSE; static VIO_Real clock_ticks_per_second; if( !initialized ) { initialized = TRUE; #if HAVE_SYSCONF clock_ticks_per_second = (VIO_Real) sysconf( _SC_CLK_TCK ); #else clock_ticks_per_second = (VIO_Real) CLK_TCK; #endif } return( clock_ticks_per_second ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : current_cpu_seconds @INPUT : @OUTPUT : @RETURNS : # seconds @DESCRIPTION: Returns the number of cpu seconds used by the program to date. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real current_cpu_seconds( void ) { static VIO_BOOL first_call = TRUE; static clock_t first; clock_t current; VIO_Real secs; if (first_call) { first_call = FALSE; first = clock(); secs = (VIO_Real) first / get_clock_ticks_per_second(); } else { current = clock(); secs = (VIO_Real) (current - first) / get_clock_ticks_per_second(); } return (secs); } /* ----------------------------- MNI Header ----------------------------------- @NAME : current_realtime_seconds @INPUT : @OUTPUT : @RETURNS : # seconds @DESCRIPTION: Returns the number of seconds since the first invocation of this : function. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real current_realtime_seconds( void ) { static VIO_Real first_seconds = -1.0; VIO_Real current_seconds; #if HAVE_CLOCK_GETTIME struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { fprintf(stderr, "ERROR: clock_gettime failed.\n"); current_seconds = first_seconds; } else { current_seconds = ts.tv_sec + (ts.tv_nsec / 1.0e9); } #elif HAVE_GETTIMEOFDAY struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { fprintf(stderr, "ERROR: gettimeofday failed.\n"); current_seconds = first_seconds; } else { current_seconds = tv.tv_sec + (tv.tv_usec / 1.0e6); } #else /* This case is actually INCORRECT, in that users of the function * assume a fractional number of seconds is returned, but this * method can only return a whole number. This means that tests * that assume less than a second's precision will be unreliable. */ current_seconds = (VIO_Real) time(NULL); #endif if( first_seconds < 0.0 ) { first_seconds = current_seconds; } return ( current_seconds - first_seconds ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : format_time @INPUT : format : seconds @OUTPUT : str @RETURNS : @DESCRIPTION: Decides what time unit to use and displays the seconds value : in str, using format. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR format_time( VIO_STR format, VIO_Real seconds ) { int i; static char *units[] = { "us", "ms", "sec", "min", "hrs", "days", "years" }; static VIO_Real scales[] = { 1000.0, 1000.0, 60.0, 60.0, 24.0, 365.0 }; char buffer[VIO_EXTREMELY_LARGE_STRING_SIZE]; VIO_BOOL negative; negative = seconds < 0.0; if( negative ) seconds = -seconds; seconds *= 1.0e6; for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY(units)-1 ) { if( seconds > 2.0 * scales[i] ) { seconds /= scales[i]; } else { break; } } seconds = (VIO_Real) VIO_ROUND( 10.0 * seconds ) / 10.0; if( negative ) seconds = -seconds; (void) sprintf( buffer, format, seconds, units[i] ); return( create_string( buffer ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : print_time @INPUT : format : seconds @OUTPUT : @RETURNS : @DESCRIPTION: Prints out the time in suitable units. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void print_time( VIO_STR format, VIO_Real seconds ) { VIO_STR str; str = format_time( format, seconds ); print( "%s", str ); delete_string( str ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_clock_time @INPUT : @OUTPUT : time_str @RETURNS : @DESCRIPTION: Stores the current time of day in the "time_str". @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_clock_time( void ) { time_t clock_time; struct tm *time_tm; char *str; (void) time( &clock_time ); time_tm = localtime( &clock_time ); str = asctime( time_tm ); return( create_string( str ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : sleep_program @INPUT : seconds @OUTPUT : @RETURNS : @DESCRIPTION: Make the program sleep for the specified number of seconds. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void sleep_program( VIO_Real seconds ) { #if HAVE_SELECT struct timeval timeout; timeout.tv_sec = (long) seconds; timeout.tv_usec = (long) (1.0e6 * (seconds - (VIO_Real) timeout.tv_sec) + 0.5); (void) select( 0, NULL, NULL, NULL, &timeout ); #else sleep((unsigned int) seconds); #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_date @INPUT : @OUTPUT : date_str @RETURNS : @DESCRIPTION: Fills in the date into the string. @METHOD : @GLOBALS : @CALLS : @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_date( void ) { time_t clock_time; struct tm *time_tm; char *str; (void) time( &clock_time ); time_tm = localtime( &clock_time ); str = asctime( time_tm ); return( create_string( str ) ); } libminc-libminc-2-3-00/volume_io/SourceFiles000066400000000000000000000016171257462267400210120ustar00rootroot00000000000000SRCS = \ Geometry/colour.c \ Geometry/colour_def.c \ Geometry/gaussian.c \ Geometry/inverse.c \ Geometry/newton.c \ Geometry/points.c \ Geometry/splines.c \ Geometry/tensors.c \ Geometry/transforms.c \ MNI_formats/gen_xf_io.c \ MNI_formats/gen_xfs.c \ MNI_formats/grid_transforms.c \ MNI_formats/mni_io.c \ MNI_formats/tag_points.c \ MNI_formats/thin_plate_spline.c \ Prog_utils/alloc.c \ Prog_utils/alloc_check.c \ Prog_utils/arrays.c \ Prog_utils/files.c \ Prog_utils/print.c \ Prog_utils/progress.c \ Prog_utils/string.c \ Prog_utils/time.c \ Volumes/evaluate.c \ Volumes/get_hyperslab.c \ Volumes/input_free.c \ Volumes/input_mnc.c \ Volumes/input_volume.c \ Volumes/multidim_arrays.c \ Volumes/output_mnc.c \ Volumes/output_volume.c \ Volumes/set_hyperslab.c \ Volumes/volume_cache.c \ Volumes/volumes.c libminc-libminc-2-3-00/volume_io/Volumes/000077500000000000000000000000001257462267400202715ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/Volumes/evaluate.c000066400000000000000000001063631257462267400222540ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_voxel_to_value @INPUT : volume voxel @OUTPUT : @RETURNS : real value @DESCRIPTION: Converts a voxel value to a real value. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real convert_voxel_to_value( VIO_Volume volume, VIO_Real voxel ) { if( volume->real_range_set ) return( volume->real_value_scale * voxel + volume->real_value_translation ); else return( voxel ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_value_to_voxel @INPUT : volume value @OUTPUT : @RETURNS : voxel value @DESCRIPTION: Converts a real value to a voxel value. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real convert_value_to_voxel( VIO_Volume volume, VIO_Real value ) { if( volume->real_range_set ) return( (value - volume->real_value_translation) / volume->real_value_scale ); else return( value ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_voxel_value @INPUT : volume v0 voxel indices v1 v2 v3 v4 @OUTPUT : @RETURNS : Voxel value @DESCRIPTION: Returns the voxel at the specified voxel index. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_voxel_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4 ) { VIO_Real voxel; GET_VOXEL_TYPED( voxel, (VIO_Real), volume, v0, v1, v2, v3, v4 ); return( voxel ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_real_value @INPUT : volume v0 voxel indices v1 v2 v3 v4 @OUTPUT : @RETURNS : VIO_Real value @DESCRIPTION: Returns the volume real value at the specified voxel index. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_real_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4 ) { VIO_Real voxel, value; voxel = get_volume_voxel_value( volume, v0, v1, v2, v3, v4 ); value = convert_voxel_to_value( volume, voxel ); return( value ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_voxel_value @INPUT : volume v0 voxel indices v1 v2 v3 v4 voxel @OUTPUT : @RETURNS : @DESCRIPTION: Sets the voxel at the specified voxel index. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_voxel_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, VIO_Real voxel ) { SET_VOXEL( volume, v0, v1, v2, v3, v4, voxel ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_real_value @INPUT : volume v0 voxel indices v1 v2 v3 v4 value @OUTPUT : @RETURNS : @DESCRIPTION: Sets the volume real value at the specified voxel index. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_real_value( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, VIO_Real value ) { VIO_Real voxel; VIO_Data_types data_type; voxel = convert_value_to_voxel( volume, value ); data_type = get_volume_data_type( volume ); if( data_type != VIO_FLOAT && data_type != VIO_DOUBLE ) { voxel = (VIO_Real) VIO_ROUND( voxel ); } set_volume_voxel_value( volume, v0, v1, v2, v3, v4, voxel ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : trilinear_interpolate @INPUT : volume voxel outside_value @OUTPUT : value derivs @RETURNS : @DESCRIPTION: Computes the trilinear interpolation of the 8 coeficients, passing the value back in the 'value' parameter. If the derivs parameter is not null, then the 3 derivatives are also computed and passed back. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : Jan. 8, 1996 D. MacDonald - now check for outside volume is done inside this procedure ---------------------------------------------------------------------------- */ static void trilinear_interpolate( VIO_Volume volume, VIO_Real voxel[], VIO_Real outside_value, VIO_Real *value, VIO_Real derivs[] ) { int c, i, j, k, *sizes, dx, dy, dz; VIO_Real x, y, z, u, v, w; VIO_Real coefs[8]; VIO_Real du00, du01, du10, du11, c00, c01, c10, c11, c0, c1, du0, du1; VIO_Real dv0, dv1, dw, scale_factor; sizes = &volume->array.sizes[0]; x = voxel[0]; y = voxel[1]; z = voxel[2]; if( x >= 0.0 && x < (VIO_Real) sizes[0]-1.0 && y >= 0.0 && y < (VIO_Real) sizes[1]-1.0 && z >= 0.0 && z < (VIO_Real) sizes[2]-1.0 ) { i = (int) x; j = (int) y; k = (int) z; GET_VOXEL_3D_TYPED( coefs[0], (VIO_Real), volume, i , j , k ); GET_VOXEL_3D_TYPED( coefs[1], (VIO_Real), volume, i , j , k+1 ); GET_VOXEL_3D_TYPED( coefs[2], (VIO_Real), volume, i , j+1, k ); GET_VOXEL_3D_TYPED( coefs[3], (VIO_Real), volume, i , j+1, k+1 ); GET_VOXEL_3D_TYPED( coefs[4], (VIO_Real), volume, i+1, j , k ); GET_VOXEL_3D_TYPED( coefs[5], (VIO_Real), volume, i+1, j , k+1 ); GET_VOXEL_3D_TYPED( coefs[6], (VIO_Real), volume, i+1, j+1, k ); GET_VOXEL_3D_TYPED( coefs[7], (VIO_Real), volume, i+1, j+1, k+1 ); } else { outside_value = convert_value_to_voxel( volume, outside_value ); i = VIO_FLOOR( x ); j = VIO_FLOOR( y ); k = VIO_FLOOR( z ); c = 0; for_less( dx, 0, 2 ) for_less( dy, 0, 2 ) for_less( dz, 0, 2 ) { if( i + dx >= 0 && i + dx < sizes[0] && j + dy >= 0 && j + dy < sizes[1] && k + dz >= 0 && k + dz < sizes[2] ) { GET_VOXEL_3D_TYPED( coefs[c], (VIO_Real), volume, i+dx, j+dy, k+dz); } else coefs[c] = outside_value; ++c; } } u = x - (VIO_Real) i; v = y - (VIO_Real) j; w = z - (VIO_Real) k; /*--- get the 4 differences in the u direction */ du00 = coefs[4] - coefs[0]; du01 = coefs[5] - coefs[1]; du10 = coefs[6] - coefs[2]; du11 = coefs[7] - coefs[3]; /*--- reduce to a 2D problem, by interpolating in the u direction */ c00 = coefs[0] + u * du00; c01 = coefs[1] + u * du01; c10 = coefs[2] + u * du10; c11 = coefs[3] + u * du11; /*--- get the 2 differences in the v direction for the 2D problem */ dv0 = c10 - c00; dv1 = c11 - c01; /*--- reduce 2D to a 1D problem, by interpolating in the v direction */ c0 = c00 + v * dv0; c1 = c01 + v * dv1; /*--- get the 1 difference in the w direction for the 1D problem */ dw = c1 - c0; /*--- if the value is desired, interpolate in 1D to get the value */ if( value != NULL ) { *value = convert_voxel_to_value( volume, c0 + w * dw ); } /*--- if the derivatives are desired, compute them */ if( derivs != NULL ) { if( volume->real_range_set ) scale_factor = volume->real_value_scale; else scale_factor = 1.0; /*--- reduce the 2D u derivs to 1D */ du0 = VIO_INTERPOLATE( v, du00, du10 ); du1 = VIO_INTERPOLATE( v, du01, du11 ); /*--- interpolate the 1D problems in w, or for VIO_Z deriv, just use dw */ derivs[VIO_X] = scale_factor * VIO_INTERPOLATE( w, du0, du1 ); derivs[VIO_Y] = scale_factor * VIO_INTERPOLATE( w, dv0, dv1 ); derivs[VIO_Z] = scale_factor * dw; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : interpolate_volume @INPUT : n_dims - number of dimensions to interpolate parameters[] - 0 to 1 parameters for each dim n_values - number of values to interpolate degree - degree of interpolation, 4 == cubic coefs - [degree*degree*degree... *n_values] coeficients @OUTPUT : values - pass back values first_deriv - pass first derivs [n_values][n_dims] second_deriv - pass back values [n_values][n_dims][n_dims] @RETURNS : @DESCRIPTION: Computes the interpolation of the box specified by coefs and its derivatives, if necessary. @METHOD : @GLOBALS : @CALLS : @CREATED : Mar. 20, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define MAX_DERIV_SIZE 100 static void interpolate_volume( int n_dims, VIO_Real parameters[], int n_values, int degree, VIO_Real coefs[], VIO_Real values[], VIO_Real **first_deriv, VIO_Real ***second_deriv ) { int v, d, d2, n_derivs, derivs_per_value, mult, mult2; VIO_Real fixed_size_derivs[MAX_DERIV_SIZE]; VIO_Real *derivs; /*--- determine how many derivatives should be computed */ if( second_deriv != NULL ) n_derivs = 2; else if( first_deriv != NULL ) n_derivs = 1; else n_derivs = 0; /*--- compute the total number of values, 1st and 2nd derivatives per val*/ derivs_per_value = 1; for_less( d, 0, n_dims ) derivs_per_value *= 1 + n_derivs; /*--- make storage for the spline routines to place the answers */ if( n_values * derivs_per_value <= MAX_DERIV_SIZE ) { derivs = fixed_size_derivs; } else { ALLOC( derivs, n_values * derivs_per_value ); } /*--- evaluate the interpolating spline */ evaluate_interpolating_spline( n_dims, parameters, degree, n_values, coefs, n_derivs, derivs ); /*--- derivs is now a one dimensional array representing derivs[n_values][1+n_derivs][1+n_derivs]..., where derivs[0][0][0][0]... = the interpolated value for 1st comp, derivs[1][0][0][0]... = the interpolated value for 2nd comp, derivs[n_values-1][0][0][0]... = interpolated value for last, derivs[0][1][0][0]... = derivative of 1st comp wrt x derivs[0][0][1][0]... = derivative of 1st comp wrt y derivs[1][1][0][0]... = derivative of 2nd comp wrt x, etc. */ if( values != NULL ) { for_less( v, 0, n_values ) values[v] = derivs[v*derivs_per_value]; } /*--- fill in the first derivatives, if required */ if( first_deriv != NULL ) { mult = 1; for_down( d, n_dims-1, 0 ) { for_less( v, 0, n_values ) first_deriv[v][d] = derivs[mult + v*derivs_per_value]; mult *= 1 + n_derivs; } } /*--- fill in the second derivatives, if required */ if( second_deriv != NULL ) { mult = 1; for_down( d, n_dims-1, 0 ) { for_less( v, 0, n_values ) second_deriv[v][d][d] = derivs[2*mult + v*derivs_per_value]; mult *= 1 + n_derivs; } mult = 1; for_down( d, n_dims-1, 0 ) { mult2 = 1; for_down( d2, n_dims-1, d+1 ) { for_less( v, 0, n_values ) { second_deriv[v][d][d2] = derivs[mult+mult2+v*derivs_per_value]; second_deriv[v][d2][d] = derivs[mult+mult2+v*derivs_per_value]; } mult2 *= 1 + n_derivs; } mult *= 1 + n_derivs; } } if( n_values * derivs_per_value > MAX_DERIV_SIZE ) { FREE( derivs ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : extract_coefficients @INPUT : volume start end inc @OUTPUT : coefs @RETURNS : @DESCRIPTION: Extracts the coefficients from a volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void extract_coefficients( VIO_Volume volume, int start[], int end[], VIO_Real coefs[], int inc[] ) { int inc0, inc1, inc2, inc3, inc4; int ind; int start0, start1, start2, start3, start4; int end0, end1, end2, end3, end4, n_dims; int v0, v1, v2, v3, v4; /*--- for speed, use non-array variables for the loops */ start0 = start[0]; start1 = start[1]; start2 = start[2]; start3 = start[3]; start4 = start[4]; end0 = end[0]; end1 = end[1]; end2 = end[2]; end3 = end[3]; end4 = end[4]; inc0 = inc[0]; inc1 = inc[1]; inc2 = inc[2]; inc3 = inc[3]; inc4 = inc[4]; /*--- adjust the inc stride for stepping through coefs to account for the additions of the inner loops */ n_dims = get_volume_n_dimensions(volume); if( n_dims >= 2 ) inc0 -= inc1 * (end1 - start1); if( n_dims >= 3 ) inc1 -= inc2 * (end2 - start2); if( n_dims >= 4 ) inc2 -= inc3 * (end3 - start3); if( n_dims >= 5 ) inc3 -= inc4 * (end4 - start4); /*--- get the coefs[] from the volume. For speed, do each dimension separately */ ind = 0; switch( n_dims ) { case 1: for_less( v0, start0, end0 ) { GET_VALUE_1D_TYPED( coefs[ind], (VIO_Real), volume, v0 ); ind += inc0; } break; case 2: for_less( v0, start0, end0 ) { for_less( v1, start1, end1 ) { GET_VALUE_2D_TYPED( coefs[ind], (VIO_Real), volume, v0, v1 ); ind += inc1; } ind += inc0; } break; case 3: for_less( v0, start0, end0 ) { for_less( v1, start1, end1 ) { for_less( v2, start2, end2 ) { GET_VALUE_3D_TYPED( coefs[ind], (VIO_Real), volume, v0, v1, v2); ind += inc2; } ind += inc1; } ind += inc0; } break; case 4: for_less( v0, start0, end0 ) { for_less( v1, start1, end1 ) { for_less( v2, start2, end2 ) { for_less( v3, start3, end3 ) { GET_VALUE_4D_TYPED( coefs[ind], (VIO_Real), volume, v0, v1, v2, v3 ); ind += inc3; } ind += inc2; } ind += inc1; } ind += inc0; } break; case 5: for_less( v0, start0, end0 ) { for_less( v1, start1, end1 ) { for_less( v2, start2, end2 ) { for_less( v3, start3, end3 ) { for_less( v4, start4, end4 ) { GET_VALUE_5D_TYPED( coefs[ind], (VIO_Real), volume, v0, v1, v2, v3, v4 ); ind += inc4; } ind += inc3; } ind += inc2; } ind += inc1; } ind += inc0; } break; } } static VIO_Real interpolation_tolerance = 0.0; /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_interpolation_tolerance @INPUT : tolerance @OUTPUT : @RETURNS : @DESCRIPTION: Sets the tolerance which defines how close to a voxel centre we must be in order to just pass back the voxel value, rather than interpolating. @METHOD : @GLOBALS : @CALLS : @CREATED : Apr. 11, 1996 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_interpolation_tolerance( VIO_Real tolerance ) { interpolation_tolerance = tolerance; } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_volume @INPUT : volume voxel interpolating_dimensions - whether each dimension is interpolated degrees_continuity use_linear_at_edge outside_value @OUTPUT : values first_deriv second_deriv @RETURNS : @DESCRIPTION: Takes a voxel space position and evaluates the value within the volume by nearest_neighbour, linear, quadratic, or cubic interpolation. degrees_continuity == 2 corresponds to cubic, 1 for quadratic, etc. If first_deriv is not a null pointer, then the first derivatives are passed back. Similarly for the second_deriv. If use_linear_at_edge is TRUE, then near the boundaries, either linear or nearest neighbour interpolation is used, even if cubic is specified by the degrees_continuity. If use_linear_at_edge is FALSE, then the 'outside_value' is used to provide coefficients for outside the volume, and the degree specified by degrees_continuity is used. Each dimension may or may not be interpolated, specified by the interpolating_dimensions parameter. For instance, a 4D volume of x,y,z,RGB may be interpolated in 3D (x,y,z) for each of the 3 RGB components, with one call to evaluate_volume. @CREATED : Mar 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #define MAX_COEF_SPACE 1000 VIOAPI int evaluate_volume( VIO_Volume volume, VIO_Real voxel[], VIO_BOOL interpolating_dimensions[], int degrees_continuity, VIO_BOOL use_linear_at_edge, VIO_Real outside_value, VIO_Real values[], VIO_Real **first_deriv, VIO_Real ***second_deriv ) { int inc[VIO_MAX_DIMENSIONS]; int start_index, spline_degree; int next_d; int n, v, d, dim, n_values, sizes[VIO_MAX_DIMENSIONS], n_dims; int start[VIO_MAX_DIMENSIONS], n_interp_dims; int end[VIO_MAX_DIMENSIONS]; int interp_dims[VIO_MAX_DIMENSIONS]; int n_coefs; VIO_Real fraction[VIO_MAX_DIMENSIONS], bound, *coefs, pos; VIO_Real fixed_size_coefs[MAX_COEF_SPACE]; VIO_BOOL fully_inside, fully_outside, on_grid_point; n_dims = get_volume_n_dimensions(volume); /*--- check for the common case trilinear interpolation of 1 value */ if( n_dims == 3 && degrees_continuity == 0 && second_deriv == NULL && (interpolating_dimensions == NULL || (interpolating_dimensions[0] && interpolating_dimensions[1] && interpolating_dimensions[2])) ) { VIO_Real *deriv; if( first_deriv == NULL ) deriv = NULL; else deriv = first_deriv[0]; trilinear_interpolate( volume, voxel, outside_value, &values[0], deriv ); return( 1 ); } /*--- check if the degrees continuity is between nearest neighbour and cubic */ if( degrees_continuity < -1 || degrees_continuity > 2 ) { print_error( "Warning: evaluate_volume(), degrees invalid: %d\n", degrees_continuity ); degrees_continuity = 0; } get_volume_sizes( volume, sizes ); /*--- check if we are near a voxel centre, if so just use nearest neighbour and avoid the expensive interpolation, unless we need derivatives */ if( interpolation_tolerance > 0.0 && first_deriv == NULL && second_deriv == NULL ) { on_grid_point = TRUE; for_less( d, 0, n_dims ) { if( interpolating_dimensions == NULL || interpolating_dimensions[d]) { pos = (VIO_Real) VIO_ROUND( voxel[d] ); if( voxel[d] < pos - interpolation_tolerance || voxel[d] > pos + interpolation_tolerance ) { on_grid_point = FALSE; break; } } } if( on_grid_point ) degrees_continuity = -1; } bound = (VIO_Real) degrees_continuity / 2.0; /*--- if we must use linear interpolation near the boundaries, then check if we are near the boundaries, and adjust the degrees_continuity accordingly */ if( use_linear_at_edge && degrees_continuity >= 0 ) { for_less( d, 0, n_dims ) { if( interpolating_dimensions == NULL || interpolating_dimensions[d]) { while( degrees_continuity >= 0 && (voxel[d] < bound || voxel[d] > (VIO_Real) sizes[d] - 1.0 - bound || bound == (VIO_Real) sizes[d] - 1.0 - bound ) ) { --degrees_continuity; if( degrees_continuity == 1 ) degrees_continuity = 0; bound = (VIO_Real) degrees_continuity / 2.0; } } } } /*--- now check which dimensions are being interpolated. Also, compute how many values must be interpolated, which are all the values not in the interpolated dimensions */ n_interp_dims = 0; n_values = 1; n_coefs = 1; spline_degree = degrees_continuity + 2; fully_inside = TRUE; fully_outside = FALSE; for_less( d, 0, n_dims ) { if( interpolating_dimensions == NULL || interpolating_dimensions[d]) { interp_dims[n_interp_dims] = d; pos = voxel[d] - bound; start[d] = VIO_FLOOR( pos ); fraction[n_interp_dims] = pos - (VIO_Real) start[d]; if( voxel[d] == (VIO_Real) sizes[d] - 1.0 - bound ) { --start[d]; fraction[n_interp_dims] = 1.0; } end[d] = start[d] + spline_degree; n_coefs *= spline_degree; if( start[d] < 0 || end[d] > sizes[d] ) { fully_inside = FALSE; if( end[d] <= 0 || start[d] >= sizes[d] ) { fully_outside = TRUE; break; } } ++n_interp_dims; } else n_values *= sizes[d]; } /*--- check if the first derivatives are uncomputable */ if( first_deriv != NULL && (fully_outside || degrees_continuity < 0) ) { for_less( v, 0, n_values ) for_less( d, 0, n_interp_dims ) first_deriv[v][d] = 0.0; } /*--- check if the second derivatives are uncomputable */ if( second_deriv != NULL && (fully_outside || degrees_continuity < 1) ) { for_less( v, 0, n_values ) for_less( d, 0, n_interp_dims ) for_less( dim, 0, n_interp_dims ) second_deriv[v][d][dim] = 0.0; } /*--- check if the values are uncomputable, i.e., outside volume */ if( fully_outside ) { if( values != NULL ) { for_less( v, 0, n_values ) values[v] = outside_value; } return( n_values ); } /*--- add the non-interpolated dimensions to the list of dimensions, in order, after the interpolated dimensions */ n = 0; for_less( d, 0, n_dims ) { if( interpolating_dimensions != NULL && !interpolating_dimensions[d] ) { interp_dims[n_interp_dims+n] = d; start[d] = 0; end[d] = sizes[d]; ++n; } } /*--- make room for the coeficients */ if( n_values * n_coefs > MAX_COEF_SPACE ) { ALLOC( coefs, n_values * n_coefs ); } else coefs = fixed_size_coefs; /*--- figure out the offset within coefs. If we are inside, the offset is zero, since all coefs must be filled in. If we are partially inside, set the offset to the first coef within the volume. */ if( !fully_inside ) { /*--- compute the increments in the coefs[] array for each dimension, in order to simulate a multidimensional array with a single dim array, coefs */ inc[interp_dims[n_dims-1]] = 1; for_down( d, n_dims-2, 0 ) { next_d = interp_dims[d+1]; inc[interp_dims[d]] = inc[next_d] * (end[next_d] - start[next_d]); } start_index = 0; for_less( d, 0, n_dims ) { if( start[d] < 0 ) { start_index += -start[d] * inc[d]; start[d] = 0; } if( end[d] > sizes[d] ) end[d] = sizes[d]; } for_less( v, 0, n_values * n_coefs ) coefs[v] = outside_value; /*--- get the necessary coeficients from the volume */ extract_coefficients( volume, start, end, &coefs[start_index], inc ); } else { /*--- get the necessary coeficients from the volume */ for_less( d, n_dims, VIO_MAX_DIMENSIONS ) { start[d] = 0; end[d] = 0; } get_volume_value_hyperslab( volume, start[0], start[1], start[2], start[3], start[4], end[0] - start[0], end[1] - start[1], end[2] - start[2], end[3] - start[3], end[4] - start[4], coefs ); } /*--- now that we have the coeficients, do the interpolation */ switch( degrees_continuity ) { case -1: /*--- nearest neighbour interpolation */ for_less( v, 0, n_values ) values[v] = coefs[v]; break; case 0: case 1: case 2: interpolate_volume( n_interp_dims, fraction, n_values, spline_degree, coefs, values, first_deriv, second_deriv ); break; } if( n_values * n_coefs > MAX_COEF_SPACE ) { FREE( coefs ); } return( n_values ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : evaluate_volume_in_world @INPUT : volume x y z degrees_continuity - 0 = linear, 2 = cubic use_linear_at_edge outside_value @OUTPUT : values deriv_x deriv_y deriv_z deriv_xx deriv_xy deriv_xz deriv_yy deriv_yz deriv_zz @RETURNS : @DESCRIPTION: Takes a world space position and evaluates the value within the volume. If deriv_x is not a null pointer, then the 3 derivatives are passed back. If deriv_xx is not null, then the 6 second derivatives are passed back. If the volume is 3D, then only one value, and one derivative per deriv_x,etc. is passed back. If the volume has more than 3 dimensions, say 5 dimensions, with dimensions 3 and 4 being the non-spatial dimensions, then there will be sizes[3] * sizes[4] values passed back. The derivatives are converted to world space. @CREATED : Mar 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void evaluate_volume_in_world( VIO_Volume volume, VIO_Real x, VIO_Real y, VIO_Real z, int degrees_continuity, VIO_BOOL use_linear_at_edge, VIO_Real outside_value, VIO_Real values[], VIO_Real deriv_x[], VIO_Real deriv_y[], VIO_Real deriv_z[], VIO_Real deriv_xx[], VIO_Real deriv_xy[], VIO_Real deriv_xz[], VIO_Real deriv_yy[], VIO_Real deriv_yz[], VIO_Real deriv_zz[] ) { VIO_Real ignore; VIO_Real voxel[VIO_MAX_DIMENSIONS]; VIO_Real **first_deriv, ***second_deriv; VIO_Real t[VIO_N_DIMENSIONS][VIO_MAX_DIMENSIONS]; int c, d, dim, v, n_values, n_dims, axis; int sizes[VIO_MAX_DIMENSIONS], dims_interpolated[VIO_N_DIMENSIONS]; VIO_BOOL interpolating_dimensions[VIO_MAX_DIMENSIONS]; /*--- convert the world space to a voxel coordinate */ convert_world_to_voxel( volume, x, y, z, voxel ); get_volume_sizes( volume, sizes ); /*--- initialize all dimensions to not being interpolated */ n_dims = get_volume_n_dimensions( volume ); for_less( d, 0, n_dims ) interpolating_dimensions[d] = FALSE; /*--- set each spatial dimension to being interpolated */ for_less( d, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[d]; if( axis < 0 ) { // print_error( // "evaluate_volume_in_world(): must have 3 spatial axes.\n" ); // return; } else { interpolating_dimensions[axis] = TRUE; } } /*--- compute the number of values, the product of the sizes of the non-interpolating dimensions */ n_values = 1; for_less( d, 0, n_dims ) { if( !interpolating_dimensions[d] ) n_values *= sizes[d]; } /*--- make room for the first derivative, if necessary */ if( deriv_x != NULL ) { VIO_ALLOC2D( first_deriv, n_values, VIO_N_DIMENSIONS ); } else first_deriv = NULL; /*--- make room for the second derivative, if necessary */ if( deriv_xx != NULL ) { VIO_ALLOC3D( second_deriv, n_values, VIO_N_DIMENSIONS, VIO_N_DIMENSIONS ); } else second_deriv = NULL; /*--- evaluate the volume and derivatives in voxel space */ n_values = evaluate_volume( volume, voxel, interpolating_dimensions, degrees_continuity, use_linear_at_edge, outside_value, values, first_deriv, second_deriv ); /*--- if the derivative is desired, convert the voxel derivative to world space */ if( deriv_x != NULL || deriv_xx != NULL ) { /*--- figure out the dimensions interpolated, in order */ dim = 0; for_less( d, 0, n_dims ) { if( interpolating_dimensions[d] ) { dims_interpolated[dim] = d; ++dim; } } } if( deriv_x != NULL ) { for_less( v, 0, n_values ) /*--- convert the deriv of each value */ { /*--- get the voxel coordinates of the first derivative */ for_less( c, 0, VIO_N_DIMENSIONS ) voxel[dims_interpolated[c]] = first_deriv[v][c]; /*--- convert the voxel-space derivative to a world derivative */ convert_voxel_normal_vector_to_world( volume, voxel, &deriv_x[v], &deriv_y[v], &deriv_z[v] ); } VIO_FREE2D( first_deriv ); } /*--- if the derivative is desired, convert the voxel derivative to world space */ if( deriv_xx != (VIO_Real *) 0 ) { for_less( v, 0, n_values ) /*--- convert the deriv of each value */ { /*--- get the voxel coordinates of the first derivative */ for_less( dim, 0, VIO_N_DIMENSIONS ) { for_less( c, 0, VIO_N_DIMENSIONS ) voxel[dims_interpolated[c]] = second_deriv[v][dim][c]; /*--- convert the voxel-space derivative to a world derivative*/ convert_voxel_normal_vector_to_world( volume, voxel, &t[VIO_X][dims_interpolated[dim]], &t[VIO_Y][dims_interpolated[dim]], &t[VIO_Z][dims_interpolated[dim]] ); } /*--- now convert the results to world */ convert_voxel_normal_vector_to_world( volume, t[VIO_X], &deriv_xx[v], &ignore, &ignore ); convert_voxel_normal_vector_to_world( volume, t[VIO_Y], &deriv_xy[v], &deriv_yy[v], &ignore ); convert_voxel_normal_vector_to_world( volume, t[VIO_Z], &deriv_xz[v], &deriv_yz[v], &deriv_zz[v] ); } VIO_FREE3D( second_deriv ); } } libminc-libminc-2-3-00/volume_io/Volumes/get_hyperslab.c000066400000000000000000001154161257462267400232750ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include VIOAPI void convert_voxels_to_values( VIO_Volume volume, int n_voxels, VIO_Real voxels[], VIO_Real values[] ) { int v; VIO_Real scale, trans; if( !volume->real_range_set ) { if( voxels != values ) { for_less( v, 0, n_voxels ) values[v] = voxels[v]; } return; } scale = volume->real_value_scale; trans = volume->real_value_translation; for_less( v, 0, n_voxels ) values[v] = scale * voxels[v] + trans; } VIOAPI void get_volume_value_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { switch( get_volume_n_dimensions(volume) ) { case 1: get_volume_value_hyperslab_1d( volume, v0, n0, values ); break; case 2: get_volume_value_hyperslab_2d( volume, v0, v1, n0, n1, values ); break; case 3: get_volume_value_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, values ); break; case 4: get_volume_value_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, values ); break; case 5: get_volume_value_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, values ); break; } } VIOAPI void get_volume_value_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { get_volume_voxel_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, values ); convert_voxels_to_values( volume, n0 * n1 * n2 * n3 * n4, values, values ); } VIOAPI void get_volume_value_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ) { get_volume_voxel_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, values ); convert_voxels_to_values( volume, n0 * n1 * n2 * n3, values, values ); } VIOAPI void get_volume_value_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ) { get_volume_voxel_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, values ); convert_voxels_to_values( volume, n0 * n1 * n2, values, values ); } VIOAPI void get_volume_value_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ) { get_volume_voxel_hyperslab_2d( volume, v0, v1, n0, n1, values ); convert_voxels_to_values( volume, n0 * n1, values, values ); } VIOAPI void get_volume_value_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ) { get_volume_voxel_hyperslab_1d( volume, v0, n0, values ); convert_voxels_to_values( volume, n0, values, values ); } static void slow_get_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { int i0, i1, i2, i3, i4, n_dims; n_dims = get_volume_n_dimensions( volume ); if( n_dims < 5 ) n4 = 1; if( n_dims < 4 ) n3 = 1; if( n_dims < 3 ) n2 = 1; if( n_dims < 2 ) n1 = 1; if( n_dims < 1 ) n0 = 1; for_less( i0, 0, n0 ) for_less( i1, 0, n1 ) for_less( i2, 0, n2 ) for_less( i3, 0, n3 ) for_less( i4, 0, n4 ) { *values = get_volume_voxel_value( volume, v0 + i0, v1 + i1, v2 + i2, v3 + i3, v4 + i4 ); ++values; } } static VIO_Real *int_to_real_conversion = NULL; static void check_real_conversion_lookup( void ) { VIO_Real min_value1, max_value1, min_value2, max_value2; long i, long_min, long_max; if( int_to_real_conversion != NULL ) return; get_type_range( VIO_UNSIGNED_SHORT, &min_value1, &max_value1 ); get_type_range( VIO_SIGNED_SHORT, &min_value2, &max_value2 ); long_min = (long) MIN( min_value1, min_value2 ); long_max = (long) MAX( max_value1, max_value2 ); ALLOC( int_to_real_conversion, long_max - long_min + 1 ); #ifndef NO_DEBUG_ALLOC (void) unrecord_ptr_alloc_check( int_to_real_conversion, __FILE__, __LINE__ ); #endif int_to_real_conversion -= long_min; for_inclusive( i, long_min, long_max ) int_to_real_conversion[i] = (VIO_Real) i; } VIOAPI void get_voxel_values_5d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2, step3, step4; int i0, i1, i2, i3, i4; int n0, n1, n2, n3, n4; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; n3 = counts[3]; n4 = counts[4]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; step3 = steps[3]; step4 = steps[4]; step0 -= n1 * step1; step1 -= n2 * step2; step2 -= n3 * step3; step3 -= n4 * step4; check_real_conversion_lookup(); switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = int_to_real_conversion[ (long) *VIO_UCHAR_ptr]; ++values; VIO_UCHAR_ptr += step4; } VIO_UCHAR_ptr += step3; } VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = int_to_real_conversion[ (long) *signed_byte_ptr]; ++values; signed_byte_ptr += step4; } signed_byte_ptr += step3; } signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = int_to_real_conversion[ (long) *unsigned_short_ptr]; ++values; unsigned_short_ptr += step4; } unsigned_short_ptr += step3; } unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = int_to_real_conversion[ (long) *signed_short_ptr]; ++values; signed_short_ptr += step4; } signed_short_ptr += step3; } signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = (VIO_Real) *unsigned_int_ptr; ++values; unsigned_int_ptr += step4; } unsigned_int_ptr += step3; } unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = (VIO_Real) *signed_int_ptr; ++values; signed_int_ptr += step4; } signed_int_ptr += step3; } signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = (VIO_Real) *float_ptr; ++values; float_ptr += step4; } float_ptr += step3; } float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *values = (VIO_Real) *double_ptr; ++values; double_ptr += step4; } double_ptr += step3; } double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } VIOAPI void get_voxel_values_4d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2, step3; int i0, i1, i2, i3; int n0, n1, n2, n3; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; n3 = counts[3]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; step3 = steps[3]; step0 -= n1 * step1; step1 -= n2 * step2; step2 -= n3 * step3; check_real_conversion_lookup(); switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = int_to_real_conversion[ (long) *VIO_UCHAR_ptr]; ++values; VIO_UCHAR_ptr += step3; } VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = int_to_real_conversion[(long) *signed_byte_ptr]; ++values; signed_byte_ptr += step3; } signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = int_to_real_conversion[ (long) *unsigned_short_ptr]; ++values; unsigned_short_ptr += step3; } unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = int_to_real_conversion[ (long) *signed_short_ptr]; ++values; signed_short_ptr += step3; } signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = (VIO_Real) *unsigned_int_ptr; ++values; unsigned_int_ptr += step3; } unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = (VIO_Real) *signed_int_ptr; ++values; signed_int_ptr += step3; } signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = (VIO_Real) *float_ptr; ++values; float_ptr += step3; } float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *values = (VIO_Real) *double_ptr; ++values; double_ptr += step3; } double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } VIOAPI void get_voxel_values_3d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2; int i0, i1, i2; int n0, n1, n2; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; check_real_conversion_lookup(); n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; /*--- special case, for speed */ if( data_type == VIO_UNSIGNED_BYTE && n0 == 2 && n1 == 2 && n2 == 2 && step2 == 1 ) { step0 -= step1 + 1; step1 -= 1; ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; values[0] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; ++VIO_UCHAR_ptr; values[1] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; VIO_UCHAR_ptr += step1; values[2] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; ++VIO_UCHAR_ptr; values[3] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; VIO_UCHAR_ptr += step0; values[4] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; ++VIO_UCHAR_ptr; values[5] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; VIO_UCHAR_ptr += step1; values[6] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; ++VIO_UCHAR_ptr; values[7] = int_to_real_conversion[(unsigned long) *VIO_UCHAR_ptr]; return; } step0 -= n1 * step1; step1 -= n2 * step2; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = int_to_real_conversion[(long) *VIO_UCHAR_ptr]; ++values; VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = int_to_real_conversion[(long) *signed_byte_ptr]; ++values; signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = int_to_real_conversion[(long) *unsigned_short_ptr]; ++values; unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = int_to_real_conversion[(long) *signed_short_ptr]; ++values; signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = (VIO_Real) *unsigned_int_ptr; ++values; unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = (VIO_Real) *signed_int_ptr; ++values; signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = (VIO_Real) *float_ptr; ++values; float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *values = (VIO_Real) *double_ptr; ++values; double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } VIOAPI void get_voxel_values_2d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1; int i0, i1; int n0, n1; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; step0 = steps[0]; step1 = steps[1]; step0 -= n1 * step1; check_real_conversion_lookup(); switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = int_to_real_conversion[(long) *VIO_UCHAR_ptr]; ++values; VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = int_to_real_conversion[(long) *signed_byte_ptr]; ++values; signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = int_to_real_conversion[(long) *unsigned_short_ptr]; ++values; unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = int_to_real_conversion[(long) *signed_short_ptr]; ++values; signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = (VIO_Real) *unsigned_int_ptr; ++values; unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = (VIO_Real) *signed_int_ptr; ++values; signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = (VIO_Real) *float_ptr; ++values; float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *values = (VIO_Real) *double_ptr; ++values; double_ptr += step1; } double_ptr += step0; } break; } } VIOAPI void get_voxel_values_1d( VIO_Data_types data_type, void *void_ptr, int step0, int n0, VIO_Real values[] ) { int i0; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; check_real_conversion_lookup(); switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = int_to_real_conversion[(long) *VIO_UCHAR_ptr]; ++values; VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = int_to_real_conversion[(long) *signed_byte_ptr]; ++values; signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = int_to_real_conversion[(long) *unsigned_short_ptr]; ++values; unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = int_to_real_conversion[(long) *signed_short_ptr]; ++values; signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = (VIO_Real) *unsigned_int_ptr; ++values; unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = (VIO_Real) *signed_int_ptr; ++values; signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = (VIO_Real) *float_ptr; ++values; float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { *values = (VIO_Real) *double_ptr; ++values; double_ptr += step0; } break; } } static void get_voxel_values( VIO_Volume volume, void *void_ptr, int n_dims, int steps[], int counts[], VIO_Real values[] ) { VIO_Data_types data_type; data_type = get_volume_data_type( volume ); switch( n_dims ) { case 0: get_voxel_values_1d( data_type, void_ptr, 1, 1, values ); break; case 1: get_voxel_values_1d( data_type, void_ptr, steps[0], counts[0], values ); break; case 2: get_voxel_values_2d( data_type, void_ptr, steps, counts, values ); break; case 3: get_voxel_values_3d( data_type, void_ptr, steps, counts, values ); break; case 4: get_voxel_values_4d( data_type, void_ptr, steps, counts, values ); break; case 5: get_voxel_values_5d( data_type, void_ptr, steps, counts, values ); break; } } VIOAPI void get_volume_voxel_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_get_volume_voxel_hyperslab( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_5D( void_ptr, volume->array, v0, v1, v2, v3, v4 ) stride = 1; dim = 5; if( n4 > 1 ) { --dim; counts[dim] = n4; steps[dim] = stride; } stride *= sizes[4]; if( n3 > 1 ) { --dim; counts[dim] = n3; steps[dim] = stride; } stride *= sizes[3]; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } get_voxel_values( volume, void_ptr, 5 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void get_volume_voxel_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_get_volume_voxel_hyperslab( volume, v0, v1, v2, v3, 0, n0, n1, n2, n3, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_4D( void_ptr, volume->array, v0, v1, v2, v3 ) stride = 1; dim = 4; if( n3 > 1 ) { --dim; counts[dim] = n3; steps[dim] = stride; } stride *= sizes[3]; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } get_voxel_values( volume, void_ptr, 4 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void get_volume_voxel_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_get_volume_voxel_hyperslab( volume, v0, v1, v2, 0, 0, n0, n1, n2, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_3D( void_ptr, volume->array, v0, v1, v2 ) stride = 1; dim = 3; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } get_voxel_values( volume, void_ptr, 3 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void get_volume_voxel_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_get_volume_voxel_hyperslab( volume, v0, v1, 0, 0, 0, n0, n1, 0, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_2D( void_ptr, volume->array, v0, v1 ) stride = 1; dim = 2; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } get_voxel_values( volume, void_ptr, 2 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void get_volume_voxel_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim; void *void_ptr; if( volume->is_cached_volume ) { slow_get_volume_voxel_hyperslab( volume, v0, 0, 0, 0, 0, n0, 0, 0, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_1D( void_ptr, volume->array, v0 ) dim = 1; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = 1; } get_voxel_values( volume, void_ptr, 1 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void get_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real voxels[] ) { switch( get_volume_n_dimensions(volume) ) { case 1: get_volume_voxel_hyperslab_1d( volume, v0, n0, voxels ); break; case 2: get_volume_voxel_hyperslab_2d( volume, v0, v1, n0, n1, voxels ); break; case 3: get_volume_voxel_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, voxels ); break; case 4: get_volume_voxel_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, voxels ); break; case 5: get_volume_voxel_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, voxels ); break; } } libminc-libminc-2-3-00/volume_io/Volumes/input_free.c000066400000000000000000000504731257462267400226060ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_MINC1 #include #endif /*HAVE_MINC1*/ #define DEFAULT_SUFFIX "fre" #define NUM_BYTE_VALUES 256 static VIO_Status input_slice( volume_input_struct *volume_input ); /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_free_format_input @INPUT : @OUTPUT : volume volume_input @RETURNS : VIO_OK if successful @DESCRIPTION: Initializes loading a free format file by reading the header. If the file contains short, but the convert_to_byte flag is set in volume_input, then the file is converted to bytes on input. This function assumes that volume->filename has been assigned. @CREATED : David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now uses volume starts ---------------------------------------------------------------------------- */ VIOAPI VIO_Status initialize_free_format_input( VIO_STR filename, VIO_Volume volume, volume_input_struct *volume_input ) { VIO_Status status, file_status; VIO_STR volume_filename, abs_volume_filename, slice_filename; int sizes[VIO_N_DIMENSIONS]; int c, volume_byte_offset, int_size; int n_bytes_per_voxel, min_value, max_value, i; int n_slices, n_voxels_in_slice; long slice; nc_type desired_data_type; int value; char ch; VIO_Real file_separations[VIO_MAX_DIMENSIONS]; VIO_Real volume_separations[VIO_MAX_DIMENSIONS]; VIO_Real trans[VIO_N_DIMENSIONS]; FILE *file; VIO_BOOL axis_valid; int axis; file_status = open_file_with_default_suffix( filename, DEFAULT_SUFFIX, READ_FILE, ASCII_FORMAT, &file ); status = file_status; abs_volume_filename = NULL; /* read the line containing: n_bytes_per_voxel */ if( status == VIO_OK ) status = input_int( file, &n_bytes_per_voxel ); /* input the 3 translation values used for the voxel_to_world_transform */ if( status == VIO_OK && (input_real( file, &trans[VIO_X] ) != VIO_OK || input_real( file, &trans[VIO_Y] ) != VIO_OK || input_real( file, &trans[VIO_Z] ) != VIO_OK) ) { print_error( "Error reading x,y,z translations from %s.\n", filename ); status = VIO_ERROR; } if( status == VIO_OK ) { /* decide what type of data is in image file */ if( n_bytes_per_voxel == 1 ) volume_input->file_data_type = VIO_UNSIGNED_BYTE; else if( n_bytes_per_voxel == 2 ) volume_input->file_data_type = VIO_UNSIGNED_SHORT; else { print_error( "Must be either 1 or 2 bytes per voxel.\n" ); status = VIO_ERROR; } /* decide how to store data in memory */ if( get_volume_data_type(volume) == VIO_NO_DATA_TYPE ) desired_data_type = NC_BYTE; else desired_data_type = volume->nc_data_type; } /* read 3 lines, 1 for each axis: number_voxels +/-voxel_separation x|y|z where the x, y, or z is used to indicate the ordering of the axes within the file, with the 3rd one being the fastest varying index. negative voxel separation means flip on display */ if( volume->spatial_axes[VIO_X] < 0 || volume->spatial_axes[VIO_Y] < 0 || volume->spatial_axes[VIO_Z] < 0 ) { print_error( "warning initialize_free_format_input: setting spatial axes to XYZ.\n"); volume->spatial_axes[VIO_X] = 0; volume->spatial_axes[VIO_Y] = 1; volume->spatial_axes[VIO_Z] = 2; } if( status == VIO_OK ) for_less( axis, 0, VIO_N_DIMENSIONS ) { status = VIO_ERROR; if( input_int( file, &int_size ) != VIO_OK ) break; volume_input->sizes_in_file[axis] = (long) int_size; if( input_real( file, &file_separations[axis] ) != VIO_OK ) break; if( input_nonwhite_character( file, &ch ) != VIO_OK ) break; axis_valid = TRUE; switch( ch ) { case 'x': case 'X': volume_input->axis_index_from_file[axis] = volume->spatial_axes[VIO_X]; break; case 'y': case 'Y': volume_input->axis_index_from_file[axis] = volume->spatial_axes[VIO_Y]; break; case 'z': case 'Z': volume_input->axis_index_from_file[axis] = volume->spatial_axes[VIO_Z]; break; default: axis_valid = FALSE; break; } if( !axis_valid ) { print_error( "Invalid axis.\n" ); break; } status = VIO_OK; } for_less( c, 0, VIO_N_DIMENSIONS ) { volume->spatial_axes[c] = c; volume->direction_cosines[c][VIO_X] = 0.0; volume->direction_cosines[c][VIO_Y] = 0.0; volume->direction_cosines[c][VIO_Z] = 0.0; volume->direction_cosines[c][c] = 1.0; } if( status == VIO_OK && (volume_input->axis_index_from_file[VIO_X] == volume_input->axis_index_from_file[VIO_Y] || volume_input->axis_index_from_file[VIO_X] == volume_input->axis_index_from_file[VIO_Z] || volume_input->axis_index_from_file[VIO_Y] == volume_input->axis_index_from_file[VIO_Z]) ) { print_error( "Two axis indices are equal.\n" ); status = VIO_ERROR; } volume_input->directory = extract_directory( filename ); if( status == VIO_OK ) { if( volume_input->sizes_in_file[0] <= 0 ) { n_slices = 0; while( input_string( file, &slice_filename, (char) ' ' )==VIO_OK) { SET_ARRAY_SIZE( volume_input->slice_filenames, n_slices, n_slices+1, DEFAULT_CHUNK_SIZE ); volume_input->slice_filenames[n_slices] = slice_filename; SET_ARRAY_SIZE( volume_input->slice_byte_offsets, n_slices, n_slices+1, DEFAULT_CHUNK_SIZE ); if( input_int( file, &volume_input->slice_byte_offsets[n_slices] ) != VIO_OK ) { volume_input->slice_byte_offsets[n_slices] = 0; } ++n_slices; } volume_input->sizes_in_file[0] = (long) n_slices; volume_input->one_file_per_slice = TRUE; } else { volume_input->one_file_per_slice = FALSE; status = input_string( file, &volume_filename, (char) ' ' ); abs_volume_filename = get_absolute_filename( volume_filename, volume_input->directory ); delete_string( volume_filename ); if( input_int( file, &volume_byte_offset ) != VIO_OK ) volume_byte_offset = 0; } } if( status == VIO_OK ) status = close_file( file ); /* record the information in the volume struct */ if( status == VIO_OK ) { for_less( axis, 0, VIO_N_DIMENSIONS ) { sizes[volume_input->axis_index_from_file[axis]] = (int) volume_input->sizes_in_file[axis]; volume_separations[volume_input->axis_index_from_file[axis]] = file_separations[axis]; if( volume_separations[axis] < 0.0 ) { trans[axis] += -volume_separations[axis] * (VIO_Real) (sizes[axis]-1); } } set_volume_separations( volume, volume_separations ); set_volume_starts( volume, trans ); } set_volume_type( volume, desired_data_type, FALSE, 0.0, 0.0 ); set_volume_sizes( volume, sizes ); /* allocate the slice buffer */ n_voxels_in_slice = (int) volume_input->sizes_in_file[1] * (int) volume_input->sizes_in_file[2]; if( status == VIO_OK ) switch( volume_input->file_data_type ) { case VIO_UNSIGNED_BYTE: ALLOC( volume_input->byte_slice_buffer, n_voxels_in_slice ); break; case VIO_UNSIGNED_SHORT: ALLOC( volume_input->short_slice_buffer, n_voxels_in_slice ); break; default: handle_internal_error( "initialize_free_format_input" ); break; } /* if the data must be converted to byte, read the entire image file simply to find the max and min values, and set the value_scale and value_translation */ if( status == VIO_OK && get_volume_data_type(volume) != volume_input->file_data_type ) { if( status == VIO_OK && !volume_input->one_file_per_slice ) { status = open_file( abs_volume_filename, READ_FILE, BINARY_FORMAT, &volume_input->volume_file ); if( status == VIO_OK ) status = set_file_position( file, (long) volume_byte_offset ); } min_value = 0; max_value = 0; volume_input->slice_index = 0; for_less( slice, 0, volume_input->sizes_in_file[0] ) { (void) input_slice( volume_input ); for_less( i, 0, n_voxels_in_slice ) { value = (int) volume_input->short_slice_buffer[i]; if( ( slice == 0 && i == 0 ) || value < min_value ) min_value = value; if( ( slice == 0 && i == 0 ) || value > max_value ) max_value = value; } } set_volume_voxel_range( volume, (VIO_Real) min_value, (VIO_Real) max_value ); if( status == VIO_OK && !volume_input->one_file_per_slice ) status = close_file( volume_input->volume_file ); } if( status == VIO_OK && !volume_input->one_file_per_slice ) { status = open_file( abs_volume_filename, READ_FILE, BINARY_FORMAT, &volume_input->volume_file ); delete_string( abs_volume_filename ); if( status == VIO_OK ) status = set_file_position( file, (long) volume_byte_offset ); } volume_input->slice_index = 0; delete_string( abs_volume_filename ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_free_format_input @INPUT : volume_input @OUTPUT : @RETURNS : @DESCRIPTION: Frees the slice buffer and closes the image file. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_free_format_input( volume_input_struct *volume_input ) { long slice; if( volume_input->file_data_type == VIO_UNSIGNED_BYTE ) { FREE( volume_input->byte_slice_buffer ); } else { FREE( volume_input->short_slice_buffer ); } delete_string( volume_input->directory ); if( volume_input->one_file_per_slice ) { for_less( slice, 0, volume_input->sizes_in_file[0] ) delete_string( volume_input->slice_filenames[slice] ); FREE( volume_input->slice_filenames ); FREE( volume_input->slice_byte_offsets ); } else { (void) close_file( volume_input->volume_file ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_slice @INPUT : volume volume_input @OUTPUT : @RETURNS : VIO_OK if success @DESCRIPTION: Reads the next slice from the volume. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status input_slice( volume_input_struct *volume_input ) { VIO_Status status; FILE *file; VIO_STR slice_filename; status = VIO_OK; if( (long) volume_input->slice_index < volume_input->sizes_in_file[0] ) { if( volume_input->one_file_per_slice ) { slice_filename = get_absolute_filename( volume_input->slice_filenames[volume_input->slice_index], volume_input->directory ); status = open_file( slice_filename, READ_FILE, BINARY_FORMAT, &file ); delete_string( slice_filename ); if( status == VIO_OK ) status = set_file_position( file, (long) (volume_input->slice_byte_offsets [volume_input->slice_index]) ); } else file = volume_input->volume_file; if( status == VIO_OK ) switch( volume_input->file_data_type ) { case VIO_UNSIGNED_BYTE: status = io_binary_data( file, READ_FILE, (void *) volume_input->byte_slice_buffer, sizeof(volume_input->byte_slice_buffer[0]), (int) volume_input->sizes_in_file[1] * (int) volume_input->sizes_in_file[2] ); break; case VIO_UNSIGNED_SHORT: status = io_binary_data( file, READ_FILE, (void *) volume_input->short_slice_buffer, sizeof(volume_input->short_slice_buffer[0]), (int) volume_input->sizes_in_file[1] * (int) volume_input->sizes_in_file[2] ); break; default: handle_internal_error( "input_slice" ); break; } if( status == VIO_OK && volume_input->one_file_per_slice ) { status = close_file( file ); } ++volume_input->slice_index; } else status = VIO_ERROR; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_more_free_format_file @INPUT : volume volume_input @OUTPUT : fraction_done @RETURNS : TRUE if more to input after this call @DESCRIPTION: Reads in one more slice from the image file. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL input_more_free_format_file( VIO_Volume volume, volume_input_struct *volume_input, VIO_Real *fraction_done ) { VIO_Real min_value, max_value, value; int x, y, z, sizes[VIO_MAX_DIMENSIONS]; long i; VIO_Status status; VIO_BOOL more_to_do, scaling_flag; VIO_Real value_translation, value_scale; VIO_Real original_min_voxel, original_max_voxel; int *inner_index, indices[VIO_MAX_DIMENSIONS]; unsigned char *byte_buffer_ptr; unsigned short *short_buffer_ptr; if( (long) volume_input->slice_index < volume_input->sizes_in_file[0] ) { if( !volume_is_alloced( volume ) ) { alloc_volume_data( volume ); if( !volume_is_alloced( volume ) ) return( FALSE ); } status = input_slice( volume_input ); scaling_flag = FALSE; if( get_volume_data_type(volume) != volume_input->file_data_type ) { scaling_flag = TRUE; get_volume_voxel_range( volume, &original_min_voxel, &original_max_voxel ); value_translation = original_min_voxel; value_scale = (original_max_voxel - original_min_voxel) / (VIO_Real) (NUM_BYTE_VALUES - 1); } inner_index = &indices[volume_input->axis_index_from_file[2]]; indices[volume_input->axis_index_from_file[0]] = volume_input->slice_index-1; if( status == VIO_OK ) switch( volume_input->file_data_type ) { case VIO_UNSIGNED_BYTE: byte_buffer_ptr = volume_input->byte_slice_buffer; for_less( i, 0, volume_input->sizes_in_file[1] ) { indices[volume_input->axis_index_from_file[1]] = (int) i; for_less( *inner_index, 0, (int) volume_input->sizes_in_file[2] ) { if( scaling_flag ) { value = ((VIO_Real) (*byte_buffer_ptr) - value_translation)/ value_scale; if( value < 0.0 ) value = 0.0; else if( value > 255.0 ) value = 255.0; } else value = (VIO_Real) (*byte_buffer_ptr); set_volume_voxel_value( volume, indices[VIO_X], indices[VIO_Y], indices[VIO_Z], 0, 0, value ); ++byte_buffer_ptr; } } break; case VIO_UNSIGNED_SHORT: short_buffer_ptr = volume_input->short_slice_buffer; for_less( i, 0, volume_input->sizes_in_file[1] ) { indices[volume_input->axis_index_from_file[1]] = (int) i; for_less( *inner_index, 0, (int) volume_input->sizes_in_file[2]) { if( scaling_flag ) { value = ((VIO_Real) (*short_buffer_ptr) - value_translation) / value_scale; } else value = (VIO_Real) (*short_buffer_ptr); set_volume_voxel_value( volume, indices[VIO_X], indices[VIO_Y], indices[VIO_Z], 0, 0, value ); ++short_buffer_ptr; } } break; default: handle_internal_error( "input_more_free_format_file" ); break; } } get_volume_sizes( volume, sizes ); *fraction_done = (VIO_Real) volume_input->slice_index / (VIO_Real) sizes[volume_input->axis_index_from_file[0]]; more_to_do = TRUE; if( volume_input->slice_index == sizes[volume_input->axis_index_from_file[0]] ) { more_to_do = FALSE; min_value = get_volume_voxel_value( volume, 0, 0, 0, 0, 0 ); max_value = min_value; for_less( x, 0, sizes[VIO_X] ) { for_less( y, 0, sizes[VIO_Y] ) { for_less( z, 0, sizes[VIO_Z] ) { value = get_volume_voxel_value( volume, x, y, z, 0,0); if( value < min_value ) min_value = value; else if( value > max_value ) max_value = value; } } } set_volume_voxel_range( volume, min_value, max_value ); if( get_volume_data_type(volume) != volume_input->file_data_type ) { set_volume_real_range( volume, original_min_voxel, original_max_voxel ); } } return( more_to_do ); } libminc-libminc-2-3-00/volume_io/Volumes/input_mgh.c000066400000000000000000000513321257462267400224330ustar00rootroot00000000000000/** * \file Reader for MGH/MGZ (FreeSurfer) format files. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "input_mgh.h" #include /* for ntohl and ntohs */ #include "znzlib.h" #include "errno.h" #define NUM_BYTE_VALUES (UCHAR_MAX + 1) #define MGH_MAX_DIMS 4 /* Maximum number of dimensions */ #define MGH_N_SPATIAL VIO_N_DIMENSIONS /* Number of spatial dimensions */ #define MGH_N_COMPONENTS 4 /* Number of transform components. */ #define MGH_N_XFORM (MGH_N_COMPONENTS * MGH_N_SPATIAL) #define MGH_HEADER_SIZE 284 /* Total number of bytes in the header. */ #define MGH_EXTRA_SIZE 194 /* Number of "unused" bytes in the header. */ #define MGH_TYPE_UCHAR 0 /**< Voxels are 1-byte unsigned integers. */ #define MGH_TYPE_INT 1 /**< Voxels are 4-byte signed integers. */ #define MGH_TYPE_LONG 2 /**< Unsupported here. */ #define MGH_TYPE_FLOAT 3 /**< Voxels are 4-byte floating point. */ #define MGH_TYPE_SHORT 4 /**< Voxels are 2-byte signed integers. */ #define MGH_TYPE_BITMAP 5 /**< Unsupported here. */ #define MGH_TYPE_TENSOR 6 /**< Unsupported here. */ /* MGH tag types, at least the ones that are minimally documented. */ #define TAG_OLD_COLORTABLE 1 #define TAG_OLD_USEREALRAS 2 #define TAG_CMDLINE 3 #define TAG_USEREALRAS 4 #define TAG_COLORTABLE 5 #define TAG_GCAMORPH_GEOM 10 #define TAG_GCAMORPH_TYPE 11 #define TAG_GCAMORPH_LABELS 12 #define TAG_OLD_SURF_GEOM 20 #define TAG_SURF_GEOM 21 #define TAG_OLD_MGH_XFORM 30 #define TAG_MGH_XFORM 31 #define TAG_GROUP_AVG_SURFACE_AREA 32 /** * Information in the MGH/MGZ file header. */ struct mgh_header { int version; /**< Must be 0x00000001. */ int sizes[MGH_MAX_DIMS]; /**< Dimension sizes, fastest-varying FIRST. */ int type; /**< One of the MGH_TYPE_xxx values. */ int dof; /**< Degrees of freedom, if used. */ short goodRASflag; /**< True if spacing and dircos valid. */ float spacing[MGH_N_SPATIAL]; /**< Dimension spacing. */ float dircos[MGH_N_COMPONENTS][MGH_N_SPATIAL]; /**< Dimension transform. */ }; /** * Trailer information found immediately AFTER the hyperslab of data. */ struct mgh_trailer { float TR; float FlipAngle; float TE; float TI; float FoV; }; /** * Trivial function to swap floats if necessary. * \param f The big-endian 4-byte float value. * \return The float value in host byte order. */ static float swapFloat(float f) { union { float f; int i; } sl; sl.f = f; sl.i = ntohl(sl.i); return sl.f; } /** * Reads the next slice from the MGH volume. As a side effect, it advances * the slice_index value in the volume input structure if successful. * * \param in_ptr The volume input information. * \return VIO_OK if success */ static VIO_Status input_next_slice( volume_input_struct *in_ptr ) { size_t n_voxels_in_slice; size_t n_bytes_per_voxel; znzFile fp = (znzFile) in_ptr->volume_file; if (in_ptr->slice_index >= in_ptr->sizes_in_file[2]) { fprintf(stderr, "Read past final slice.\n"); return VIO_ERROR; } n_bytes_per_voxel = get_type_size(in_ptr->file_data_type); n_voxels_in_slice = (in_ptr->sizes_in_file[0] * in_ptr->sizes_in_file[1]); if (znzread(in_ptr->generic_slice_buffer, n_bytes_per_voxel, n_voxels_in_slice, fp) != n_voxels_in_slice) { fprintf(stderr, "read error %d\n", errno); return VIO_ERROR; } ++in_ptr->slice_index; return VIO_OK; } /** * Converts a MGH file header into a general linear transform for the * Volume IO library. There are two different ways of defining the " "centre" of the volume in the MGH world. One uses the values in * c_r, c_a, and c_s (the last row of the dircos field) to offset * the origin. The other, more common case ignores these fields and * just uses the voxel size and spacing to determine a value for * the centre. * Note that the geometric structures produced by MGH tools use * the latter case. * * \param hdr_ptr A pointer to the MGH header structure. * \param in_ptr A pointer to the input information for this volume. * \param ignore_offsets TRUE if we should use grid centres. * \param linear_xform_ptr A pointer to the output transform */ static void mgh_header_to_linear_transform(const struct mgh_header *hdr_ptr, const volume_input_struct *in_ptr, VIO_BOOL ignore_offsets, struct VIO_General_transform *linear_xform_ptr) { int i, j; VIO_Transform mnc_xform; VIO_Real mgh_xform[MGH_N_SPATIAL][MGH_N_COMPONENTS]; make_identity_transform(&mnc_xform); #if DEBUG /* Print out the raw MGH transform information. */ for (i = 0; i < MGH_N_SPATIAL; i++) { for (j = 0; j < MGH_N_COMPONENTS; j++) { printf("%c_%c %8.4f ", "xyzc"[j], "ras"[i], hdr_ptr->dircos[j][i]); } printf("\n"); } #endif // DEBUG /* Multiply the direction cosines by the spacings. */ for (i = 0; i < MGH_N_SPATIAL; i++) { for (j = 0; j < MGH_N_SPATIAL; j++) { mgh_xform[i][j] = hdr_ptr->dircos[j][i] * hdr_ptr->spacing[i]; } } /* Work out the final MGH transform. This requires that we figure out * the origin values to fill in the final column of the transform. */ for (i = 0; i < MGH_N_SPATIAL; i++) { double temp = 0.0; for (j = 0; j < MGH_N_SPATIAL; j++) { temp += hdr_ptr->dircos[j][i] * (hdr_ptr->sizes[j] / 2.0); } /* Set the origin for the voxel-to-world transform . */ if (ignore_offsets) { mgh_xform[i][MGH_N_COMPONENTS - 1] = -temp; } else { mgh_xform[i][MGH_N_COMPONENTS - 1] = hdr_ptr->dircos[MGH_N_COMPONENTS - 1][i] - temp; } } #if DEBUG printf("mgh_xform:\n"); /* DEBUG */ for (i = 0; i < MGH_N_SPATIAL; i++) { for (j = 0; j < MGH_N_COMPONENTS; j++) { printf("%.4f ", mgh_xform[i][j]); } printf("\n"); } #endif // DEBUG /* Convert MGH transform to the MINC format. The only difference is * that our transform is always written in XYZ (RAS) order, so we * have to swap the _columns_ as needed. */ for (i = 0; i < MGH_N_SPATIAL; i++) { for (j = 0; j < MGH_N_COMPONENTS; j++) { int volume_axis = j; if (j < VIO_N_DIMENSIONS) { volume_axis = in_ptr->axis_index_from_file[j]; } Transform_elem(mnc_xform, i, volume_axis) = mgh_xform[i][j]; } } create_linear_transform(linear_xform_ptr, &mnc_xform); #if DEBUG output_transform(stdout, "debug", NULL, NULL, linear_xform_ptr); #endif // DEBUG } /** * Read an MGH header from an open file stream. * \param fp The open file (may be a pipe). * \param hdr_ptr A pointer to the header structure to fill in. * \returns TRUE if successful. */ static VIO_BOOL mgh_header_from_file(znzFile fp, struct mgh_header *hdr_ptr) { int i, j; char dummy[MGH_HEADER_SIZE]; /* Read in the header. We do this piecemeal because the mgh_header * struct will not be properly aligned on most systems, so a single * fread() WILL NOT WORK. */ if (znzread(&hdr_ptr->version, sizeof(int), 1, fp) != 1 || znzread(hdr_ptr->sizes, sizeof(int), MGH_MAX_DIMS, fp) != MGH_MAX_DIMS || znzread(&hdr_ptr->type, sizeof(int), 1, fp) != 1 || znzread(&hdr_ptr->dof, sizeof(int), 1, fp) != 1 || znzread(&hdr_ptr->goodRASflag, sizeof(short), 1, fp) != 1 || /* The rest of the fields are optional, but we can safely read them * now and check goodRASflag later to see if we should really trust * them. */ znzread(hdr_ptr->spacing, sizeof(float), MGH_N_SPATIAL, fp) != MGH_N_SPATIAL || znzread(hdr_ptr->dircos, sizeof(float), MGH_N_XFORM, fp) != MGH_N_XFORM || znzread(dummy, 1, MGH_EXTRA_SIZE, fp) != MGH_EXTRA_SIZE) { print_error("Problem reading MGH file header."); return FALSE; } /* Successfully read all of the data. Now we have to convert it to the * machine's byte ordering. */ hdr_ptr->version = ntohl(hdr_ptr->version); for (i = 0; i < MGH_MAX_DIMS; i++) { hdr_ptr->sizes[i] = ntohl(hdr_ptr->sizes[i]); } hdr_ptr->type = ntohl(hdr_ptr->type); hdr_ptr->dof = ntohl(hdr_ptr->dof); hdr_ptr->goodRASflag = ntohs(hdr_ptr->goodRASflag); if (hdr_ptr->version != 1) { print_error("Must be MGH version 1.\n"); return FALSE; } if (hdr_ptr->goodRASflag) { for (i = 0; i < MGH_N_SPATIAL; i++) { hdr_ptr->spacing[i] = swapFloat(hdr_ptr->spacing[i]); for (j = 0; j < MGH_N_COMPONENTS; j++) { hdr_ptr->dircos[j][i] = swapFloat(hdr_ptr->dircos[j][i]); } } } else { /* Flag is zero, so just use the defaults. */ for (i = 0; i < MGH_N_SPATIAL; i++) { /* Default spacing is 1.0. */ hdr_ptr->spacing[i] = 1.0; /* Assume coronal orientation. */ for (j = 0; j < MGH_N_COMPONENTS; j++) { hdr_ptr->dircos[j][i] = 0.0; } hdr_ptr->dircos[0][0] = -1.0; hdr_ptr->dircos[1][2] = -1.0; hdr_ptr->dircos[2][1] = 1.0; } } return TRUE; } static VIO_BOOL mgh_scan_for_voxel_range(volume_input_struct *in_ptr, long n_voxels_in_slice, float *min_value_ptr, float *max_value_ptr) { znzFile fp = (znzFile) in_ptr->volume_file; float min_value = FLT_MAX; float max_value = -FLT_MAX; int slice; float value = 0; int i; void *data_ptr; long int data_offset = znztell((znzFile) fp); if (data_offset < 0) return FALSE; for (slice = 0; slice < in_ptr->sizes_in_file[2]; slice++) { input_next_slice( in_ptr ); data_ptr = in_ptr->generic_slice_buffer; for (i = 0; i < n_voxels_in_slice; i++) { switch (in_ptr->file_data_type) { case VIO_UNSIGNED_BYTE: value = ((unsigned char *)data_ptr)[i]; break; case VIO_SIGNED_SHORT: value = ntohs(((short *)data_ptr)[i]); break; case VIO_SIGNED_INT: value = ntohl(((int *)data_ptr)[i]); break; case VIO_FLOAT: value = swapFloat(((float *)data_ptr)[i]); break; case VIO_NO_DATA_TYPE: case VIO_SIGNED_BYTE: case VIO_UNSIGNED_SHORT: case VIO_UNSIGNED_INT: case VIO_DOUBLE: case VIO_MAX_DATA_TYPE: break; } if (value < min_value ) min_value = value; if (value > max_value ) max_value = value; } } *min_value_ptr = min_value; *max_value_ptr = max_value; in_ptr->slice_index = 0; znzseek((znzFile) fp, data_offset, SEEK_SET); return TRUE; } VIOAPI VIO_Status initialize_mgh_format_input(VIO_STR filename, VIO_Volume volume, volume_input_struct *in_ptr) { int sizes[VIO_MAX_DIMENSIONS]; long n_voxels_in_slice; int n_bytes_per_voxel; nc_type desired_nc_type; znzFile fp; int axis; struct mgh_header hdr; VIO_General_transform mnc_native_xform; VIO_Real mnc_dircos[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS]; VIO_Real mnc_steps[VIO_MAX_DIMENSIONS]; VIO_Real mnc_starts[VIO_MAX_DIMENSIONS]; int n_dimensions; nc_type file_nc_type; VIO_BOOL signed_flag; if ((fp = znzopen(filename, "rb", 1)) == NULL) { print_error("Unable to open file %s, errno %d.\n", filename, errno); return VIO_ERROR; } if (!mgh_header_from_file(fp, &hdr)) { znzclose(fp); return VIO_ERROR; } /* Translate from MGH to VIO types. */ switch (hdr.type) { case MGH_TYPE_UCHAR: in_ptr->file_data_type = VIO_UNSIGNED_BYTE; file_nc_type = NC_BYTE; signed_flag = FALSE; break; case MGH_TYPE_INT: in_ptr->file_data_type = VIO_SIGNED_INT; file_nc_type = NC_INT; signed_flag = TRUE; break; case MGH_TYPE_FLOAT: in_ptr->file_data_type = VIO_FLOAT; file_nc_type = NC_FLOAT; signed_flag = TRUE; break; case MGH_TYPE_SHORT: in_ptr->file_data_type = VIO_SIGNED_SHORT; file_nc_type = NC_SHORT; signed_flag = TRUE; break; default: print_error("Unknown MGH data type.\n"); znzclose(fp); return VIO_ERROR; } /* Decide how to store data in memory. */ if ( get_volume_data_type(volume) == VIO_NO_DATA_TYPE ) desired_nc_type = file_nc_type; else desired_nc_type = get_volume_nc_data_type(volume, &signed_flag); if( volume->spatial_axes[VIO_X] < 0 || volume->spatial_axes[VIO_Y] < 0 || volume->spatial_axes[VIO_Z] < 0 ) { print_error("warning: setting MGH spatial axes to XYZ.\n"); volume->spatial_axes[VIO_X] = 0; volume->spatial_axes[VIO_Y] = 1; volume->spatial_axes[VIO_Z] = 2; } /* Calculate the number of non-trivial dimensions in the file. */ n_dimensions = 0; for_less( axis, 0, MGH_MAX_DIMS ) { in_ptr->sizes_in_file[axis] = hdr.sizes[axis]; if (hdr.sizes[axis] > 1) { n_dimensions++; } } if (!set_volume_n_dimensions(volume, n_dimensions)) { printf("Problem setting number of dimensions to %d\n", n_dimensions); } /* Set up the correspondence between the file axes and the MINC * spatial axes. Each row contains the 'x', 'y', and 'z' components * along the right/left, anterior/posterior, or superior/inferior * axes (RAS). The "xspace" axis will be the one that has the largest * component in the RL direction, "yspace" refers to AP, and "zspace" * to SI. This tells us both how to convert the transform and how the * file data is arranged. */ for_less( axis, 0, MGH_N_SPATIAL ) { int spatial_axis = VIO_X; float c_x = fabsf(hdr.dircos[axis][VIO_X]); float c_y = fabsf(hdr.dircos[axis][VIO_Y]); float c_z = fabsf(hdr.dircos[axis][VIO_Z]); if (c_y > c_x && c_y > c_z) { spatial_axis = VIO_Y; } if (c_z > c_x && c_z > c_y) { spatial_axis = VIO_Z; } in_ptr->axis_index_from_file[axis] = spatial_axis; } mgh_header_to_linear_transform(&hdr, in_ptr, TRUE, &mnc_native_xform); convert_transform_to_starts_and_steps(&mnc_native_xform, VIO_N_DIMENSIONS, NULL, volume->spatial_axes, mnc_starts, mnc_steps, mnc_dircos); delete_general_transform(&mnc_native_xform); for_less( axis, 0, VIO_N_DIMENSIONS) { int volume_axis = volume->spatial_axes[axis]; int file_axis = in_ptr->axis_index_from_file[volume_axis]; sizes[file_axis] = in_ptr->sizes_in_file[volume_axis]; set_volume_direction_cosine(volume, volume_axis, mnc_dircos[volume_axis]); } #if DEBUG for_less( axis, 0, VIO_N_DIMENSIONS) { int volume_axis = volume->spatial_axes[axis]; printf("%d %d size:%4d step:%6.3f start:%9.4f dc:[%7.4f %7.4f %7.4f]\n", axis, in_ptr->axis_index_from_file[volume_axis], sizes[volume_axis], mnc_steps[volume_axis], mnc_starts[volume_axis], mnc_dircos[volume_axis][0], mnc_dircos[volume_axis][1], mnc_dircos[volume_axis][2]); } #endif // DEBUG set_volume_separations( volume, mnc_steps ); set_volume_starts( volume, mnc_starts ); /* If we are a 4D image, we need to copy the size here. */ sizes[3] = in_ptr->sizes_in_file[3]; set_volume_type( volume, desired_nc_type, signed_flag, 0.0, 0.0 ); set_volume_sizes( volume, sizes ); n_bytes_per_voxel = get_type_size( in_ptr->file_data_type ); n_voxels_in_slice = (in_ptr->sizes_in_file[0] * in_ptr->sizes_in_file[1]); in_ptr->min_value = FLT_MAX; in_ptr->max_value = -FLT_MAX; /* Allocate the slice buffer. */ in_ptr->generic_slice_buffer = malloc(n_voxels_in_slice * n_bytes_per_voxel); if (in_ptr->generic_slice_buffer == NULL) { return VIO_ERROR; } in_ptr->volume_file = (FILE *) fp; in_ptr->slice_index = 0; /* If the data must be converted to byte, read the entire image file simply * to find the max and min values. This allows us to set the value_scale and * value_translation properly when we read the file. */ if (get_volume_data_type(volume) != in_ptr->file_data_type ) { float min_value, max_value; mgh_scan_for_voxel_range(in_ptr, n_voxels_in_slice, &min_value, &max_value); set_volume_voxel_range(volume, min_value, max_value); } return VIO_OK; } VIOAPI void delete_mgh_format_input( volume_input_struct *in_ptr ) { znzFile fp = (znzFile) in_ptr->volume_file; free( in_ptr->generic_slice_buffer ); znzclose( fp ); } VIOAPI VIO_BOOL input_more_mgh_format_file( VIO_Volume volume, volume_input_struct *in_ptr, VIO_Real *fraction_done ) { int i; VIO_Real value = 0; VIO_Status status; VIO_Real value_translation, value_scale; VIO_Real original_min_voxel, original_max_voxel; int *inner_index, indices[VIO_MAX_DIMENSIONS]; void *data_ptr; int data_ind; if ( in_ptr->slice_index < in_ptr->sizes_in_file[2] ) { /* If the memory for the volume has not been allocated yet, * initialize that memory now. */ if (!volume_is_alloced(volume)) { alloc_volume_data(volume); if (!volume_is_alloced(volume)) { print_error("Failed to allocate volume.\n"); return FALSE; } } status = input_next_slice( in_ptr ); /* See if we need to apply scaling to this slice. This is only * needed if the volume voxel type is not the same as the file * voxel type. THIS IS ONLY REALLY LEGAL FOR BYTE VOLUME TYPES. */ if (get_volume_data_type(volume) != in_ptr->file_data_type) { get_volume_voxel_range(volume, &original_min_voxel, &original_max_voxel); value_translation = original_min_voxel; value_scale = (original_max_voxel - original_min_voxel) / (VIO_Real) (NUM_BYTE_VALUES - 1); } else { /* Just do the trivial scaling. */ value_translation = 0.0; value_scale = 1.0; } /* Set up the indices. */ inner_index = &indices[in_ptr->axis_index_from_file[0]]; indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index - 1; if ( status == VIO_OK ) { data_ptr = in_ptr->generic_slice_buffer; data_ind = 0; for_less( i, 0, in_ptr->sizes_in_file[1] ) { indices[in_ptr->axis_index_from_file[1]] = i; for_less( *inner_index, 0, in_ptr->sizes_in_file[0] ) { switch ( in_ptr->file_data_type ) { case VIO_UNSIGNED_BYTE: value = ((unsigned char *)data_ptr)[data_ind++]; break; case VIO_SIGNED_SHORT: value = ntohs(((short *)data_ptr)[data_ind++]); break; case VIO_SIGNED_INT: value = ntohl(((int *)data_ptr)[data_ind++]); break; case VIO_FLOAT: value = swapFloat(((float *)data_ptr)[data_ind++]); break; default: handle_internal_error( "input_more_mgh_format_file" ); break; } value = (value - value_translation) / value_scale; if (value > in_ptr->max_value) { in_ptr->max_value = value; } if (value < in_ptr->min_value) { in_ptr->min_value = value; } set_volume_voxel_value( volume, indices[VIO_X], indices[VIO_Y], indices[VIO_Z], 0, 0, value); } } } } *fraction_done = (VIO_Real) in_ptr->slice_index / in_ptr->sizes_in_file[2]; /* See if we are all done. If so, we need to perform a final check * of the volume to set the ranges appropriately. */ if (in_ptr->slice_index == in_ptr->sizes_in_file[2]) { set_volume_voxel_range( volume, in_ptr->min_value, in_ptr->max_value ); /* Make sure we scale the data up to the original real range, * if appropriate. */ if (get_volume_data_type(volume) != in_ptr->file_data_type) { set_volume_real_range(volume, original_min_voxel, original_max_voxel); } return FALSE; } else { return TRUE; /* More work to do. */ } } libminc-libminc-2-3-00/volume_io/Volumes/input_mgh.h000066400000000000000000000030351257462267400224350ustar00rootroot00000000000000/** * \file Reader for MGH/MGZ (FreeSurfer) format files. */ #include #include #include /** * Initializes loading a MGH format file by reading the header. * This function assumes that volume->filename has been assigned. * * \param filename The filename to open for input. * \param volume The volume that will ultimately hold the input data. * \param in_ptr State information for the current input operation. * \return VIO_OK if successful. */ VIOAPI VIO_Status initialize_mgh_format_input(VIO_STR filename, VIO_Volume volume, volume_input_struct *in_ptr); /** * Dispose of the resources used to read an MGH file. * \param in_ptr The volume_input_struct that is to be deleted. */ VIOAPI void delete_mgh_format_input( volume_input_struct *in_ptr ); /** * Read the next slice of an MGH (MGZ) format file. * \param volume The volume associated with this input operation. * \param in_ptr State information for the current input operation. * \param fraction_done A number from 0 to 1 indicating the fraction * of the operation that has completed after this call returns. * \return TRUE if successful. */ VIOAPI VIO_BOOL input_more_mgh_format_file( VIO_Volume volume, volume_input_struct *in_ptr, VIO_Real *fraction_done ); libminc-libminc-2-3-00/volume_io/Volumes/input_mnc.c000066400000000000000000001462361257462267400224450ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_MINC1 #include #define INVALID_AXIS -1 static VIO_BOOL match_dimension_names( int n_volume_dims, VIO_STR volume_dimension_names[], int n_file_dims, VIO_STR file_dimension_names[], int to_volume_index[] ); /* ----------------------------- MNI Header ----------------------------------- @NAME : get_minc_file_n_dimensions @INPUT : filename @OUTPUT : @RETURNS : n_dims @DESCRIPTION: Returns the number of dimensions in the minc file. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 4, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_minc_file_n_dimensions( VIO_STR filename ) { int cdfid, img_var, n_dims; int dim_vars[MAX_VAR_DIMS]; nc_type file_datatype; VIO_STR expanded; ncopts = NC_VERBOSE; expanded = expand_filename( filename ); cdfid = miopen( expanded, NC_NOWRITE ); if( cdfid == MI_ERROR ) { print_error( "Error opening %s\n", expanded ); delete_string( expanded ); return( -1 ); } delete_string( expanded ); img_var = ncvarid( cdfid, MIimage ); ncvarinq( cdfid, img_var, (char *) NULL, &file_datatype, &n_dims, dim_vars, (int *) NULL ); (void) miclose( cdfid ); return( n_dims ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc_input_from_minc_id @INPUT : minc_id volume options @OUTPUT : @RETURNS : Minc_file @DESCRIPTION: Initializes input of volumes from an already opened MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - added handling of space type @MODIFIED : May 20, 1997 D. MacDonald - removed float arithmetic in transformations and made volumes store starts/steps/dircos ---------------------------------------------------------------------------- */ VIOAPI Minc_file initialize_minc_input_from_minc_id( int minc_id, VIO_Volume volume, minc_input_options *options ) { minc_file_struct *file; int dim_vars[MAX_VAR_DIMS], n_vol_dims; int i, slab_size, prev_sizes[MAX_VAR_DIMS]; nc_type prev_nc_type; VIO_BOOL different; VIO_BOOL range_specified; double valid_range[2]; long long_size; VIO_BOOL converted_sign, space_type_consensus; nc_type converted_type; char signed_flag[MI_MAX_ATTSTR_LEN+1], *ptr; char dim_name[MI_MAX_ATTSTR_LEN+1]; char prev_space_type[MI_MAX_ATTSTR_LEN+1]; char space_type[MI_MAX_ATTSTR_LEN+1]; nc_type file_datatype; int sizes[MAX_VAR_DIMS]; double file_separations[MAX_VAR_DIMS]; VIO_Real volume_separations[MAX_VAR_DIMS]; VIO_Real volume_starts[MAX_VAR_DIMS]; VIO_Real voxel_zero; double start_position[MAX_VAR_DIMS]; double dir_cosines[MAX_VAR_DIMS][MI_NUM_SPACE_DIMS]; double tmp_cosines[MI_NUM_SPACE_DIMS]; VIO_BOOL spatial_dim_flags[MAX_VAR_DIMS]; double real_min, real_max; int d, dimvar, which_valid_axis, axis; int spatial_axis_indices[MAX_VAR_DIMS]; minc_input_options default_options; VIO_BOOL no_volume_data_type; char spacing_type[MI_MAX_ATTSTR_LEN+1]; double *irr_starts[MAX_VAR_DIMS]; double *irr_widths[MAX_VAR_DIMS]; ALLOC( file, 1 ); file->cdfid = minc_id; file->file_is_being_read = TRUE; file->volume = volume; if( options == (minc_input_options *) NULL ) { set_default_minc_input_options( &default_options ); set_default_minc_input_options( &file->original_input_options ); options = &default_options; } else file->original_input_options = *options; get_volume_sizes( volume, prev_sizes ); prev_nc_type = volume->nc_data_type; /* --- find the image variable */ file->img_var = ncvarid( file->cdfid, MIimage ); ncvarinq( file->cdfid, file->img_var, (char *) NULL, &file_datatype, &file->n_file_dimensions, dim_vars, (int *) NULL ); /* Set the number of dimensions iff the file has fewer dimensions * than the initially created volume. */ if (get_volume_n_dimensions( volume ) > file->n_file_dimensions) { set_volume_n_dimensions( volume, file->n_file_dimensions ); } for_less( d, 0, file->n_file_dimensions ) { (void) ncdiminq( file->cdfid, dim_vars[d], dim_name, &long_size ); file->dim_names[d] = create_string( dim_name ); file->sizes_in_file[d] = long_size; } file->converting_to_colour = FALSE; if( equal_strings( file->dim_names[file->n_file_dimensions-1], MIvector_dimension ) ) { if( options->convert_vector_to_colour_flag && file->sizes_in_file[file->n_file_dimensions-1] >= (long) options->dimension_size_for_colour_data && file->sizes_in_file[file->n_file_dimensions-1] <= (long) options->max_dimension_size_for_colour_data ) { for_less( i, 0, 4 ) { file->rgba_indices[i] = options->rgba_indices[i]; if( (long) options->rgba_indices[i] >= file->sizes_in_file[file->n_file_dimensions-1] ) { file->rgba_indices[i] = -1; if( i != 3 ) print_error( "Warning: rgba indices out of range.\n" ); } } set_volume_type( volume, NC_INT, FALSE, 0.0, 0.0 ); set_rgb_volume_flag( volume, TRUE ); file->converting_to_colour = TRUE; delete_string( file->dim_names[file->n_file_dimensions-1] ); --file->n_file_dimensions; } else if( options->convert_vector_to_scalar_flag ) { delete_string( file->dim_names[file->n_file_dimensions-1] ); --file->n_file_dimensions; } } n_vol_dims = get_volume_n_dimensions( volume ); if( file->n_file_dimensions < n_vol_dims ) { print_error( "Error: MINC file has only %d dims, volume requires %d.\n", file->n_file_dimensions, n_vol_dims ); FREE( file ); return( (Minc_file) 0 ); } else if( file->n_file_dimensions > MAX_VAR_DIMS ) { print_error( "Error: MINC file has %d dims, can only handle %d.\n", file->n_file_dimensions, MAX_VAR_DIMS ); FREE( file ); return( (Minc_file) NULL ); } /* --- match the dimension names of the volume with those in the file */ if( !match_dimension_names( get_volume_n_dimensions(volume), volume->dimension_names, file->n_file_dimensions, file->dim_names, file->to_volume_index ) ) { print_error( "Error: dimension names did not match: \n" ); print_error( "\n" ); print_error( "Requested:\n" ); for_less( d, 0, n_vol_dims ) print_error( "%d: %s\n", d+1, volume->dimension_names[d] ); print_error( "\n" ); print_error( "In File:\n" ); for_less( d, 0, file->n_file_dimensions ) print_error( "%d: %s\n", d+1, file->dim_names[d] ); FREE( file ); return( (Minc_file) NULL ); } for_less( d, 0, n_vol_dims ) file->to_file_index[d] = INVALID_AXIS; for_less( d, 0, file->n_file_dimensions ) { if( file->to_volume_index[d] != INVALID_AXIS ) file->to_file_index[file->to_volume_index[d]] = d; } file->n_volumes_in_file = 1; /* --- find the spatial axes (x,y,z) */ which_valid_axis = 0; for_less( d, 0, VIO_N_DIMENSIONS ) { volume->spatial_axes[d] = INVALID_AXIS; file->spatial_axes[d] = INVALID_AXIS; } for_less( d, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[d], &axis ) ) { spatial_axis_indices[d] = axis; file->spatial_axes[axis] = d; } else spatial_axis_indices[d] = INVALID_AXIS; spatial_dim_flags[d] = (spatial_axis_indices[d] != INVALID_AXIS); if( file->to_volume_index[d] != INVALID_AXIS ) { file->valid_file_axes[which_valid_axis] = d; if( spatial_dim_flags[d] ) { volume->spatial_axes[spatial_axis_indices[d]] = file->to_volume_index[d]; } ++which_valid_axis; } } /* --- get the spatial axis info, slice separation, start pos, etc. */ prev_space_type[0] = (char) 0; space_type_consensus = TRUE; for_less( d, 0, file->n_file_dimensions ) { file_separations[d] = 1.0; start_position[d] = 0.0; irr_starts[d] = NULL; irr_widths[d] = NULL; if( spatial_dim_flags[d] ) { dir_cosines[d][0] = 0.0; dir_cosines[d][1] = 0.0; dir_cosines[d][2] = 0.0; dir_cosines[d][spatial_axis_indices[d]] = 1.0; } dimvar = ncvarid( file->cdfid, file->dim_names[d] ); if( dimvar != MI_ERROR ) { (void) miattget1( file->cdfid, dimvar, MIstep, NC_DOUBLE, (void *) (&file_separations[d]) ); if( spatial_dim_flags[d] ) { if( miattget1( file->cdfid, dimvar, MIstart, NC_DOUBLE, (void *) (&start_position[d]) ) == MI_ERROR ) start_position[d] = 0.0; if( miattget( file->cdfid, dimvar, MIdirection_cosines, NC_DOUBLE, MI_NUM_SPACE_DIMS, (void *) tmp_cosines, (int *) NULL ) != MI_ERROR ) { dir_cosines[d][0] = tmp_cosines[0]; dir_cosines[d][1] = tmp_cosines[1]; dir_cosines[d][2] = tmp_cosines[2]; } ptr = miattgetstr( file->cdfid, dimvar, MIspacetype, MI_MAX_ATTSTR_LEN+1, space_type ); if( ptr != NULL ) { if( string_length( prev_space_type ) > 0 && string_length( space_type ) > 0 && !equal_strings( prev_space_type, space_type ) ) { space_type_consensus = FALSE; } if( string_length( space_type ) > 0 ) (void) strcpy( prev_space_type, space_type ); } } else if (!strcmp(MItime, file->dim_names[d])) { /* For the moment this is implemented for time dimensions * only. */ miattgetstr(file->cdfid, dimvar, MIspacing, MI_MAX_ATTSTR_LEN+1, spacing_type); if (!strcmp(spacing_type, MI_IRREGULAR)) { long start[1]; long count[1]; irr_starts[d] = malloc(sizeof(VIO_Real) * file->sizes_in_file[d]); irr_widths[d] = malloc(sizeof(VIO_Real) * file->sizes_in_file[d]); start[0] = 0; count[0] = file->sizes_in_file[d]; mivarget(file->cdfid, dimvar, start, count, NC_DOUBLE, MI_SIGNED, irr_starts[d]); dimvar = ncvarid(file->cdfid, MItime_width); if (dimvar < 0) { for (i = 0; i < count[0]; i++) { irr_widths[d][i] = file_separations[d]; } } else { mivarget(file->cdfid, dimvar, start, count, NC_DOUBLE, MI_SIGNED, irr_widths[d]); } } else { if( miattget1(file->cdfid, dimvar, MIstart, NC_DOUBLE, (void *) (&start_position[d]) ) == MI_ERROR ) start_position[d] = 0.0; } } } if( file->to_volume_index[d] == INVALID_AXIS ) { file->n_volumes_in_file *= (int) file->sizes_in_file[d]; } else { sizes[file->to_volume_index[d]] = (int) file->sizes_in_file[d]; volume_separations[file->to_volume_index[d]] = file_separations[d]; if( file->to_volume_index[d] != INVALID_AXIS ) { volume_starts[file->to_volume_index[d]] = start_position[d]; set_volume_direction_unit_cosine( volume, file->to_volume_index[d], dir_cosines[d] ); } } } /* --- create the world transform stored in the volume */ set_volume_separations( volume, volume_separations ); set_volume_starts( volume, volume_starts ); if( space_type_consensus ) set_volume_space_type( volume, prev_space_type ); /* --- create the file world transform */ compute_world_transform( file->spatial_axes, file_separations, dir_cosines, start_position, &file->voxel_to_world_transform ); /* --- decide on type conversion */ if( file->converting_to_colour ) { converted_type = NC_FLOAT; converted_sign = FALSE; } else { no_volume_data_type = (get_volume_data_type(volume) == VIO_NO_DATA_TYPE); if( no_volume_data_type ) /* --- use type of file */ { if( miattgetstr( file->cdfid, file->img_var, MIsigntype, MI_MAX_ATTSTR_LEN, signed_flag ) != NULL ) { converted_sign = equal_strings( signed_flag, MI_SIGNED ); } else converted_sign = file_datatype != NC_BYTE; converted_type = file_datatype; set_volume_type( volume, converted_type, converted_sign, 0.0, 0.0 ); } else /* --- use specified type */ { converted_type = get_volume_nc_data_type( volume, &converted_sign ); } } set_volume_sizes( volume, sizes ); for_less( d, 0, file->n_file_dimensions ) { if (file->to_volume_index[d] == INVALID_AXIS) { continue; } if (irr_starts[d] != NULL) { set_volume_irregular_starts(volume, file->to_volume_index[d], file->sizes_in_file[d], irr_starts[d]); FREE( irr_starts[d] ); } if (irr_widths[d] != NULL) { set_volume_irregular_widths(volume, file->to_volume_index[d], file->sizes_in_file[d], irr_widths[d]); FREE( irr_widths[d] ); } } /* --- create the image conversion variable */ file->minc_icv = miicv_create(); (void) miicv_setint( file->minc_icv, MI_ICV_TYPE, (int) converted_type ); (void) miicv_setstr( file->minc_icv, MI_ICV_SIGN, converted_sign ? MI_SIGNED : MI_UNSIGNED ); (void) miicv_setint( file->minc_icv, MI_ICV_DO_NORM, TRUE ); (void) miicv_setint( file->minc_icv, MI_ICV_DO_FILLVALUE, TRUE ); get_volume_voxel_range( volume, &valid_range[0], &valid_range[1] ); range_specified = (valid_range[0] < valid_range[1]); valid_range[0] = 0.0; valid_range[1] = 0.0; if( file->converting_to_colour ) { valid_range[0] = 0.0; valid_range[1] = 2.0 * (double) (1ul << 31ul) - 1.0; set_volume_voxel_range( volume, valid_range[0], valid_range[1] ); } else if( no_volume_data_type ) { if (miget_valid_range( file->cdfid, file->img_var, valid_range) == MI_ERROR) { valid_range[0] = valid_range[1] = 0.0; } } if( !file->converting_to_colour && (no_volume_data_type || !range_specified) ) { set_volume_voxel_range( volume, valid_range[0], valid_range[1] ); } if( !file->converting_to_colour ) { get_volume_voxel_range( volume, &valid_range[0], &valid_range[1] ); (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MIN, valid_range[0]); (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MAX, valid_range[1]); } else { (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MIN, 0.0 ); (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MAX, 1.0 ); } if (options->user_real_range[0] < options->user_real_range[1]) { (void) miicv_setint( file->minc_icv, MI_ICV_USER_NORM, TRUE ); (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MIN, options->user_real_range[0] ); (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MAX, options->user_real_range[1] ); } if( options->convert_vector_to_scalar_flag && !file->converting_to_colour ) { (void) miicv_setint( file->minc_icv, MI_ICV_DO_DIM_CONV, TRUE ); (void) miicv_setint( file->minc_icv, MI_ICV_DO_SCALAR, TRUE ); (void) miicv_setint( file->minc_icv, MI_ICV_XDIM_DIR, FALSE ); (void) miicv_setint( file->minc_icv, MI_ICV_YDIM_DIR, FALSE ); (void) miicv_setint( file->minc_icv, MI_ICV_ZDIM_DIR, FALSE ); (void) miicv_setint( file->minc_icv, MI_ICV_KEEP_ASPECT, FALSE ); } (void) miicv_attach( file->minc_icv, file->cdfid, file->img_var ); /* --- compute the mapping to real values */ (void) miicv_inqdbl( file->minc_icv, MI_ICV_NORM_MIN, &real_min ); (void) miicv_inqdbl( file->minc_icv, MI_ICV_NORM_MAX, &real_max ); if( !file->converting_to_colour ) set_volume_real_range( volume, real_min, real_max ); /* --- if promoting invalid values to zero, then we need to detach and reattach in order to change the fillvalue in the icv */ if( options->promote_invalid_to_zero_flag ) { (void) miicv_detach( file->minc_icv ); if( !file->converting_to_colour ) { if( real_min == real_max ) voxel_zero = valid_range[0]; else if( real_min > 0.0 ) voxel_zero = valid_range[0]; else if( real_max < 0.0 ) voxel_zero = valid_range[1]; else { voxel_zero = valid_range[0] + (valid_range[1] - valid_range[0]) * (0.0 - real_min) / (real_max - real_min); } (void) miicv_setdbl( file->minc_icv, MI_ICV_FILLVALUE, voxel_zero ); } else (void) miicv_setdbl( file->minc_icv, MI_ICV_FILLVALUE, 0.0 ); (void) miicv_attach( file->minc_icv, file->cdfid, file->img_var ); } for_less( d, 0, file->n_file_dimensions ) file->indices[d] = 0; file->end_volume_flag = FALSE; ncopts = NC_VERBOSE | NC_FATAL; /* --- decide how many full dimensions to read in at a time to max out the read/write buffer and make it like the chunking dimensions for compression */ file->n_slab_dims = 0; slab_size = 1; int unit_size = get_type_size( get_volume_data_type(volume) ); int full_dim = 1; for( d = file->n_file_dimensions-1; d >= 0; d-- ) { if( file->to_volume_index[d] != INVALID_AXIS ) { if( MI_MAX_VAR_BUFFER_SIZE > file->sizes_in_file[d] * slab_size * unit_size && full_dim ) { slab_size *= file->sizes_in_file[d]; file->n_slab_dims++; /* integral number of complete dimensions */ } else { slab_size *= MIN( file->sizes_in_file[d], (long)( MI_MAX_VAR_BUFFER_SIZE / ( slab_size * unit_size ) ) ); full_dim = 0; } } } /* --- decide whether the volume data must be freed (if it changed size) */ different = FALSE; for_less( d, 0, n_vol_dims ) { if( sizes[d] != prev_sizes[d] ) different = TRUE; } if( prev_nc_type != converted_type ) different = TRUE; if( different && volume_is_alloced( volume ) && !volume_is_cached(volume) ) free_volume_data( volume ); return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc_input @INPUT : filename volume @OUTPUT : @RETURNS : Minc_file @DESCRIPTION: Initializes the input of a MINC file, passing back a MINC file pointer. It assumes that the volume has been created, with the desired type, or MI_ORIGINAL_TYPE type if it is desired to use whatever type is in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI Minc_file initialize_minc_input( VIO_STR filename, VIO_Volume volume, minc_input_options *options ) { Minc_file file; int minc_id; VIO_STR expanded; ncopts = 0; expanded = expand_filename( filename ); minc_id = miopen( expanded, NC_NOWRITE ); if( minc_id == MI_ERROR ) { print_error( "Error: opening MINC file \"%s\".\n", expanded ); return( (Minc_file) 0 ); } file = initialize_minc_input_from_minc_id( minc_id, volume, options ); if( file == (Minc_file) NULL ) (void) miclose( minc_id ); else file->filename = create_string( expanded ); delete_string( expanded ); return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_n_input_volumes @INPUT : file @OUTPUT : @RETURNS : number of input volumes @DESCRIPTION: After initializing the file input with a specified volume, the user calls this function to decide how many volumes are stored in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_n_input_volumes( Minc_file file ) { return( file->n_volumes_in_file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_input @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Closes the minc input file. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status close_minc_input( Minc_file file ) { int d; if( file == (Minc_file) NULL ) { print_error( "close_minc_input(): NULL file.\n" ); return( VIO_ERROR ); } (void) miclose( file->cdfid ); (void) miicv_free( file->minc_icv ); for_less( d, 0, file->n_file_dimensions ) delete_string( file->dim_names[d] ); delete_string( file->filename ); delete_general_transform( &file->voxel_to_world_transform ); FREE( file ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_minc_hyperslab @INPUT : file data_type n_array_dims array_sizes array_data_ptr to_array start count @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs a hyperslab from the file into the array pointer. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_minc_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int start[], int count[] ) { VIO_Status status; int ind, expected_ind, file_ind, d, i, dim; int size0, size1, size2, size3, size4; int n_tmp_dims, n_file_dims; void *void_ptr; VIO_BOOL direct_to_array, non_full_size_found; int tmp_ind, tmp_sizes[MAX_VAR_DIMS]; int vol1_indices[VIO_MAX_DIMENSIONS]; int v[VIO_MAX_DIMENSIONS], voxel[VIO_MAX_DIMENSIONS]; long used_start[MAX_VAR_DIMS], used_count[MAX_VAR_DIMS]; VIO_Real rgb[4]; VIO_Colour colour; VIO_multidim_array buffer_array, rgb_array; n_file_dims = file->n_file_dimensions; direct_to_array = TRUE; expected_ind = n_array_dims-1; tmp_ind = n_file_dims-1; non_full_size_found = FALSE; for_less( ind, 0, n_array_dims ) vol1_indices[ind] = -1; /*--- check if the hyperslab is a continuous chunk of memory in the array */ for( file_ind = n_file_dims-1; file_ind >= 0; --file_ind ) { used_start[file_ind] = (long) start[file_ind]; used_count[file_ind] = (long) count[file_ind]; ind = to_array[file_ind]; if( ind != INVALID_AXIS ) { if( !non_full_size_found && (long) count[file_ind] < file->sizes_in_file[file_ind] ) non_full_size_found = TRUE; else if( non_full_size_found && count[file_ind] > 1 ) direct_to_array = FALSE; if( count[file_ind] > 1 && ind != expected_ind ) direct_to_array = FALSE; if( count[file_ind] != 1 || file->sizes_in_file[file_ind] == 1 ) { tmp_sizes[tmp_ind] = count[file_ind]; vol1_indices[tmp_ind] = ind; --tmp_ind; } --expected_ind; } } if( !direct_to_array || file->converting_to_colour ) { /*--- make a temporary buffer array, so that there is a continuous chunk */ n_tmp_dims = n_file_dims - tmp_ind - 1; for_less( dim, 0, n_tmp_dims ) { tmp_sizes[dim] = tmp_sizes[dim+tmp_ind+1]; vol1_indices[dim] = vol1_indices[dim+tmp_ind+1]; } create_multidim_array( &buffer_array, n_tmp_dims, tmp_sizes, data_type); if( file->converting_to_colour ) { used_start[n_file_dims] = 0; used_count[n_file_dims] = file->sizes_in_file[n_file_dims]; tmp_sizes[n_tmp_dims] = (int) used_count[n_file_dims]; create_multidim_array( &rgb_array, n_tmp_dims+1, tmp_sizes, VIO_FLOAT ); GET_MULTIDIM_PTR( void_ptr, rgb_array, 0, 0, 0, 0, 0 ); } else { GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); } } else { void_ptr = array_data_ptr; } if( miicv_get( file->minc_icv, used_start, used_count, void_ptr ) == MI_ERROR ) { status = VIO_ERROR; if( file->converting_to_colour ) delete_multidim_array( &rgb_array ); if( !direct_to_array || file->converting_to_colour ) delete_multidim_array( &buffer_array ); } else status = VIO_OK; if( status == VIO_OK && (!direct_to_array || file->converting_to_colour) ) { if( file->converting_to_colour ) { for_less( dim, n_tmp_dims, VIO_MAX_DIMENSIONS ) tmp_sizes[dim] = 1; size0 = tmp_sizes[0]; size1 = tmp_sizes[1]; size2 = tmp_sizes[2]; size3 = tmp_sizes[3]; size4 = tmp_sizes[4]; for_less( v[4], 0, size4 ) for_less( v[3], 0, size3 ) for_less( v[2], 0, size2 ) for_less( v[1], 0, size1 ) for_less( v[0], 0, size0 ) { for_less( d, 0, n_tmp_dims ) voxel[d] = v[d]; for_less( i, 0, 4 ) { if( file->rgba_indices[i] < 0 ) { if( i < 3 ) rgb[i] = 0.0; else rgb[i] = 1.0; } else { voxel[n_tmp_dims] = file->rgba_indices[i]; GET_MULTIDIM( rgb[i], (VIO_Real), rgb_array, voxel[0], voxel[1], voxel[2], voxel[3], voxel[4] ); } } colour = make_rgba_Colour_0_1( rgb[0], rgb[1], rgb[2], rgb[3] ); SET_MULTIDIM( buffer_array, voxel[0], voxel[1], voxel[2], voxel[3], voxel[4], colour ); } delete_multidim_array( &rgb_array ); } GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); copy_multidim_data_reordered( get_type_size(data_type), array_data_ptr, n_array_dims, array_sizes, void_ptr, n_tmp_dims, tmp_sizes, tmp_sizes, vol1_indices, FALSE ); delete_multidim_array( &buffer_array ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_slab @INPUT : file volume start count @OUTPUT : @RETURNS : @DESCRIPTION: Inputs a multidimensional slab from the file and copies it into the appropriate part of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void input_slab( Minc_file file, VIO_Volume volume, int to_volume[], long start[], long count[] ) { int file_ind, ind; int volume_start[MAX_VAR_DIMS]; int file_start[VIO_MAX_DIMENSIONS]; int file_count[VIO_MAX_DIMENSIONS]; int array_sizes[VIO_MAX_DIMENSIONS]; void *array_data_ptr; for_less( file_ind, 0, file->n_file_dimensions ) { file_start[file_ind] = (int) start[file_ind]; file_count[file_ind] = (int) count[file_ind]; ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) volume_start[ind] = file_start[file_ind]; } get_multidim_sizes( &volume->array, array_sizes ); GET_MULTIDIM_PTR( array_data_ptr, volume->array, volume_start[0], volume_start[1], volume_start[2], volume_start[3], volume_start[4] ); (void) input_minc_hyperslab( file, get_multidim_data_type(&volume->array), get_multidim_n_dimensions(&volume->array), array_sizes, array_data_ptr, to_volume, file_start, file_count ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_more_minc_file @INPUT : file @OUTPUT : fraction_done - amount of file read @RETURNS : TRUE if volume has more left to read @DESCRIPTION: Reads another chunk from the input file, passes back the total fraction read so far, and returns FALSE when the whole volume has been read. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL input_more_minc_file( Minc_file file, VIO_Real *fraction_done ) { int d, ind, n_done, total, n_slab; long count[MAX_VAR_DIMS]; VIO_Volume volume; VIO_BOOL increment; if( file->end_volume_flag ) { print_error( "End of file in input_more_minc_file()\n" ); return( FALSE ); } volume = file->volume; if( !volume_is_alloced( volume ) ) { alloc_volume_data( volume ); if( volume->is_cached_volume ) { open_cache_volume_input_file( &volume->cache, volume, file->filename, &file->original_input_options ); } if( !volume_is_alloced( volume ) ) return( FALSE ); } if( volume->is_cached_volume ) { *fraction_done = 1.0; file->end_volume_flag = TRUE; } else { /* --- set the counts for reading, actually these will be the same every time */ for_less( ind, 0, file->n_file_dimensions ) count[ind] = 1; n_slab = 0; for( d = file->n_file_dimensions-1; d >= 0 && n_slab < file->n_slab_dims; --d ) { if( file->to_volume_index[d] != INVALID_AXIS ) { count[d] = file->sizes_in_file[d]; ++n_slab; } } input_slab( file, volume, file->to_volume_index, file->indices, count ); /* --- advance to next slab */ increment = TRUE; n_slab = 0; total = 1; n_done = 0; for( d = file->n_file_dimensions-1; d >= 0; --d ) { if( n_slab >= file->n_slab_dims && file->to_volume_index[d] != INVALID_AXIS ) { if( increment ) { ++file->indices[d]; if( file->indices[d] < file->sizes_in_file[d] ) increment = FALSE; else file->indices[d] = 0; } n_done += total * (int) file->indices[d]; total *= (int) file->sizes_in_file[d]; } if( file->to_volume_index[d] != INVALID_AXIS ) ++n_slab; } if( increment ) { *fraction_done = 1.0; file->end_volume_flag = TRUE; } else { *fraction_done = (VIO_Real) n_done / (VIO_Real) total; } } return( !file->end_volume_flag ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : advance_input_volume @INPUT : file @OUTPUT : @RETURNS : TRUE if more volumes to read @DESCRIPTION: Advances the file indices to prepare for reading the next volume from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL advance_input_volume( Minc_file file ) { int ind, c, axis; VIO_Real voxel[VIO_MAX_DIMENSIONS], world_space[VIO_N_DIMENSIONS]; VIO_Real vol_world_space[VIO_N_DIMENSIONS]; VIO_Transform offset; VIO_General_transform offset_transform, new_transform; ind = file->n_file_dimensions-1; while( ind >= 0 ) { if( file->to_volume_index[ind] == INVALID_AXIS ) { ++file->indices[ind]; if( file->indices[ind] < file->sizes_in_file[ind] ) break; file->indices[ind] = 0; } --ind; } if( ind >= 0 ) { file->end_volume_flag = FALSE; for_less( ind, 0, get_volume_n_dimensions( file->volume ) ) file->indices[file->valid_file_axes[ind]] = 0; /*--- update the volume's voxel-to-world transform */ for_less( c, 0, VIO_N_DIMENSIONS ) { axis = file->spatial_axes[c]; if( axis != INVALID_AXIS ) voxel[c] = (VIO_Real) file->indices[axis]; else voxel[c] = 0.0; } general_transform_point( &file->voxel_to_world_transform, voxel[0], voxel[1], voxel[2], &world_space[VIO_X], &world_space[VIO_Y], &world_space[VIO_Z]); for_less( c, 0, get_volume_n_dimensions(file->volume) ) voxel[c] = 0.0; convert_voxel_to_world( file->volume, voxel, &vol_world_space[VIO_X], &vol_world_space[VIO_Y], &vol_world_space[VIO_Z]); make_identity_transform( &offset ); for_less( c, 0, VIO_N_DIMENSIONS ) Transform_elem(offset,c,3) = world_space[c] - vol_world_space[c]; create_linear_transform( &offset_transform, &offset ); concat_general_transforms( get_voxel_to_world_transform(file->volume), &offset_transform, &new_transform ); set_voxel_to_world_transform( file->volume, &new_transform ); delete_general_transform( &offset_transform ); /*--- update the volume if it is cached */ if( file->volume->is_cached_volume ) set_cache_volume_file_offset( &file->volume->cache, file->volume, file->indices ); } else file->end_volume_flag = TRUE; return( file->end_volume_flag ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : reset_input_volume @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Rewinds the file indices to start inputting volumes from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void reset_input_volume( Minc_file file ) { int d; for_less( d, 0, file->n_file_dimensions ) file->indices[d] = 0; file->end_volume_flag = FALSE; set_cache_volume_file_offset( &file->volume->cache, file->volume, file->indices ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : match_dimension_names @INPUT : n_volume_dims volume_dimension_names n_file_dims file_dimension_names @OUTPUT : to_volume_index @RETURNS : TRUE if match found @DESCRIPTION: Attempts to match all the volume dimensions with the file dimensions. This is done in 3 passes. In the first pass, exact matches are found. In the second pass, volume dimensions of "any_spatial_dimension" are matched. On the final pass, volume dimension names which are empty strings are matched to any remaining file dimensions. If a dimension matches on "any_spatial_dimension" or empty string, then the name from the file is copied to the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 22, 1995 D. MacDonald - copies the name from the file to the volume ---------------------------------------------------------------------------- */ static VIO_BOOL match_dimension_names( int n_volume_dims, VIO_STR volume_dimension_names[], int n_file_dims, VIO_STR file_dimension_names[], int to_volume_index[] ) { int i, j, iteration, n_matches, dummy; int to_file_index[VIO_MAX_DIMENSIONS]; VIO_BOOL match; VIO_BOOL volume_dim_found[VIO_MAX_DIMENSIONS]; n_matches = 0; for_less( i, 0, n_file_dims ) to_volume_index[i] = INVALID_AXIS; for_less( i, 0, n_volume_dims ) { volume_dim_found[i] = FALSE; to_file_index[i] = -1; } for_less( iteration, 0, 3 ) { for( i = n_volume_dims-1; i >= 0; --i ) { if( !volume_dim_found[i] ) { for( j = n_file_dims-1; j >= 0; --j ) { if( to_volume_index[j] == INVALID_AXIS ) { switch( iteration ) { case 0: match = equal_strings( volume_dimension_names[i], file_dimension_names[j] ); break; case 1: match = equal_strings( volume_dimension_names[i], ANY_SPATIAL_DIMENSION ) && convert_dim_name_to_spatial_axis( file_dimension_names[j], &dummy ); break; case 2: match = string_length(volume_dimension_names[i]) == 0; break; } if( match ) { to_volume_index[j] = i; to_file_index[i] = j; volume_dim_found[i] = TRUE; ++n_matches; break; } } } } } } if( n_matches == n_volume_dims ) { for_less( i, 0, n_volume_dims ) { if( equal_strings( volume_dimension_names[i], ANY_SPATIAL_DIMENSION ) || string_length(volume_dimension_names[i]) == 0 ) { replace_string( &volume_dimension_names[i], create_string( file_dimension_names[to_file_index[i]] ) ); } } } return( n_matches == n_volume_dims ); } #endif /*HAVE_MINC1*/ /* ----------------------------- MNI Header ----------------------------------- @NAME : get_minc_file_id @INPUT : file @OUTPUT : @RETURNS : minc file id @DESCRIPTION: Returns the minc file id to allow user to perform MINC calls on this file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_minc_file_id( Minc_file file ) { return( file->cdfid ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_default_minc_input_options @INPUT : @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the default minc input options. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_default_minc_input_options( minc_input_options *options ) { static int default_rgba_indices[4] = { 0, 1, 2, 3 }; set_minc_input_promote_invalid_to_zero_flag( options, TRUE ); set_minc_input_vector_to_scalar_flag( options, TRUE ); set_minc_input_vector_to_colour_flag( options, FALSE ); set_minc_input_colour_dimension_size( options, 3 ); set_minc_input_colour_max_dimension_size( options, 4 ); set_minc_input_colour_indices( options, default_rgba_indices ); set_minc_input_user_real_range(options, 0.0, 0.0); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_promote_invalid_to_zero_flag @INPUT : flag @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the invalid promotion flag of the input options. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 25, 1996 D. MacDonald - changed to promote to 0, used to be min_valid ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_promote_invalid_to_zero_flag( minc_input_options *options, VIO_BOOL flag ) { options->promote_invalid_to_zero_flag = flag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_promote_invalid_to_zero_flag @INPUT : flag @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the invalid promotion flag of the input options. Maintained for functional interface backward compatibility. Programmers should now be calling set_minc_input_promote_invalid_to_zero_flag. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 25, 1996 D. MacDonald - replaced with above function ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_promote_invalid_to_min_flag( minc_input_options *options, VIO_BOOL flag ) { set_minc_input_promote_invalid_to_zero_flag( options, flag ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_vector_to_scalar_flag @INPUT : flag @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the vector conversion flag of the input options. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_vector_to_scalar_flag( minc_input_options *options, VIO_BOOL flag ) { options->convert_vector_to_scalar_flag = flag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_vector_to_colour_flag @INPUT : flag @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the colour conversion flag of the input options. Any volume with a vector dimension of length 3 will be converted to a 32 bit colour. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_vector_to_colour_flag( minc_input_options *options, VIO_BOOL flag ) { options->convert_vector_to_colour_flag = flag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_colour_dimension_size @INPUT : size @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the required number of vector components in a file that contains colour data. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_colour_dimension_size( minc_input_options *options, int size ) { if( size > 0 ) options->dimension_size_for_colour_data = size; else { print_error( "Warning: set_minc_input_colour_dimension_size:\n" ); print_error( " illegal size: %d\n", size ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_colour_max_dimension_size @INPUT : size @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the maximum number of vector components in a file that contains colour data. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_colour_max_dimension_size( minc_input_options *options, int size ) { if( size > 0 ) options->max_dimension_size_for_colour_data = size; else { print_error( "Warning: set_minc_input_colour_max_dimension_size:\n" ); print_error( " illegal size: %d\n", size ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_colour_indices @INPUT : indices @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the indices of the red, green, blue, and alpha in files that contain colours as the vector dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_colour_indices( minc_input_options *options, int indices[4] ) { int i; for_less( i, 0, 4 ) options->rgba_indices[i] = indices[i]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_input_user_real_range @INPUT : minimum, maximum - real range for scaling of input @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the user-defined real range for scaling input when the Volume type is integer. The max must be greater than the min for this option to take effect. Setting this will force the Volume to have a particular real range, rather than using the full range of the input file. @METHOD : @GLOBALS : @CALLS : @CREATED : 2001 Peter Neelin @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_input_user_real_range( minc_input_options *options, double minimum, double maximum ) { options->user_real_range[0] = minimum; options->user_real_range[1] = maximum; } libminc-libminc-2-3-00/volume_io/Volumes/input_mnc2.c000066400000000000000000001131611257462267400225160ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_MINC2 #include #define INVALID_AXIS -1 static VIO_BOOL match_dimension_names( int n_volume_dims, VIO_STR volume_dimension_names[], int n_file_dims, VIO_STR file_dimension_names[], int to_volume_index[] ); /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc_input_from_minc2_id @INPUT : minc_id volume options @OUTPUT : @RETURNS : Minc_file @DESCRIPTION: Initializes input of volumes from an already opened MINC2 file. @METHOD : @GLOBALS : @CALLS : ---------------------------------------------------------------------------- */ static Minc_file initialize_minc_input_from_minc2_id( mihandle_t minc_id, VIO_Volume volume, minc_input_options *options ) { minc_file_struct *file; int n_vol_dims; int i, slab_size, prev_sizes[MAX_VAR_DIMS]; mitype_t prev_minc_type; VIO_BOOL different; VIO_BOOL range_specified; double valid_range[2]; VIO_BOOL space_type_consensus; mitype_t converted_minc_type; char *dim_name; char prev_space_type[MI_MAX_ATTSTR_LEN+1]; mitype_t file_datatype; int sizes[MAX_VAR_DIMS]; double file_separations[MAX_VAR_DIMS]; VIO_Real volume_separations[MAX_VAR_DIMS]; VIO_Real volume_starts[MAX_VAR_DIMS]; double start_position[MAX_VAR_DIMS]; double dir_cosines[MAX_VAR_DIMS][MI_NUM_SPACE_DIMS]; double tmp_cosines[MI_NUM_SPACE_DIMS]; VIO_BOOL spatial_dim_flags[MAX_VAR_DIMS]; midimhandle_t file_dims[MAX_VAR_DIMS]; misize_t dimension_size[MAX_VAR_DIMS]; double file_step[MAX_VAR_DIMS]; double file_start[MAX_VAR_DIMS]; int d, which_valid_axis, axis; int spatial_axis_indices[MAX_VAR_DIMS]; minc_input_options default_options; VIO_BOOL no_volume_data_type = TRUE; double *irr_starts[MAX_VAR_DIMS]; double *irr_widths[MAX_VAR_DIMS]; double volume_min=0.0,volume_max=0.0; double valid_min=0.0,valid_max=0.0; miboolean_t slice_scaling_flag=0; miboolean_t global_scaling_flag=0; ALLOC( file, 1 ); file->cdfid = 0; file->minc2id=minc_id; file->file_is_being_read = TRUE; file->volume = volume; if( options == (minc_input_options *) NULL ) { set_default_minc_input_options( &default_options ); set_default_minc_input_options( &file->original_input_options ); options = &default_options; } else file->original_input_options = *options; get_volume_sizes( volume, prev_sizes ); prev_minc_type = get_volume_minc2_data_type(volume); miget_data_type(file->minc2id, &file_datatype); miget_volume_valid_range(file->minc2id,&valid_max,&valid_min); miget_slice_scaling_flag(file->minc2id, &slice_scaling_flag); if( !slice_scaling_flag ) { miget_volume_range(file->minc2id,&volume_max,&volume_min); global_scaling_flag=!(volume_min == valid_min && volume_max == valid_max); } /*Some logic to determine what we are dealing with*/ if(slice_scaling_flag || global_scaling_flag ) { /*force reading in float*/ if(file_datatype!=MI_TYPE_FLOAT && file_datatype!=MI_TYPE_DOUBLE) file_datatype=MI_TYPE_FLOAT; } miget_volume_dimension_count(file->minc2id, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &file->n_file_dimensions); /* Set the number of dimensions iff the file has fewer dimensions * than the initially created volume. */ if (get_volume_n_dimensions( volume ) > file->n_file_dimensions ) { set_volume_n_dimensions( volume, file->n_file_dimensions ); } miget_volume_dimensions(file->minc2id, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, MI_DIMORDER_FILE, file->n_file_dimensions, file_dims); miget_dimension_sizes(file_dims, file->n_file_dimensions, dimension_size); miget_dimension_separations(file_dims, MI_ORDER_FILE, file->n_file_dimensions, file_step); miget_dimension_starts(file_dims, MI_ORDER_FILE, file->n_file_dimensions, file_start); for_less( d, 0, file->n_file_dimensions ) { miget_dimension_name(file_dims[d], &dim_name); file->dim_names[d] = create_string( dim_name ); free(dim_name); file->sizes_in_file[d] = dimension_size[d]; } /*calculate global volume range, to satisfy volume_io*/ if(slice_scaling_flag) { int n_slice_dimensions=file->n_file_dimensions; int slices_count=1; misize_t *slice_start; if(miget_slice_dimension_count(file->minc2id, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &n_slice_dimensions)<0) print_error("Can't get slice dimensions!\n"); if(n_slice_dimensions==file->n_file_dimensions) { slice_scaling_flag=0; /*figure out what to do?*/ } n_slice_dimensions=file->n_file_dimensions-n_slice_dimensions; /*now iterate throught all slices to find out global image intensity range*/ for_less(d,0,n_slice_dimensions) { slices_count*=dimension_size[d]; } slice_start=(misize_t *)calloc(n_slice_dimensions,sizeof(misize_t)); miget_slice_range(file->minc2id,slice_start,n_slice_dimensions,&volume_max,&volume_min); do { double slice_min,slice_max; d=0; /*iteration trough dimensions*/ while(d=dimension_size[d]) { slice_start[d]=0; d++; } else break; } if(d==n_slice_dimensions) break;/*all dimensions are finished*/ miget_slice_range(file->minc2id,slice_start,n_slice_dimensions,&slice_max,&slice_min); if(volume_maxslice_min) volume_min=slice_min; } while(1); free(slice_start); } file->converting_to_colour = FALSE; if( equal_strings( file->dim_names[file->n_file_dimensions-1], MIvector_dimension ) ) { if( options->convert_vector_to_colour_flag && file->sizes_in_file[file->n_file_dimensions-1] >= (long) options->dimension_size_for_colour_data && file->sizes_in_file[file->n_file_dimensions-1] <= (long) options->max_dimension_size_for_colour_data ) { for_less( i, 0, 4 ) { file->rgba_indices[i] = options->rgba_indices[i]; if( (long) options->rgba_indices[i] >= file->sizes_in_file[file->n_file_dimensions-1] ) { file->rgba_indices[i] = -1; if( i != 3 ) print_error( "Warning: rgba indices out of range.\n" ); } } set_volume_type( volume, NC_INT, FALSE, 0.0, 0.0 ); set_rgb_volume_flag( volume, TRUE ); file->converting_to_colour = TRUE; delete_string( file->dim_names[file->n_file_dimensions-1] ); --file->n_file_dimensions; } else if( options->convert_vector_to_scalar_flag ) { delete_string( file->dim_names[file->n_file_dimensions-1] ); --file->n_file_dimensions; } } n_vol_dims = get_volume_n_dimensions( volume ); if( file->n_file_dimensions < n_vol_dims ) { print_error( "Error: MINC file has only %d dims, volume requires %d.\n", file->n_file_dimensions, n_vol_dims ); FREE( file ); return( (Minc_file) 0 ); } else if( file->n_file_dimensions > MAX_VAR_DIMS ) { print_error( "Error: MINC file has %d dims, can only handle %d.\n", file->n_file_dimensions, MAX_VAR_DIMS ); FREE( file ); return( (Minc_file) NULL ); } /* --- match the dimension names of the volume with those in the file */ if( !match_dimension_names( get_volume_n_dimensions(volume), volume->dimension_names, file->n_file_dimensions, file->dim_names, file->to_volume_index ) ) { print_error( "Error: dimension names did not match: \n" ); print_error( "\n" ); print_error( "Requested:\n" ); for_less( d, 0, n_vol_dims ) print_error( "%d: %s\n", d+1, volume->dimension_names[d] ); print_error( "\n" ); print_error( "In File:\n" ); for_less( d, 0, file->n_file_dimensions ) print_error( "%d: %s\n", d+1, file->dim_names[d] ); FREE( file ); return( (Minc_file) NULL ); } for_less( d, 0, n_vol_dims ) file->to_file_index[d] = INVALID_AXIS; for_less( d, 0, file->n_file_dimensions ) { if( file->to_volume_index[d] != INVALID_AXIS ) file->to_file_index[file->to_volume_index[d]] = d; } file->n_volumes_in_file = 1; /* --- find the spatial axes (x,y,z) */ which_valid_axis = 0; for_less( d, 0, VIO_N_DIMENSIONS ) { volume->spatial_axes[d] = INVALID_AXIS; file->spatial_axes[d] = INVALID_AXIS; } for_less( d, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[d], &axis ) ) { spatial_axis_indices[d] = axis; file->spatial_axes[axis] = d; } else spatial_axis_indices[d] = INVALID_AXIS; spatial_dim_flags[d] = (spatial_axis_indices[d] != INVALID_AXIS); if( file->to_volume_index[d] != INVALID_AXIS ) { file->valid_file_axes[which_valid_axis] = d; if( spatial_dim_flags[d] ) { volume->spatial_axes[spatial_axis_indices[d]] = file->to_volume_index[d]; } ++which_valid_axis; } } /* --- get the spatial axis info, slice separation, start pos, etc. */ prev_space_type[0] = (char) 0; space_type_consensus = TRUE; for_less( d, 0, file->n_file_dimensions ) { file_separations[d] = 1.0; start_position[d] = 0.0; irr_starts[d] = NULL; irr_widths[d] = NULL; if( spatial_dim_flags[d] ) { dir_cosines[d][0] = 0.0; dir_cosines[d][1] = 0.0; dir_cosines[d][2] = 0.0; dir_cosines[d][spatial_axis_indices[d]] = 1.0; } file_separations[d]=file_step[d]; if( spatial_dim_flags[d] ) { start_position[d]=file_start[d]; miget_dimension_cosines(file_dims[d],tmp_cosines); dir_cosines[d][0] = tmp_cosines[0]; dir_cosines[d][1] = tmp_cosines[1]; dir_cosines[d][2] = tmp_cosines[2]; } else if (!strcmp(MItime, file->dim_names[d])) { miboolean_t sampling_flag; /* For the moment this is implemented for time dimensions * only. */ miget_dimension_sampling_flag(file_dims[d],&sampling_flag); if (!sampling_flag) { int j; irr_starts[d] = malloc(sizeof(VIO_Real) * file->sizes_in_file[d]); irr_widths[d] = malloc(sizeof(VIO_Real) * file->sizes_in_file[d]); miget_dimension_widths(file_dims[d],MI_ORDER_FILE,(misize_t)file->sizes_in_file[d],0,irr_widths[d]); /*TODO: figure out how to do it in MINC2 API, right now it is not obvious*/ irr_starts[d][0] = file_start[d]; for (j = 1; j < file->sizes_in_file[d]; j++) { irr_starts[d][j] = irr_starts[d][j-1]+irr_widths[d][j-1]; } } else { start_position[d]=file_start[d]; } } if( file->to_volume_index[d] == INVALID_AXIS ) { file->n_volumes_in_file *= (int) file->sizes_in_file[d]; } else { sizes[file->to_volume_index[d]] = (int) file->sizes_in_file[d]; volume_separations[file->to_volume_index[d]] = file_separations[d]; if( file->to_volume_index[d] != INVALID_AXIS ) { volume_starts[file->to_volume_index[d]] = start_position[d]; set_volume_direction_unit_cosine( volume, file->to_volume_index[d], dir_cosines[d] ); } } } /* --- create the world transform stored in the volume */ set_volume_separations( volume, volume_separations ); set_volume_starts( volume, volume_starts ); if( space_type_consensus ) set_volume_space_type( volume, prev_space_type ); /* --- create the file world transform */ compute_world_transform( file->spatial_axes, file_separations, dir_cosines, start_position, &file->voxel_to_world_transform ); /* --- decide on type conversion */ if( file->converting_to_colour ) { converted_minc_type = MI_TYPE_FLOAT ; } else { no_volume_data_type = (get_volume_data_type(volume) == VIO_NO_DATA_TYPE); if( no_volume_data_type ) /* --- use type of file */ { set_volume_type2( volume, file_datatype, 0.0, 0.0 ); } converted_minc_type = get_volume_minc2_data_type( volume ); } set_volume_sizes( volume, sizes ); for_less( d, 0, file->n_file_dimensions ) { if (file->to_volume_index[d] == INVALID_AXIS) { continue; } if (irr_starts[d] != NULL) { set_volume_irregular_starts(volume, file->to_volume_index[d], file->sizes_in_file[d], irr_starts[d]); FREE( irr_starts[d] ); } if (irr_widths[d] != NULL) { set_volume_irregular_widths(volume, file->to_volume_index[d], file->sizes_in_file[d], irr_widths[d]); FREE( irr_widths[d] ); } } /* --- create the image conversion variable */ get_volume_voxel_range( volume, &valid_range[0], &valid_range[1] ); range_specified = (valid_range[0] < valid_range[1]); valid_range[0] = 0.0; valid_range[1] = 0.0; if( file->converting_to_colour ) { valid_range[0] = 0.0; valid_range[1] = 2.0 * (double) (1ul << 31ul) - 1.0; set_volume_voxel_range( volume, valid_range[0], valid_range[1] ); } else if( no_volume_data_type ) { valid_range[0]=valid_min; valid_range[1]=valid_max; } if( !file->converting_to_colour && (no_volume_data_type || !range_specified) ) { set_volume_voxel_range( volume, valid_range[0], valid_range[1] ); } if( !file->converting_to_colour ) set_volume_real_range( volume, volume_min, volume_max ); for_less( d, 0, file->n_file_dimensions ) file->indices[d] = 0; file->end_volume_flag = FALSE; /* --- decide how many full dimensions to read in at a time to max out the read/write buffer and make it like the chunking dimensions for compression */ file->n_slab_dims = 0; slab_size = 1; for( d = file->n_file_dimensions-1; d >= 0; d-- ) { if( file->to_volume_index[d] != INVALID_AXIS ) { slab_size *= file->sizes_in_file[d]; file->n_slab_dims++; /* integral number of complete dimensions */ } } /* --- decide whether the volume data must be freed (if it changed size) */ different = FALSE; for_less( d, 0, n_vol_dims ) { if( sizes[d] != prev_sizes[d] ) different = TRUE; } if( prev_minc_type != converted_minc_type ) different = TRUE; if( different && volume_is_alloced( volume ) ) free_volume_data( volume ); return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc2_input @INPUT : filename volume @OUTPUT : @RETURNS : Minc_file @DESCRIPTION: Initializes the input of a MINC file, passing back a MINC file pointer. It assumes that the volume has been created, with the desired type, or MI_ORIGINAL_TYPE type if it is desired to use whatever type is in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI Minc_file initialize_minc2_input( VIO_STR filename, VIO_Volume volume, minc_input_options *options ) { Minc_file file; VIO_STR expanded; mihandle_t minc_id; expanded = expand_filename( filename ); if ( miopen_volume(expanded, MI2_OPEN_READ, &minc_id) < 0 ) { // Error opening the volume print_error( "Error: opening MINC file \"%s\".\n", expanded ); return( (Minc_file) 0 ); } file = initialize_minc_input_from_minc2_id( minc_id, volume, options ); if( file == (Minc_file) NULL ) miclose_volume( minc_id ); else file->filename = create_string( expanded ); delete_string( expanded ); return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_input @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Closes the minc input file. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status close_minc2_input( Minc_file file ) { int d; if( file == (Minc_file) NULL ) { print_error( "close_minc_input(): NULL file.\n" ); return( VIO_ERROR ); } miclose_volume( file->minc2id ); for_less( d, 0, file->n_file_dimensions ) delete_string( file->dim_names[d] ); delete_string( file->filename ); delete_general_transform( &file->voxel_to_world_transform ); FREE( file ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_minc_hyperslab @INPUT : file data_type n_array_dims array_sizes array_data_ptr to_array start count @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Inputs a hyperslab from the file into the array pointer. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status input_minc2_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int start[], int count[] ) { VIO_Status status; int ind, expected_ind, file_ind, d, i, dim; int size0, size1, size2, size3, size4; int n_tmp_dims, n_file_dims; void *void_ptr; VIO_BOOL direct_to_array, non_full_size_found; int tmp_ind, tmp_sizes[MAX_VAR_DIMS]; int vol1_indices[VIO_MAX_DIMENSIONS]; int v[VIO_MAX_DIMENSIONS], voxel[VIO_MAX_DIMENSIONS]; misize_t used_start[MAX_VAR_DIMS], used_count[MAX_VAR_DIMS]; VIO_Real rgb[4]; VIO_Colour colour; VIO_multidim_array buffer_array, rgb_array; n_tmp_dims = file->n_file_dimensions; n_file_dims = file->n_file_dimensions; direct_to_array = TRUE; expected_ind = n_array_dims-1; tmp_ind = n_file_dims-1; non_full_size_found = FALSE; for_less( ind, 0, n_array_dims ) vol1_indices[ind] = -1; /*--- check if the hyperslab is a continuous chunk of memory in the array */ for( file_ind = n_file_dims-1; file_ind >= 0; --file_ind ) { used_start[file_ind] = (misize_t) start[file_ind]; used_count[file_ind] = (misize_t) count[file_ind]; ind = to_array[file_ind]; if( ind != INVALID_AXIS ) { if( !non_full_size_found && (long) count[file_ind] < file->sizes_in_file[file_ind] ) non_full_size_found = TRUE; else if( non_full_size_found && count[file_ind] > 1 ) direct_to_array = FALSE; if( count[file_ind] > 1 && ind != expected_ind ) direct_to_array = FALSE; if( count[file_ind] != 1 || file->sizes_in_file[file_ind] == 1 ) { tmp_sizes[tmp_ind] = count[file_ind]; vol1_indices[tmp_ind] = ind; --tmp_ind; } --expected_ind; } } if( !direct_to_array || file->converting_to_colour ) { /*--- make a temporary buffer array, so that there is a continuous chunk */ n_tmp_dims = n_file_dims - tmp_ind - 1; for_less( dim, 0, n_tmp_dims ) { tmp_sizes[dim] = tmp_sizes[dim+tmp_ind+1]; vol1_indices[dim] = vol1_indices[dim+tmp_ind+1]; } create_multidim_array( &buffer_array, n_tmp_dims, tmp_sizes, data_type); if( file->converting_to_colour ) { used_start[n_file_dims] = 0; used_count[n_file_dims] = file->sizes_in_file[n_file_dims]; tmp_sizes[n_tmp_dims] = (int) used_count[n_file_dims]; create_multidim_array( &rgb_array, n_tmp_dims+1, tmp_sizes, VIO_FLOAT ); GET_MULTIDIM_PTR( void_ptr, rgb_array, 0, 0, 0, 0, 0 ); } else { GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); } } else { void_ptr = array_data_ptr; } if( miget_hyperslab_with_icv(file->minc2id, get_volume_minc2_data_type(file->volume), used_start,used_count,void_ptr) < 0 ) { status = VIO_ERROR; if( file->converting_to_colour ) delete_multidim_array( &rgb_array ); if( !direct_to_array || file->converting_to_colour ) delete_multidim_array( &buffer_array ); } else status = VIO_OK; if( status == VIO_OK && (!direct_to_array || file->converting_to_colour) ) { if( file->converting_to_colour ) { for_less( dim, n_tmp_dims, VIO_MAX_DIMENSIONS ) tmp_sizes[dim] = 1; size0 = tmp_sizes[0]; size1 = tmp_sizes[1]; size2 = tmp_sizes[2]; size3 = tmp_sizes[3]; size4 = tmp_sizes[4]; for_less( v[4], 0, size4 ) for_less( v[3], 0, size3 ) for_less( v[2], 0, size2 ) for_less( v[1], 0, size1 ) for_less( v[0], 0, size0 ) { for_less( d, 0, n_tmp_dims ) voxel[d] = v[d]; for_less( i, 0, 4 ) { if( file->rgba_indices[i] < 0 ) { if( i < 3 ) rgb[i] = 0.0; else rgb[i] = 1.0; } else { voxel[n_tmp_dims] = file->rgba_indices[i]; GET_MULTIDIM( rgb[i], (VIO_Real), rgb_array, voxel[0], voxel[1], voxel[2], voxel[3], voxel[4] ); } } colour = make_rgba_Colour_0_1( rgb[0], rgb[1], rgb[2], rgb[3] ); SET_MULTIDIM( buffer_array, voxel[0], voxel[1], voxel[2], voxel[3], voxel[4], colour ); } delete_multidim_array( &rgb_array ); } GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); copy_multidim_data_reordered( get_type_size(data_type), array_data_ptr, n_array_dims, array_sizes, void_ptr, n_tmp_dims, tmp_sizes, tmp_sizes, vol1_indices, FALSE ); delete_multidim_array( &buffer_array ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_slab @INPUT : file volume start count @OUTPUT : @RETURNS : @DESCRIPTION: Inputs a multidimensional slab from the file and copies it into the appropriate part of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void input_slab( Minc_file file, VIO_Volume volume, int to_volume[], long start[], long count[] ) { int file_ind, ind; int volume_start[MAX_VAR_DIMS]; int file_start[VIO_MAX_DIMENSIONS]; int file_count[VIO_MAX_DIMENSIONS]; int array_sizes[VIO_MAX_DIMENSIONS]; void *array_data_ptr; for_less( file_ind, 0, file->n_file_dimensions ) { file_start[file_ind] = (int) start[file_ind]; file_count[file_ind] = (int) count[file_ind]; ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) volume_start[ind] = file_start[file_ind]; } get_multidim_sizes( &volume->array, array_sizes ); GET_MULTIDIM_PTR( array_data_ptr, volume->array, volume_start[0], volume_start[1], volume_start[2], volume_start[3], volume_start[4] ); input_minc2_hyperslab( file, get_multidim_data_type(&volume->array), get_multidim_n_dimensions(&volume->array), array_sizes, array_data_ptr, to_volume, file_start, file_count ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_more_minc2_file @INPUT : file @OUTPUT : fraction_done - amount of file read @RETURNS : TRUE if volume has more left to read @DESCRIPTION: Reads another chunk from the input file, passes back the total fraction read so far, and returns FALSE when the whole volume has been read. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL input_more_minc2_file( Minc_file file, VIO_Real *fraction_done ) { int d, ind, n_done, total, n_slab; long count[MAX_VAR_DIMS]; VIO_Volume volume; VIO_BOOL increment; if( file->end_volume_flag ) { print_error( "End of file in input_more_minc_file()\n" ); return( FALSE ); } volume = file->volume; if( !volume_is_alloced( volume ) ) { alloc_volume_data( volume ); if( !volume_is_alloced( volume ) ) return( FALSE ); } /* --- set the counts for reading, actually these will be the same every time */ for_less( ind, 0, file->n_file_dimensions ) count[ind] = 1; n_slab = 0; for( d = file->n_file_dimensions-1; d >= 0 && n_slab < file->n_slab_dims; --d ) { if( file->to_volume_index[d] != INVALID_AXIS ) { count[d] = file->sizes_in_file[d]; ++n_slab; } } input_slab( file, volume, file->to_volume_index, file->indices, count ); /* --- advance to next slab */ increment = TRUE; n_slab = 0; total = 1; n_done = 0; for( d = file->n_file_dimensions-1; d >= 0; --d ) { if( n_slab >= file->n_slab_dims && file->to_volume_index[d] != INVALID_AXIS ) { if( increment ) { ++file->indices[d]; if( file->indices[d] < file->sizes_in_file[d] ) increment = FALSE; else file->indices[d] = 0; } n_done += total * (int) file->indices[d]; total *= (int) file->sizes_in_file[d]; } if( file->to_volume_index[d] != INVALID_AXIS ) ++n_slab; } if( increment ) { *fraction_done = 1.0; file->end_volume_flag = TRUE; } else { *fraction_done = (VIO_Real) n_done / (VIO_Real) total; } return( !file->end_volume_flag ); } #ifdef INPUT_MNC2_UNUSED /* ----------------------------- MNI Header ----------------------------------- @NAME : advance_input_volume2 @INPUT : file @OUTPUT : @RETURNS : TRUE if more volumes to read @DESCRIPTION: Advances the file indices to prepare for reading the next volume from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL advance_input_volume2( Minc_file file ) { int ind, c, axis; VIO_Real voxel[VIO_MAX_DIMENSIONS], world_space[VIO_N_DIMENSIONS]; VIO_Real vol_world_space[VIO_N_DIMENSIONS]; VIO_Transform offset; VIO_General_transform offset_transform; VIO_General_transform new_transform; ind = file->n_file_dimensions-1; while( ind >= 0 ) { if( file->to_volume_index[ind] == INVALID_AXIS ) { ++file->indices[ind]; if( file->indices[ind] < file->sizes_in_file[ind] ) break; file->indices[ind] = 0; } --ind; } if( ind >= 0 ) { file->end_volume_flag = FALSE; for_less( ind, 0, get_volume_n_dimensions( file->volume ) ) file->indices[file->valid_file_axes[ind]] = 0; /*--- update the volume's voxel-to-world transform */ for_less( c, 0, VIO_N_DIMENSIONS ) { axis = file->spatial_axes[c]; if( axis != INVALID_AXIS ) voxel[c] = (VIO_Real) file->indices[axis]; else voxel[c] = 0.0; } general_transform_point( &file->voxel_to_world_transform, voxel[0], voxel[1], voxel[2], &world_space[VIO_X], &world_space[VIO_Y], &world_space[VIO_Z]); for_less( c, 0, get_volume_n_dimensions(file->volume) ) voxel[c] = 0.0; convert_voxel_to_world( file->volume, voxel, &vol_world_space[VIO_X], &vol_world_space[VIO_Y], &vol_world_space[VIO_Z]); make_identity_transform( &offset ); for_less( c, 0, VIO_N_DIMENSIONS ) Transform_elem(offset,c,3) = world_space[c] - vol_world_space[c]; create_linear_transform( &offset_transform, &offset ); concat_general_transforms( get_voxel_to_world_transform(file->volume), &offset_transform, &new_transform ); set_voxel_to_world_transform( file->volume, &new_transform ); delete_general_transform( &offset_transform ); } else file->end_volume_flag = TRUE; return( file->end_volume_flag ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : reset_input_volume2 @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Rewinds the file indices to start inputting volumes from the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void reset_input_volume2( Minc_file file ) { int d; for_less( d, 0, file->n_file_dimensions ) file->indices[d] = 0; file->end_volume_flag = FALSE; } #endif /* INPUT_MNC2_UNUSED */ /* ----------------------------- MNI Header ----------------------------------- @NAME : match_dimension_names @INPUT : n_volume_dims volume_dimension_names n_file_dims file_dimension_names @OUTPUT : to_volume_index @RETURNS : TRUE if match found @DESCRIPTION: Attempts to match all the volume dimensions with the file dimensions. This is done in 3 passes. In the first pass, exact matches are found. In the second pass, volume dimensions of "any_spatial_dimension" are matched. On the final pass, volume dimension names which are empty strings are matched to any remaining file dimensions. If a dimension matches on "any_spatial_dimension" or empty string, then the name from the file is copied to the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Oct. 22, 1995 D. MacDonald - copies the name from the file to the volume ---------------------------------------------------------------------------- */ static VIO_BOOL match_dimension_names( int n_volume_dims, VIO_STR volume_dimension_names[], int n_file_dims, VIO_STR file_dimension_names[], int to_volume_index[] ) { int i, j, iteration, n_matches, dummy; int to_file_index[VIO_MAX_DIMENSIONS]; VIO_BOOL match = FALSE; VIO_BOOL volume_dim_found[VIO_MAX_DIMENSIONS]; n_matches = 0; for_less( i, 0, n_file_dims ) to_volume_index[i] = INVALID_AXIS; for_less( i, 0, n_volume_dims ) { volume_dim_found[i] = FALSE; to_file_index[i] = -1; } for_less( iteration, 0, 3 ) { for( i = n_volume_dims-1; i >= 0; --i ) { if( !volume_dim_found[i] ) { for( j = n_file_dims-1; j >= 0; --j ) { if( to_volume_index[j] == INVALID_AXIS ) { switch( iteration ) { case 0: match = equal_strings( volume_dimension_names[i], file_dimension_names[j] ); break; case 1: match = equal_strings( volume_dimension_names[i], ANY_SPATIAL_DIMENSION ) && convert_dim_name_to_spatial_axis( file_dimension_names[j], &dummy ); break; case 2: match = string_length(volume_dimension_names[i]) == 0; break; } if( match ) { to_volume_index[j] = i; to_file_index[i] = j; volume_dim_found[i] = TRUE; ++n_matches; break; } } } } } } if( n_matches == n_volume_dims ) { for_less( i, 0, n_volume_dims ) { if( equal_strings( volume_dimension_names[i], ANY_SPATIAL_DIMENSION ) || string_length(volume_dimension_names[i]) == 0 ) { replace_string( &volume_dimension_names[i], create_string( file_dimension_names[to_file_index[i]] ) ); } } } return( n_matches == n_volume_dims ); } VIOAPI int get_minc2_file_n_dimensions( VIO_STR filename ) { int n_dims; VIO_STR expanded; mihandle_t minc_id; expanded = expand_filename( filename ); if ( miopen_volume(expanded, MI2_OPEN_READ, &minc_id) < 0 ) { print_error( "Error opening %s\n", expanded ); delete_string( expanded ); return -1 ; } miget_volume_dimension_count(minc_id, MI_DIMCLASS_ANY, MI_DIMATTR_ALL, &n_dims); delete_string( expanded ); miclose_volume( minc_id ); return( n_dims ); } #endif /*HAVE_MINC2*/ libminc-libminc-2-3-00/volume_io/Volumes/input_nifti.c000066400000000000000000000507341257462267400227760ustar00rootroot00000000000000/** * \file Reader for NIfTI-1 format files. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include "input_nifti.h" #include "nifti1.h" #include "nifti1_io.h" #define NUM_BYTE_VALUES (UCHAR_MAX + 1) /** * Scan through the file to determine the range of the data. */ #define CHUNK_SIZE 10240 static void nifti_find_data_range(nifti_image *nii_ptr, znzFile zfp, VIO_Real *min_value_ptr, VIO_Real *max_value_ptr) { size_t i, j; double slope; double inter; long int initial_offset = znztell(zfp); double data[CHUNK_SIZE / sizeof(double)]; size_t n_voxels_per_chunk; if (initial_offset < 0) /* Did znztell() give an error? */ { print_error("nifti_find_data_range: Tell failed.\n"); return; } *min_value_ptr = FLT_MAX; *max_value_ptr = -FLT_MAX; if (nii_ptr->scl_slope <= 0.0) { slope = 1.0; inter = 0.0; } else { slope = nii_ptr->scl_slope; inter = nii_ptr->scl_inter; } n_voxels_per_chunk = CHUNK_SIZE / nii_ptr->nbyper; for (i = 0; i < nii_ptr->nvox; i += n_voxels_per_chunk) { double tmp = 0.0; size_t n_bytes_to_read; if (i + n_voxels_per_chunk > nii_ptr->nvox) { n_bytes_to_read = (nii_ptr->nvox - i) * nii_ptr->nbyper; } else { n_bytes_to_read = CHUNK_SIZE; } if (nifti_read_buffer(zfp, data, n_bytes_to_read, nii_ptr) != n_bytes_to_read) { print_error("nifti_find_data_range: Read error.\n"); return; } for (j = 0; j < n_voxels_per_chunk; j++) { switch (nii_ptr->datatype) { case DT_INT8: tmp = (double) ((char *)data)[j]; break; case DT_UINT8: tmp = (double) ((unsigned char *)data)[j]; break; case DT_INT16: tmp = (double) ((short *)data)[j]; break; case DT_UINT16: tmp = (double) ((unsigned short *)data)[j]; break; case DT_INT32: tmp = (double) ((int *)data)[j]; break; case DT_UINT32: tmp = (double) ((unsigned int *)data)[j]; break; case DT_FLOAT32: tmp = (double) ((float *)data)[j]; break; case DT_FLOAT64: tmp = (double) ((double *)data)[j]; break; default: fprintf(stderr, "NIfTI-1 data type %d not handled\n", nii_ptr->datatype); break; } if (tmp < *min_value_ptr) { *min_value_ptr = tmp; } if (tmp > *max_value_ptr) { *max_value_ptr = tmp; } } } *min_value_ptr = (*min_value_ptr * slope) + inter; *max_value_ptr = (*max_value_ptr * slope) + inter; znzseek(zfp, initial_offset, SEEK_SET); } /** * Converts the fields in a nifti_image to the appropriate MINC attributes. */ static void nifti_image_to_minc_attributes(nifti_image *nii_ptr, int mnc_index_from_file[], VIO_Real mnc_starts[], VIO_Real mnc_steps[], VIO_Real mnc_dircos[][VIO_N_DIMENSIONS]) { size_t i, j; VIO_Transform mnc_xform; VIO_General_transform mnc_linear_xform; make_identity_transform(&mnc_xform); if (nii_ptr->nifti_type == NIFTI_FTYPE_ANALYZE || (nii_ptr->sform_code == NIFTI_XFORM_UNKNOWN && nii_ptr->qform_code == NIFTI_XFORM_UNKNOWN)) { print_error("No transform found in header, guessing.\n"); for (i = 0; i < VIO_MAX_DIMENSIONS; i++) { mnc_index_from_file[i] = i; } Transform_elem(mnc_xform, 0, 0) *= nii_ptr->dx; Transform_elem(mnc_xform, 1, 1) *= nii_ptr->dy; Transform_elem(mnc_xform, 2, 2) *= nii_ptr->dz; Transform_elem(mnc_xform, 0, 3) = -(nii_ptr->dx * nii_ptr->nx) / 2; Transform_elem(mnc_xform, 1, 3) = -(nii_ptr->dy * nii_ptr->ny) / 2; Transform_elem(mnc_xform, 2, 3) = -(nii_ptr->dz * nii_ptr->nz) / 2; create_linear_transform(&mnc_linear_xform, &mnc_xform); convert_transform_to_starts_and_steps(&mnc_linear_xform, VIO_N_DIMENSIONS, NULL, mnc_index_from_file, mnc_starts, mnc_steps, mnc_dircos); } else { if (nii_ptr->sform_code != NIFTI_XFORM_UNKNOWN) { for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { Transform_elem(mnc_xform, i, j) = nii_ptr->sto_xyz.m[i][j]; } } } else { for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { Transform_elem(mnc_xform, i, j) = nii_ptr->qto_xyz.m[i][j]; } } } create_linear_transform(&mnc_linear_xform, &mnc_xform); for_less (i, 0, 6) { switch (i) { case 0: mnc_index_from_file[0] = VIO_X; mnc_index_from_file[1] = VIO_Y; mnc_index_from_file[2] = VIO_Z; break; case 1: mnc_index_from_file[0] = VIO_X; mnc_index_from_file[1] = VIO_Z; mnc_index_from_file[2] = VIO_Y; break; case 2: mnc_index_from_file[0] = VIO_Y; mnc_index_from_file[1] = VIO_X; mnc_index_from_file[2] = VIO_Z; break; case 3: mnc_index_from_file[0] = VIO_Y; mnc_index_from_file[1] = VIO_Z; mnc_index_from_file[2] = VIO_X; break; case 4: mnc_index_from_file[0] = VIO_Z; mnc_index_from_file[1] = VIO_X; mnc_index_from_file[2] = VIO_Y; break; case 5: mnc_index_from_file[0] = VIO_Z; mnc_index_from_file[1] = VIO_Y; mnc_index_from_file[2] = VIO_X; break; } /* For the time axis, if present. */ mnc_index_from_file[3] = 3; convert_transform_to_starts_and_steps(&mnc_linear_xform, VIO_N_DIMENSIONS, NULL, mnc_index_from_file, mnc_starts, mnc_steps, mnc_dircos); if( fabs( mnc_dircos[0][VIO_X] ) > fabs( mnc_dircos[0][VIO_Y] ) && fabs( mnc_dircos[0][VIO_X] ) > fabs( mnc_dircos[0][VIO_Z] ) && fabs( mnc_dircos[1][VIO_Y] ) > fabs( mnc_dircos[1][VIO_X] ) && fabs( mnc_dircos[1][VIO_Y] ) > fabs( mnc_dircos[1][VIO_Z] ) && fabs( mnc_dircos[2][VIO_Z] ) > fabs( mnc_dircos[2][VIO_X] ) && fabs( mnc_dircos[2][VIO_Z] ) > fabs( mnc_dircos[2][VIO_Y] ) ) { break; } } } /* Adjust start and step values if alternate units are specified. */ switch (nii_ptr->xyz_units) { case NIFTI_UNITS_METER: for (i = 0; i < VIO_N_DIMENSIONS; i++) { mnc_starts[i] *= 1000.0; mnc_steps[i] *= 1000.0; } break; case NIFTI_UNITS_MICRON: for (i = 0; i < VIO_N_DIMENSIONS; i++) { mnc_starts[i] /= 1000.0; mnc_steps[i] /= 1000.0; } break; default: break; } /* Store the start and step values for the time dimension, adjusting * the units as needed. */ switch (nii_ptr->time_units) { case NIFTI_UNITS_MSEC: mnc_starts[3] = nii_ptr->toffset / 1000.0; mnc_steps[3] = nii_ptr->dt / 1000.0; break; case NIFTI_UNITS_USEC: mnc_starts[3] = nii_ptr->toffset / 1000000.0; mnc_steps[3] = nii_ptr->dt / 1000000.0; break; default: /* Either seconds or unknown. */ mnc_starts[3] = nii_ptr->toffset; mnc_steps[3] = nii_ptr->dt; break; } } /** * nifti_image_open() gives us a file pointer to the image data, but * it unfortunately does not skip past the header if present. So we * have to figure out the right offset to skip. This is a bit tricky, * and there is no public function in the library to do it, so I've * essentially copied the procedure here. */ static int nifti_skip_header(znzFile zfp, nifti_image *nii_ptr) { size_t ioff; size_t volsize = nifti_get_volsize(nii_ptr); // total bytes to read. // A negative offset means that it is relative to the end-of-file. if (nii_ptr->iname_offset < 0) { size_t filesize = nifti_get_filesize(nii_ptr->iname); if ( filesize <= 0 ) // Empty image file? { return VIO_ERROR; } ioff = (filesize > volsize) ? filesize - volsize : 0; } else { ioff = nii_ptr->iname_offset; } znzseek(zfp, ioff, SEEK_SET); return VIO_OK; } VIOAPI VIO_Status initialize_nifti_format_input(VIO_STR filename, VIO_Volume volume, volume_input_struct *in_ptr) { int sizes[VIO_MAX_DIMENSIONS]; long n_voxels_in_slice; nc_type desired_nc_type; int axis; nifti_image *nii_ptr; VIO_Real mnc_dircos[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS]; VIO_Real mnc_steps[VIO_MAX_DIMENSIONS]; VIO_Real mnc_starts[VIO_MAX_DIMENSIONS]; int n_dimensions; znzFile zfp; nc_type file_nc_type; VIO_BOOL signed_flag; /* Read in the NIfTI file header and get a znzFile handle to the data. */ zfp = nifti_image_open(filename, "rb", &nii_ptr); if (znz_isnull(zfp)) { nifti_image_free(nii_ptr); return VIO_ERROR; } else { nifti_skip_header(zfp, nii_ptr); } /* Translate from NIfTI to VIO types. */ switch (nii_ptr->datatype) { case DT_UINT8: in_ptr->file_data_type = VIO_UNSIGNED_BYTE; file_nc_type = NC_BYTE; signed_flag = FALSE; break; case DT_INT8: in_ptr->file_data_type = VIO_SIGNED_BYTE; file_nc_type = NC_BYTE; signed_flag = TRUE; break; case DT_UINT16: in_ptr->file_data_type = VIO_UNSIGNED_SHORT; file_nc_type = NC_SHORT; signed_flag = FALSE; break; case DT_INT16: in_ptr->file_data_type = VIO_SIGNED_SHORT; file_nc_type = NC_SHORT; signed_flag = TRUE; break; case DT_UINT32: in_ptr->file_data_type = VIO_UNSIGNED_INT; file_nc_type = NC_INT; signed_flag = FALSE; break; case DT_INT32: in_ptr->file_data_type = VIO_SIGNED_INT; file_nc_type = NC_INT; signed_flag = TRUE; break; case DT_FLOAT32: in_ptr->file_data_type = VIO_FLOAT; file_nc_type = NC_FLOAT; signed_flag = TRUE; break; case DT_FLOAT64: in_ptr->file_data_type = VIO_DOUBLE; file_nc_type = NC_DOUBLE; signed_flag = TRUE; break; default: print_error("Unknown NIfTI-1 data type.\n"); nifti_image_free(nii_ptr); znzclose(zfp); return VIO_ERROR; } n_dimensions = nii_ptr->dim[0]; /* Ignore trailing dimensions of length 1 or less. The library * does not set the "ndim" field correctly in all cases. */ while (n_dimensions > 1 && nii_ptr->dim[n_dimensions] <= 1) { n_dimensions--; } for (axis = 0; axis < n_dimensions; axis++) { in_ptr->sizes_in_file[axis] = nii_ptr->dim[axis + 1]; } /* Decide how to store data in memory. */ if ( get_volume_data_type(volume) == VIO_NO_DATA_TYPE ) { desired_nc_type = file_nc_type; } else { desired_nc_type = get_volume_nc_data_type(volume, &signed_flag); } if( volume->spatial_axes[VIO_X] < 0 || volume->spatial_axes[VIO_Y] < 0 || volume->spatial_axes[VIO_Z] < 0 ) { print_error("warning: setting NIfTI-1 spatial axes to XYZ.\n"); volume->spatial_axes[VIO_X] = 0; volume->spatial_axes[VIO_Y] = 1; volume->spatial_axes[VIO_Z] = 2; } if (!set_volume_n_dimensions(volume, n_dimensions)) { print_error("Problem setting number of dimensions.\n"); nifti_image_free(nii_ptr); znzclose(zfp); return VIO_ERROR; } nifti_image_to_minc_attributes(nii_ptr, in_ptr->axis_index_from_file, mnc_starts, mnc_steps, mnc_dircos); for_less( axis, 0, n_dimensions) { int volume_axis = in_ptr->axis_index_from_file[axis]; sizes[volume_axis] = in_ptr->sizes_in_file[axis]; if (axis < 3) { /* DEBUG */ printf("%d %d size:%4d step:%6.3f start:%9.4f dc:[%7.4f %7.4f %7.4f]\n", axis, volume_axis, sizes[volume_axis], mnc_steps[volume_axis], mnc_starts[volume_axis], mnc_dircos[volume_axis][0], mnc_dircos[volume_axis][1], mnc_dircos[volume_axis][2]); set_volume_direction_cosine(volume, volume_axis, mnc_dircos[axis]); } else { /* DEBUG */ printf("%d %d size:%4d step:%6.3f start:%9.4f\n", axis, volume_axis, sizes[volume_axis], mnc_steps[volume_axis], mnc_starts[volume_axis]); } } set_volume_separations( volume, mnc_steps ); set_volume_starts( volume, mnc_starts ); set_volume_type( volume, desired_nc_type, signed_flag, 0.0, 0.0 ); set_volume_sizes( volume, sizes ); n_voxels_in_slice = (in_ptr->sizes_in_file[0] * in_ptr->sizes_in_file[1]); /* If the data must be converted to byte, read the entire image file simply * to find the max and min values. This allows us to set the value_scale and * value_translation properly when we read the file. */ if (get_volume_data_type(volume) != in_ptr->file_data_type ) { VIO_Real original_min_value, original_max_value; nifti_find_data_range(nii_ptr, zfp, &original_min_value, &original_max_value); set_volume_voxel_range(volume, original_min_value, original_max_value); } in_ptr->min_value = FLT_MAX; in_ptr->max_value = -FLT_MAX; in_ptr->slice_index = 0; in_ptr->volume_file = (FILE *) zfp; in_ptr->header_info = nii_ptr; in_ptr->generic_slice_buffer = malloc(n_voxels_in_slice * nii_ptr->nbyper); if (in_ptr->generic_slice_buffer == NULL) { return VIO_ERROR; } return VIO_OK; } VIOAPI void delete_nifti_format_input( volume_input_struct *in_ptr ) { nifti_image *nii_ptr = (nifti_image *) in_ptr->header_info; znzFile zfp = (znzFile) in_ptr->volume_file; nifti_image_free(nii_ptr); znzclose(zfp); free(in_ptr->generic_slice_buffer); } VIOAPI VIO_BOOL input_more_nifti_format_file( VIO_Volume volume, volume_input_struct *in_ptr, VIO_Real *fraction_done ) { nifti_image *nii_ptr = (nifti_image *) in_ptr->header_info; znzFile zfp = (znzFile) in_ptr->volume_file; void *data_ptr = in_ptr->generic_slice_buffer; int data_ind = 0; double value = 0; double value_offset, value_scale; int *inner_index; int indices[VIO_MAX_DIMENSIONS]; VIO_Real original_min_value, original_max_value; int i; int total_slices; total_slices = in_ptr->sizes_in_file[2]; if (get_volume_n_dimensions(volume) > 3) { /* If there is a time dimension, incorporate that into our slice * count. */ total_slices *= in_ptr->sizes_in_file[3]; } if ( in_ptr->slice_index < total_slices ) { size_t n_bytes_per_slice; size_t n_bytes_read; n_bytes_per_slice = (in_ptr->sizes_in_file[0] * in_ptr->sizes_in_file[1] * nii_ptr->nbyper); /* If the memory for the volume has not been allocated yet, * initialize that memory now. */ if (!volume_is_alloced(volume)) { alloc_volume_data(volume); if (!volume_is_alloced(volume)) { print_error("Failed to allocate volume.\n"); return FALSE; } } n_bytes_read = nifti_read_buffer(zfp, data_ptr, n_bytes_per_slice, nii_ptr); if (n_bytes_read < n_bytes_per_slice) { return FALSE; } /* See if we need to apply scaling to this slice. This is only * needed if the volume voxel type is not the same as the file * voxel type. THIS IS ONLY REALLY LEGAL FOR BYTE VOLUME TYPES. */ if (get_volume_data_type(volume) != in_ptr->file_data_type) { get_volume_voxel_range(volume, &original_min_value, &original_max_value); value_offset = original_min_value; value_scale = (original_max_value - original_min_value) / (VIO_Real) (NUM_BYTE_VALUES - 1); } else { /* Just do trivial scaling. */ value_offset = 0.0; value_scale = 1.0; } /* Set up the indices. */ inner_index = &indices[in_ptr->axis_index_from_file[0]]; if (get_volume_n_dimensions(volume) > 3) { /* If a time dimension is present, convert the slice index into * both a time and slice coordinate using the number of slices. */ indices[in_ptr->axis_index_from_file[3]] = in_ptr->slice_index / in_ptr->sizes_in_file[2]; indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index % in_ptr->sizes_in_file[2]; } else { indices[in_ptr->axis_index_from_file[2]] = in_ptr->slice_index; } for_less( i, 0, in_ptr->sizes_in_file[1] ) { indices[in_ptr->axis_index_from_file[1]] = i; for_less( *inner_index, 0, in_ptr->sizes_in_file[0] ) { switch ( in_ptr->file_data_type ) { case VIO_UNSIGNED_BYTE: value = ((unsigned char *) data_ptr)[data_ind++]; break; case VIO_SIGNED_BYTE: value = ((char *) data_ptr)[data_ind++]; break; case VIO_UNSIGNED_SHORT: value = ((unsigned short *) data_ptr)[data_ind++]; break; case VIO_SIGNED_SHORT: value = ((short *) data_ptr)[data_ind++]; break; case VIO_UNSIGNED_INT: value = ((unsigned int *) data_ptr)[data_ind++]; break; case VIO_SIGNED_INT: value = ((int *) data_ptr)[data_ind++]; break; case VIO_FLOAT: value = ((float *) data_ptr)[data_ind++]; break; case VIO_DOUBLE: value = ((double *)data_ptr)[data_ind++]; break; default: handle_internal_error( "input_more_nifti_format_file" ); break; } value = (value - value_offset) / value_scale; switch (get_volume_data_type(volume)) { case VIO_UNSIGNED_BYTE: if (value < 0 || value > UCHAR_MAX) { print_error("clipping uint8 value\n"); } break; case VIO_SIGNED_BYTE: if (value < SCHAR_MIN || value > SCHAR_MAX) { print_error("clipping int8 value\n"); } break; case VIO_UNSIGNED_SHORT: if (value < 0 || value > USHRT_MAX) { print_error("clipping uint16 value\n"); } break; case VIO_SIGNED_SHORT: if (value < SHRT_MIN || value > SHRT_MAX) { print_error("clipping int16 value\n"); } break; case VIO_UNSIGNED_INT: if (value < 0 || value > UINT_MAX) { print_error("clipping uint32 value\n"); } break; case VIO_SIGNED_INT: if (value < INT_MIN || value > INT_MAX) { print_error("clipping int32 value\n"); } break; case VIO_NO_DATA_TYPE: case VIO_FLOAT: case VIO_DOUBLE: case VIO_MAX_DATA_TYPE: break; } if (value > in_ptr->max_value) { in_ptr->max_value = value; } if (value < in_ptr->min_value) { in_ptr->min_value = value; } set_volume_voxel_value( volume, indices[VIO_X], indices[VIO_Y], indices[VIO_Z], indices[3], indices[4], value); } } in_ptr->slice_index++; /* Advance to the next slice. */ } *fraction_done = (VIO_Real) in_ptr->slice_index / total_slices; if (in_ptr->slice_index >= total_slices) { set_volume_voxel_range( volume, in_ptr->min_value, in_ptr->max_value ); /* Make sure we scale the data up to the original real range, * if appropriate. */ if (get_volume_data_type(volume) != in_ptr->file_data_type) { set_volume_real_range(volume, original_min_value, original_max_value); } return FALSE; } else { return TRUE; } } libminc-libminc-2-3-00/volume_io/Volumes/input_nifti.h000066400000000000000000000030531257462267400227730ustar00rootroot00000000000000/** * \file Reader for NIfTI-1 format files. */ #include #include #include /** * Initializes loading a NIfTI-1 format file by reading the header. * This function assumes that volume->filename has been assigned. * * \param filename The filename to open for input. * \param volume The volume that will ultimately hold the input data. * \param in_ptr State information for the current input operation. * \return VIO_OK if successful. */ VIOAPI VIO_Status initialize_nifti_format_input(VIO_STR filename, VIO_Volume volume, volume_input_struct *in_ptr); /** * Dispose of the resources used to read a NIfTI-1 file. * \param in_ptr The volume_input_struct that is to be deleted. */ VIOAPI void delete_nifti_format_input( volume_input_struct *in_ptr ); /** * Read the next slice of an NIfTI-1 format file. * \param volume The volume associated with this input operation. * \param in_ptr State information for the current input operation. * \param fraction_done A number from 0 to 1 indicating the fraction * of the operation that has completed after this call returns. * \return TRUE if successful. */ VIOAPI VIO_BOOL input_more_nifti_format_file( VIO_Volume volume, volume_input_struct *in_ptr, VIO_Real *fraction_done ); libminc-libminc-2-3-00/volume_io/Volumes/input_volume.c000066400000000000000000000333051257462267400231670ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include "input_mgh.h" #include "input_nifti.h" #ifdef HAVE_MINC1 #include #elif defined HAVE_MINC2 #include #endif /*HAVE_MINC1*/ #define FREE_ENDING "fre" #ifdef INPUT_VOLUME_UNUSED /* This function is only intended for debugging purposes. */ void print_volume(FILE *fp, VIO_Volume volume) { int n_dimensions = volume->array.n_dimensions; int i; if (volume == NULL) { fprintf(fp, "Volume is NULL.\n"); return; } fprintf(fp, "VIO_Volume at %lx has %d dimensions.\n", (unsigned long)volume, n_dimensions); fprintf(fp, " nc_data_type: %d, signed_flag: %d\n", volume->nc_data_type, volume->signed_flag); fprintf(fp," is_cached_volume: %d, is_rgba_data: %d\n", volume->is_cached_volume, volume->is_rgba_data); fprintf(fp, " voxel_min: %g, voxel_max: %g\n", volume->voxel_min, volume->voxel_max); fprintf(fp, " real_range_set: %d\n", volume->real_range_set); fprintf(fp, " real_value_scale: %g, real_value_translation: %g\n", volume->real_value_scale, volume->real_value_translation); fprintf(fp, " voxel_to_world_transform_uptodate: %d\n", volume->voxel_to_world_transform_uptodate); fprintf(fp, " coordinate_system_name: %s\n", volume->coordinate_system_name); for (i = 0; i < n_dimensions; i++) { fprintf(fp, " %d. %s %d size %d step %g start %g cosines:[%g %g %g]\n", i, volume->dimension_names[i], volume->spatial_axes[i], volume->array.sizes[i], volume->separations[i], volume->starts[i], volume->direction_cosines[i][VIO_X], volume->direction_cosines[i][VIO_Y], volume->direction_cosines[i][VIO_Z]); } fprintf(fp, "VIO_Volume end.\n"); } #endif /* INPUT_VOLUME_UNUSED */ /* ----------------------------- MNI Header ----------------------------------- @NAME : start_volume_input @INPUT : filename - file to input dim_names - names of dimensions, or null convert_to_byte_flag - whether to convert volume data to byte @OUTPUT : volume - the volume data input_info - information for use while inputting @RETURNS : VIO_OK if successful @DESCRIPTION: Opens the file and reads the header, but does not read any volume data yet. Allocates the data also. Note: if you wish to modify the volume file input routines, then look at the new_C_dev/Include/volume.h for the description of Volum and volume_input_struct. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status start_volume_input( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], nc_type volume_nc_data_type, VIO_BOOL volume_signed_flag, VIO_Real volume_voxel_min, VIO_Real volume_voxel_max, VIO_BOOL create_volume_flag, VIO_Volume *volume, minc_input_options *options, volume_input_struct *input_info ) { VIO_Status status; int d; VIO_STR expanded_filename; status = VIO_OK; if( create_volume_flag || *volume == (VIO_Volume) NULL ) { if( n_dimensions < 1 || n_dimensions > VIO_MAX_DIMENSIONS ) { n_dimensions = VIO_MAX_DIMENSIONS; } if( dim_names == (VIO_STR *) NULL ) dim_names = get_default_dim_names( n_dimensions ); *volume = create_volume( n_dimensions, dim_names, volume_nc_data_type, volume_signed_flag, volume_voxel_min, volume_voxel_max ); } else if( n_dimensions != get_volume_n_dimensions( *volume ) && volume_is_alloced( *volume ) ) { free_volume_data( *volume ); } expanded_filename = expand_filename( filename ); if (filename_extension_matches( expanded_filename, FREE_ENDING ) ) { input_info->file_format = FREE_FORMAT; } else if (filename_extension_matches( expanded_filename, "mgh" ) || filename_extension_matches( expanded_filename, "mgz" ) ) { input_info->file_format = MGH_FORMAT; /* FreeSurfer */ } else if (filename_extension_matches( expanded_filename, "nii" ) || filename_extension_matches( expanded_filename, "hdr" )) { input_info->file_format = NII_FORMAT; /* NIfTI-1 */ } else { #ifdef HAVE_MINC1 input_info->file_format = MNC_FORMAT; #elif defined HAVE_MINC2 input_info->file_format = MNC2_FORMAT; #endif } switch( input_info->file_format ) { #ifdef HAVE_MINC1 case MNC_FORMAT: if( !file_exists( expanded_filename ) ) { file_exists_as_compressed( expanded_filename, &expanded_filename ); } input_info->minc_file = initialize_minc_input( expanded_filename, *volume, options ); if( input_info->minc_file == (Minc_file) NULL ) status = VIO_ERROR; else { for_less( d, 0, VIO_MAX_DIMENSIONS ) input_info->axis_index_from_file[d] = d; } break; #endif /*HAVE_MINC1*/ #ifdef HAVE_MINC2 case MNC2_FORMAT: input_info->minc_file = initialize_minc2_input( expanded_filename, *volume, options ); if( input_info->minc_file == (Minc_file) NULL ) status = VIO_ERROR; else { for_less( d, 0, VIO_MAX_DIMENSIONS ) input_info->axis_index_from_file[d] = d; } break; #endif /*HAVE_MINC2*/ case FREE_FORMAT: status = initialize_free_format_input( expanded_filename, *volume, input_info ); break; case MGH_FORMAT: status = initialize_mgh_format_input( expanded_filename, *volume, input_info ); break; case NII_FORMAT: status = initialize_nifti_format_input( expanded_filename, *volume, input_info ); break; default: /*Unsupported file format*/ status = VIO_ERROR; break; } if( status != VIO_OK && create_volume_flag ) delete_volume( *volume ); delete_string( expanded_filename ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_volume_input @INPUT : input_info @OUTPUT : @RETURNS : @DESCRIPTION: Frees up any memory allocated for the volume input, i.e., any temporary_buffer. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_volume_input( volume_input_struct *input_info ) { switch( input_info->file_format ) { default: #ifdef HAVE_MINC1 case MNC_FORMAT: close_minc_input( input_info->minc_file ); break; #endif /*HAVE_MINC1*/ #ifdef HAVE_MINC2 case MNC2_FORMAT: close_minc2_input( input_info->minc_file ); break; #endif /*HAVE_MINC2*/ case FREE_FORMAT: delete_free_format_input( input_info ); break; case MGH_FORMAT: delete_mgh_format_input ( input_info ); break; case NII_FORMAT: delete_nifti_format_input ( input_info ); break; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_more_of_volume @INPUT : volume input_info @OUTPUT : fraction_done - number between 0 and 1 @RETURNS : TRUE - if there is remains more to input after this call @DESCRIPTION: Reads in more of the volume file. This routine is provided, rather than a read_entire_volume(), so that programs can multiprocess loading with other tasks. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL input_more_of_volume( VIO_Volume volume, volume_input_struct *input_info, VIO_Real *fraction_done ) { VIO_BOOL more_to_do; switch( input_info->file_format ) { default: #ifdef HAVE_MINC1 case MNC_FORMAT: more_to_do = input_more_minc_file( input_info->minc_file, fraction_done ); break; #endif #ifdef HAVE_MINC2 case MNC2_FORMAT: more_to_do = input_more_minc2_file( input_info->minc_file, fraction_done ); break; #endif /*HAVE_MINC2*/ case FREE_FORMAT: more_to_do = input_more_free_format_file( volume, input_info, fraction_done ); break; case MGH_FORMAT: more_to_do = input_more_mgh_format_file( volume, input_info, fraction_done ); break; case NII_FORMAT: more_to_do = input_more_nifti_format_file( volume, input_info, fraction_done ); break; } return( more_to_do ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cancel_volume_input @INPUT : volume input_info @OUTPUT : @RETURNS : @DESCRIPTION: Cancels loading the volume. Merely deletes the volume, then deletes the input buffer. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void cancel_volume_input( VIO_Volume volume, volume_input_struct *input_info ) { delete_volume( volume ); delete_volume_input( input_info ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : input_volume @INPUT : filename dim_names convert_to_byte_flag @OUTPUT : volume @RETURNS : VIO_OK if loaded alright @DESCRIPTION: Inputs the entire volume. @CREATED : David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status input_volume( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], nc_type volume_nc_data_type, VIO_BOOL volume_signed_flag, VIO_Real volume_voxel_min, VIO_Real volume_voxel_max, VIO_BOOL create_volume_flag, VIO_Volume *volume, minc_input_options *options ) { VIO_Status status; VIO_Real amount_done; volume_input_struct input_info; VIO_progress_struct progress; static const int FACTOR = 1000; VIO_Real volume_min=0.0,volume_max=0.0; status = start_volume_input( filename, n_dimensions, dim_names, volume_nc_data_type, volume_signed_flag, volume_voxel_min, volume_voxel_max, create_volume_flag, volume, options, &input_info ); if( status == VIO_OK ) { initialize_progress_report( &progress, FALSE, FACTOR, "Reading Volume"); while( input_more_of_volume( *volume, &input_info, &amount_done ) ) { update_progress_report( &progress, VIO_ROUND( (VIO_Real) FACTOR * amount_done)); } terminate_progress_report( &progress ); delete_volume_input( &input_info ); if( !volume_is_alloced( *volume ) ) { delete_volume( *volume ); *volume = NULL; status = VIO_ERROR; } } if (status == VIO_OK) { get_volume_voxel_range( *volume, &volume_min, &volume_max ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_input_minc_file @INPUT : volume_input @OUTPUT : @RETURNS : Minc_file @DESCRIPTION: Returns the minc file attached to a particular volume input structure. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 15, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI Minc_file get_volume_input_minc_file( volume_input_struct *volume_input ) { return( volume_input->minc_file ); } libminc-libminc-2-3-00/volume_io/Volumes/multidim_arrays.c000066400000000000000000000537011257462267400236500ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include /* ----------------------------- MNI Header ----------------------------------- @NAME : create_empty_multidim_array @INPUT : n_dimensions data_type @OUTPUT : array @RETURNS : @DESCRIPTION: Creates a multidimensional array, without allocating its data. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_empty_multidim_array( VIO_multidim_array *array, int n_dimensions, VIO_Data_types data_type ) { if( n_dimensions < 1 || n_dimensions > VIO_MAX_DIMENSIONS ) { print_error( "create_empty_multidim_array(): n_dimensions (%d) not in range 1 to %d.\n", n_dimensions, VIO_MAX_DIMENSIONS ); } array->n_dimensions = n_dimensions; array->data_type = data_type; array->data = (void *) NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_multidim_data_type @INPUT : array @OUTPUT : @RETURNS : data type @DESCRIPTION: Returns the data type of the multidimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Data_types get_multidim_data_type( VIO_multidim_array *array ) { return( array->data_type ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_multidim_data_type @INPUT : array data_type @OUTPUT : @RETURNS : @DESCRIPTION: Sets the data type of the array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_multidim_data_type( VIO_multidim_array *array, VIO_Data_types data_type ) { array->data_type = data_type; } /* ---------------------------------------------------------------------------- @NAME : get_type_size @INPUT : type @OUTPUT : @RETURNS : size of the type @DESCRIPTION: Returns the size of the given type. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_type_size( VIO_Data_types type ) { int size = sizeof(double); /*default : double*/ switch( type ) { case VIO_UNSIGNED_BYTE: size = sizeof( unsigned char ); break; case VIO_SIGNED_BYTE: size = sizeof( signed char ); break; case VIO_UNSIGNED_SHORT: size = sizeof( unsigned short ); break; case VIO_SIGNED_SHORT: size = sizeof( signed short ); break; case VIO_UNSIGNED_INT: size = sizeof( unsigned int ); break; case VIO_SIGNED_INT: size = sizeof( signed int ); break; case VIO_FLOAT: size = sizeof( float ); break; default: case VIO_DOUBLE: size = sizeof( double ); break; } return( size ); } VIOAPI void get_type_range( VIO_Data_types type, VIO_Real *min_value, VIO_Real *max_value ) { *min_value = (VIO_Real) -DBL_MAX; *max_value = (VIO_Real) DBL_MAX; switch( type ) { case VIO_UNSIGNED_BYTE: *min_value = 0.0; *max_value = (VIO_Real) UCHAR_MAX; break; case VIO_SIGNED_BYTE: *min_value = (VIO_Real) SCHAR_MIN; *max_value = (VIO_Real) SCHAR_MAX; break; case VIO_UNSIGNED_SHORT: *min_value = 0.0; *max_value = (VIO_Real) USHRT_MAX; break; case VIO_SIGNED_SHORT: *min_value = (VIO_Real) SHRT_MIN; *max_value = (VIO_Real) SHRT_MAX; break; case VIO_UNSIGNED_INT: *min_value = 0.0; *max_value = (VIO_Real) UINT_MAX; break; case VIO_SIGNED_INT: *min_value = (VIO_Real) INT_MIN; *max_value = (VIO_Real) INT_MAX; break; case VIO_FLOAT: *min_value = (VIO_Real) -FLT_MAX; *max_value = (VIO_Real) FLT_MAX; break; default: case VIO_DOUBLE: *min_value = (VIO_Real) -DBL_MAX; *max_value = (VIO_Real) DBL_MAX; break; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_multidim_n_dimensions @INPUT : array n_dimensions @OUTPUT : @RETURNS : TRUE if successful. @DESCRIPTION: Sets the number of dimensions in the array. The array should not already be allocated. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL set_multidim_n_dimensions( VIO_multidim_array *array, int n_dimensions ) { if (array->data != NULL) { print_error("set_multidim_n_dimensions: memory already allocated.\n"); return FALSE; } if (n_dimensions < 1 || n_dimensions > VIO_MAX_DIMENSIONS) { return FALSE; } array->n_dimensions = n_dimensions; return TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_multidim_sizes @INPUT : array sizes @OUTPUT : @RETURNS : @DESCRIPTION: Sets the sizes of the array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_multidim_sizes( VIO_multidim_array *array, int sizes[] ) { int dim; if (array->data != NULL) { print_error("set_multidim_sizes: memory already allocated.\n"); } for_less( dim, 0, array->n_dimensions ) array->sizes[dim] = sizes[dim]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_multidim_sizes @INPUT : array @OUTPUT : sizes @RETURNS : @DESCRIPTION: Passes back the sizes of the multidimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_multidim_sizes( VIO_multidim_array *array, int sizes[] ) { int i; for_less( i, 0, array->n_dimensions ) sizes[i] = array->sizes[i]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : multidim_array_is_alloced @INPUT : array @OUTPUT : @RETURNS : TRUE if array is allocated @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL multidim_array_is_alloced( VIO_multidim_array *array ) { VIO_BOOL status = FALSE; void **p1, ***p2, ****p3, *****p4, ******p5; if( array == NULL ) return( FALSE ); switch( array->n_dimensions ) { case 1: p1 = (void **)&array->data; status = ( *p1 != NULL ); break; case 2: p2 = (void ***)&array->data; if( *p2 == NULL ) break; if( **p2 == NULL ) break; status = TRUE; break; case 3: p3 = (void ****)&array->data; if( *p3 == NULL ) break; if( **p3 == NULL ) break; if( ***p3 == NULL ) break; status = TRUE; break; case 4: p4 = (void *****)&array->data; if( *p4 == NULL ) break; if( **p4 == NULL ) break; if( ***p4 == NULL ) break; if( ****p4 == NULL ) break; status = TRUE; break; case 5: p5 = (void ******)&array->data; if( *p5 == NULL ) break; if( **p5 == NULL ) break; if( ***p5 == NULL ) break; if( ****p5 == NULL ) break; if( *****p5 == NULL ) break; status = TRUE; break; } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_multidim_array @INPUT : array @OUTPUT : @RETURNS : @DESCRIPTION: Allocates the data for the multidimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void alloc_multidim_array( VIO_multidim_array *array ) { int dim; size_t type_size, sizes[5]; void *p1, **p2, ***p3, ****p4, *****p5; if( multidim_array_is_alloced( array ) ) delete_multidim_array( array ); if( array->data_type == VIO_NO_DATA_TYPE ) { print_error( "Error: cannot allocate array data until size specified.\n" ); return; } for_less( dim, 0, array->n_dimensions ) sizes[dim] = (size_t) array->sizes[dim]; type_size = (size_t) get_type_size( array->data_type ); switch( array->n_dimensions ) { case 1: ASSIGN_PTR(p1) = alloc_memory_1d( sizes[0], type_size _ALLOC_SOURCE_LINE ); array->data = (void *) p1; break; case 2: ASSIGN_PTR(p2) = alloc_memory_2d( sizes[0], sizes[1], type_size _ALLOC_SOURCE_LINE); array->data = (void *) p2; break; case 3: ASSIGN_PTR(p3) = alloc_memory_3d( sizes[0], sizes[1], sizes[2], type_size _ALLOC_SOURCE_LINE ); array->data = (void *) p3; break; case 4: ASSIGN_PTR(p4) = alloc_memory_4d( sizes[0], sizes[1], sizes[2], sizes[3], type_size _ALLOC_SOURCE_LINE ); array->data = (void *) p4; break; case 5: ASSIGN_PTR(p5) = alloc_memory_5d( sizes[0], sizes[1], sizes[2], sizes[3], sizes[4], type_size _ALLOC_SOURCE_LINE ); array->data = (void *) p5; break; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_multidim_array @INPUT : array n_dimensions sizes data_type @OUTPUT : @RETURNS : @DESCRIPTION: Creates a multidimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void create_multidim_array( VIO_multidim_array *array, int n_dimensions, int sizes[], VIO_Data_types data_type ) { create_empty_multidim_array( array, n_dimensions, data_type ); set_multidim_sizes( array, sizes ); alloc_multidim_array( array ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_multidim_array @INPUT : array @OUTPUT : @RETURNS : @DESCRIPTION: Deletes the multidimensional array. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_multidim_array( VIO_multidim_array *array ) { if( array->data == NULL ) { print_error( "Warning: cannot free NULL multidim data.\n" ); return; } switch( array->n_dimensions ) { case 1: free_memory_1d( (void **) &array->data _ALLOC_SOURCE_LINE ); break; case 2: free_memory_2d( (void ***) &array->data _ALLOC_SOURCE_LINE ); break; case 3: free_memory_3d( (void ****) &array->data _ALLOC_SOURCE_LINE ); break; case 4: free_memory_4d( (void *****) &array->data _ALLOC_SOURCE_LINE ); break; case 5: free_memory_5d( (void ******) &array->data _ALLOC_SOURCE_LINE ); break; } array->data = NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_multidim_n_dimensions @INPUT : array @OUTPUT : @RETURNS : number of dimensions @DESCRIPTION: Returns the number of dimensions of the array @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_multidim_n_dimensions( VIO_multidim_array *array ) { return( array->n_dimensions ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_multidim_data_reordered @INPUT : type_size void_dest_ptr n_dest_dims dest_sizes void_src_ptr n_src_dims src_sizes counts to_dest_index use_src_order - whether to step through arrays in the reverse order of src or dest @OUTPUT : @RETURNS : @DESCRIPTION: Copies any type of multidimensional data from the src array to the destination array. to_dest_index is a lookup that converts src indices to destination indices, to allow arbitrary reordering of array data. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : Feb. 27, 1996 D. MacDonald - made more efficient ---------------------------------------------------------------------------- */ VIOAPI void copy_multidim_data_reordered( int type_size, void *void_dest_ptr, int n_dest_dims, int dest_sizes[], void *void_src_ptr, int n_src_dims, int src_sizes[], int counts[], int to_dest_index[], VIO_BOOL use_src_order ) { char *src_ptr, *dest_ptr; int d; int dest_offsets[VIO_MAX_DIMENSIONS], src_offsets[VIO_MAX_DIMENSIONS]; int dest_offset0, dest_offset1, dest_offset2, dest_offset3; int dest_offset4; int src_offset0, src_offset1, src_offset2, src_offset3; int src_offset4; int dest_steps[VIO_MAX_DIMENSIONS], src_steps[VIO_MAX_DIMENSIONS]; int dest_index; int n_transfer_dims; int src_axis[VIO_MAX_DIMENSIONS], dest_axis[VIO_MAX_DIMENSIONS]; int transfer_counts[VIO_MAX_DIMENSIONS]; int v0, v1, v2, v3, v4; int size0, size1, size2, size3, size4; VIO_BOOL full_count_used; /*--- initialize dest */ dest_ptr = (char *) void_dest_ptr; dest_steps[n_dest_dims-1] = type_size; for_down( d, n_dest_dims-2, 0 ) dest_steps[d] = dest_steps[d+1] * dest_sizes[d+1]; /*--- initialize src */ src_ptr = (char *) void_src_ptr; src_steps[n_src_dims-1] = type_size; for_down( d, n_src_dims-2, 0 ) src_steps[d] = src_steps[d+1] * src_sizes[d+1]; n_transfer_dims = 0; if( getenv( "VOLUME_IO_SRC_ORDER" ) ) use_src_order = TRUE; else if( getenv( "VOLUME_IO_DEST_ORDER" ) ) use_src_order = FALSE; if( use_src_order ) { for_less( d, 0, n_src_dims ) { dest_index = to_dest_index[d]; if( dest_index >= 0 ) { src_axis[n_transfer_dims] = d; dest_axis[n_transfer_dims] = dest_index; src_offsets[n_transfer_dims] = src_steps[d]; dest_offsets[n_transfer_dims] = dest_steps[dest_index]; transfer_counts[n_transfer_dims] = counts[d]; ++n_transfer_dims; } } } else { for_less( dest_index, 0, n_dest_dims ) { for_less( d, 0, n_src_dims ) if( to_dest_index[d] == dest_index ) break; if( d < n_src_dims ) { src_axis[n_transfer_dims] = d; dest_axis[n_transfer_dims] = dest_index; src_offsets[n_transfer_dims] = src_steps[d]; dest_offsets[n_transfer_dims] = dest_steps[dest_index]; transfer_counts[n_transfer_dims] = counts[d]; ++n_transfer_dims; } } } /*--- check if we can transfer more than one at once */ full_count_used = TRUE; while( n_transfer_dims > 0 && src_axis[n_transfer_dims-1] == n_src_dims-1 && dest_axis[n_transfer_dims-1] == n_dest_dims-1 && full_count_used ) { if( transfer_counts[n_transfer_dims-1] != src_sizes[n_src_dims-1] || transfer_counts[n_transfer_dims-1] != dest_sizes[n_dest_dims-1] ) { full_count_used = FALSE; } type_size *= transfer_counts[n_transfer_dims-1]; --n_src_dims; --n_dest_dims; --n_transfer_dims; } for_less( d, 0, n_transfer_dims-1 ) { src_offsets[d] -= src_offsets[d+1] * transfer_counts[d+1]; dest_offsets[d] -= dest_offsets[d+1] * transfer_counts[d+1]; } /*--- slide the transfer dims to the last of the 5 dimensions */ for_down( d, n_transfer_dims-1, 0 ) { src_offsets[d+VIO_MAX_DIMENSIONS-n_transfer_dims] = src_offsets[d]; dest_offsets[d+VIO_MAX_DIMENSIONS-n_transfer_dims] = dest_offsets[d]; transfer_counts[d+VIO_MAX_DIMENSIONS-n_transfer_dims] = transfer_counts[d]; } for_less( d, 0, VIO_MAX_DIMENSIONS-n_transfer_dims ) { transfer_counts[d] = 1; src_offsets[d] = 0; dest_offsets[d] = 0; } size0 = transfer_counts[0]; size1 = transfer_counts[1]; size2 = transfer_counts[2]; size3 = transfer_counts[3]; size4 = transfer_counts[4]; src_offset0 = src_offsets[0]; src_offset1 = src_offsets[1]; src_offset2 = src_offsets[2]; src_offset3 = src_offsets[3]; src_offset4 = src_offsets[4]; dest_offset0 = dest_offsets[0]; dest_offset1 = dest_offsets[1]; dest_offset2 = dest_offsets[2]; dest_offset3 = dest_offsets[3]; dest_offset4 = dest_offsets[4]; for_less( v0, 0, size0 ) { for_less( v1, 0, size1 ) { for_less( v2, 0, size2 ) { for_less( v3, 0, size3 ) { for_less( v4, 0, size4 ) { (void) memcpy( dest_ptr, src_ptr, (size_t) type_size ); src_ptr += src_offset4; dest_ptr += dest_offset4; } src_ptr += src_offset3; dest_ptr += dest_offset3; } src_ptr += src_offset2; dest_ptr += dest_offset2; } src_ptr += src_offset1; dest_ptr += dest_offset1; } src_ptr += src_offset0; dest_ptr += dest_offset0; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_multidim_reordered @INPUT : dest dest_ind src src_ind counts to_dest_index @OUTPUT : @RETURNS : @DESCRIPTION: Copies data from src array to dest array, with dimension translation given by to_dest_index[]. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void copy_multidim_reordered( VIO_multidim_array *dest, int dest_ind[], VIO_multidim_array *src, int src_ind[], int counts[], int to_dest_index[] ) { int n_src_dims, n_dest_dims, type_size; int src_sizes[VIO_MAX_DIMENSIONS], dest_sizes[VIO_MAX_DIMENSIONS]; char *dest_ptr, *src_ptr; void *void_ptr; type_size = get_type_size( get_multidim_data_type(dest) ); /*--- initialize dest */ n_dest_dims = get_multidim_n_dimensions( dest ); get_multidim_sizes( dest, dest_sizes ); GET_MULTIDIM_PTR( void_ptr, *dest, dest_ind[0], dest_ind[1], dest_ind[2], dest_ind[3], dest_ind[4] ); ASSIGN_PTR( dest_ptr ) = void_ptr; /*--- initialize src */ n_src_dims = get_multidim_n_dimensions( src ); get_multidim_sizes( src, src_sizes ); GET_MULTIDIM_PTR( void_ptr, *src, src_ind[0], src_ind[1], src_ind[2], src_ind[3], src_ind[4] ); ASSIGN_PTR( src_ptr ) = void_ptr; copy_multidim_data_reordered( type_size, dest_ptr, n_dest_dims, dest_sizes, src_ptr, n_src_dims, src_sizes, counts, to_dest_index, TRUE ); } libminc-libminc-2-3-00/volume_io/Volumes/output_mnc.c000066400000000000000000001633041257462267400226410ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_MINC1 #include #define INVALID_AXIS -1 #define UNITS "mm" static VIO_Status get_dimension_ordering( int n_vol_dims, VIO_STR vol_dim_names[], int n_file_dims, VIO_STR file_dim_names[], int to_volume[], int to_file[] ); /* ----------------------------- MNI Header ----------------------------------- @NAME : is_default_direction_cosine @INPUT : axis dir_cosines @OUTPUT : @RETURNS : TRUE if is default @DESCRIPTION: Checks to see if the cosine is the default for the axis, i.e., for x axis, is it ( 1, 0, 0 ). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL is_default_direction_cosine( int axis, double dir_cosines[] ) { VIO_BOOL is_default; int i; is_default = TRUE; for_less( i, 0, VIO_N_DIMENSIONS ) { if( (i == axis && dir_cosines[i] != 1.0) || (i != axis && dir_cosines[i] != 0.0) ) { is_default = FALSE; break; } } return( is_default ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_world_transform @INPUT : file space_type voxel_to_world_transform @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the voxel to world transformation, in terms of MINC starts, steps, and direction cosines. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 24, 1995 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - added handling of space type @MODIFIED : May. 20, 1997 D. MacDonald - removed float arithmetic @MODIFIED : May 22, 1997 D. MacDonald - added use_volume_starts_and_steps ---------------------------------------------------------------------------- */ static VIO_Status output_world_transform( Minc_file file, VIO_STR space_type, VIO_General_transform *voxel_to_world_transform, VIO_BOOL use_volume_starts_and_steps_flag ) { double step[MAX_VAR_DIMS]; VIO_Real start[MAX_VAR_DIMS]; double dir_cosines[VIO_MAX_DIMENSIONS][VIO_N_DIMENSIONS]; int dim, axis, spatial_axes[VIO_N_DIMENSIONS]; /*--- set all starts/steps/dir_cosines to default */ for_less( dim, 0, file->n_file_dimensions ) { start[dim] = 0.0; step[dim] = 1.0; dir_cosines[dim][VIO_X] = 0.0; dir_cosines[dim][VIO_Y] = 0.0; dir_cosines[dim][VIO_Z] = 0.0; } /*--- if must use the volume's starts and steps */ if( use_volume_starts_and_steps_flag ) { for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) { if( file->to_volume_index[dim] == INVALID_AXIS ) dir_cosines[dim][axis] = 1.0; /*--- default */ else { start[dim] = file->volume->starts[file->to_volume_index[dim]]; step[dim] = file->volume->separations[file->to_volume_index[dim]]; dir_cosines[dim][VIO_X] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_X]; dir_cosines[dim][VIO_Y] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_Y]; dir_cosines[dim][VIO_Z] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_Z]; } } } } else /*--- convert the linear transform to starts/steps/dir cosines */ { if( voxel_to_world_transform == NULL || get_transform_type( voxel_to_world_transform ) != LINEAR ) { print_error( "Cannot output null or non-linear transforms. Using identity.\n"); for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) dir_cosines[dim][axis] = 1.0; } } else { spatial_axes[0] = INVALID_AXIS; spatial_axes[1] = INVALID_AXIS; spatial_axes[2] = INVALID_AXIS; for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) spatial_axes[axis] = dim; } convert_transform_to_starts_and_steps( voxel_to_world_transform, file->n_file_dimensions, NULL, spatial_axes, start, step, dir_cosines ); } } for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis ) ) { file->dim_ids[dim] = micreate_std_variable( file->cdfid, file->dim_names[dim], NC_DOUBLE, 0, NULL); if( file->dim_ids[dim] < 0 ) return( VIO_ERROR ); (void) miattputdbl( file->cdfid, file->dim_ids[dim], MIstep, step[dim]); (void) miattputdbl( file->cdfid, file->dim_ids[dim], MIstart, start[dim]); if( !is_default_direction_cosine( axis, dir_cosines[dim] ) ) { (void) ncattput( file->cdfid, file->dim_ids[dim], MIdirection_cosines, NC_DOUBLE, VIO_N_DIMENSIONS, dir_cosines[dim]); } (void) miattputstr( file->cdfid, file->dim_ids[dim], MIunits, UNITS ); if( !equal_strings( space_type, MI_UNKNOWN_SPACE ) ) { (void) miattputstr( file->cdfid, file->dim_ids[dim], MIspacetype, space_type ); } } else file->dim_ids[dim] = -1; } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_image_variable @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Defines the image variable in the output minc file. This should be done as the last thing before ending the header definition. @METHOD : @GLOBALS : @CALLS : @CREATED : September 13, 2001 Peter Neelin @MODIFIED : ---------------------------------------------------------------------------- */ static void create_image_variable(Minc_file file) { int old_ncopts; old_ncopts = ncopts; /* Create the variable */ file->img_var_id = micreate_std_variable( file->cdfid, MIimage, file->nc_data_type, file->n_file_dimensions, file->image_dims ); /* Copy all attributes if required */ if( file->src_img_var != MI_ERROR ) { ncopts = 0; (void) micopy_all_atts( file->src_cdfid, file->src_img_var, file->cdfid, file->img_var_id ); (void) ncattdel( file->cdfid, file->img_var_id, MIvalid_max ); (void) ncattdel( file->cdfid, file->img_var_id, MIvalid_min ); (void) ncattdel( file->cdfid, file->img_var_id, MIvalid_range ); ncopts = old_ncopts; } (void) miattputstr( file->cdfid, file->img_var_id, MIcomplete, MI_FALSE ); if( file->signed_flag ) (void) miattputstr( file->cdfid, file->img_var_id, MIsigntype, MI_SIGNED ); else (void) miattputstr( file->cdfid, file->img_var_id, MIsigntype, MI_UNSIGNED ); /* --- put the valid voxel range */ if( file->valid_range[0] < file->valid_range[1] ) { (void) miset_valid_range( file->cdfid, file->img_var_id, file->valid_range); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : end_file_def @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Ends the definition of the file, calling ncendef, but first creating the image variable as the last variable in the file. This is done to allow images > 2 GB (on 64-bit machines) and to ensure that data is written right to the end of the file for backwards compatibility. @METHOD : @GLOBALS : @CALLS : @CREATED : September 13, 2001 Peter Neelin @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status end_file_def(Minc_file file) { int ret; create_image_variable(file); ret = ncendef( file->cdfid ); return ( ret == MI_ERROR ? VIO_ERROR : VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc_output @INPUT : filename n_dimensions dim_names sizes file_nc_data_type file_signed_flag file_voxel_min file_voxel_max voxel_to_world_transform volume_to_attach options @OUTPUT : @RETURNS : minc file @DESCRIPTION: Creates a minc file for outputting volumes. The n_dimensions, dim_names, sizes, file_nc_data_type, file_signed_flag, file_voxel_min, file_voxel_max, and voxel_to_world_transform define the type and shape of the file. The volume_to_attach is the volume that will be output once or many times to fill up the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - added handling of space type @MODIFIED : Nov. 2, 1998 D. MacDonald - fixed the bug with non-global limits on multiple volumes, found by peter ---------------------------------------------------------------------------- */ VIOAPI Minc_file initialize_minc_output( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], int sizes[], nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_General_transform *voxel_to_world_transform, VIO_Volume volume_to_attach, minc_output_options *options ) { minc_file_struct *file; int volume_sizes[VIO_MAX_DIMENSIONS]; int n_volume_dims; int d, vol_index, n_range_dims; static VIO_STR default_dim_names[] = { MIzspace, MIyspace, MIxspace }; VIO_STR *vol_dimension_names; minc_output_options default_options; if( options == (minc_output_options *) NULL ) { set_default_minc_output_options( &default_options ); options = &default_options; } if( dim_names == NULL ) { if( n_dimensions != 3 ) { print_error( "initialize_minc_output: " ); print_error( "can't use NULL dim_names except with 3 dimensions.\n" ); return( (Minc_file) NULL ); } dim_names = default_dim_names; } if( file_nc_data_type == MI_ORIGINAL_TYPE ) { file_nc_data_type = get_volume_nc_data_type( volume_to_attach, &file_signed_flag ); get_volume_voxel_range( volume_to_attach, &file_voxel_min, &file_voxel_max ); } else if( (file_nc_data_type == NC_FLOAT || file_nc_data_type == NC_DOUBLE) && file_voxel_min >= file_voxel_max ) { get_volume_real_range( volume_to_attach, &file_voxel_min, &file_voxel_max ); } /* --- check if dimension name correspondence between volume and file */ n_volume_dims = get_volume_n_dimensions( volume_to_attach ); if( n_volume_dims > n_dimensions ) { print_error( "initialize_minc_output:" ); print_error( " volume (%d) has more dimensions than file (%d).\n", n_volume_dims, n_dimensions ); return( (Minc_file) NULL ); } ALLOC( file, 1 ); file->file_is_being_read = FALSE; file->n_file_dimensions = n_dimensions; file->volume = volume_to_attach; file->outputting_in_order = TRUE; file->entire_file_written = FALSE; file->ignoring_because_cached = FALSE; file->src_img_var = MI_ERROR; file->filename = expand_filename( filename ); if( volume_to_attach->is_cached_volume && volume_to_attach->cache.output_file_is_open && equal_strings( volume_to_attach->cache.output_filename, file->filename)) { file->ignoring_because_cached = TRUE; #ifdef HAVE_MINC1 flush_volume_cache( volume_to_attach ); #endif /*HAVE_MINC1*/ return( file ); } /*--- find correspondence between volume dimensions and file dimensions */ vol_dimension_names = get_volume_dimension_names( volume_to_attach ); if( get_dimension_ordering( n_volume_dims, vol_dimension_names, n_dimensions, dim_names, file->to_volume_index, file->to_file_index ) != VIO_OK ) { FREE( file ); return( (Minc_file) NULL ); } delete_dimension_names( volume_to_attach, vol_dimension_names ); /*--- check if image range specified */ if( options->global_image_range[0] >= options->global_image_range[1] ) { n_range_dims = n_dimensions - 2; if( equal_strings( dim_names[n_dimensions-1], MIvector_dimension ) ) --n_range_dims; for_less( d, n_range_dims, n_dimensions ) { if( file->to_volume_index[d] == INVALID_AXIS ) { print_error( "initialize_minc_output: " ); print_error( "if outputting volumes which don't contain all image\n"); print_error( "dimensions, then must specify global image range.\n" ); FREE( file ); return( (Minc_file) NULL ); } } } /*--- check sizes match between volume and file */ get_volume_sizes( volume_to_attach, volume_sizes ); for_less( d, 0, n_dimensions ) { vol_index = file->to_volume_index[d]; if( vol_index >= 0 && volume_sizes[vol_index] != sizes[d] ) { print_error( "initialize_minc_output: " ); print_error( "volume size[%d]=%d does not match file[%d]=%d.\n", vol_index, volume_sizes[vol_index], d, sizes[d] ); return( NULL ); } } /*--- create the file */ ncopts = NC_VERBOSE; file->cdfid = micreate( file->filename, NC_CLOBBER ); if( file->cdfid == MI_ERROR ) { print_error( "Error: opening MINC file \"%s\".\n", file->filename ); return( NULL ); } /* Create the root variable */ (void) micreate_std_variable(file->cdfid, MIrootvariable, NC_INT, 0, NULL); for_less( d, 0, n_dimensions ) { file->sizes_in_file[d] = (long) sizes[d]; file->indices[d] = 0; file->dim_names[d] = create_string( dim_names[d] ); file->image_dims[d] = ncdimdef( file->cdfid, file->dim_names[d], (long) sizes[d] ); } if( output_world_transform( file, volume_to_attach->coordinate_system_name, voxel_to_world_transform, options->use_volume_starts_and_steps ) != VIO_OK ) { FREE( file ); return( NULL ); } /* * Save information for creating image variable later */ file->nc_data_type = file_nc_data_type; file->signed_flag = file_signed_flag; file->valid_range[0] = file_voxel_min; file->valid_range[1] = file_voxel_max; file->image_range[0] = options->global_image_range[0]; file->image_range[1] = options->global_image_range[1]; if( file->image_range[0] < file->image_range[1] ) { file->min_id = micreate_std_variable( file->cdfid, MIimagemin, NC_DOUBLE, 0, (int *) NULL ); file->max_id = micreate_std_variable( file->cdfid, MIimagemax, NC_DOUBLE, 0, (int *) NULL ); } else { n_range_dims = n_dimensions - 2; if( equal_strings( dim_names[n_dimensions-1], MIvector_dimension ) ) --n_range_dims; file->min_id = micreate_std_variable( file->cdfid, MIimagemin, NC_DOUBLE, n_range_dims, file->image_dims); file->max_id = micreate_std_variable( file->cdfid, MIimagemax, NC_DOUBLE, n_range_dims, file->image_dims ); } ncopts = NC_VERBOSE | NC_FATAL; file->end_def_done = FALSE; file->variables_written = FALSE; return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_auxiliary_data_from_minc_file @INPUT : file filename history_string @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Copies the auxiliary data from the filename to the opened Minc file, 'file'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_auxiliary_data_from_minc_file( Minc_file file, VIO_STR filename, VIO_STR history_string ) { VIO_Status status; int src_cdfid; VIO_STR expanded; if( file->ignoring_because_cached ) return( VIO_OK ); ncopts = NC_VERBOSE; expanded = expand_filename( filename ); src_cdfid = miopen( expanded, NC_NOWRITE ); if( src_cdfid == MI_ERROR ) { print_error( "Error opening %s\n", expanded ); return( VIO_ERROR ); } delete_string( expanded ); status = copy_auxiliary_data_from_open_minc_file( file, src_cdfid, history_string ); (void) miclose( src_cdfid ); ncopts = NC_VERBOSE | NC_FATAL; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_auxiliary_data_from_open_minc_file @INPUT : file src_cdfid history_string @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Copies the auxiliary data from the opened minc file specified by src_cdfid to the opened minc file specified by 'file'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_auxiliary_data_from_open_minc_file( Minc_file file, int src_cdfid, VIO_STR history_string ) { int src_img_var, varid, n_excluded, excluded_vars[10]; int i, src_min_id, src_max_id, src_root_id; VIO_Status status; VIO_STR excluded_list[] = { MIxspace, MIyspace, MIzspace, MItime, MItfrequency, MIxfrequency, MIyfrequency, MIzfrequency, MIvector_dimension }; if( file->ignoring_because_cached ) return( VIO_OK ); if( file->end_def_done ) { print_error( "Cannot call copy_auxiliary_data_from_open_minc_file when not in define mode\n" ); return( VIO_ERROR ); } ncopts = 0; n_excluded = 0; for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY( excluded_list ) ) { if( (varid = ncvarid(src_cdfid, excluded_list[i] )) != MI_ERROR ) excluded_vars[n_excluded++] = varid; } if( (src_img_var = ncvarid(src_cdfid, MIimage )) != MI_ERROR ) excluded_vars[n_excluded++] = src_img_var; if( (src_max_id = ncvarid(src_cdfid, MIimagemax )) != MI_ERROR ) excluded_vars[n_excluded++] = src_max_id; if( (src_min_id = ncvarid(src_cdfid, MIimagemin )) != MI_ERROR ) excluded_vars[n_excluded++] = src_min_id; if( (src_root_id = ncvarid(src_cdfid, MIrootvariable )) != MI_ERROR ) excluded_vars[n_excluded++] = src_root_id; ncopts = NC_VERBOSE; (void) micopy_all_var_defs( src_cdfid, file->cdfid, n_excluded, excluded_vars ); if( src_min_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_min_id, file->cdfid, file->min_id ); } if( src_max_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_max_id, file->cdfid, file->max_id ); } if( src_root_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_root_id, file->cdfid, ncvarid( file->cdfid, MIrootvariable) ); } status = VIO_OK; if( history_string != NULL ) status = add_minc_history( file, history_string ); if( status == VIO_OK ) { /* Set info for copying image attributes. Unset afterwards, just to be safe. */ file->src_cdfid = src_cdfid; file->src_img_var = src_img_var; status = end_file_def( file ); file->src_img_var = MI_ERROR; if( status != VIO_OK ) { print_error( "Error outputting volume: possibly disk full?\n" ); } } if( status == VIO_OK ) { file->end_def_done = TRUE; (void) micopy_all_var_values( src_cdfid, file->cdfid, n_excluded, excluded_vars ); } ncopts = NC_VERBOSE | NC_FATAL; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : add_minc_history @INPUT : file history_string @OUTPUT : @RETURNS : @DESCRIPTION: Adds the history_string to the history in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 2, 1998 D. MacDonald - fixed error P. Neelin found with concating to non-existent history ---------------------------------------------------------------------------- */ VIOAPI VIO_Status add_minc_history( Minc_file file, VIO_STR history_string ) { int old_att_length; nc_type datatype; VIO_STR new_history; if( file->ignoring_because_cached ) return( VIO_OK ); if( file->end_def_done ) { print_error( "Cannot call add_minc_history when not in define mode\n" ); return( VIO_ERROR ); } ncopts = 0; if( ncattinq(file->cdfid, NC_GLOBAL, MIhistory, &datatype, &old_att_length) == MI_ERROR || datatype != NC_CHAR ) { old_att_length = 0; } /* Allocate a string and get the old history */ new_history = alloc_string( old_att_length ); new_history[0] = (char) 0; (void) miattgetstr( file->cdfid, NC_GLOBAL, MIhistory, old_att_length+1, new_history ); /* if( new_history[old_att_length] != (char) 0 ) { print( "old_att_length %d %d\n", old_att_length, new_history[old_att_length] ); new_history[old_att_length] = (char) 0; print( "string: %d\n", new_history ); } */ /* Add the new command and put the new history. */ concat_to_string( &new_history, history_string ); ncopts = NC_VERBOSE | NC_FATAL; (void) miattputstr( file->cdfid, NC_GLOBAL, MIhistory, new_history ); delete_string( new_history ); return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dimension_ordering @INPUT : n_vol_dims vol_dim_names n_file_dims file_dim_names @OUTPUT : to_volume to_file @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Matches dimension names between the volume and file, setting the axis conversion from file to_volume and from volume to_file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status get_dimension_ordering( int n_vol_dims, VIO_STR vol_dim_names[], int n_file_dims, VIO_STR file_dim_names[], int to_volume[], int to_file[] ) { VIO_Status status; int v, f, n_found; for_less( f, 0, n_file_dims ) to_volume[f] = -1; for_less( v, 0, n_vol_dims ) to_file[v] = -1; n_found = 0; for_less( v, 0, n_vol_dims ) { for_less( f, 0, n_file_dims ) { if( to_volume[f] < 0 && equal_strings( vol_dim_names[v], file_dim_names[f] ) ) { to_volume[f] = v; to_file[v] = f; ++n_found; } } } if( n_found != n_vol_dims ) { print_error( "Unsuccessful matching of volume and output dimension names.\n"); status = VIO_ERROR; } else status = VIO_OK; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_minc_output_variables @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Checks if the file variables has been put into data mode, and does so if necessary. Then it checks if the variables have been written, and does so if necessary. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status check_minc_output_variables( Minc_file file ) { int d, axis; long start_index, mindex[MAX_VAR_DIMS]; VIO_Real voxel_min, voxel_max, real_min, real_max; double dim_value; VIO_Volume volume; VIO_Status status; if( !file->end_def_done ) { /* --- Get into data mode */ ncopts = NC_VERBOSE; status = end_file_def( file ); ncopts = NC_VERBOSE | NC_FATAL; file->end_def_done = TRUE; if( status != VIO_OK ) { print_error( "Error outputting volume: possibly disk full?\n" ); return( status ); } } if( !file->variables_written ) { volume = file->volume; file->variables_written = TRUE; ncopts = NC_VERBOSE; for_less( d, 0, file->n_file_dimensions ) mindex[d] = 0; dim_value = 0.0; for_less( d, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[d], &axis ) ) { (void) mivarput1( file->cdfid, file->dim_ids[d], mindex, NC_DOUBLE, MI_SIGNED, &dim_value ); } } file->minc_icv = miicv_create(); (void) miicv_setint( file->minc_icv, MI_ICV_TYPE, (int) volume->nc_data_type); (void) miicv_setstr( file->minc_icv, MI_ICV_SIGN, volume->signed_flag ? MI_SIGNED : MI_UNSIGNED ); (void) miicv_setint( file->minc_icv, MI_ICV_DO_NORM, TRUE ); (void) miicv_setint( file->minc_icv, MI_ICV_USER_NORM, TRUE ); if( file->image_range[0] < file->image_range[1] ) { (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MIN, file->image_range[0] ); (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MAX, file->image_range[1] ); } else { get_volume_real_range( volume, &real_min, &real_max ); (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MIN, real_min ); (void) miicv_setdbl( file->minc_icv, MI_ICV_IMAGE_MAX, real_max ); } get_volume_voxel_range( volume, &voxel_min, &voxel_max ); if( voxel_min < voxel_max ) { (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MIN, voxel_min ); (void) miicv_setdbl( file->minc_icv, MI_ICV_VALID_MAX, voxel_max ); } else print_error( "Volume has invalid min and max voxel value\n" ); (void) miicv_attach( file->minc_icv, file->cdfid, file->img_var_id ); start_index = 0; if( file->image_range[0] < file->image_range[1] ) { (void) mivarput1( file->cdfid, file->min_id, &start_index, NC_DOUBLE, MI_SIGNED, &file->image_range[0] ); (void) mivarput1( file->cdfid, file->max_id, &start_index, NC_DOUBLE, MI_SIGNED, &file->image_range[1] ); } ncopts = NC_VERBOSE | NC_FATAL; } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_output_random_order @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Sets the file into random order access, used by volume caching. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 26, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status set_minc_output_random_order( Minc_file file ) { VIO_Status status; status = check_minc_output_variables( file ); file->outputting_in_order = FALSE; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_minc_hyperslab @INPUT : file data_type n_array_dims array_sizes array_data_ptr to_array file_start file_count @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs a hyperslab from an array to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_minc_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int file_start[], int file_count[] ) { int ind, expected_ind, file_ind, dim; int n_file_dims, n_tmp_dims; long long_file_start[VIO_MAX_DIMENSIONS]; long long_file_count[VIO_MAX_DIMENSIONS]; void *void_ptr; VIO_BOOL direct_from_array, non_full_size_found; int tmp_ind, tmp_sizes[VIO_MAX_DIMENSIONS]; int array_indices[VIO_MAX_DIMENSIONS]; int array_counts[MAX_VAR_DIMS]; VIO_Status status; VIO_multidim_array buffer_array; status = check_minc_output_variables( file ); if( status != VIO_OK ) return( status ); n_file_dims = file->n_file_dimensions; expected_ind = n_array_dims-1; tmp_ind = n_file_dims-1; non_full_size_found = FALSE; for_less( ind, 0, n_array_dims ) { array_indices[ind] = -1; array_counts[ind] = 1; } direct_from_array = TRUE; /*--- determine if the hyperslab represents a consecutive chunk of memory in the array */ for( file_ind = n_file_dims-1; file_ind >= 0; --file_ind ) { long_file_start[file_ind] = (long) file_start[file_ind]; long_file_count[file_ind] = (long) file_count[file_ind]; ind = to_array[file_ind]; if( ind != INVALID_AXIS ) { array_counts[ind] = file_count[file_ind]; if( !non_full_size_found && (long) file_count[file_ind] < file->sizes_in_file[file_ind] ) non_full_size_found = TRUE; else if( non_full_size_found && file_count[file_ind] > 1 ) direct_from_array = FALSE; if( file_count[file_ind] > 1 && ind != expected_ind ) direct_from_array = FALSE; if( file_count[file_ind] != 1 || file->sizes_in_file[file_ind] == 1 ) { tmp_sizes[tmp_ind] = file_count[file_ind]; array_indices[ind] = tmp_ind; --tmp_ind; } --expected_ind; } } if( direct_from_array ) /* hyperslab is consecutive chunk of memory */ { void_ptr = array_data_ptr; } else { /*--- create a temporary array to copy hyperslab to, so that we have a consecutive chunk of memory to write from */ n_tmp_dims = n_file_dims - tmp_ind - 1; for_less( dim, 0, n_tmp_dims ) tmp_sizes[dim] = tmp_sizes[dim+tmp_ind+1]; for_less( dim, 0, n_array_dims ) array_indices[dim] -= tmp_ind + 1; create_multidim_array( &buffer_array, n_tmp_dims, tmp_sizes, data_type); GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); /*--- copy from the array argument to the temporary array */ copy_multidim_data_reordered( get_type_size(data_type), void_ptr, n_tmp_dims, tmp_sizes, array_data_ptr, n_array_dims, array_sizes, array_counts, array_indices, TRUE ); GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); } /*--- output the data to the file */ if( miicv_put( file->minc_icv, long_file_start, long_file_count, void_ptr ) == MI_ERROR ) status = VIO_ERROR; else status = VIO_OK; if( !direct_from_array ) delete_multidim_array( &buffer_array ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_slab @INPUT : file volume to_volume file_start file_count @OUTPUT : @RETURNS : @DESCRIPTION: Outputs the slab specified by the file start and count arrays, from the volume. The to_volume array translates axes in the file to axes in the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Sep 1, 1995 D. MacDonald - added cached volumes. ---------------------------------------------------------------------------- */ static void output_slab( Minc_file file, VIO_Volume volume, int to_volume[], long file_start[], long file_count[] ) { int dim, file_ind, ind, n_slab_dims; int to_array[VIO_MAX_DIMENSIONS]; int volume_start[VIO_MAX_DIMENSIONS]; int volume_sizes[VIO_MAX_DIMENSIONS]; int array_to_volume[VIO_MAX_DIMENSIONS]; int int_file_count[VIO_MAX_DIMENSIONS]; int int_file_start[VIO_MAX_DIMENSIONS]; int slab_sizes[VIO_MAX_DIMENSIONS]; int v[VIO_MAX_DIMENSIONS]; int size0, size1, size2, size3, size4; VIO_Real value; void *array_data_ptr; VIO_multidim_array array; for_less( file_ind, 0, file->n_file_dimensions ) { int_file_start[file_ind] = (int) file_start[file_ind]; int_file_count[file_ind] = (int) file_count[file_ind]; ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) volume_start[ind] = int_file_start[file_ind]; else volume_start[ind] = 0; } #ifdef HAVE_MINC1 if( volume->is_cached_volume ) { /*--- must make a temporary hyperslab array to contain the volume */ for_less( dim, 0, get_volume_n_dimensions(volume) ) volume_sizes[dim] = 1; for_less( dim, 0, VIO_MAX_DIMENSIONS ) array_to_volume[dim] = 0; for_less( dim, get_volume_n_dimensions(volume), VIO_MAX_DIMENSIONS ) { volume_start[dim] = 0; volume_sizes[dim] = 1; } n_slab_dims = 0; for_less( file_ind, 0, file->n_file_dimensions ) { ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) { to_array[file_ind] = n_slab_dims; array_to_volume[n_slab_dims] = ind; slab_sizes[n_slab_dims] = int_file_count[file_ind]; volume_sizes[ind] = int_file_count[file_ind]; ++n_slab_dims; } else { to_array[file_ind] = INVALID_AXIS; } } create_multidim_array( &array, n_slab_dims, slab_sizes, get_volume_data_type(volume) ); /*--- copy from the cached volume to the temporary array */ size0 = volume_sizes[0]; size1 = volume_sizes[1]; size2 = volume_sizes[2]; size3 = volume_sizes[3]; size4 = volume_sizes[4]; for_less( v[0], 0, size0 ) for_less( v[1], 0, size1 ) for_less( v[2], 0, size2 ) for_less( v[3], 0, size3 ) for_less( v[4], 0, size4 ) { value = get_volume_voxel_value( volume, volume_start[0] + v[0], volume_start[1] + v[1], volume_start[2] + v[2], volume_start[3] + v[3], volume_start[4] + v[4] ); SET_MULTIDIM( array, v[array_to_volume[0]], v[array_to_volume[1]], v[array_to_volume[2]], v[array_to_volume[3]], v[array_to_volume[4]], value ); } /*--- output the temporary array */ GET_MULTIDIM_PTR( array_data_ptr, array, 0, 0, 0, 0, 0 ); (void) output_minc_hyperslab( file, get_volume_data_type(volume), n_slab_dims, slab_sizes, array_data_ptr, to_array, int_file_start, int_file_count); delete_multidim_array( &array ); } else { #endif /*HAVE_MINC1*/ GET_MULTIDIM_PTR( array_data_ptr, volume->array, volume_start[0], volume_start[1], volume_start[2], volume_start[3], volume_start[4] ); get_volume_sizes( volume, volume_sizes ); #ifdef HAVE_MINC1 (void) output_minc_hyperslab( file, get_volume_data_type(volume), get_volume_n_dimensions(volume), volume_sizes, array_data_ptr, to_volume, int_file_start, int_file_count ); #else /*HAVE_MINC1*/ /*TODO: implement MINC2 api based reader*/ #endif /*HAVE_MINC1*/ #ifdef HAVE_MINC1 } #endif /*HAVE_MINC1*/ } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_the_volume @INPUT : file volume volume_count file_start @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the volume to the file in the given position. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status output_the_volume( Minc_file file, VIO_Volume volume, int volume_count[], long file_start[] ) { VIO_Status status; int d, n_volume_dims, sizes[VIO_MAX_DIMENSIONS]; int slab_size, n_slab, this_count; int vol_index, step, n_steps, n_range_dims; int to_volume_index[MAX_VAR_DIMS]; int to_file_index[VIO_MAX_DIMENSIONS]; long file_indices[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; VIO_Real real_min, real_max; VIO_STR *vol_dimension_names; VIO_BOOL increment; VIO_progress_struct progress; status = check_minc_output_variables( file ); if( status != VIO_OK ) return( status ); /* --- check if dimension name correspondence between volume and file */ n_volume_dims = get_volume_n_dimensions( volume ); if( n_volume_dims > file->n_file_dimensions ) { print_error( "output_volume_to_minc_file_position:" ); print_error( " volume (%d) has more dimensions than file (%d).\n", n_volume_dims, file->n_file_dimensions ); return( VIO_ERROR ); } /*--- find correspondence between volume dimensions and file dimensions */ vol_dimension_names = get_volume_dimension_names( volume ); status = get_dimension_ordering( n_volume_dims, vol_dimension_names, file->n_file_dimensions, file->dim_names, to_volume_index, to_file_index ); delete_dimension_names( volume, vol_dimension_names ); if( status != VIO_OK ) return( VIO_ERROR ); /*--- check sizes match between volume and file */ get_volume_sizes( volume, sizes ); for_less( d, 0, file->n_file_dimensions ) { vol_index = to_volume_index[d]; if( vol_index >= 0 ) { if( volume_count[vol_index] < 0 || volume_count[vol_index] > sizes[vol_index] ) { print_error( "output_the_volume: invalid volume count.\n" ); print_error( " count[%d] = %d\n", vol_index, volume_count[vol_index] ); return( VIO_ERROR ); } this_count = volume_count[vol_index]; } else { this_count = 1; } if( file_start[d] < 0 || file_start[d] + (long) this_count > file->sizes_in_file[d] ) { print_error( "output_the_volume: invalid minc file position.\n" ); print_error( " start[%d] = %ld count[%d] = %d\n", d, file_start[d], d, this_count ); return( VIO_ERROR ); } } /*--- if per slice image ranges, output the ranges corresponding to this volume */ if( file->image_range[0] >= file->image_range[1] ) { long n_ranges, range_start[MAX_VAR_DIMS], range_count[MAX_VAR_DIMS]; long r; double *image_range; n_range_dims = file->n_file_dimensions - 2; if( equal_strings( file->dim_names[file->n_file_dimensions-1], MIvector_dimension ) ) --n_range_dims; n_ranges = 1; for_less( d, 0, n_range_dims ) { vol_index = to_volume_index[d]; if( vol_index == INVALID_AXIS ) { range_count[d] = 1; range_start[d] = file_start[d]; } else { n_ranges *= (long) volume_count[vol_index]; range_count[d] = (long) volume_count[vol_index]; range_start[d] = 0; } } get_volume_real_range( volume, &real_min, &real_max ); ALLOC( image_range, n_ranges ); for_less( r, 0, n_ranges ) image_range[r] = real_min; (void) mivarput( file->cdfid, file->min_id, range_start, range_count, NC_DOUBLE, MI_UNSIGNED, (void *) image_range ); for_less( r, 0, n_ranges ) image_range[r] = real_max; (void) mivarput( file->cdfid, file->max_id, range_start, range_count, NC_DOUBLE, MI_UNSIGNED, (void *) image_range ); FREE( image_range ); } /* --- determine which contiguous blocks of volume to output to max out the read/write buffer and make it like the chunking dimensions for compression (for efficiency) */ file->n_slab_dims = 0; slab_size = 1; n_steps = 1; int unit_size = get_type_size( get_volume_data_type(volume) ); for( d = file->n_file_dimensions-1; d >= 0; d-- ) { count[d] = 1; file_indices[d] = file_start[d]; if( to_volume_index[d] != INVALID_AXIS ) { if( MI_MAX_VAR_BUFFER_SIZE > volume_count[to_volume_index[d]] * slab_size * unit_size && n_steps == 1 ) { count[d] = volume_count[to_volume_index[d]]; file->n_slab_dims++; /* integral number of complete dimensions */ } else { count[d] = MIN( volume_count[to_volume_index[d]], (long)( MI_MAX_VAR_BUFFER_SIZE / ( slab_size * unit_size ) ) ); n_steps *= (int)( ( volume_count[to_volume_index[d]] + count[d] - 1 ) / count[d] ); } slab_size *= count[d]; } } if( file->n_slab_dims == 0 ) { fprintf( stderr, "Error: You seem to be processing a minc file\n" ); fprintf( stderr, "with no valid axes. Please check your volume.\n" ); exit(1); } /*--- now write entire volume in contiguous chunks (possibly only 1 req'd)*/ step = 0; initialize_progress_report( &progress, FALSE, n_steps,"Outputting Volume" ); increment = FALSE; while( !increment ) { /*--- set the indices of the file slab to write */ long local_count[MAX_VAR_DIMS]; for( d = 0; d < file->n_file_dimensions; d++ ) { vol_index = to_volume_index[d]; local_count[d] = MIN( volume_count[vol_index] - file_indices[d], count[d] ); } output_slab( file, volume, to_volume_index, file_indices, local_count ); /*--- increment the file index dimensions which correspond for the next slab to write to output */ increment = TRUE; d = file->n_file_dimensions-1; n_slab = 0; while( increment && d >= 0 ) { vol_index = to_volume_index[d]; if( vol_index != INVALID_AXIS && n_slab >= file->n_slab_dims ) { file_indices[d] += local_count[d]; if( file_indices[d] < file_start[d] + (long) volume_count[vol_index] ) { increment = FALSE; } else { file_indices[d] = file_start[d]; } } if( vol_index != INVALID_AXIS ) ++n_slab; --d; } ++step; if( n_steps > 1 ) update_progress_report( &progress, step ); } terminate_progress_report( &progress ); if( step != n_steps ) { fprintf( stderr, "Error: Your output minc file may be incomplete\n" ); fprintf( stderr, "(wrote only %d out of %d buffers)\n", step, n_steps ); exit(1); } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_volume_to_minc_file_position @INPUT : file volume volume_count file_start @OUTPUT : Outputs the volume to the specified file position. @RETURNS : @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_volume_to_minc_file_position( Minc_file file, VIO_Volume volume, int volume_count[], long file_start[] ) { if( file->ignoring_because_cached ) return( VIO_OK ); file->outputting_in_order = FALSE; return( output_the_volume( file, volume, volume_count, file_start ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_minc_volume @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the attached volume to the MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_minc_volume( Minc_file file ) { int d, volume_count[VIO_MAX_DIMENSIONS]; VIO_BOOL increment; if( file->ignoring_because_cached ) return( VIO_OK ); /*--- check number of volumes written */ d = 0; while( d < file->n_file_dimensions && file->to_volume_index[d] != INVALID_AXIS ) ++d; if( d < file->n_file_dimensions && file->indices[d] >= file->sizes_in_file[d] ) { print_error( "output_minc_volume: attempted to write too many subvolumes.\n"); return( VIO_ERROR ); } get_volume_sizes( file->volume, volume_count ); if( output_the_volume( file, file->volume, volume_count, file->indices ) != VIO_OK ) return( VIO_ERROR ); /*--- increment the file index dimensions which do not correspond to volume dimensions */ increment = TRUE; d = file->n_file_dimensions-1; while( increment && d >= 0 ) { if( file->to_volume_index[d] == INVALID_AXIS ) { ++file->indices[d]; if( file->indices[d] < file->sizes_in_file[d] ) increment = FALSE; else file->indices[d] = 0; } --d; } if( increment ) file->entire_file_written = TRUE; return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_output @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Closes the MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status close_minc_output( Minc_file file ) { int d; if( file == (Minc_file) NULL ) { print_error( "close_minc_output(): NULL file.\n" ); return( VIO_ERROR ); } if( !file->ignoring_because_cached ) { if( file->outputting_in_order && !file->entire_file_written ) { print_error( "Warning: the MINC file has been " ); print_error( "closed without writing part of it.\n"); } (void) miattputstr( file->cdfid, file->img_var_id, MIcomplete, MI_TRUE); (void) miclose( file->cdfid ); (void) miicv_free( file->minc_icv ); for_less( d, 0, file->n_file_dimensions ) delete_string( file->dim_names[d] ); } delete_string( file->filename ); FREE( file ); return( VIO_OK ); } #endif /*HAVE_MINC1*/ /* ----------------------------- MNI Header ----------------------------------- @NAME : set_default_minc_output_options @INPUT : @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the minc output options to the default. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - added use_volume_starts_and_steps ---------------------------------------------------------------------------- */ VIOAPI void set_default_minc_output_options( minc_output_options *options ) { int dim; for_less( dim, 0, VIO_MAX_DIMENSIONS ) options->dimension_names[dim] = NULL; options->global_image_range[0] = 0.0; options->global_image_range[1] = -1.0; options->use_volume_starts_and_steps = FALSE; options->use_starts_set = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_minc_output_options @INPUT : src @OUTPUT : dest @RETURNS : @DESCRIPTION: Copies the minc output options to a new structure. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 12, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void copy_minc_output_options( minc_output_options *src, minc_output_options *dest ) { int dim; if( src == NULL ) set_default_minc_output_options( dest ); else { *dest = *src; for_less( dim, 0, VIO_MAX_DIMENSIONS ) { if( src->dimension_names[dim] != NULL ) dest->dimension_names[dim] = create_string( src->dimension_names[dim] ); } } } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_minc_output_options @INPUT : options @OUTPUT : @RETURNS : @DESCRIPTION: Deletes the minc output options. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_minc_output_options( minc_output_options *options ) { int i; for_less( i, 0, VIO_MAX_DIMENSIONS ) delete_string( options->dimension_names[i] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_output_dimensions_order @INPUT : n_dimensions dimension_names @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the dimension ordering of the minc output options. This option is used by output_volume, but not by initialize_minc_output. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_output_dimensions_order( minc_output_options *options, int n_dimensions, VIO_STR dimension_names[] ) { int i; for_less( i, 0, n_dimensions ) { replace_string( &options->dimension_names[i], create_string(dimension_names[i]) ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_output_real_range @INPUT : real_min real_max @OUTPUT : options @RETURNS : @DESCRIPTION: Sets the global real range of the entire file, unless real_min >= real_max. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_output_real_range( minc_output_options *options, VIO_Real real_min, VIO_Real real_max ) { options->global_image_range[0] = real_min; options->global_image_range[1] = real_max; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc_output_use_volume_starts_and_steps_flag @INPUT : options flag @OUTPUT : @RETURNS : @DESCRIPTION: Tells MINC output to use the exact starts and steps stored in the volume, not the voxel-to-world-transform. This avoids round-off errors in converting to transform on input, then from transform on output. @METHOD : @GLOBALS : @CALLS : @CREATED : May. 22, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_minc_output_use_volume_starts_and_steps_flag( minc_output_options *options, VIO_BOOL flag ) { options->use_volume_starts_and_steps = flag; options->use_starts_set = TRUE; } libminc-libminc-2-3-00/volume_io/Volumes/output_mnc2.c000066400000000000000000001314551257462267400227250ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #ifdef HAVE_MINC2 #include #define INVALID_AXIS -1 static VIO_Status get_dimension_ordering( int n_vol_dims, VIO_STR vol_dim_names[], int n_file_dims, VIO_STR file_dim_names[], int to_volume[], int to_file[] ); /* ----------------------------- MNI Header ----------------------------------- @NAME : is_default_direction_cosine @INPUT : axis dir_cosines @OUTPUT : @RETURNS : TRUE if is default @DESCRIPTION: Checks to see if the cosine is the default for the axis, i.e., for x axis, is it ( 1, 0, 0 ). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_BOOL is_default_direction_cosine( int axis, double dir_cosines[] ) { VIO_BOOL is_default; int i; is_default = TRUE; for_less( i, 0, VIO_N_DIMENSIONS ) { if( (i == axis && dir_cosines[i] != 1.0) || (i != axis && dir_cosines[i] != 0.0) ) { is_default = FALSE; break; } } return( is_default ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_world_transform @INPUT : file space_type voxel_to_world_transform @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the voxel to world transformation, in terms of MINC starts, steps, and direction cosines. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 24, 1995 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - added handling of space type @MODIFIED : May. 20, 1997 D. MacDonald - removed float arithmetic @MODIFIED : May 22, 1997 D. MacDonald - added use_volume_starts_and_steps ---------------------------------------------------------------------------- */ static VIO_Status output_world_transform( Minc_file file, VIO_STR space_type, VIO_General_transform *voxel_to_world_transform, VIO_BOOL use_volume_starts_and_steps_flag, midimhandle_t *minc_dimensions ) { double step[MAX_VAR_DIMS]; VIO_Real start[MAX_VAR_DIMS]; double dir_cosines[VIO_MAX_DIMENSIONS][VIO_N_DIMENSIONS]; int dim, axis, spatial_axes[VIO_N_DIMENSIONS]; /*--- set all starts/steps/dir_cosines to default */ for_less( dim, 0, file->n_file_dimensions ) { start[dim] = 0.0; step[dim] = 1.0; dir_cosines[dim][VIO_X] = 0.0; dir_cosines[dim][VIO_Y] = 0.0; dir_cosines[dim][VIO_Z] = 0.0; } /*--- if must use the volume's starts and steps */ if( use_volume_starts_and_steps_flag ) { for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) { if( file->to_volume_index[dim] == INVALID_AXIS ) dir_cosines[dim][axis] = 1.0; /*--- default */ else { start[dim] = file->volume->starts[file->to_volume_index[dim]]; step[dim] = file->volume->separations[file->to_volume_index[dim]]; dir_cosines[dim][VIO_X] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_X]; dir_cosines[dim][VIO_Y] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_Y]; dir_cosines[dim][VIO_Z] = file->volume->direction_cosines [file->to_volume_index[dim]][VIO_Z]; } } } } else /*--- convert the linear transform to starts/steps/dir cosines */ { if( voxel_to_world_transform == NULL || get_transform_type( voxel_to_world_transform ) != LINEAR ) { print_error( "Cannot output null or non-linear transforms. Using identity.\n"); for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) dir_cosines[dim][axis] = 1.0; } } else { spatial_axes[0] = INVALID_AXIS; spatial_axes[1] = INVALID_AXIS; spatial_axes[2] = INVALID_AXIS; for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis )) spatial_axes[axis] = dim; } convert_transform_to_starts_and_steps( voxel_to_world_transform, file->n_file_dimensions, NULL, spatial_axes, start, step, dir_cosines ); } } for_less( dim, 0, file->n_file_dimensions ) { if( convert_dim_name_to_spatial_axis( file->dim_names[dim], &axis ) ) { miset_dimension_separation(minc_dimensions[dim],step[dim]); miset_dimension_start(minc_dimensions[dim],start[dim]); miset_dimension_apparent_voxel_order(minc_dimensions[dim],MI_POSITIVE); if( !is_default_direction_cosine( axis, dir_cosines[dim] ) ) { miset_dimension_cosines(minc_dimensions[dim],dir_cosines[dim]); } } else file->dim_ids[dim] = -1; } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_minc2_output @INPUT : filename n_dimensions dim_names sizes file_nc_data_type file_signed_flag file_voxel_min file_voxel_max voxel_to_world_transform volume_to_attach options @OUTPUT : @RETURNS : minc file @DESCRIPTION: Creates a minc file for outputting volumes. The n_dimensions, dim_names, sizes, file_nc_data_type, file_signed_flag, file_voxel_min, file_voxel_max, and voxel_to_world_transform define the type and shape of the file. The volume_to_attach is the volume that will be output once or many times to fill up the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - added handling of space type @MODIFIED : Nov. 2, 1998 D. MacDonald - fixed the bug with non-global limits on multiple volumes, found by peter ---------------------------------------------------------------------------- */ VIOAPI Minc_file initialize_minc2_output( VIO_STR filename, int n_dimensions, VIO_STR dim_names[], int sizes[], nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_General_transform *voxel_to_world_transform, VIO_Volume volume_to_attach, minc_output_options *options ) { minc_file_struct *file; int volume_sizes[VIO_MAX_DIMENSIONS]; int n_volume_dims; int d, vol_index, n_range_dims; static VIO_STR default_dim_names[] = { MIzspace, MIyspace, MIxspace }; VIO_STR *vol_dimension_names; minc_output_options default_options; mitype_t file_minc_datatype; midimhandle_t minc_dimensions[VIO_MAX_DIMENSIONS]; mivolumeprops_t hprops; if( minew_volume_props(&hprops) < 0) { return NULL; } if( options == (minc_output_options *) NULL ) { set_default_minc_output_options( &default_options ); options = &default_options; } /*TODO: provide some other compression ? */ miset_props_compression_type(hprops, MI_COMPRESS_ZLIB); miset_props_zlib_compression(hprops,4); if( dim_names == NULL ) { if( n_dimensions != 3 ) { print_error( "initialize_minc_output: " ); print_error( "can't use NULL dim_names except with 3 dimensions.\n" ); mifree_volume_props( hprops ); return( (Minc_file) NULL ); } dim_names = default_dim_names; } if( file_nc_data_type == MI_ORIGINAL_TYPE ) { file_nc_data_type = get_volume_nc_data_type( volume_to_attach, &file_signed_flag ); get_volume_voxel_range( volume_to_attach, &file_voxel_min, &file_voxel_max ); } else if( (file_nc_data_type == NC_FLOAT || file_nc_data_type == NC_DOUBLE) && file_voxel_min >= file_voxel_max ) { get_volume_real_range( volume_to_attach, &file_voxel_min, &file_voxel_max ); } file_minc_datatype = nc_type_to_minc2_type(file_nc_data_type , file_signed_flag); /* --- check if dimension name correspondence between volume and file */ n_volume_dims = get_volume_n_dimensions( volume_to_attach ); if( n_volume_dims > n_dimensions ) { print_error( "initialize_minc_output:" ); print_error( " volume (%d) has more dimensions than file (%d).\n", n_volume_dims, n_dimensions ); mifree_volume_props( hprops ); return( (Minc_file) NULL ); } ALLOC( file, 1 ); file->file_is_being_read = FALSE; file->n_file_dimensions = n_dimensions; file->volume = volume_to_attach; file->outputting_in_order = TRUE; file->entire_file_written = FALSE; file->ignoring_because_cached = FALSE; file->src_img_var = MI_ERROR; file->filename = expand_filename( filename ); /*--- find correspondence between volume dimensions and file dimensions */ vol_dimension_names = get_volume_dimension_names( volume_to_attach ); if( get_dimension_ordering( n_volume_dims, vol_dimension_names, n_dimensions, dim_names, file->to_volume_index, file->to_file_index ) != VIO_OK ) { FREE( file ); delete_dimension_names( volume_to_attach, vol_dimension_names ); mifree_volume_props( hprops ); return( (Minc_file) NULL ); } delete_dimension_names( volume_to_attach, vol_dimension_names ); /*--- check if image range specified */ if( options->global_image_range[0] >= options->global_image_range[1] ) { n_range_dims = n_dimensions - 2; if( equal_strings( dim_names[n_dimensions-1], MIvector_dimension ) ) --n_range_dims; for_less( d, n_range_dims, n_dimensions ) { if( file->to_volume_index[d] == INVALID_AXIS ) { print_error( "initialize_minc_output: " ); print_error( "if outputting volumes which don't contain all image\n"); print_error( "dimensions, then must specify global image range.\n" ); FREE( file ); mifree_volume_props( hprops ); return( (Minc_file) NULL ); } } } /*--- check sizes match between volume and file */ get_volume_sizes( volume_to_attach, volume_sizes ); for_less( d, 0, n_dimensions ) { vol_index = file->to_volume_index[d]; if( vol_index >= 0 && volume_sizes[vol_index] != sizes[d] ) { print_error( "initialize_minc_output: " ); print_error( "volume size[%d]=%d does not match file[%d]=%d.\n", vol_index, volume_sizes[vol_index], d, sizes[d] ); mifree_volume_props( hprops ); return( NULL ); } } for_less( d, 0, n_dimensions ) { file->sizes_in_file[d] = (long) sizes[d]; file->indices[d] = 0; file->dim_names[d] = create_string( dim_names[d] ); if(equal_strings( dim_names[d], MIvector_dimension ) ) { micreate_dimension(file->dim_names[d], MI_DIMCLASS_RECORD, MI_DIMATTR_REGULARLY_SAMPLED, (misize_t)file->sizes_in_file[d], &minc_dimensions[d] ); } else if (equal_strings( dim_names[d], MItime ) ) { micreate_dimension(file->dim_names[d], MI_DIMCLASS_TIME, MI_DIMATTR_REGULARLY_SAMPLED,/*TODO: make sure time is regularly sampled here*/ (misize_t)file->sizes_in_file[d], &minc_dimensions[d] ); } else { micreate_dimension(file->dim_names[d], MI_DIMCLASS_SPATIAL, MI_DIMATTR_REGULARLY_SAMPLED, (misize_t)file->sizes_in_file[d], &minc_dimensions[d] ); } } if( output_world_transform( file, volume_to_attach->coordinate_system_name, voxel_to_world_transform, options->use_volume_starts_and_steps , minc_dimensions) != VIO_OK ) { /*TODO: print error message*/ mifree_volume_props( hprops ); FREE( file ); return( NULL ); } /*--- create the file */ if( micreate_volume ( file->filename, n_dimensions , minc_dimensions, file_minc_datatype, MI_CLASS_REAL, hprops, &file->minc2id) <0 ) { print_error( "Error: creating MINC2 file \"%s\".\n", file->filename ); mifree_volume_props( hprops ); return( NULL ); } if ( micreate_volume_image ( file->minc2id ) <0 ) { print_error( "Error: creating MINC2 file \"%s\".\n", file->filename ); mifree_volume_props( hprops ); return( NULL ); } /* * Save information for creating image variable later */ file->nc_data_type = file_nc_data_type; file->signed_flag = file_signed_flag; file->valid_range[0] = file_voxel_min; file->valid_range[1] = file_voxel_max; file->image_range[0] = options->global_image_range[0]; file->image_range[1] = options->global_image_range[1]; miset_volume_valid_range(file->minc2id,file->valid_range[1],file->valid_range[0]); if( file->image_range[0] < file->image_range[1] ) { miset_slice_scaling_flag(file->minc2id, 0 ); miset_volume_range(file->minc2id,file->image_range[1],file->image_range[0]); } else { get_volume_real_range( volume_to_attach, &file->image_range[0], &file->image_range[1] ); /*miset_slice_scaling_flag(file->minc2id, 1 );*/ miset_slice_scaling_flag(file->minc2id, 0 ); miset_volume_range(file->minc2id,file->image_range[1],file->image_range[0]); } file->variables_written = FALSE; return( file ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_auxiliary_data_from_minc_file @INPUT : file filename history_string @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Copies the auxiliary data from the filename to the opened Minc file, 'file'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_auxiliary_data_from_minc2_file( Minc_file file, VIO_STR filename, VIO_STR history_string ) { VIO_Status status; VIO_STR expanded; mihandle_t minc_id; if( file->ignoring_because_cached ) return( VIO_OK ); expanded = expand_filename( filename ); if ( miopen_volume(expanded, MI2_OPEN_READ, &minc_id) < 0 ) { print_error( "Error opening %s\n", expanded ); delete_string( expanded ); return VIO_ERROR; } status = copy_auxiliary_data_from_open_minc2_file( file, minc_id, history_string ); delete_string( expanded ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_auxiliary_data_from_open_minc2_file @INPUT : file src_cdfid history_string @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Copies the auxiliary data from the opened minc file specified by src_cdfid to the opened minc file specified by 'file'. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_auxiliary_data_from_open_minc2_file( Minc_file file, mihandle_t minc_id, VIO_STR history_string ) { VIO_Status status; /*TODO: convert this to MINC2*/ #if 0 int src_img_var, varid, n_excluded, excluded_vars[10]; int i, src_min_id, src_max_id, src_root_id; VIO_STR excluded_list[] = { MIxspace, MIyspace, MIzspace, MItime, MItfrequency, MIxfrequency, MIyfrequency, MIzfrequency, MIvector_dimension }; if( file->end_def_done ) { print_error( "Cannot call copy_auxiliary_data_from_open_minc_file when not in define mode\n" ); return( VIO_ERROR ); } n_excluded = 0; /*TODO: convert this to MINC2*/ for_less( i, 0, VIO_SIZEOF_STATIC_ARRAY( excluded_list ) ) { if( (varid = ncvarid(src_cdfid, excluded_list[i] )) != MI_ERROR ) excluded_vars[n_excluded++] = varid; } if( (src_img_var = ncvarid(src_cdfid, MIimage )) != MI_ERROR ) excluded_vars[n_excluded++] = src_img_var; if( (src_max_id = ncvarid(src_cdfid, MIimagemax )) != MI_ERROR ) excluded_vars[n_excluded++] = src_max_id; if( (src_min_id = ncvarid(src_cdfid, MIimagemin )) != MI_ERROR ) excluded_vars[n_excluded++] = src_min_id; if( (src_root_id = ncvarid(src_cdfid, MIrootvariable )) != MI_ERROR ) excluded_vars[n_excluded++] = src_root_id; (void) micopy_all_var_defs( src_cdfid, file->cdfid, n_excluded, excluded_vars ); if( src_min_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_min_id, file->cdfid, file->min_id ); } if( src_max_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_max_id, file->cdfid, file->max_id ); } if( src_root_id != MI_ERROR ) { (void) micopy_all_atts( src_cdfid, src_root_id, file->cdfid, ncvarid( file->cdfid, MIrootvariable) ); } #endif /*0*/ status = VIO_OK; if( history_string != NULL ) status = add_minc2_history( file, history_string ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : add_minc2_history @INPUT : file history_string @OUTPUT : @RETURNS : @DESCRIPTION: Adds the history_string to the history in the file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 2, 1998 D. MacDonald - fixed error P. Neelin found with concating to non-existent history ---------------------------------------------------------------------------- */ VIOAPI VIO_Status add_minc2_history( Minc_file file, VIO_STR history_string ) { VIO_STR new_history; size_t minc_history_length=0; if( file->end_def_done ) { print_error( "Cannot call add_minc_history when not in define mode\n" ); return( VIO_ERROR ); } if(miget_attr_length(file->minc2id,"","history",&minc_history_length) == MI_NOERROR) { new_history=alloc_string(minc_history_length+strlen(history_string)+1); if( miget_attr_values(file->minc2id,MI_TYPE_STRING,"","history",minc_history_length,new_history) == MI_NOERROR) { concat_to_string( &new_history, history_string ); miset_attr_values(file->minc2id,MI_TYPE_STRING,"","history",minc_history_length+strlen(history_string),new_history); } delete_string( new_history ); } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_dimension_ordering @INPUT : n_vol_dims vol_dim_names n_file_dims file_dim_names @OUTPUT : to_volume to_file @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Matches dimension names between the volume and file, setting the axis conversion from file to_volume and from volume to_file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status get_dimension_ordering( int n_vol_dims, VIO_STR vol_dim_names[], int n_file_dims, VIO_STR file_dim_names[], int to_volume[], int to_file[] ) { VIO_Status status; int v, f, n_found; for_less( f, 0, n_file_dims ) to_volume[f] = -1; for_less( v, 0, n_vol_dims ) to_file[v] = -1; n_found = 0; for_less( v, 0, n_vol_dims ) { for_less( f, 0, n_file_dims ) { if( to_volume[f] < 0 && equal_strings( vol_dim_names[v], file_dim_names[f] ) ) { to_volume[f] = v; to_file[v] = f; ++n_found; } } } if( n_found != n_vol_dims ) { print_error( "Unsuccessful matching of volume and output dimension names.\n"); status = VIO_ERROR; } else status = VIO_OK; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_minc2_output_variables @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Checks if the file variables has been put into data mode, and does so if necessary. Then it checks if the variables have been written, and does so if necessary. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status check_minc2_output_variables( Minc_file file ) { VIO_Real voxel_min, voxel_max; VIO_Volume volume; if( !file->variables_written ) { volume = file->volume; file->variables_written = TRUE; get_volume_voxel_range( volume, &voxel_min, &voxel_max ); if( voxel_min < voxel_max ) { miset_volume_valid_range(file->minc2id,voxel_max,voxel_min); } else print_error( "Volume has invalid min and max voxel value\n" ); } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_minc2_output_random_order @INPUT : file @OUTPUT : @RETURNS : @DESCRIPTION: Sets the file into random order access, used by volume caching. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 26, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status set_minc2_output_random_order( Minc_file file ) { VIO_Status status; status = check_minc2_output_variables( file ); file->outputting_in_order = FALSE; return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_minc2_hyperslab @INPUT : file data_type n_array_dims array_sizes array_data_ptr to_array file_start file_count @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs a hyperslab from an array to the file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status output_minc2_hyperslab( Minc_file file, VIO_Data_types data_type, int n_array_dims, int array_sizes[], void *array_data_ptr, int to_array[], int file_start[], int file_count[] ) { int ind, expected_ind, file_ind, dim; int n_file_dims, n_tmp_dims; misize_t long_file_start[VIO_MAX_DIMENSIONS]; misize_t long_file_count[VIO_MAX_DIMENSIONS]; void *void_ptr; VIO_BOOL direct_from_array, non_full_size_found; int tmp_ind, tmp_sizes[VIO_MAX_DIMENSIONS]; int array_indices[VIO_MAX_DIMENSIONS]; int array_counts[MAX_VAR_DIMS]; VIO_Status status; VIO_multidim_array buffer_array; status = check_minc2_output_variables( file ); if( status != VIO_OK ) return( status ); n_file_dims = file->n_file_dimensions; expected_ind = n_array_dims-1; tmp_ind = n_file_dims-1; non_full_size_found = FALSE; for_less( ind, 0, n_array_dims ) { array_indices[ind] = -1; array_counts[ind] = 1; } direct_from_array = TRUE; /*--- determine if the hyperslab represents a consecutive chunk of memory in the array */ for( file_ind = n_file_dims-1; file_ind >= 0; --file_ind ) { long_file_start[file_ind] = (long) file_start[file_ind]; long_file_count[file_ind] = (long) file_count[file_ind]; ind = to_array[file_ind]; if( ind != INVALID_AXIS ) { array_counts[ind] = file_count[file_ind]; if( !non_full_size_found && (long) file_count[file_ind] < file->sizes_in_file[file_ind] ) non_full_size_found = TRUE; else if( non_full_size_found && file_count[file_ind] > 1 ) direct_from_array = FALSE; if( file_count[file_ind] > 1 && ind != expected_ind ) direct_from_array = FALSE; if( file_count[file_ind] != 1 || file->sizes_in_file[file_ind] == 1 ) { tmp_sizes[tmp_ind] = file_count[file_ind]; array_indices[ind] = tmp_ind; --tmp_ind; } --expected_ind; } } if( direct_from_array ) /* hyperslab is consecutive chunk of memory */ { void_ptr = array_data_ptr; } else { /*--- create a temporary array to copy hyperslab to, so that we have a consecutive chunk of memory to write from */ n_tmp_dims = n_file_dims - tmp_ind - 1; for_less( dim, 0, n_tmp_dims ) tmp_sizes[dim] = tmp_sizes[dim+tmp_ind+1]; for_less( dim, 0, n_array_dims ) array_indices[dim] -= tmp_ind + 1; create_multidim_array( &buffer_array, n_tmp_dims, tmp_sizes, data_type); GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); /*--- copy from the array argument to the temporary array */ copy_multidim_data_reordered( get_type_size(data_type), void_ptr, n_tmp_dims, tmp_sizes, array_data_ptr, n_array_dims, array_sizes, array_counts, array_indices, TRUE ); GET_MULTIDIM_PTR( void_ptr, buffer_array, 0, 0, 0, 0, 0 ); } /*--- output the data to the file */ status = VIO_OK; if( miset_hyperslab_with_icv(file->minc2id, vio_type_to_minc2_type(data_type), long_file_start,long_file_count,void_ptr) <0 ) status = VIO_ERROR; if( !direct_from_array ) delete_multidim_array( &buffer_array ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_slab2 @INPUT : file volume to_volume file_start file_count @OUTPUT : @RETURNS : @DESCRIPTION: Outputs the slab specified by the file start and count arrays, from the volume. The to_volume array translates axes in the file to axes in the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Sep 1, 1995 D. MacDonald - added cached volumes. ---------------------------------------------------------------------------- */ static void output_slab2( Minc_file file, VIO_Volume volume, int to_volume[], long file_start[], long file_count[] ) { int dim, file_ind, ind, n_slab_dims; int to_array[VIO_MAX_DIMENSIONS]; int volume_start[VIO_MAX_DIMENSIONS]; int volume_sizes[VIO_MAX_DIMENSIONS]; int array_to_volume[VIO_MAX_DIMENSIONS]; int int_file_count[VIO_MAX_DIMENSIONS]; int int_file_start[VIO_MAX_DIMENSIONS]; int slab_sizes[VIO_MAX_DIMENSIONS]; int v[VIO_MAX_DIMENSIONS]; int size0, size1, size2, size3, size4; VIO_Real value; void *array_data_ptr; VIO_multidim_array array; for_less( file_ind, 0, file->n_file_dimensions ) { int_file_start[file_ind] = (int) file_start[file_ind]; int_file_count[file_ind] = (int) file_count[file_ind]; ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) volume_start[ind] = int_file_start[file_ind]; else volume_start[ind] = 0; } if( volume->is_cached_volume ) { /*--- must make a temporary hyperslab array to contain the volume */ for_less( dim, 0, get_volume_n_dimensions(volume) ) volume_sizes[dim] = 1; for_less( dim, 0, VIO_MAX_DIMENSIONS ) array_to_volume[dim] = 0; for_less( dim, get_volume_n_dimensions(volume), VIO_MAX_DIMENSIONS ) { volume_start[dim] = 0; volume_sizes[dim] = 1; } n_slab_dims = 0; for_less( file_ind, 0, file->n_file_dimensions ) { ind = to_volume[file_ind]; if( ind != INVALID_AXIS ) { to_array[file_ind] = n_slab_dims; array_to_volume[n_slab_dims] = ind; slab_sizes[n_slab_dims] = int_file_count[file_ind]; volume_sizes[ind] = int_file_count[file_ind]; ++n_slab_dims; } else { to_array[file_ind] = INVALID_AXIS; } } create_multidim_array( &array, n_slab_dims, slab_sizes, get_volume_data_type(volume) ); /*--- copy from the cached volume to the temporary array */ size0 = volume_sizes[0]; size1 = volume_sizes[1]; size2 = volume_sizes[2]; size3 = volume_sizes[3]; size4 = volume_sizes[4]; for_less( v[0], 0, size0 ) for_less( v[1], 0, size1 ) for_less( v[2], 0, size2 ) for_less( v[3], 0, size3 ) for_less( v[4], 0, size4 ) { value = get_volume_voxel_value( volume, volume_start[0] + v[0], volume_start[1] + v[1], volume_start[2] + v[2], volume_start[3] + v[3], volume_start[4] + v[4] ); SET_MULTIDIM( array, v[array_to_volume[0]], v[array_to_volume[1]], v[array_to_volume[2]], v[array_to_volume[3]], v[array_to_volume[4]], value ); } /*--- output the temporary array */ GET_MULTIDIM_PTR( array_data_ptr, array, 0, 0, 0, 0, 0 ); output_minc2_hyperslab( file, get_volume_data_type(volume), n_slab_dims, slab_sizes, array_data_ptr, to_array, int_file_start, int_file_count); delete_multidim_array( &array ); } else { GET_MULTIDIM_PTR( array_data_ptr, volume->array, volume_start[0], volume_start[1], volume_start[2], volume_start[3], volume_start[4] ); get_volume_sizes( volume, volume_sizes ); output_minc2_hyperslab( file, get_volume_data_type(volume), get_volume_n_dimensions(volume), volume_sizes, array_data_ptr, to_volume, int_file_start, int_file_count ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_the_volume2 @INPUT : file volume volume_count file_start @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the volume to the file in the given position. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status output_the_volume2( Minc_file file, VIO_Volume volume, int volume_count[], long file_start[] ) { VIO_Status status; int d, n_volume_dims, sizes[VIO_MAX_DIMENSIONS]; int slab_size, n_slab, this_count; int vol_index, step, n_steps; int to_volume_index[MAX_VAR_DIMS]; int to_file_index[VIO_MAX_DIMENSIONS]; long file_indices[MAX_VAR_DIMS]; long count[MAX_VAR_DIMS]; VIO_Real real_min, real_max; VIO_STR *vol_dimension_names; VIO_BOOL increment; VIO_progress_struct progress; status = check_minc2_output_variables( file ); if( status != VIO_OK ) return( status ); /* --- check if dimension name correspondence between volume and file */ n_volume_dims = get_volume_n_dimensions( volume ); if( n_volume_dims > file->n_file_dimensions ) { print_error( "output_volume_to_minc_file_position:" ); print_error( " volume (%d) has more dimensions than file (%d).\n", n_volume_dims, file->n_file_dimensions ); return( VIO_ERROR ); } /*--- find correspondence between volume dimensions and file dimensions */ vol_dimension_names = get_volume_dimension_names( volume ); status = get_dimension_ordering( n_volume_dims, vol_dimension_names, file->n_file_dimensions, file->dim_names, to_volume_index, to_file_index ); delete_dimension_names( volume, vol_dimension_names ); if( status != VIO_OK ) return( VIO_ERROR ); /*--- check sizes match between volume and file */ get_volume_sizes( volume, sizes ); for_less( d, 0, file->n_file_dimensions ) { vol_index = to_volume_index[d]; if( vol_index >= 0 ) { if( volume_count[vol_index] < 0 || volume_count[vol_index] > sizes[vol_index] ) { print_error( "output_the_volume2: invalid volume count.\n" ); print_error( " count[%d] = %d\n", vol_index, volume_count[vol_index] ); return( VIO_ERROR ); } this_count = volume_count[vol_index]; } else { this_count = 1; } if( file_start[d] < 0 || file_start[d] + (long) this_count > file->sizes_in_file[d] ) { print_error( "output_the_volume2: invalid minc file position.\n" ); print_error( " start[%d] = %ld count[%d] = %d\n", d, file_start[d], d, this_count ); return( VIO_ERROR ); } } /* --- determine which contiguous blocks of volume to output to max out the read/write buffer and make it like the chunking dimensions for compression (for efficiency) */ file->n_slab_dims = 0; slab_size = 1; n_steps = 1; for( d = file->n_file_dimensions-1; d >= 0; d-- ) { count[d] = 1; file_indices[d] = file_start[d]; if( to_volume_index[d] != INVALID_AXIS ) { count[d] = volume_count[to_volume_index[d]]; file->n_slab_dims++; /* integral number of complete dimensions */ slab_size *= count[d]; } } if( file->n_slab_dims == 0 ) { print_error( "Error: You seem to be processing a minc file\n" ); print_error( "with no valid axes. Please check your volume.\n" ); exit(1); } /*DUMB code for fake intrslice scaling*/ if( file->image_range[0] >= file->image_range[1] ) get_volume_real_range( volume, &real_min, &real_max ); /*--- now write entire volume in contiguous chunks (possibly only 1 req'd)*/ step = 0; initialize_progress_report( &progress, FALSE, n_steps,"Outputting Volume" ); increment = FALSE; while( !increment ) { /*--- set the indices of the file slab to write */ long local_count[MAX_VAR_DIMS]; for( d = 0; d < file->n_file_dimensions; d++ ) { vol_index = to_volume_index[d]; local_count[d] = MIN( volume_count[vol_index] - file_indices[d], count[d] ); } if( file->image_range[0] >= file->image_range[1] ) miset_slice_range(file->minc2id,(const misize_t*)file_indices,file->n_file_dimensions,real_max,real_min); output_slab2( file, volume, to_volume_index, file_indices, local_count ); /*--- increment the file index dimensions which correspond for the next slab to write to output */ increment = TRUE; d = file->n_file_dimensions-1; n_slab = 0; while( increment && d >= 0 ) { vol_index = to_volume_index[d]; if( vol_index != INVALID_AXIS && n_slab >= file->n_slab_dims ) { file_indices[d] += local_count[d]; if( file_indices[d] < file_start[d] + (long) volume_count[vol_index] ) { increment = FALSE; } else { file_indices[d] = file_start[d]; } } if( vol_index != INVALID_AXIS ) ++n_slab; --d; } ++step; if( n_steps > 1 ) update_progress_report( &progress, step ); } terminate_progress_report( &progress ); if( step != n_steps ) { print_error( "Error: Your output minc file may be incomplete\n" ); print_error( "(wrote only %d out of %d buffers)\n", step, n_steps ); exit(1); } return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_volume_to_minc_file_position @INPUT : file volume volume_count file_start @OUTPUT : Outputs the volume to the specified file position. @RETURNS : @DESCRIPTION: @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ #ifdef OUTPUT_MNC2_UNUSED VIOAPI VIO_Status output_volume_to_minc2_file_position( Minc_file file, VIO_Volume volume, int volume_count[], long file_start[] ) { file->outputting_in_order = FALSE; return( output_the_volume2( file, volume, volume_count, file_start ) ); } #endif /* OUTPUT_MNC2_UNUSED */ /* ----------------------------- MNI Header ----------------------------------- @NAME : output_minc2_volume @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Outputs the attached volume to the MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_minc2_volume( Minc_file file ) { int d, volume_count[VIO_MAX_DIMENSIONS]; VIO_BOOL increment; /*--- check number of volumes written */ d = 0; while( d < file->n_file_dimensions && file->to_volume_index[d] != INVALID_AXIS ) ++d; if( d < file->n_file_dimensions && file->indices[d] >= file->sizes_in_file[d] ) { print_error( "output_minc2_volume: attempted to write too many subvolumes.\n"); return( VIO_ERROR ); } get_volume_sizes( file->volume, volume_count ); if( output_the_volume2( file, file->volume, volume_count, file->indices ) != VIO_OK ) return( VIO_ERROR ); /*--- increment the file index dimensions which do not correspond to volume dimensions */ increment = TRUE; d = file->n_file_dimensions-1; while( increment && d >= 0 ) { if( file->to_volume_index[d] == INVALID_AXIS ) { ++file->indices[d]; if( file->indices[d] < file->sizes_in_file[d] ) increment = FALSE; else file->indices[d] = 0; } --d; } if( increment ) file->entire_file_written = TRUE; return( VIO_OK ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : close_minc_output @INPUT : file @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Closes the MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status close_minc2_output( Minc_file file ) { int d; if( file == (Minc_file) NULL ) { print_error( "close_minc_output(): NULL file.\n" ); return( VIO_ERROR ); } if( file->outputting_in_order && !file->entire_file_written ) { print_error( "Warning: the MINC2 file has been " ); print_error( "closed without writing part of it.\n"); } for_less( d, 0, file->n_file_dimensions ) delete_string( file->dim_names[d] ); miclose_volume( file->minc2id ); delete_string( file->filename ); FREE( file ); return( VIO_OK ); } #endif /*HAVE_MINC2*/ libminc-libminc-2-3-00/volume_io/Volumes/output_volume.c000066400000000000000000000354451257462267400233770ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include /* ----------------------------- MNI Header ----------------------------------- @NAME : get_file_dimension_names @INPUT : filename n_dims @OUTPUT : dim_names @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Gets the names of the dimensions from the specified file. dim_names is an array of VIO_STRS, where the array has been allocated, but not each string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status get_file_dimension_names( VIO_STR filename, int *n_dims, VIO_STR *dim_names[] ) { int i; VIO_Status status; volume_input_struct volume_input; VIO_Volume tmp_volume; status = start_volume_input( filename, -1, File_order_dimension_names, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0, TRUE, &tmp_volume, (minc_input_options *) NULL, &volume_input ); if( status == VIO_OK ) { *n_dims = get_volume_n_dimensions( tmp_volume ); ALLOC( *dim_names, *n_dims ); for_less( i, 0, *n_dims ) { (*dim_names)[i] = create_string( volume_input.minc_file->dim_names[i]); } delete_volume_input( &volume_input ); delete_volume( tmp_volume ); } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_output_dim_names @INPUT : volume original_filename options @OUTPUT : file_sizes @RETURNS : array of names @DESCRIPTION: Creates an array of dimension names for file output. If the options contains a set of names, they are used. Otherwise, if the original_filename is specified (non-NULL), then the dimension names from it are used, if they contain all the dimension names in the volume. Otherwise, those from the volume are used. The file_sizes[] array is set to match the sizes in the volume cross-referenced with the dimension names. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 4, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR *create_output_dim_names( VIO_Volume volume, VIO_STR original_filename, minc_output_options *options, int file_sizes[] ) { int n_dims, n_file_dims, dim_index; int vol_sizes[VIO_MAX_DIMENSIONS]; int i, j, n_found; VIO_STR *file_dim_names; VIO_STR *vol_dimension_names; VIO_STR *dim_names; get_volume_sizes( volume, vol_sizes ); n_dims = get_volume_n_dimensions(volume); vol_dimension_names = get_volume_dimension_names( volume ); ALLOC( dim_names, n_dims ); /*--- either get the output dim name ordering from the original filename, the volume, or the options */ if( options != NULL && string_length( options->dimension_names[0] ) > 0 ) { for_less( i, 0, n_dims ) dim_names[i] = create_string( options->dimension_names[i] ); } else { if( original_filename != NULL && file_exists(original_filename) && get_file_dimension_names( original_filename, &n_file_dims, &file_dim_names ) == VIO_OK ) { /*--- extract the dimension names from the file which match those of the volume, so a 3D volume of z, y, x with a 4D file of x y z t, will generate a list of dim_names: x y z */ dim_index = 0; for_less( i, 0, n_file_dims ) { for_less( j, 0, n_dims ) { if( equal_strings( vol_dimension_names[j], file_dim_names[i] ) ) { dim_names[dim_index] = create_string( vol_dimension_names[j] ); ++dim_index; break; } } if( dim_index == n_dims ) /*--- save time */ break; } /*--- check that all volume dimensions exist in the file */ if( dim_index != n_dims ) { for_less( i, 0, dim_index ) delete_string( dim_names[i] ); for_less( i, 0, n_dims ) dim_names[i] = create_string( vol_dimension_names[i] ); } /*--- free up the file dimension names */ for_less( i, 0, n_file_dims ) delete_string( file_dim_names[i] ); FREE( file_dim_names ); } else /*--- no original file specified, use the volumes own list */ { for_less( i, 0, n_dims ) dim_names[i] = create_string( vol_dimension_names[i] ); } } /*--- check that the set of dimension names are simply a permutation of the ones in the volume */ n_found = 0; for_less( i, 0, n_dims ) { for_less( j, 0, n_dims ) { if( equal_strings( dim_names[j], vol_dimension_names[i] ) ) { file_sizes[j] = vol_sizes[i]; ++n_found; } } } /*--- act on whether the set of names was a permutation or not */ if( n_found != n_dims ) { print_error( "create_output_dim_names: dimension name mismatch.\n" ); delete_dimension_names( volume, dim_names ); dim_names = NULL; } /*--- no longer need the volume dimension names */ delete_dimension_names( volume, vol_dimension_names ); return( dim_names ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_volume_auxiliary_and_history @INPUT : minc_file filename original_filename history @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: If the original_filename is specified, Copies the auxiliary data from it. If the history is specified, adds the history line. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 24, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status copy_volume_auxiliary_and_history( Minc_file minc_file, VIO_STR filename, VIO_STR original_filename, VIO_STR history ) { VIO_Status status; VIO_BOOL copy_original_file_data; VIO_STR full_filename, full_original_filename; copy_original_file_data = FALSE; if( original_filename != NULL ) { full_filename = expand_filename( filename ); full_original_filename = expand_filename( original_filename ); if( !equal_strings( full_filename, full_original_filename ) && file_exists( full_original_filename ) ) { copy_original_file_data = TRUE; } delete_string( full_filename ); delete_string( full_original_filename ); } status = VIO_OK; if( copy_original_file_data ) { #ifdef HAVE_MINC1 status = copy_auxiliary_data_from_minc_file( minc_file, original_filename, history ); #elif defined HAVE_MINC2 status = copy_auxiliary_data_from_minc2_file( minc_file, original_filename, history ); #endif } else if( history != NULL ) { #ifdef HAVE_MINC1 status = add_minc_history( minc_file, history ); #elif defined HAVE_MINC2 status = add_minc2_history( minc_file, history ); #endif } return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_modified_volume @INPUT : filename file_nc_data_type file_signed_flag file_voxel_min file_voxel_max volume original_filename history options @OUTPUT : @RETURNS : VIO_OK or VIO_ERROR @DESCRIPTION: Creates a Minc file and outputs the volume to it. The data type of the file is either specified by the second through fifth parameters, or by the volume, if file_nc_data_type is MI_ORIGINAL_TYPE. The volume is assumed to be derived, in some fashion, from an existing MINC file, and the auxiliary data from the existing MINC file, 'original_filename', is copied to the output file, along with the 'history' string. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now sets use_volume_starts_and_steps flag ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_modified_volume( VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_Volume volume, VIO_STR original_filename, VIO_STR history, minc_output_options *options ) { VIO_Status status; Minc_file minc_file; int n_dims, sizes[VIO_MAX_DIMENSIONS]; VIO_Real real_min, real_max; VIO_STR *dim_names; minc_output_options used_options; dim_names = create_output_dim_names( volume, original_filename, options, sizes ); if( dim_names == NULL ) return( VIO_ERROR ); n_dims = get_volume_n_dimensions(volume); if( options == NULL ) set_default_minc_output_options( &used_options ); else used_options = *options; if( used_options.global_image_range[0] >= used_options.global_image_range[1] ) { get_volume_real_range( volume, &real_min, &real_max ); set_minc_output_real_range( &used_options, real_min, real_max ); } /*--- if the user has not explicitly set the use_volume_starts_and_steps flag, let's set it if the transform is linear, to output the same starts as was input, and avoid round-off error */ if( !used_options.use_starts_set && !used_options.use_volume_starts_and_steps && get_transform_type(get_voxel_to_world_transform(volume)) == LINEAR ) { set_minc_output_use_volume_starts_and_steps_flag( &used_options, TRUE ); } #ifdef HAVE_MINC1 minc_file = initialize_minc_output( filename, n_dims, dim_names, sizes, file_nc_data_type, file_signed_flag, file_voxel_min, file_voxel_max, get_voxel_to_world_transform(volume), volume, &used_options ); #elif defined HAVE_MINC2 minc_file = initialize_minc2_output( filename, n_dims, dim_names, sizes, file_nc_data_type, file_signed_flag, file_voxel_min, file_voxel_max, get_voxel_to_world_transform(volume), volume, &used_options ); #endif if( minc_file == NULL ) return( VIO_ERROR ); status = copy_volume_auxiliary_and_history( minc_file, filename, original_filename, history ); if( status == VIO_OK ) #ifdef HAVE_MINC1 status = output_minc_volume( minc_file ); #elif defined HAVE_MINC2 status = output_minc2_volume( minc_file ); #else print_error("Can't output file!"); #endif if( status == VIO_OK ) #ifdef HAVE_MINC1 status = close_minc_output( minc_file ); #elif defined HAVE_MINC2 status = close_minc2_output( minc_file ); #else print_error("Can't output file!"); #endif delete_dimension_names( volume, dim_names ); return( status ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : output_volume @INPUT : filename file_nc_data_type file_signed_flag file_voxel_min file_voxel_max volume history options @OUTPUT : @RETURNS : @DESCRIPTION: Sames as output_modified_volume, above, but the volume is not a modification of an existing MINC file. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Status output_volume( VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_Volume volume, VIO_STR history, minc_output_options *options ) { return( output_modified_volume( filename, file_nc_data_type, file_signed_flag, file_voxel_min, file_voxel_max, volume, NULL, history, options ) ); } libminc-libminc-2-3-00/volume_io/Volumes/set_hyperslab.c000066400000000000000000001127521257462267400233110ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include VIOAPI void convert_values_to_voxels( VIO_Volume volume, int n_voxels, VIO_Real values[], VIO_Real voxels[] ) { int v; VIO_Real scale, trans; if( !volume->real_range_set ) { if( voxels != values ) { for_less( v, 0, n_voxels ) voxels[v] = values[v]; } return; } scale = volume->real_value_scale; trans = volume->real_value_translation; for_less( v, 0, n_voxels ) voxels[v] = (values[v] - trans) / scale; } VIOAPI void set_volume_value_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { switch( get_volume_n_dimensions(volume) ) { case 1: set_volume_value_hyperslab_1d( volume, v0, n0, values ); break; case 2: set_volume_value_hyperslab_2d( volume, v0, v1, n0, n1, values ); break; case 3: set_volume_value_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, values ); break; case 4: set_volume_value_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, values ); break; case 5: set_volume_value_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, values ); break; } } VIOAPI void set_volume_value_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { int size; VIO_Real *voxels; size = n0 * n1 * n2 * n3 * n4; ALLOC( voxels, size ); convert_values_to_voxels( volume, size, values, voxels ); set_volume_voxel_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, voxels ); FREE( voxels ); } VIOAPI void set_volume_value_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ) { int size; VIO_Real *voxels; size = n0 * n1 * n2 * n3; ALLOC( voxels, size ); convert_values_to_voxels( volume, size, values, voxels ); set_volume_voxel_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, voxels ); FREE( voxels ); } VIOAPI void set_volume_value_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ) { int size; VIO_Real *voxels; size = n0 * n1 * n2; ALLOC( voxels, size ); convert_values_to_voxels( volume, size, values, voxels ); set_volume_voxel_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, voxels ); FREE( voxels ); } VIOAPI void set_volume_value_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ) { int size; VIO_Real *voxels; size = n0 * n1; ALLOC( voxels, size ); convert_values_to_voxels( volume, size, values, voxels ); set_volume_voxel_hyperslab_2d( volume, v0, v1, n0, n1, voxels ); FREE( voxels ); } VIOAPI void set_volume_value_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ) { VIO_Real *voxels; ALLOC( voxels, n0 ); convert_values_to_voxels( volume, n0, values, voxels ); set_volume_voxel_hyperslab_1d( volume, v0, n0, voxels ); FREE( voxels ); } static void slow_set_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { int i0, i1, i2, i3, i4, n_dims; n_dims = get_volume_n_dimensions( volume ); if( n_dims < 5 ) n4 = 1; if( n_dims < 4 ) n3 = 1; if( n_dims < 3 ) n2 = 1; if( n_dims < 2 ) n1 = 1; if( n_dims < 1 ) n0 = 1; for_less( i0, 0, n0 ) for_less( i1, 0, n1 ) for_less( i2, 0, n2 ) for_less( i3, 0, n3 ) for_less( i4, 0, n4 ) { set_volume_voxel_value( volume, v0 + i0, v1 + i1, v2 + i2, v3 + i3, v4 + i4, *values ); ++values; } } static void set_voxel_values_5d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2, step3, step4; int i0, i1, i2, i3, i4; int n0, n1, n2, n3, n4; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; n3 = counts[3]; n4 = counts[4]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; step3 = steps[3]; step4 = steps[4]; step0 -= n1 * step1; step1 -= n2 * step2; step2 -= n3 * step3; step3 -= n4 * step4; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *VIO_UCHAR_ptr = (unsigned char) floor( *values + 0.5 ); ++values; VIO_UCHAR_ptr += step4; } VIO_UCHAR_ptr += step3; } VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *signed_byte_ptr = (signed char) floor( *values + 0.5 ); ++values; signed_byte_ptr += step4; } signed_byte_ptr += step3; } signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *unsigned_short_ptr = (unsigned short) floor( *values + 0.5 ); ++values; unsigned_short_ptr += step4; } unsigned_short_ptr += step3; } unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *signed_short_ptr = (signed short) floor( *values + 0.5 ); ++values; signed_short_ptr += step4; } signed_short_ptr += step3; } signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *unsigned_int_ptr = (unsigned int) floor( *values + 0.5 ); ++values; unsigned_int_ptr += step4; } unsigned_int_ptr += step3; } unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *signed_int_ptr = (signed int) floor( *values + 0.5 ); ++values; signed_int_ptr += step4; } signed_int_ptr += step3; } signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *float_ptr = (float) *values; ++values; float_ptr += step4; } float_ptr += step3; } float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { for_less( i4, 0, n4 ) { *double_ptr = (double) *values; ++values; double_ptr += step4; } double_ptr += step3; } double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } static void set_voxel_values_4d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2, step3; int i0, i1, i2, i3; int n0, n1, n2, n3; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; n3 = counts[3]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; step3 = steps[3]; step0 -= n1 * step1; step1 -= n2 * step2; step2 -= n3 * step3; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *VIO_UCHAR_ptr = (unsigned char) floor( *values + 0.5 ); ++values; VIO_UCHAR_ptr += step3; } VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *signed_byte_ptr = (signed char) floor( *values + 0.5 ); ++values; signed_byte_ptr += step3; } signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *unsigned_short_ptr = (unsigned short) floor( *values + 0.5 ); ++values; unsigned_short_ptr += step3; } unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *signed_short_ptr = (signed short) floor( *values + 0.5 ); ++values; signed_short_ptr += step3; } signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *unsigned_int_ptr = (unsigned int) floor( *values + 0.5 ); ++values; unsigned_int_ptr += step3; } unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *signed_int_ptr = (signed int) floor( *values + 0.5 ); ++values; signed_int_ptr += step3; } signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *float_ptr = (float) *values; ++values; float_ptr += step3; } float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { for_less( i3, 0, n3 ) { *double_ptr = (double) *values; ++values; double_ptr += step3; } double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } static void set_voxel_values_3d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1, step2; int i0, i1, i2; int n0, n1, n2; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; n2 = counts[2]; step0 = steps[0]; step1 = steps[1]; step2 = steps[2]; step0 -= n1 * step1; step1 -= n2 * step2; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *VIO_UCHAR_ptr = (unsigned char) floor( *values + 0.5 ); ++values; VIO_UCHAR_ptr += step2; } VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *signed_byte_ptr = (signed char) floor( *values + 0.5 ); ++values; signed_byte_ptr += step2; } signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *unsigned_short_ptr = (unsigned short) floor( *values + 0.5 ); ++values; unsigned_short_ptr += step2; } unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *signed_short_ptr = (signed short) floor( *values + 0.5 ); ++values; signed_short_ptr += step2; } signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *unsigned_int_ptr = (unsigned int) floor( *values + 0.5 ); ++values; unsigned_int_ptr += step2; } unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *signed_int_ptr = (signed int) floor( *values + 0.5 ); ++values; signed_int_ptr += step2; } signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *float_ptr = (float) *values; ++values; float_ptr += step2; } float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { for_less( i2, 0, n2 ) { *double_ptr = (double) *values; ++values; double_ptr += step2; } double_ptr += step1; } double_ptr += step0; } break; } } static void set_voxel_values_2d( VIO_Data_types data_type, void *void_ptr, int steps[], int counts[], VIO_Real values[] ) { int step0, step1; int i0, i1; int n0, n1; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; n0 = counts[0]; n1 = counts[1]; step0 = steps[0]; step1 = steps[1]; step0 -= n1 * step1; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *VIO_UCHAR_ptr = (unsigned char) floor( *values + 0.5 ); ++values; VIO_UCHAR_ptr += step1; } VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *signed_byte_ptr = (signed char) floor( *values + 0.5 ); ++values; signed_byte_ptr += step1; } signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *unsigned_short_ptr = (unsigned short) floor( *values + 0.5 ); ++values; unsigned_short_ptr += step1; } unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *signed_short_ptr = (signed short) floor( *values + 0.5 ); ++values; signed_short_ptr += step1; } signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *unsigned_int_ptr = (unsigned int) floor( *values + 0.5 ); ++values; unsigned_int_ptr += step1; } unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *signed_int_ptr = (signed int) floor( *values + 0.5 ); ++values; signed_int_ptr += step1; } signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *float_ptr = (float) *values; ++values; float_ptr += step1; } float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { for_less( i1, 0, n1 ) { *double_ptr = (double) *values; ++values; double_ptr += step1; } double_ptr += step0; } break; } } static void set_voxel_values_1d( VIO_Data_types data_type, void *void_ptr, int step0, int n0, VIO_Real values[] ) { int i0; unsigned char *VIO_UCHAR_ptr; signed char *signed_byte_ptr; unsigned short *unsigned_short_ptr; signed short *signed_short_ptr; unsigned int *unsigned_int_ptr; signed int *signed_int_ptr; float *float_ptr; double *double_ptr; switch( data_type ) { case VIO_UNSIGNED_BYTE: ASSIGN_PTR(VIO_UCHAR_ptr) = void_ptr; for_less( i0, 0, n0 ) { *VIO_UCHAR_ptr = (unsigned char) floor( *values + 0.5 ); ++values; VIO_UCHAR_ptr += step0; } break; case VIO_SIGNED_BYTE: ASSIGN_PTR(signed_byte_ptr) = void_ptr; for_less( i0, 0, n0 ) { *signed_byte_ptr = (signed char) floor( *values + 0.5 ); ++values; signed_byte_ptr += step0; } break; case VIO_UNSIGNED_SHORT: ASSIGN_PTR(unsigned_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { *unsigned_short_ptr = (unsigned short) floor( *values + 0.5 ); ++values; unsigned_short_ptr += step0; } break; case VIO_SIGNED_SHORT: ASSIGN_PTR(signed_short_ptr) = void_ptr; for_less( i0, 0, n0 ) { *signed_short_ptr = (signed short) floor( *values + 0.5 ); ++values; signed_short_ptr += step0; } break; case VIO_UNSIGNED_INT: ASSIGN_PTR(unsigned_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { *unsigned_int_ptr = (unsigned int) floor( *values + 0.5 ); ++values; unsigned_int_ptr += step0; } break; case VIO_SIGNED_INT: ASSIGN_PTR(signed_int_ptr) = void_ptr; for_less( i0, 0, n0 ) { *signed_int_ptr = (signed int) floor( *values + 0.5 ); ++values; signed_int_ptr += step0; } break; case VIO_FLOAT: ASSIGN_PTR(float_ptr) = void_ptr; for_less( i0, 0, n0 ) { *float_ptr = (float) *values; ++values; float_ptr += step0; } break; default: case VIO_DOUBLE: ASSIGN_PTR(double_ptr) = void_ptr; for_less( i0, 0, n0 ) { *double_ptr = (double) *values; ++values; double_ptr += step0; } break; } } static void set_voxel_values( VIO_Volume volume, void *void_ptr, int n_dims, int steps[], int counts[], VIO_Real values[] ) { VIO_Data_types data_type; data_type = get_volume_data_type( volume ); switch( n_dims ) { case 0: set_voxel_values_1d( data_type, void_ptr, 1, 1, values ); break; case 1: set_voxel_values_1d( data_type, void_ptr, steps[0], counts[0], values ); break; case 2: set_voxel_values_2d( data_type, void_ptr, steps, counts, values ); break; case 3: set_voxel_values_3d( data_type, void_ptr, steps, counts, values ); break; case 4: set_voxel_values_4d( data_type, void_ptr, steps, counts, values ); break; case 5: set_voxel_values_5d( data_type, void_ptr, steps, counts, values ); break; } } VIOAPI void set_volume_voxel_hyperslab_5d( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_set_volume_voxel_hyperslab( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_5D( void_ptr, volume->array, v0, v1, v2, v3, v4 ) stride = 1; dim = 5; if( n4 > 1 ) { --dim; counts[dim] = n4; steps[dim] = stride; } stride *= sizes[4]; if( n3 > 1 ) { --dim; counts[dim] = n3; steps[dim] = stride; } stride *= sizes[3]; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } set_voxel_values( volume, void_ptr, 5 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void set_volume_voxel_hyperslab_4d( VIO_Volume volume, int v0, int v1, int v2, int v3, int n0, int n1, int n2, int n3, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_set_volume_voxel_hyperslab( volume, v0, v1, v2, v3, 0, n0, n1, n2, n3, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_4D( void_ptr, volume->array, v0, v1, v2, v3 ) stride = 1; dim = 4; if( n3 > 1 ) { --dim; counts[dim] = n3; steps[dim] = stride; } stride *= sizes[3]; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } set_voxel_values( volume, void_ptr, 4 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void set_volume_voxel_hyperslab_3d( VIO_Volume volume, int v0, int v1, int v2, int n0, int n1, int n2, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_set_volume_voxel_hyperslab( volume, v0, v1, v2, 0, 0, n0, n1, n2, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_3D( void_ptr, volume->array, v0, v1, v2 ) stride = 1; dim = 3; if( n2 > 1 ) { --dim; counts[dim] = n2; steps[dim] = stride; } stride *= sizes[2]; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } set_voxel_values( volume, void_ptr, 3 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void set_volume_voxel_hyperslab_2d( VIO_Volume volume, int v0, int v1, int n0, int n1, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim, stride; void *void_ptr; if( volume->is_cached_volume ) { slow_set_volume_voxel_hyperslab( volume, v0, v1, 0, 0, 0, n0, n1, 0, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_2D( void_ptr, volume->array, v0, v1 ) stride = 1; dim = 2; if( n1 > 1 ) { --dim; counts[dim] = n1; steps[dim] = stride; } stride *= sizes[1]; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = stride; } set_voxel_values( volume, void_ptr, 2 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void set_volume_voxel_hyperslab_1d( VIO_Volume volume, int v0, int n0, VIO_Real values[] ) { int steps[VIO_MAX_DIMENSIONS]; int counts[VIO_MAX_DIMENSIONS]; int sizes[VIO_MAX_DIMENSIONS]; int dim; void *void_ptr; if( volume->is_cached_volume ) { slow_set_volume_voxel_hyperslab( volume, v0, 0, 0, 0, 0, n0, 0, 0, 0, 0, values ); return; } get_volume_sizes( volume, sizes ); GET_MULTIDIM_PTR_1D( void_ptr, volume->array, v0 ) dim = 1; if( n0 > 1 ) { --dim; counts[dim] = n0; steps[dim] = 1; } set_voxel_values( volume, void_ptr, 1 - dim, &steps[dim], &counts[dim], values ); } VIOAPI void set_volume_voxel_hyperslab( VIO_Volume volume, int v0, int v1, int v2, int v3, int v4, int n0, int n1, int n2, int n3, int n4, VIO_Real voxels[] ) { switch( get_volume_n_dimensions(volume) ) { case 1: set_volume_voxel_hyperslab_1d( volume, v0, n0, voxels ); break; case 2: set_volume_voxel_hyperslab_2d( volume, v0, v1, n0, n1, voxels ); break; case 3: set_volume_voxel_hyperslab_3d( volume, v0, v1, v2, n0, n1, n2, voxels ); break; case 4: set_volume_voxel_hyperslab_4d( volume, v0, v1, v2, v3, n0, n1, n2, n3, voxels ); break; case 5: set_volume_voxel_hyperslab_5d( volume, v0, v1, v2, v3, v4, n0, n1, n2, n3, n4, voxels ); break; } } libminc-libminc-2-3-00/volume_io/Volumes/volume_cache.c000066400000000000000000001374711257462267400231040ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #define HASH_FUNCTION_CONSTANT 0.6180339887498948482 #define HASH_TABLE_SIZE_FACTOR 3 #define DEFAULT_BLOCK_SIZE 64 #define DEFAULT_CACHE_THRESHOLD -1 #define DEFAULT_MAX_BYTES_IN_CACHE 100000000 static VIO_BOOL n_bytes_cache_threshold_set = FALSE; static int n_bytes_cache_threshold = DEFAULT_CACHE_THRESHOLD; static VIO_BOOL default_cache_size_set = FALSE; static int default_cache_size = DEFAULT_MAX_BYTES_IN_CACHE; static VIO_Cache_block_size_hints block_size_hint = RANDOM_VOLUME_ACCESS; static VIO_BOOL default_block_sizes_set = FALSE; static int default_block_sizes[VIO_MAX_DIMENSIONS] = { DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_SIZE }; static void alloc_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ); #ifdef CACHE_DEBUGGING static void initialize_cache_debug( VIO_volume_cache_struct *cache ); static void record_cache_hit( VIO_volume_cache_struct *cache ); static void record_cache_prev_hit( VIO_volume_cache_struct *cache ); static void record_cache_no_hit( VIO_volume_cache_struct *cache ); #endif /* ----------------------------- MNI Header ----------------------------------- @NAME : set_n_bytes_cache_threshold @INPUT : threshold @OUTPUT : @RETURNS : @DESCRIPTION: Sets the threshold number of bytes which decides if a volume is small enough to be held entirely in memory, or whether it should be cached. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_n_bytes_cache_threshold( int threshold ) { n_bytes_cache_threshold = threshold; n_bytes_cache_threshold_set = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_n_bytes_cache_threshold @INPUT : @OUTPUT : @RETURNS : number of bytes @DESCRIPTION: Returns the number of bytes defining the cache threshold. If it hasn't been set, returns the program initialized value, or the value set by the environment variable. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_n_bytes_cache_threshold( void ) { int n_bytes; if( !n_bytes_cache_threshold_set ) { if( getenv( "VOLUME_CACHE_THRESHOLD" ) != NULL && sscanf( getenv( "VOLUME_CACHE_THRESHOLD" ), "%d", &n_bytes ) == 1 ) { n_bytes_cache_threshold = n_bytes; } n_bytes_cache_threshold_set = TRUE; } return( n_bytes_cache_threshold ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_default_max_bytes_in_cache @INPUT : max_bytes @OUTPUT : @RETURNS : @DESCRIPTION: Sets the default value for the maximum amount of memory in a single volume's cache. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_default_max_bytes_in_cache( int max_bytes ) { default_cache_size_set = TRUE; default_cache_size = max_bytes; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_max_bytes_in_cache @INPUT : @OUTPUT : @RETURNS : number of bytes @DESCRIPTION: Returns the maximum number of bytes allowed for a single volume's cache. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_default_max_bytes_in_cache( void ) { int n_bytes; if( !default_cache_size_set ) { if( getenv( "VOLUME_CACHE_SIZE" ) != NULL && sscanf( getenv( "VOLUME_CACHE_SIZE" ), "%d", &n_bytes ) == 1 ) { default_cache_size = n_bytes; } default_cache_size_set = TRUE; } return( default_cache_size ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_default_cache_block_sizes @INPUT : block_sizes @OUTPUT : @RETURNS : @DESCRIPTION: Sets the default values for the volume cache block sizes. A non-positive value will result in a block size equal to the number of voxels in that dimension of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 19, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_default_cache_block_sizes( int block_sizes[] ) { int dim; for_less( dim, 0, VIO_MAX_DIMENSIONS ) default_block_sizes[dim] = block_sizes[dim]; default_block_sizes_set = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_cache_block_sizes_hint @INPUT : hint @OUTPUT : @RETURNS : @DESCRIPTION: Sets the hint for deciding on block sizes. This turns off the default_block_sizes_set flag, thereby overriding any previous calls to set_default_cache_block_sizes(). @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 25, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_cache_block_sizes_hint( VIO_Cache_block_size_hints hint ) { block_size_hint = hint; default_block_sizes_set = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_cache_block_sizes @INPUT : @OUTPUT : block_sizes[] @RETURNS : @DESCRIPTION: Passes back the size (in voxels) of each dimension of a cache block. If it hasn't been set, returns the program initialized value, or the value set by the environment variable. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void get_default_cache_block_sizes( int n_dims, int volume_sizes[], int block_sizes[] ) { int dim, block_size; if( !default_block_sizes_set && block_size_hint == SLICE_ACCESS ) { for_less( dim, 0, n_dims - 2 ) block_sizes[dim] = 1; /*--- set the last two dimensions to be entire size of dimension */ for_less( dim, MAX( 0, n_dims - 2), VIO_MAX_DIMENSIONS ) block_sizes[dim] = -1; } else if( !default_block_sizes_set && block_size_hint == RANDOM_VOLUME_ACCESS ) { if( getenv( "VOLUME_CACHE_BLOCK_SIZE" ) == NULL || sscanf( getenv( "VOLUME_CACHE_BLOCK_SIZE" ), "%d", &block_size ) != 1 || block_size < 1 ) { block_size = DEFAULT_BLOCK_SIZE; } for_less( dim, 0, VIO_MAX_DIMENSIONS ) block_sizes[dim] = block_size; } else { for_less( dim, 0, VIO_MAX_DIMENSIONS ) block_sizes[dim] = default_block_sizes[dim]; } /*--- now change any non-positive values to the correct volume size */ for_less( dim, 0, VIO_MAX_DIMENSIONS ) { if( block_sizes[dim] <= 0 || block_sizes[dim] > volume_sizes[dim] ) block_sizes[dim] = volume_sizes[dim]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : initialize_volume_cache @INPUT : cache volume @OUTPUT : @RETURNS : @DESCRIPTION: Initializes the cache for a volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void initialize_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ) { int dim, n_dims, sizes[VIO_MAX_DIMENSIONS]; n_dims = get_volume_n_dimensions( volume ); cache->n_dimensions = n_dims; cache->writing_to_temp_file = FALSE; for_less( dim, 0, VIO_MAX_DIMENSIONS ) cache->file_offset[dim] = 0; cache->minc_file = NULL; cache->input_filename = NULL; cache->output_filename = NULL; cache->original_filename = NULL; cache->history = NULL; set_default_minc_output_options( &cache->options ); cache->output_file_is_open = FALSE; cache->must_read_blocks_before_use = FALSE; get_volume_sizes( volume, sizes ); get_default_cache_block_sizes( n_dims, sizes, cache->block_sizes ); cache->max_cache_bytes = get_default_max_bytes_in_cache(); alloc_volume_cache( cache, volume ); #ifdef CACHE_DEBUGGING initialize_cache_debug( cache ); #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_volume_cache @INPUT : cache volume @OUTPUT : @RETURNS : @DESCRIPTION: Allocates the volume cache. Uses the current value of the volumes max cache size and block sizes to decide how much to allocate. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void alloc_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ) { int dim, n_dims, sizes[VIO_MAX_DIMENSIONS], block, block_size; int x, block_stride, remainder, block_index; get_volume_sizes( volume, sizes ); n_dims = get_volume_n_dimensions( volume ); /*--- count number of blocks needed per dimension */ block_size = 1; block_stride = 1; for_down( dim, n_dims - 1, 0 ) { cache->blocks_per_dim[dim] = (sizes[dim] - 1) / cache->block_sizes[dim] + 1; ALLOC( cache->lookup[dim], sizes[dim] ); for_less( x, 0, sizes[dim] ) { remainder = x % cache->block_sizes[dim]; block_index = x / cache->block_sizes[dim]; cache->lookup[dim][x].block_index_offset = block_index * block_stride; cache->lookup[dim][x].block_offset = remainder * block_size; } block_size *= cache->block_sizes[dim]; block_stride *= cache->blocks_per_dim[dim]; } cache->total_block_size = block_size; cache->max_blocks = cache->max_cache_bytes / block_size / get_type_size(get_volume_data_type(volume)); if( cache->max_blocks < 1 ) cache->max_blocks = 1; /*--- create and initialize an empty hash table */ cache->hash_table_size = cache->max_blocks * HASH_TABLE_SIZE_FACTOR; ALLOC( cache->hash_table, cache->hash_table_size ); for_less( block, 0, cache->hash_table_size ) cache->hash_table[block] = NULL; /*--- set up the initial pointers */ cache->previous_block_index = -1; cache->head = NULL; cache->tail = NULL; cache->n_blocks = 0; } VIOAPI VIO_BOOL volume_cache_is_alloced( VIO_volume_cache_struct *cache ) { return( cache->hash_table != NULL ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_block_start @INPUT : cache block_index @OUTPUT : block_start[] @RETURNS : @DESCRIPTION: Computes the starting voxel indices for a block. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void get_block_start( VIO_volume_cache_struct *cache, int block_index, int block_start[] ) { int dim, block_i; for_down( dim, cache->n_dimensions-1, 0 ) { block_i = block_index % cache->blocks_per_dim[dim]; block_start[dim] = block_i * cache->block_sizes[dim]; block_index /= cache->blocks_per_dim[dim]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : write_cache_block @INPUT : cache volume block @OUTPUT : @RETURNS : @DESCRIPTION: Writes out a cache block to the appropriate position in the corresponding file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void write_cache_block( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_cache_block_struct *block ) { Minc_file minc_file; int dim, ind, n_dims; int file_start[VIO_MAX_DIMENSIONS]; int file_count[VIO_MAX_DIMENSIONS]; int volume_sizes[VIO_MAX_DIMENSIONS]; int block_start[VIO_MAX_DIMENSIONS]; void *array_data_ptr; minc_file = (Minc_file) cache->minc_file; get_block_start( cache, block->block_index, block_start ); get_volume_sizes( volume, volume_sizes ); for_less( dim, 0, minc_file->n_file_dimensions ) { ind = minc_file->to_volume_index[dim]; if( ind >= 0 ) { file_start[dim] = cache->file_offset[dim] + block_start[ind]; file_count[dim] = MIN( volume_sizes[ind] - file_start[dim], cache->block_sizes[ind] ); } else { file_start[dim] = cache->file_offset[dim]; file_count[dim] = 0; } } GET_MULTIDIM_PTR( array_data_ptr, block->array, 0, 0, 0, 0, 0 ); n_dims = cache->n_dimensions; #ifdef HAVE_MINC1 output_minc_hyperslab( (Minc_file) cache->minc_file, get_multidim_data_type(&block->array), n_dims, cache->block_sizes, array_data_ptr, minc_file->to_volume_index, file_start, file_count ); #elif defined HAVE_MINC2 /*TODO: Write out minc file using MINC2 api*/ #endif cache->must_read_blocks_before_use = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : flush_cache_blocks @INPUT : cache volume deleting_volume_flag @OUTPUT : @RETURNS : @DESCRIPTION: Writes out all blocks that have been modified, unless we are writing to a temporary file and the volume is being deleted. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void flush_cache_blocks( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_BOOL deleting_volume_flag ) { VIO_cache_block_struct *block; /*--- don't bother flushing if deleting volume and just writing to temp */ if( cache->writing_to_temp_file && deleting_volume_flag ) return; /*--- step through linked list, freeing blocks */ block = cache->head; while( block != NULL ) { if( block->modified_flag ) { write_cache_block( cache, volume, block ); block->modified_flag = FALSE; } block = block->next_used; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : flush_volume_cache @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Writes out all blocks that have been modified. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void flush_volume_cache( VIO_Volume volume ) { flush_cache_blocks( &volume->cache, volume, FALSE ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_cache_blocks @INPUT : cache volume deleting_volume_flag - TRUE if deleting the volume @OUTPUT : @RETURNS : @DESCRIPTION: Deletes all cache blocks, writing out all blocks, if the volume has been modified. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void delete_cache_blocks( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_BOOL deleting_volume_flag ) { int block; VIO_cache_block_struct *current, *next; /*--- if required, write out cache blocks */ if( !cache->writing_to_temp_file || !deleting_volume_flag ) flush_cache_blocks( cache, volume, deleting_volume_flag ); /*--- step through linked list, freeing blocks */ current = cache->head; while( current != NULL ) { next = current->next_used; delete_multidim_array( ¤t->array ); FREE( current ); current = next; } /*--- initialize cache to no blocks present */ cache->n_blocks = 0; for_less( block, 0, cache->hash_table_size ) cache->hash_table[block] = NULL; cache->previous_block_index = -1; cache->head = NULL; cache->tail = NULL; } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_volume_cache @INPUT : cache volume @OUTPUT : @RETURNS : @DESCRIPTION: Deletes the volume cache. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_volume_cache( VIO_volume_cache_struct *cache, VIO_Volume volume ) { int dim, n_dims; delete_cache_blocks( cache, volume, TRUE ); FREE( cache->hash_table ); cache->hash_table = NULL; n_dims = cache->n_dimensions; for_less( dim, 0, n_dims ) { FREE( cache->lookup[dim] ); } delete_string( cache->input_filename ); delete_string( cache->output_filename ); delete_string( cache->original_filename ); delete_string( cache->history ); delete_minc_output_options( &cache->options ); /*--- close the file that cache was reading from or writing to */ if( cache->minc_file != NULL ) { if( cache->output_file_is_open ) { #ifdef HAVE_MINC1 (void) close_minc_output( (Minc_file) cache->minc_file ); #elif defined HAVE_MINC2 (void) close_minc2_output( (Minc_file) cache->minc_file ); #endif } else #ifdef HAVE_MINC1 (void) close_minc_input( (Minc_file) cache->minc_file ); #elif defined HAVE_MINC2 (void) close_minc2_input( (Minc_file) cache->minc_file ); #endif } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_cache_block_sizes @INPUT : volume block_sizes @OUTPUT : @RETURNS : @DESCRIPTION: Changes the sizes of the cache blocks for the volume, if it is a cached volume. This flushes the cache blocks, since they have changed. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 24, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_cache_block_sizes( VIO_Volume volume, int block_sizes[] ) { VIO_volume_cache_struct *cache; int d, dim, sizes[VIO_N_DIMENSIONS]; VIO_BOOL changed; if( !volume->is_cached_volume ) return; cache = &volume->cache; get_volume_sizes( volume, sizes ); changed = FALSE; for_less( d, 0, get_volume_n_dimensions(volume) ) { if( block_sizes[d] < 1 || block_sizes[d] > sizes[d] ) block_sizes[d] = sizes[d]; if( cache->block_sizes[d] != block_sizes[d] ) changed = TRUE; } /*--- if the block sizes have not changed, do nothing */ if( !changed ) return; delete_cache_blocks( cache, volume, FALSE ); FREE( cache->hash_table ); for_less( dim, 0, get_volume_n_dimensions( volume ) ) { FREE( cache->lookup[dim] ); } for_less( d, 0, get_volume_n_dimensions(volume) ) cache->block_sizes[d] = block_sizes[d]; alloc_volume_cache( cache, volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_cache_size @INPUT : volume max_memory_bytes @OUTPUT : @RETURNS : @DESCRIPTION: Changes the maximum amount of memory in the cache for this volume, if it is a cached volume. This flushes the cache, in order to reallocate the hash table to a new size. @METHOD : @GLOBALS : @CALLS : @CREATED : Oct. 24, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_cache_size( VIO_Volume volume, int max_memory_bytes ) { int dim; VIO_volume_cache_struct *cache; if( !volume->is_cached_volume ) return; cache = &volume->cache; delete_cache_blocks( cache, volume, FALSE ); FREE( cache->hash_table ); for_less( dim, 0, get_volume_n_dimensions( volume ) ) { FREE( cache->lookup[dim] ); } cache->max_cache_bytes = max_memory_bytes; alloc_volume_cache( cache, volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_cache_output_volume_parameters @INPUT : volume filename file_nc_data_type file_signed_flag file_voxel_min file_voxel_max original_filename - if non-NULL copies auxiliary info from this history options @OUTPUT : @RETURNS : @DESCRIPTION: Indicates that rather than using a temporary file for the cached volume, read and write to this file with the associated parameters (similar to output_modified_volume()). @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 4, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_cache_output_volume_parameters( VIO_Volume volume, VIO_STR filename, nc_type file_nc_data_type, VIO_BOOL file_signed_flag, VIO_Real file_voxel_min, VIO_Real file_voxel_max, VIO_STR original_filename, VIO_STR history, minc_output_options *options ) { volume->cache.output_filename = create_string( filename ); volume->cache.file_nc_data_type = file_nc_data_type; volume->cache.file_signed_flag = file_signed_flag; volume->cache.file_voxel_min = file_voxel_min; volume->cache.file_voxel_max = file_voxel_max; volume->cache.original_filename = create_string( original_filename ); volume->cache.history = create_string( history ); copy_minc_output_options( options, &volume->cache.options ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_cache_volume_input_file @INPUT : cache volume filename options @OUTPUT : @RETURNS : @DESCRIPTION: Opens the volume file for reading into the cache as needed. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void open_cache_volume_input_file( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_STR filename, minc_input_options *options ) { cache->input_filename = create_string( filename ); #ifdef HAVE_MINC1 cache->minc_file = initialize_minc_input( filename, volume, options ); #elif defined HAVE_MINC2 cache->minc_file = initialize_minc2_input( filename, volume, options ); #endif cache->must_read_blocks_before_use = TRUE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : open_cache_volume_output_file @INPUT : cache volume @OUTPUT : @RETURNS : @DESCRIPTION: Opens a volume file for reading and writing cache blocks. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Status open_cache_volume_output_file( VIO_volume_cache_struct *cache, VIO_Volume volume ) { VIO_Status status=VIO_ERROR; int dim, n_dims; int out_sizes[VIO_MAX_DIMENSIONS]; int vol_sizes[VIO_MAX_DIMENSIONS]; VIO_Real min_value, max_value; Minc_file out_minc_file=NULL; VIO_STR *vol_dim_names=NULL; VIO_STR *out_dim_names=NULL, output_filename; n_dims = get_volume_n_dimensions( volume ); /*--- check if the output filename has been set */ if( string_length( cache->output_filename ) == 0 ) { cache->writing_to_temp_file = TRUE; output_filename = get_temporary_filename(); cache->file_nc_data_type = get_volume_nc_data_type( volume, &cache->file_signed_flag ); get_volume_voxel_range( volume, &cache->file_voxel_min, &cache->file_voxel_max ); ALLOC( out_dim_names, n_dims ); vol_dim_names = get_volume_dimension_names( volume ); get_volume_sizes( volume, vol_sizes ); for_less( dim, 0, n_dims ) { out_dim_names[dim] = create_string( vol_dim_names[dim] ); out_sizes[dim] = vol_sizes[dim]; } delete_dimension_names( volume, vol_dim_names ); } else { cache->writing_to_temp_file = FALSE; output_filename = create_string( cache->output_filename ); /*#ifdef HAVE_MINC1*/ out_dim_names = create_output_dim_names( volume, cache->original_filename, &cache->options, out_sizes ); /*#elif defined HAVE_MINC2*/ /*TODO: adopt for MINC2*/ /*#endif */ if( out_dim_names == NULL ) return( VIO_ERROR ); } get_volume_real_range( volume, &min_value, &max_value ); set_minc_output_real_range( &cache->options, min_value, max_value ); /*--- open the file for writing */ #ifdef HAVE_MINC1 out_minc_file = initialize_minc_output( output_filename, n_dims, out_dim_names, out_sizes, cache->file_nc_data_type, cache->file_signed_flag, cache->file_voxel_min, cache->file_voxel_max, get_voxel_to_world_transform(volume), volume, &cache->options ); #elif defined HAVE_MINC2 out_minc_file = initialize_minc2_output( output_filename, n_dims, out_dim_names, out_sizes, cache->file_nc_data_type, cache->file_signed_flag, cache->file_voxel_min, cache->file_voxel_max, get_voxel_to_world_transform(volume), volume, &cache->options ); #endif if( out_minc_file == NULL ) return( VIO_ERROR ); status = copy_volume_auxiliary_and_history( out_minc_file, output_filename, cache->original_filename, cache->history ); if( status != VIO_OK ) return( status ); out_minc_file->converting_to_colour = FALSE; /*--- make temp file disappear when the volume is deleted */ if( string_length( cache->output_filename ) == 0 ) remove_file( output_filename ); #ifdef HAVE_MINC1 status = set_minc_output_random_order( out_minc_file ); #elif defined HAVE_MINC2 status = set_minc2_output_random_order( out_minc_file ); #endif if( status != VIO_OK ) return( status ); /*--- if the volume was previously reading a file, copy the volume to the output and close the input file */ if( cache->minc_file != NULL ) { #ifdef HAVE_MINC1 (void) output_minc_volume( out_minc_file ); (void) close_minc_input( (Minc_file) cache->minc_file ); #elif defined HAVE_MINC2 (void) output_minc2_volume( out_minc_file ); (void) close_minc2_input( (Minc_file) cache->minc_file ); #endif cache->must_read_blocks_before_use = TRUE; } cache->minc_file = out_minc_file; delete_dimension_names( volume, out_dim_names ); delete_string( output_filename ); return( VIO_OK ); } VIOAPI void cache_volume_range_has_changed( VIO_Volume volume ) { if( !volume->is_cached_volume ) return; if( volume->cache.minc_file == NULL && volume->cache.n_blocks == 0 ) return; /* This message is not useful. print( "Not implemented yet in cache_volume_range_has_changed()\n" ); */ } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_cache_volume_file_offset @INPUT : cache volume file_offset @OUTPUT : @RETURNS : @DESCRIPTION: Sets the offset in the file for writing volumes. Used when writing several cached volumes to a file. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_cache_volume_file_offset( VIO_volume_cache_struct *cache, VIO_Volume volume, long file_offset[] ) { VIO_BOOL changed; int dim; changed = FALSE; for_less( dim, 0, VIO_MAX_DIMENSIONS ) { if( cache->file_offset[dim] != (int) file_offset[dim] ) changed = TRUE; cache->file_offset[dim] = (int) file_offset[dim]; } if( changed ) delete_cache_blocks( cache, volume, FALSE ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : read_cache_block @INPUT : cache volume block block_start @OUTPUT : @RETURNS : @DESCRIPTION: Reads one cache block. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void read_cache_block( VIO_volume_cache_struct *cache, VIO_Volume volume, VIO_cache_block_struct *block, int block_start[] ) { Minc_file minc_file; int dim, ind, n_dims; int sizes[VIO_MAX_DIMENSIONS]; int file_start[VIO_MAX_DIMENSIONS]; int file_count[VIO_MAX_DIMENSIONS]; void *array_data_ptr; minc_file = (Minc_file) cache->minc_file; get_volume_sizes( volume, sizes ); for_less( dim, 0, minc_file->n_file_dimensions ) { ind = minc_file->to_volume_index[dim]; if( ind >= 0 ) { file_start[dim] = cache->file_offset[dim] + block_start[ind]; file_count[dim] = MIN( sizes[ind] - file_start[dim], cache->block_sizes[ind] ); } else { file_start[dim] = cache->file_offset[dim]; file_count[dim] = 0; } } n_dims = cache->n_dimensions; GET_MULTIDIM_PTR( array_data_ptr, block->array, 0, 0, 0, 0, 0 ); #ifdef HAVE_MINC1 input_minc_hyperslab( (Minc_file) cache->minc_file, get_multidim_data_type(&block->array), n_dims, cache->block_sizes, array_data_ptr, minc_file->to_volume_index, file_start, file_count ); #elif defined HAVE_MINC2 /*TODO: call minc2 api ?*/ #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : appropriate_a_cache_block @INPUT : cache volume @OUTPUT : block @RETURNS : @DESCRIPTION: Finds an available cache block, either by allocating one, or stealing the least recently used one. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_cache_block_struct *appropriate_a_cache_block( VIO_volume_cache_struct *cache, VIO_Volume volume ) { VIO_cache_block_struct *block; /*--- if can allocate more blocks, do so */ if( cache->n_blocks < cache->max_blocks ) { ALLOC( block, 1 ); create_multidim_array( &block->array, 1, &cache->total_block_size, get_volume_data_type(volume) ); ++cache->n_blocks; } else /*--- otherwise, steal the least-recently used block */ { block = cache->tail; if( block->modified_flag ) write_cache_block( cache, volume, block ); /*--- remove from used list */ if( block->prev_used == NULL ) cache->head = block->next_used; else block->prev_used->next_used = block->next_used; if( block->next_used == NULL ) cache->tail = block->prev_used; else block->next_used->prev_used = block->prev_used; /*--- remove from hash table */ *block->prev_hash = block->next_hash; if( block->next_hash != NULL ) block->next_hash->prev_hash = block->prev_hash; } block->modified_flag = FALSE; return( block ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : hash_block_index @INPUT : key table_size @OUTPUT : @RETURNS : hash address @DESCRIPTION: Hashes a block index key into a table index, using multiplicative hashing. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static int hash_block_index( int key, int table_size ) { int index; VIO_Real v; v = (VIO_Real) key * HASH_FUNCTION_CONSTANT; index = (int) (( v - (VIO_Real) ((int) v)) * (VIO_Real) table_size); return( index ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_cache_block_for_voxel @INPUT : volume x y z t v @OUTPUT : offset @RETURNS : pointer to cache block @DESCRIPTION: Finds the cache block corresponding to a given voxel, and modifies the voxel indices to be block indices. This function gets called for every set or get voxel value, so it must be efficient. On return, offset contains the integer offset of the voxel within the cache block. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_cache_block_struct *get_cache_block_for_voxel( VIO_Volume volume, int x, int y, int z, int t, int v, int *offset ) { VIO_cache_block_struct *block; VIO_cache_lookup_struct *lookup0, *lookup1, *lookup2, *lookup3, *lookup4; int block_index; int block_start[VIO_MAX_DIMENSIONS]; int n_dims, hash_index; VIO_volume_cache_struct *cache; cache = &volume->cache; n_dims = cache->n_dimensions; switch( n_dims ) { case 1: lookup0 = &cache->lookup[0][x]; block_index = lookup0->block_index_offset; *offset = lookup0->block_offset; break; case 2: lookup0 = &cache->lookup[0][x]; lookup1 = &cache->lookup[1][y]; block_index = lookup0->block_index_offset + lookup1->block_index_offset; *offset = lookup0->block_offset + lookup1->block_offset; break; case 3: lookup0 = &cache->lookup[0][x]; lookup1 = &cache->lookup[1][y]; lookup2 = &cache->lookup[2][z]; block_index = lookup0->block_index_offset + lookup1->block_index_offset + lookup2->block_index_offset; *offset = lookup0->block_offset + lookup1->block_offset + lookup2->block_offset; break; case 4: lookup0 = &cache->lookup[0][x]; lookup1 = &cache->lookup[1][y]; lookup2 = &cache->lookup[2][z]; lookup3 = &cache->lookup[3][t]; block_index = lookup0->block_index_offset + lookup1->block_index_offset + lookup2->block_index_offset + lookup3->block_index_offset; *offset = lookup0->block_offset + lookup1->block_offset + lookup2->block_offset + lookup3->block_offset; break; case 5: lookup0 = &cache->lookup[0][x]; lookup1 = &cache->lookup[1][y]; lookup2 = &cache->lookup[2][z]; lookup3 = &cache->lookup[3][t]; lookup4 = &cache->lookup[4][v]; block_index = lookup0->block_index_offset + lookup1->block_index_offset + lookup2->block_index_offset + lookup3->block_index_offset + lookup4->block_index_offset; *offset = lookup0->block_offset + lookup1->block_offset + lookup2->block_offset + lookup3->block_offset + lookup4->block_offset; break; } /*--- if this is the same as the last access, just return the last block accessed */ if( block_index == cache->previous_block_index ) { #ifdef CACHE_DEBUGGING record_cache_prev_hit( cache ); #endif return( cache->previous_block ); } /*--- search the hash table for the block index */ hash_index = hash_block_index( block_index, cache->hash_table_size ); block = cache->hash_table[hash_index]; while( block != NULL && block->block_index != block_index ) { block = block->next_hash; } /*--- check if it was found in the hash table */ if( block == NULL ) { #ifdef CACHE_DEBUGGING record_cache_no_hit( cache ); #endif /*--- find a block to use */ block = appropriate_a_cache_block( cache, volume ); block->block_index = block_index; /*--- check if the block must be initialized from a file */ if( cache->must_read_blocks_before_use ) { get_block_start( cache, block_index, block_start ); read_cache_block( cache, volume, block, block_start ); } /*--- insert the block in cache hash table */ block->next_hash = cache->hash_table[hash_index]; if( block->next_hash != NULL ) block->next_hash->prev_hash = &block->next_hash; block->prev_hash = &cache->hash_table[hash_index]; *block->prev_hash = block; /*--- insert the block at the head of the used list */ block->prev_used = NULL; block->next_used = cache->head; if( cache->head == NULL ) cache->tail = block; else cache->head->prev_used = block; cache->head = block; } else /*--- block was found in hash table */ { #ifdef CACHE_DEBUGGING record_cache_hit( cache ); #endif /*--- move block to head of used list */ if( block != cache->head ) { block->prev_used->next_used = block->next_used; if( block->next_used != NULL ) block->next_used->prev_used = block->prev_used; else cache->tail = block->prev_used; cache->head->prev_used = block; block->prev_used = NULL; block->next_used = cache->head; cache->head = block; } /*--- move block to beginning of hash chain, so if next access to this block, we will save some time */ if( cache->hash_table[hash_index] != block ) { /*--- remove it from where it is */ *block->prev_hash = block->next_hash; if( block->next_hash != NULL ) block->next_hash->prev_hash = block->prev_hash; /*--- place it at the front of the list */ block->next_hash = cache->hash_table[hash_index]; if( block->next_hash != NULL ) block->next_hash->prev_hash = &block->next_hash; block->prev_hash = &cache->hash_table[hash_index]; *block->prev_hash = block; } } /*--- record so if next access is to same block, we save some time */ cache->previous_block = block; cache->previous_block_index = block_index; return( cache->previous_block ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_cached_volume_voxel @INPUT : volume x y z t v @OUTPUT : @RETURNS : voxel value @DESCRIPTION: Finds the voxel value for the given voxel in a cached volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_cached_volume_voxel( VIO_Volume volume, int x, int y, int z, int t, int v ) { int offset; VIO_Real value; VIO_cache_block_struct *block; if( volume->cache.minc_file == NULL ) return( get_volume_voxel_min( volume ) ); block = get_cache_block_for_voxel( volume, x, y, z, t, v, &offset ); GET_MULTIDIM_1D( value, (VIO_Real), block->array, offset ); return( value ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_cached_volume_voxel @INPUT : volume x y z t v value @OUTPUT : @RETURNS : @DESCRIPTION: Sets the voxel value for the given voxel in a cached volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_cached_volume_voxel( VIO_Volume volume, int x, int y, int z, int t, int v, VIO_Real value ) { int offset; VIO_cache_block_struct *block; if( !volume->cache.output_file_is_open ) { (void) open_cache_volume_output_file( &volume->cache, volume ); volume->cache.output_file_is_open = TRUE; } block = get_cache_block_for_voxel( volume, x, y, z, t, v, &offset ); block->modified_flag = TRUE; SET_MULTIDIM_1D( block->array, offset, value ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cached_volume_has_been_modified @INPUT : cache @OUTPUT : @RETURNS : TRUE if the volume has been modified since creation @DESCRIPTION: Determines if the volume has been modified. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL cached_volume_has_been_modified( VIO_volume_cache_struct *cache ) { return( cache->minc_file != NULL ); } VIOAPI VIO_BOOL volume_is_cached( VIO_Volume volume ) { return( volume->is_cached_volume ); } #ifndef CACHE_DEBUGGING /* ARGSUSED */ #endif VIOAPI void set_volume_cache_debugging( VIO_Volume volume, int output_every ) { #ifdef CACHE_DEBUGGING if( output_every >= 1 ) { volume->cache.debugging_on = TRUE; volume->cache.output_every = output_every; } else { volume->cache.debugging_on = FALSE; } #endif } #ifdef CACHE_DEBUGGING static void initialize_cache_debug( VIO_volume_cache_struct *cache ) { int output_every; VIO_STR debug; debug = getenv( "VOLUME_CACHE_DEBUG" ); cache->debugging_on = (debug != NULL); if( debug == NULL || sscanf( debug, "%d", &output_every ) != 1 || output_every < 1 ) { output_every = 1000; } cache->output_every = output_every; cache->n_accesses = 0; cache->n_hits = 0; cache->n_prev_hits = 0; } static void increment_n_accesses( VIO_volume_cache_struct *cache ) { ++cache->n_accesses; if( cache->n_accesses >= cache->output_every ) { print( "VIO_Volume cache: Hit ratio: %g Prev ratio: %g\n", (VIO_Real) (cache->n_hits + cache->n_prev_hits) / (VIO_Real) cache->n_accesses, (VIO_Real) cache->n_prev_hits / (VIO_Real) cache->n_accesses ); cache->n_accesses = 0; cache->n_hits = 0; cache->n_prev_hits = 0; } } static void record_cache_hit( VIO_volume_cache_struct *cache ) { if( cache->debugging_on ) { ++cache->n_hits; increment_n_accesses( cache ); } } static void record_cache_prev_hit( VIO_volume_cache_struct *cache ) { if( cache->debugging_on ) { ++cache->n_prev_hits; increment_n_accesses( cache ); } } static void record_cache_no_hit( VIO_volume_cache_struct *cache ) { if( cache->debugging_on ) { increment_n_accesses( cache ); } } #endif libminc-libminc-2-3-00/volume_io/Volumes/volumes.c000066400000000000000000002527031257462267400221400ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- @COPYRIGHT : Copyright 1993,1994,1995 David MacDonald, McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. The author and McGill University make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. ---------------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /*HAVE_CONFIG_H*/ #include #include #include VIO_STR XYZ_dimension_names[] = { MIxspace, MIyspace, MIzspace }; VIO_STR File_order_dimension_names[] = { "", "", "", "", "" }; static VIO_STR default_dimension_names[VIO_MAX_DIMENSIONS][VIO_MAX_DIMENSIONS] = { { MIxspace }, { MIyspace, MIxspace }, { MIzspace, MIyspace, MIxspace }, { "", MIzspace, MIyspace, MIxspace }, { "", "", MIzspace, MIyspace, MIxspace } }; /* ----------------------------- MNI Header ----------------------------------- @NAME : get_default_dim_names @INPUT : n_dimensions @OUTPUT : @RETURNS : list of dimension names @DESCRIPTION: Returns the list of default dimension names for the given number of dimensions. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR *get_default_dim_names( int n_dimensions ) { return( default_dimension_names[n_dimensions-1] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_spatial_axis_to_dim_name @INPUT : axis @OUTPUT : @RETURNS : dimension name @DESCRIPTION: Returns the name of the dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_STR convert_spatial_axis_to_dim_name( int axis ) { switch( axis ) { case VIO_X: return( MIxspace ); case VIO_Y: return( MIyspace ); case VIO_Z: return( MIzspace ); default: handle_internal_error( "convert_spatial_axis_to_dim_name" ); break; } return( NULL ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_dim_name_to_spatial_axis @INPUT : dimension_name @OUTPUT : axis @RETURNS : TRUE if axis name is a spatial dimension @DESCRIPTION: Checks if the dimension name corresponds to a spatial dimension and if so, passes back the corresponding axis index. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL convert_dim_name_to_spatial_axis( VIO_STR name, int *axis ) { *axis = -1; if( equal_strings( name, MIxspace ) ) *axis = VIO_X; else if( equal_strings( name, MIyspace ) ) *axis = VIO_Y; else if( equal_strings( name, MIzspace ) ) *axis = VIO_Z; return( *axis >= 0 ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : create_volume @INPUT : n_dimensions - number of dimensions (1-5) dimension_names - name of dimensions for use when reading file data_type - type of the data, e.g. NC_BYTE signed_flag - type is signed? min_value - min and max value to be stored max_value @OUTPUT : @RETURNS : VIO_Volume @DESCRIPTION: Creates a VIO_Volume structure, and initializes it. In order to later use the volume, you must call either set_volume_size() and alloc_volume_data(), or one of the input volume routines, which in turn calls these two. The dimension_names are used when inputting MINC files, in order to match with the dimension names in the file. Typically, use dimension names { MIzspace, MIyspace, MIxspace } to read the volume from the file in the order it is stored, or { MIxspace, MIyspace, MIzspace } to read it so you can subcript the volume in x, y, z order. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - handles space type @MODIFIED : May 22, 1996 D. MacDonald - now stores starts/steps ---------------------------------------------------------------------------- */ VIOAPI VIO_Volume create_volume( int n_dimensions, VIO_STR dimension_names[], nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ) { int i, axis, sizes[VIO_MAX_DIMENSIONS]; VIO_Status status; VIO_STR name; volume_struct *volume; VIO_Transform identity; status = VIO_OK; if( n_dimensions < 1 || n_dimensions > VIO_MAX_DIMENSIONS ) { print_error( "create_volume(): n_dimensions (%d) not in range 1 to %d.\n", n_dimensions, VIO_MAX_DIMENSIONS ); status = VIO_ERROR; } if( status == VIO_ERROR ) { return( (VIO_Volume) NULL ); } ALLOC( volume, 1 ); volume->is_rgba_data = FALSE; volume->is_cached_volume = FALSE; volume->real_range_set = FALSE; volume->real_value_scale = 1.0; volume->real_value_translation = 0.0; for_less( i, 0, VIO_N_DIMENSIONS ) volume->spatial_axes[i] = -1; for_less( i, 0, n_dimensions ) { volume->starts[i] = 0.0; volume->separations[i] = 1.0; volume->direction_cosines[i][VIO_X] = 0.0; volume->direction_cosines[i][VIO_Y] = 0.0; volume->direction_cosines[i][VIO_Z] = 0.0; volume->irregular_starts[i] = NULL; volume->irregular_widths[i] = NULL; sizes[i] = 0; if( dimension_names != (char **) NULL ) name = dimension_names[i]; else name = default_dimension_names[n_dimensions-1][i]; if( convert_dim_name_to_spatial_axis( name, &axis ) ) { volume->spatial_axes[axis] = i; volume->direction_cosines[i][axis] = 1.0; } volume->dimension_names[i] = create_string( name ); } create_empty_multidim_array( &volume->array, n_dimensions, VIO_NO_DATA_TYPE ); set_volume_type( volume, nc_data_type, signed_flag, voxel_min, voxel_max ); set_volume_sizes( volume, sizes ); make_identity_transform( &identity ); create_linear_transform( &volume->voxel_to_world_transform, &identity ); volume->voxel_to_world_transform_uptodate = TRUE; volume->coordinate_system_name = create_string( MI_UNKNOWN_SPACE ); return( volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_type @INPUT : volume nc_data_type signed_flag voxel_min voxel_max @OUTPUT : @RETURNS : @DESCRIPTION: Sets the data type and valid range of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_type( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ) { VIO_Data_types data_type; if( nc_data_type != MI_ORIGINAL_TYPE ) { switch( nc_data_type ) { case NC_BYTE: if( signed_flag ) data_type = VIO_SIGNED_BYTE; else data_type = VIO_UNSIGNED_BYTE; break; case NC_SHORT: if( signed_flag ) data_type = VIO_SIGNED_SHORT; else data_type = VIO_UNSIGNED_SHORT; break; case NC_INT: if( signed_flag ) data_type = VIO_SIGNED_INT; else data_type = VIO_UNSIGNED_INT; break; case NC_FLOAT: data_type = VIO_FLOAT; break; default: case NC_DOUBLE: data_type = VIO_DOUBLE; break; } set_multidim_data_type( &volume->array, data_type ); volume->signed_flag = signed_flag; set_volume_voxel_range( volume, voxel_min, voxel_max ); } volume->nc_data_type = nc_data_type; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_type @INPUT : volume minc2_data_type voxel_min voxel_max @OUTPUT : @RETURNS : @DESCRIPTION: Sets the data type and valid range of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 2013 Vladimir FONOV @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_type2( VIO_Volume volume, mitype_t minc2_data_type, VIO_Real voxel_min, VIO_Real voxel_max ) { VIO_Data_types data_type; VIO_BOOL signed_flag=0; if( minc2_data_type != MI_ORIGINAL_TYPE ) { switch( minc2_data_type ) { case MI_TYPE_BYTE: data_type = VIO_SIGNED_BYTE; signed_flag=1; volume->nc_data_type=NC_BYTE; break; case MI_TYPE_UBYTE: data_type = VIO_UNSIGNED_BYTE; volume->nc_data_type=NC_BYTE; break; case MI_TYPE_SHORT: data_type = VIO_SIGNED_SHORT; signed_flag=1; volume->nc_data_type=NC_SHORT; break; case MI_TYPE_USHORT: data_type = VIO_UNSIGNED_SHORT; volume->nc_data_type=NC_SHORT; break; case MI_TYPE_INT: data_type = VIO_SIGNED_INT; volume->nc_data_type=NC_INT; signed_flag=1; break; case MI_TYPE_UINT: data_type = VIO_UNSIGNED_INT; volume->nc_data_type=NC_INT; break; case MI_TYPE_FLOAT: data_type = VIO_FLOAT; volume->nc_data_type=NC_FLOAT; signed_flag=1; break; default: case MI_TYPE_DOUBLE: data_type = VIO_DOUBLE; signed_flag=1; volume->nc_data_type=NC_DOUBLE; break; } set_multidim_data_type( &volume->array, data_type ); volume->signed_flag = signed_flag; set_volume_voxel_range( volume, voxel_min, voxel_max ); } } VIOAPI mitype_t nc_type_to_minc2_type( nc_type nc_data_type, VIO_BOOL signed_flag ) { if(nc_data_type==MI_ORIGINAL_TYPE) return MI_ORIGINAL_TYPE; if(signed_flag) { switch(nc_data_type) { case NC_BYTE: return MI_TYPE_BYTE; case NC_SHORT: return MI_TYPE_SHORT; case NC_INT: return MI_TYPE_INT; case NC_FLOAT: return MI_TYPE_FLOAT; default: case NC_DOUBLE: return MI_TYPE_DOUBLE; } } else { switch(nc_data_type) { case NC_BYTE: return MI_TYPE_UBYTE; case NC_SHORT: return MI_TYPE_USHORT; case NC_INT: return MI_TYPE_UINT; case NC_FLOAT: return MI_TYPE_FLOAT; default: case NC_DOUBLE: return MI_TYPE_DOUBLE; } } } VIOAPI mitype_t vio_type_to_minc2_type( VIO_Data_types vio_data_type) { switch( vio_data_type ) { case VIO_SIGNED_BYTE: return MI_TYPE_BYTE; case VIO_UNSIGNED_BYTE: return MI_TYPE_UBYTE; case VIO_SIGNED_SHORT: return MI_TYPE_SHORT; case VIO_UNSIGNED_SHORT: return MI_TYPE_USHORT; case VIO_SIGNED_INT: return MI_TYPE_INT; case VIO_UNSIGNED_INT: return MI_TYPE_UINT; case VIO_FLOAT: return MI_TYPE_FLOAT; default: case VIO_DOUBLE: return MI_TYPE_DOUBLE; } } VIOAPI VIO_Data_types minc2_type_to_vio_type( mitype_t minc_data_type) { switch( minc_data_type ) { case MI_TYPE_BYTE: return VIO_SIGNED_BYTE; case MI_TYPE_UBYTE: return VIO_UNSIGNED_BYTE; case MI_TYPE_SHORT: return VIO_SIGNED_SHORT; case MI_TYPE_USHORT: return VIO_UNSIGNED_SHORT; case MI_TYPE_INT: return VIO_SIGNED_INT; case MI_TYPE_UINT: return VIO_UNSIGNED_INT; case MI_TYPE_FLOAT: return VIO_FLOAT; default: case MI_TYPE_DOUBLE: return VIO_DOUBLE; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_nc_data_type @INPUT : volume @OUTPUT : signed_flag @RETURNS : data type @DESCRIPTION: Returns the NETCDF data type of the volume and passes back the signed flag. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI nc_type get_volume_nc_data_type( VIO_Volume volume, VIO_BOOL *signed_flag ) { if( signed_flag != (VIO_BOOL *) NULL ) *signed_flag = volume->signed_flag; return( volume->nc_data_type ); } VIOAPI mitype_t get_volume_minc2_data_type( VIO_Volume volume) { return vio_type_to_minc2_type( get_multidim_data_type( &volume->array ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_data_type @INPUT : volume @OUTPUT : @RETURNS : data type @DESCRIPTION: Returns the data type of the volume (not the NETCDF type). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Data_types get_volume_data_type( VIO_Volume volume ) { return( get_multidim_data_type( &volume->array ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_rgb_volume_flag @INPUT : volume flag @OUTPUT : @RETURNS : @DESCRIPTION: Sets the flag indicating that the volume is an RGB volume. Can only set the flag to TRUE if the volume is an unsigned long volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov 13, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_rgb_volume_flag( VIO_Volume volume, VIO_BOOL flag ) { if( !flag || get_volume_data_type(volume) == VIO_UNSIGNED_INT ) volume->is_rgba_data = flag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : is_an_rgb_volume @INPUT : volume @OUTPUT : @RETURNS : TRUE if it is an RGB volume @DESCRIPTION: Tests if the volume is an RGB volume. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL is_an_rgb_volume( VIO_Volume volume ) { return( volume->is_rgba_data ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : alloc_volume_data @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Allocates the memory for the volume. Assumes that the volume type and sizes have been set. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void alloc_volume_data( VIO_Volume volume ) { #ifdef HAVE_MINC1 unsigned long data_size; data_size = (unsigned long) get_volume_total_n_voxels( volume ) * (unsigned long) get_type_size( get_volume_data_type( volume ) ); if( get_n_bytes_cache_threshold() >= 0 && data_size > (unsigned long) get_n_bytes_cache_threshold() ) { volume->is_cached_volume = TRUE; initialize_volume_cache( &volume->cache, volume ); } else { #endif /*HAVE_MINC1*/ volume->is_cached_volume = FALSE; alloc_multidim_array( &volume->array ); #ifdef HAVE_MINC1 } #endif /*HAVE_MINC1*/ } /* ----------------------------- MNI Header ----------------------------------- @NAME : volume_is_alloced @INPUT : volume @OUTPUT : @RETURNS : TRUE if the volume is allocated @DESCRIPTION: Checks if the volume data has been allocated. @METHOD : @GLOBALS : @CALLS : @CREATED : Sep. 1, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL volume_is_alloced( VIO_Volume volume ) { #ifdef HAVE_MINC1 return ( volume->is_cached_volume && volume_cache_is_alloced( &volume->cache )) || (!volume->is_cached_volume && multidim_array_is_alloced( &volume->array )) ; #else return multidim_array_is_alloced( &volume->array ) ; #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : free_volume_data @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Frees the memory associated with the volume multidimensional data. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void free_volume_data( VIO_Volume volume ) { #ifdef HAVE_MINC1 if( volume->is_cached_volume ) delete_volume_cache( &volume->cache, volume ); else #endif if( volume_is_alloced( volume ) ) delete_multidim_array( &volume->array ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_volume @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Frees all memory from the volume and the volume struct itself. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - handles space type ---------------------------------------------------------------------------- */ VIOAPI void delete_volume( VIO_Volume volume ) { int d; if( volume == (VIO_Volume) NULL ) { print_error( "delete_volume(): cannot delete a null volume.\n" ); return; } free_volume_data( volume ); delete_general_transform( &volume->voxel_to_world_transform ); for_less( d, 0, get_volume_n_dimensions(volume) ) { delete_string( volume->dimension_names[d] ); if( volume->irregular_starts[d] ) FREE( volume->irregular_starts[d] ); if( volume->irregular_widths[d] ) FREE( volume->irregular_widths[d] ); } delete_string( volume->coordinate_system_name ); FREE( volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_n_dimensions @INPUT : volume @OUTPUT : @RETURNS : TRUE if successful @DESCRIPTION: Returns the number of dimensions of the volume @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_BOOL set_volume_n_dimensions( VIO_Volume volume, int n_dimensions) { if ( volume != NULL ) { return set_multidim_n_dimensions( &volume->array, n_dimensions ); } return FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_n_dimensions @INPUT : volume @OUTPUT : @RETURNS : number of dimensions @DESCRIPTION: Returns the number of dimensions of the volume @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI int get_volume_n_dimensions( VIO_Volume volume ) { if( volume ) return( get_multidim_n_dimensions( &volume->array ) ); else return -1; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_sizes @INPUT : volume @OUTPUT : sizes @RETURNS : @DESCRIPTION: Passes back the sizes of each of the dimensions. Assumes sizes has enough room for n_dimensions integers. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_sizes( VIO_Volume volume, int sizes[] ) { get_multidim_sizes( &volume->array, sizes ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_sizes @INPUT : volume sizes @OUTPUT : @RETURNS : @DESCRIPTION: Sets the sizes (number of voxels in each dimension) of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_sizes( VIO_Volume volume, int sizes[] ) { set_multidim_sizes( &volume->array, sizes ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_total_n_voxels @INPUT : volume @OUTPUT : @RETURNS : n voxels @DESCRIPTION: Returns the total number of voxels in the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI unsigned int get_volume_total_n_voxels( VIO_Volume volume ) { unsigned int n; int i, sizes[VIO_MAX_DIMENSIONS]; n = 1; get_volume_sizes( volume, sizes ); for_less( i, 0, get_volume_n_dimensions( volume ) ) n *= (unsigned int) sizes[i]; return( n ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : assign_voxel_to_world_transform @INPUT : volume transform @OUTPUT : @RETURNS : @DESCRIPTION: Updates the volume's transformation from voxel to world coords. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 D. MacDonald - created from set_voxel_to_world_transform @MODIFIED : ---------------------------------------------------------------------------- */ static void assign_voxel_to_world_transform( VIO_Volume volume, VIO_General_transform *transform ) { delete_general_transform( &volume->voxel_to_world_transform ); volume->voxel_to_world_transform = *transform; } /* ----------------------------- MNI Header ----------------------------------- @NAME : dot_vectors @INPUT : n v1 v2 @OUTPUT : @RETURNS : Dot product @DESCRIPTION: Computes the dot product of 2 n-dimensional vectors. This function should be moved to some vector routines. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static VIO_Real dot_vectors( int n, VIO_Real v1[], VIO_Real v2[] ) { int i; VIO_Real d; d = 0.0; for_less( i, 0, n ) d += v1[i] * v2[i]; return( d ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : cross_3D_vector @INPUT : v1 v2 @OUTPUT : cross @RETURNS : @DESCRIPTION: Computes the cross product of 2 n-dimensional vectors. This function should be moved to some vector routines. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void cross_3D_vector( VIO_Real v1[], VIO_Real v2[], VIO_Real cross[] ) { cross[0] = v1[1] * v2[2] - v1[2] * v2[1]; cross[1] = v1[2] * v2[0] - v1[0] * v2[2]; cross[2] = v1[0] * v2[1] - v1[1] * v2[0]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : normalize_vector @INPUT : n v1 @OUTPUT : v1_normalized @RETURNS : @DESCRIPTION: Normalizes the length of v1 to 1, placing result in v1_normalized @METHOD : @GLOBALS : @CALLS : @CREATED : May 22, 1997 D. MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void normalize_vector( VIO_Real v1[], VIO_Real v1_normalized[] ) { int d; VIO_Real mag; mag = dot_vectors( VIO_N_DIMENSIONS, v1, v1 ); if( mag <= 0.0 ) mag = 1.0; mag = sqrt( mag ); for_less( d, 0, VIO_N_DIMENSIONS ) v1_normalized[d] = v1[d] / mag; } /* ----------------------------- MNI Header ----------------------------------- @NAME : compute_world_transform @INPUT : spatial_axes separations translation_voxel world_space_for_translation_voxel direction_cosines @OUTPUT : world_transform @RETURNS : @DESCRIPTION: Computes the linear transform from the indices of the spatial dimensions (spatial_axes), the separations, the translation (translation_voxel,world_space_for_translation_voxel) and the direction cosines. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now uses starts/steps ---------------------------------------------------------------------------- */ VIOAPI void compute_world_transform( int spatial_axes[VIO_N_DIMENSIONS], VIO_Real separations[], VIO_Real direction_cosines[][VIO_N_DIMENSIONS], VIO_Real starts[], VIO_General_transform *world_transform ) { VIO_Transform transform; VIO_Real separations_3D[VIO_N_DIMENSIONS]; VIO_Real directions[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS]; VIO_Real starts_3D[VIO_N_DIMENSIONS]; VIO_Real normal[VIO_N_DIMENSIONS]; int dim, c, a1, a2, axis, n_axes; int axis_list[VIO_N_DIMENSIONS]; /*--- find how many direction cosines are specified, and set the 3d separations and starts */ n_axes = 0; for_less( c, 0, VIO_N_DIMENSIONS ) { axis = spatial_axes[c]; if( axis >= 0 ) { separations_3D[c] = separations[axis]; starts_3D[c] = starts[axis]; directions[c][VIO_X] = direction_cosines[axis][VIO_X]; directions[c][VIO_Y] = direction_cosines[axis][VIO_Y]; directions[c][VIO_Z] = direction_cosines[axis][VIO_Z]; axis_list[n_axes] = c; ++n_axes; } else { separations_3D[c] = 1.0; starts_3D[c] = 0.0; } } if( n_axes == 0 ) { print_error( "error compute_world_transform: no axes.\n" ); return; } /*--- convert 1 or 2 axes to 3 axes */ if( n_axes == 1 ) { a1 = (axis_list[0] + 1) % VIO_N_DIMENSIONS; a2 = (axis_list[0] + 2) % VIO_N_DIMENSIONS; /*--- create an orthogonal vector */ directions[a1][VIO_X] = directions[axis_list[0]][VIO_Y] + directions[axis_list[0]][VIO_Z]; directions[a1][VIO_Y] = -directions[axis_list[0]][VIO_X] - directions[axis_list[0]][VIO_Z]; directions[a1][VIO_Z] = directions[axis_list[0]][VIO_Y] - directions[axis_list[0]][VIO_X]; cross_3D_vector( directions[axis_list[0]], directions[a1], directions[a2] ); normalize_vector( directions[a1], directions[a1] ); normalize_vector( directions[a2], directions[a2] ); } else if( n_axes == 2 ) { a2 = VIO_N_DIMENSIONS - axis_list[0] - axis_list[1]; cross_3D_vector( directions[axis_list[0]], directions[axis_list[1]], directions[a2] ); normalize_vector( directions[a2], directions[a2] ); } /*--- check to make sure that 3 axes are not a singular system */ for_less( dim, 0, VIO_N_DIMENSIONS ) { cross_3D_vector( directions[dim], directions[(dim+1)%VIO_N_DIMENSIONS], normal ); if( normal[0] == 0.0 && normal[1] == 0.0 && normal[2] == 0.0 ) break; } if( dim < VIO_N_DIMENSIONS ) { directions[0][0] = 1.0; directions[0][1] = 0.0; directions[0][2] = 0.0; directions[1][0] = 0.0; directions[1][1] = 1.0; directions[1][2] = 0.0; directions[2][0] = 0.0; directions[2][1] = 0.0; directions[2][2] = 1.0; } /*--- make the linear transformation */ make_identity_transform( &transform ); for_less( c, 0, VIO_N_DIMENSIONS ) { for_less( dim, 0, VIO_N_DIMENSIONS ) { Transform_elem(transform,dim,c) = directions[c][dim] * separations_3D[c]; Transform_elem(transform,dim,3) += directions[c][dim] * starts_3D[c]; } } create_linear_transform( world_transform, &transform ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : check_recompute_world_transform @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Recompute the voxel to world transform. Called when one of the attributes affecting this is changed. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 20, 1997 D. MacDonald - now checks update flag ---------------------------------------------------------------------------- */ static void check_recompute_world_transform( VIO_Volume volume ) { VIO_General_transform world_transform; if( !volume->voxel_to_world_transform_uptodate ) { volume->voxel_to_world_transform_uptodate = TRUE; compute_world_transform( volume->spatial_axes, volume->separations, volume->direction_cosines, volume->starts, &world_transform ); assign_voxel_to_world_transform( volume, &world_transform ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_transform_origin_to_starts @INPUT : origin n_volume_dimensions spatial_axes dir_cosines @OUTPUT : starts @RETURNS : @DESCRIPTION: Converts a transform origin into starts (multiples of the dir_cosines). dir_cosines need not be mutually orthogonal @METHOD : @GLOBALS : @CALLS : @CREATED : May. 22, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ static void convert_transform_origin_to_starts( VIO_Real origin[], int n_volume_dimensions, int spatial_axes[], VIO_Real dir_cosines[][VIO_N_DIMENSIONS], VIO_Real starts[] ) { int axis, dim, which[VIO_N_DIMENSIONS], n_axes, i, j; VIO_Real o_dot_c, c_dot_c; VIO_Real x_dot_x, x_dot_y, x_dot_v, y_dot_y, y_dot_v, bottom; VIO_Real **matrix, solution[VIO_N_DIMENSIONS]; for_less( dim, 0, n_volume_dimensions ) starts[dim] = 0.0; /*--- get the list of valid axes (which) */ n_axes = 0; for_less( dim, 0, VIO_N_DIMENSIONS ) { axis = spatial_axes[dim]; if( axis >= 0 ) { which[n_axes] = axis; ++n_axes; } } /*--- get the starts: computed differently for 1, 2, or 3 axes */ if( n_axes == 1 ) { o_dot_c = dot_vectors( VIO_N_DIMENSIONS, origin, dir_cosines[which[0]] ); c_dot_c = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[0]], dir_cosines[which[0]] ); if( c_dot_c != 0.0 ) starts[which[0]] = o_dot_c / c_dot_c; } else if( n_axes == 2 ) { x_dot_x = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[0]], dir_cosines[which[0]] ); x_dot_v = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[0]], origin ); x_dot_y = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[0]], dir_cosines[which[1]] ); y_dot_y = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[1]], dir_cosines[which[1]] ); y_dot_v = dot_vectors( VIO_N_DIMENSIONS, dir_cosines[which[1]], origin ); bottom = x_dot_x * y_dot_y - x_dot_y * x_dot_y; if( bottom != 0.0 ) { starts[which[0]] = (x_dot_v * y_dot_y - x_dot_y * y_dot_v) / bottom; starts[which[1]] = (y_dot_v * x_dot_x - x_dot_y * x_dot_v) / bottom; } } else if( n_axes == 3 ) { /*--- this is the usual case, solve the equations to find what starts give the desired origin */ VIO_ALLOC2D( matrix, VIO_N_DIMENSIONS, VIO_N_DIMENSIONS ); for_less( i, 0, VIO_N_DIMENSIONS ) for_less( j, 0, VIO_N_DIMENSIONS ) { matrix[i][j] = dir_cosines[which[j]][i]; } if( solve_linear_system( VIO_N_DIMENSIONS, matrix, origin, solution ) ) { starts[which[0]] = solution[0]; starts[which[1]] = solution[1]; starts[which[2]] = solution[2]; } VIO_FREE2D( matrix ); } else { print_error( "Invalid number of axes in convert_transform_origin_to_starts\n"); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_transform_to_starts_and_steps @INPUT : transform separation_signs @OUTPUT : starts steps dir_cosines spatial_axes @RETURNS : @DESCRIPTION: Converts a linear transform to a set of 3 starts, 3 steps, and 3 direction cosines. The separation signs determine the desired signs of each of the separations. @METHOD : @GLOBALS : @CALLS : @CREATED : May. 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void convert_transform_to_starts_and_steps( VIO_General_transform *transform, int n_volume_dimensions, VIO_Real step_signs[], int spatial_axes[], VIO_Real starts[], VIO_Real steps[], VIO_Real dir_cosines[][VIO_N_DIMENSIONS] ) { VIO_Real sign, mag; int axis, dim; VIO_Real axes[VIO_N_DIMENSIONS][VIO_N_DIMENSIONS]; VIO_Real origin[VIO_N_DIMENSIONS]; VIO_Transform *linear_transform; if( get_transform_type( transform ) != LINEAR ) { print_error( "convert_transform_to_starts_and_steps(): non-linear transform found.\n" ); return; } linear_transform = get_linear_transform_ptr( transform ); get_transform_origin_real( linear_transform, origin ); get_transform_x_axis_real( linear_transform, &axes[VIO_X][0] ); get_transform_y_axis_real( linear_transform, &axes[VIO_Y][0] ); get_transform_z_axis_real( linear_transform, &axes[VIO_Z][0] ); /*--- assign default steps */ for_less( dim, 0, n_volume_dimensions ) steps[dim] = 1.0; /*--- assign the steps and dir_cosines for the spatial axes */ for_less( dim, 0, VIO_N_DIMENSIONS ) { axis = spatial_axes[dim]; if( axis >= 0 ) { mag = dot_vectors( VIO_N_DIMENSIONS, axes[dim], axes[dim] ); if( mag <= 0.0 ) mag = 1.0; mag = sqrt( mag ); if( step_signs == NULL ) { if( axes[dim][dim] < 0.0 ) sign = -1.0; else sign = 1.0; } else /*--- make the sign of steps match the step_signs passed in */ { if( step_signs[axis] < 0.0 ) sign = -1.0; else sign = 1.0; } steps[axis] = sign * mag; dir_cosines[axis][VIO_X] = axes[dim][VIO_X] / steps[axis]; dir_cosines[axis][VIO_Y] = axes[dim][VIO_Y] / steps[axis]; dir_cosines[axis][VIO_Z] = axes[dim][VIO_Z] / steps[axis]; } } /*--- finally, get the starts */ convert_transform_origin_to_starts( origin, n_volume_dimensions, spatial_axes, dir_cosines, starts ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_voxel_to_world_transform @INPUT : volume transform @OUTPUT : @RETURNS : @DESCRIPTION: Sets the volume's transformation from voxel to world coords. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - recomputes the starts/steps ---------------------------------------------------------------------------- */ VIOAPI void set_voxel_to_world_transform( VIO_Volume volume, VIO_General_transform *transform ) { assign_voxel_to_world_transform( volume, transform ); volume->voxel_to_world_transform_uptodate = TRUE; if( get_transform_type( transform ) == LINEAR ) { convert_transform_to_starts_and_steps( transform, get_volume_n_dimensions(volume), volume->separations, volume->spatial_axes, volume->starts, volume->separations, volume->direction_cosines ); } } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_voxel_to_world_transform @INPUT : @OUTPUT : @RETURNS : transform @DESCRIPTION: Returns a pointer to the voxel to world transform of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now delays recomputing transform ---------------------------------------------------------------------------- */ VIOAPI VIO_General_transform *get_voxel_to_world_transform( VIO_Volume volume ) { check_recompute_world_transform( volume ); return( &volume->voxel_to_world_transform ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_dimension_names @INPUT : volume @OUTPUT : @RETURNS : list of dimension names @DESCRIPTION: Creates a copy of the dimension names of the volume. Therefore, after use, the calling function must free the list, by calling delete_dimension_names(). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR *get_volume_dimension_names( VIO_Volume volume ) { int i; VIO_STR *names; ALLOC( names, get_volume_n_dimensions(volume) ); for_less( i, 0, get_volume_n_dimensions(volume) ) names[i] = create_string( volume->dimension_names[i] ); for_less( i, 0, VIO_N_DIMENSIONS ) { if( volume->spatial_axes[i] >= 0 ) { replace_string( &names[volume->spatial_axes[i]], create_string( convert_spatial_axis_to_dim_name(i)) ); } } return( names ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : delete_dimension_names @INPUT : volume, dimension_names @OUTPUT : @RETURNS : @DESCRIPTION: Frees the memory allocated to the dimension names, which came from the above function, get_volume_dimension_names(). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void delete_dimension_names( VIO_Volume volume, VIO_STR dimension_names[] ) { int i; for_less( i, 0, get_volume_n_dimensions(volume) ) delete_string( dimension_names[i] ); FREE( dimension_names ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_space_type @INPUT : volume @OUTPUT : @RETURNS : @DESCRIPTION: Returns a copy of the string representing the volume coordinate system name. The calling function must delete_string() the value when done. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 15, 1996 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_STR get_volume_space_type( VIO_Volume volume ) { return( create_string( volume->coordinate_system_name ) ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_space_type @INPUT : volume name @OUTPUT : @RETURNS : @DESCRIPTION: Copies the name into the volume's coordinate system name. Copies the string, rather than just the pointer. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 15, 1996 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_space_type( VIO_Volume volume, VIO_STR name ) { delete_string( volume->coordinate_system_name ); volume->coordinate_system_name = create_string( name ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_separations @INPUT : volume @OUTPUT : separations @RETURNS : @DESCRIPTION: Passes back the slice separations for each dimensions. Assumes separations contains enough room for n_dimensions VIO_Reals. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_separations( VIO_Volume volume, VIO_Real separations[] ) { int i; for_less( i, 0, get_volume_n_dimensions( volume ) ) separations[i] = volume->separations[i]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_separations @INPUT : volume separations @OUTPUT : @RETURNS : @DESCRIPTION: Sets the separations between slices for the given volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now delays recomputing transform ---------------------------------------------------------------------------- */ VIOAPI void set_volume_separations( VIO_Volume volume, VIO_Real separations[] ) { int i; for_less( i, 0, get_volume_n_dimensions( volume ) ) volume->separations[i] = separations[i]; volume->voxel_to_world_transform_uptodate = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_starts @INPUT : volume starts[] @OUTPUT : @RETURNS : @DESCRIPTION: Sets the translation portion of the voxel to world transform, by specifying the start vector, as specified by the MINC format. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - now delays recomputing transform ---------------------------------------------------------------------------- */ VIOAPI void set_volume_starts( VIO_Volume volume, VIO_Real starts[] ) { int c; for_less( c, 0, get_volume_n_dimensions( volume ) ) volume->starts[c] = starts[c]; volume->voxel_to_world_transform_uptodate = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_starts @INPUT : volume @OUTPUT : starts @RETURNS : @DESCRIPTION: Passes back the start vector of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_starts( VIO_Volume volume, VIO_Real starts[] ) { int c; for_less( c, 0, get_volume_n_dimensions( volume ) ) starts[c] = volume->starts[c]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_direction_unit_cosine @INPUT : volume axis dir @OUTPUT : @RETURNS : @DESCRIPTION: Sets the direction cosine for one axis, assumed to be unit length. @METHOD : @GLOBALS : @CALLS : @CREATED : May 20, 1997 David MacDonald ---------------------------------------------------------------------------- */ VIOAPI void set_volume_direction_unit_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ) { int dim; if( axis < 0 || axis >= get_volume_n_dimensions(volume) ) { print_error( "set_volume_direction_cosine: cannot set dir cosine for axis %d\n", axis ); return; } /*--- check if this is a spatial axis */ for_less( dim, 0, VIO_N_DIMENSIONS ) { if( volume->spatial_axes[dim] == axis ) break; } if( dim == VIO_N_DIMENSIONS ) /* this is not a spatial axis, ignore the dir */ return; volume->direction_cosines[axis][VIO_X] = dir[VIO_X]; volume->direction_cosines[axis][VIO_Y] = dir[VIO_Y]; volume->direction_cosines[axis][VIO_Z] = dir[VIO_Z]; volume->voxel_to_world_transform_uptodate = FALSE; } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_direction_cosine @INPUT : volume axis dir @OUTPUT : @RETURNS : @DESCRIPTION: Sets the direction cosine for one axis. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : May 20, 1997 D. MacDonald - split into set_volume_direction_unit_cosine ---------------------------------------------------------------------------- */ VIOAPI void set_volume_direction_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ) { VIO_Real len, unit_vector[VIO_N_DIMENSIONS]; len = dir[VIO_X] * dir[VIO_X] + dir[VIO_Y] * dir[VIO_Y] + dir[VIO_Z] * dir[VIO_Z]; if( len == 0.0 ) { print_error( "Warning: zero length direction cosine in set_volume_direction_cosine()\n" ); return; } if( len <= 0.0 ) len = 1.0; len = sqrt( len ); unit_vector[VIO_X] = dir[VIO_X] / len; unit_vector[VIO_Y] = dir[VIO_Y] / len; unit_vector[VIO_Z] = dir[VIO_Z] / len; set_volume_direction_unit_cosine( volume, axis, unit_vector ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_direction_cosine @INPUT : volume axis @OUTPUT : dir @RETURNS : @DESCRIPTION: Passes back the direction cosine corresponding to the given voxel axis, which must be a spatial dimension. @METHOD : @GLOBALS : @CALLS : @CREATED : Nov. 15, 1996 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_direction_cosine( VIO_Volume volume, int axis, VIO_Real dir[] ) { int d; if( axis < 0 || axis >= get_volume_n_dimensions(volume) ) { print_error( "get_volume_direction_cosine: cannot get dir cosine for axis %d\n", axis ); return; } for_less( d, 0, VIO_N_DIMENSIONS ) { if( volume->spatial_axes[d] == axis ) break; } if( d == VIO_N_DIMENSIONS ) /* this is not a spatial axis, ignore the dir */ { dir[VIO_X] = 0.0; dir[VIO_Y] = 0.0; dir[VIO_Z] = 0.0; } else { dir[VIO_X] = volume->direction_cosines[axis][VIO_X]; dir[VIO_Y] = volume->direction_cosines[axis][VIO_Y]; dir[VIO_Z] = volume->direction_cosines[axis][VIO_Z]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_translation @INPUT : volume voxel world_space_voxel_maps_to @OUTPUT : @RETURNS : @DESCRIPTION: Sets the translation portion of the volume so that the given voxel maps to the given world space position. Rewrote this to provide backwards compatibility. @METHOD : @GLOBALS : @CALLS : @CREATED : Aug. 31, 1997 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_translation( VIO_Volume volume, VIO_Real voxel[], VIO_Real world_space_voxel_maps_to[] ) { int dim, dim2, axis, n_axes, a1, a2; VIO_Real world_space_origin[VIO_N_DIMENSIONS], len; VIO_Real starts[VIO_MAX_DIMENSIONS], starts_3d[VIO_N_DIMENSIONS]; VIO_Transform transform, inverse; /*--- find the world position where ( 0, 0, 0 ) maps to by taking the world position - voxel[x_axis] * Xaxis - voxel[y_axis] * Yaxis ..., and fill in the transform defined by Xaxis, Yaxis, Zaxis */ make_identity_transform( &transform ); for_less( dim, 0, VIO_N_DIMENSIONS ) { world_space_origin[dim] = world_space_voxel_maps_to[dim]; for_less( dim2, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[dim2]; if( axis >= 0 ) { world_space_origin[dim] -= volume->separations[axis] * volume->direction_cosines[axis][dim] * voxel[axis]; Transform_elem( transform, dim, dim2 ) = volume->direction_cosines[axis][dim]; } } } n_axes = 0; for_less( dim, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[dim]; if( axis >= 0 ) ++n_axes; } /*--- if only one spatial axis, make a second orthogonal vector */ if( n_axes == 1 ) { /*--- set dim to the spatial axis */ if( volume->spatial_axes[0] >= 0 ) dim = 0; else if( volume->spatial_axes[1] >= 0 ) dim = 1; else if( volume->spatial_axes[2] >= 0 ) dim = 2; /*--- set a1 to the lowest occuring non-spatial axis, and create a unit vector normal to that of the spatial axis */ if( dim == 0 ) a1 = 1; else a1 = 0; Transform_elem( transform, 0, a1 ) = Transform_elem(transform,1,dim) + Transform_elem(transform,2,dim); Transform_elem( transform, 1, a1 ) = -Transform_elem(transform,0,dim) - Transform_elem(transform,2,dim); Transform_elem( transform, 2, a1 ) = Transform_elem(transform,1,dim) - Transform_elem(transform,0,dim); len = Transform_elem(transform,0,a1)*Transform_elem(transform,0,a1) + Transform_elem(transform,1,a1)*Transform_elem(transform,1,a1) + Transform_elem(transform,2,a1)*Transform_elem(transform,2,a1); if( len == 0.0 ) len = 1.0; else len = sqrt( len ); Transform_elem(transform,0,a1) /= len; Transform_elem(transform,1,a1) /= len; Transform_elem(transform,2,a1) /= len; } /*--- if only two spatial axis, make a third orthogonal vector */ if( n_axes == 1 || n_axes == 2 ) { /*--- set dim to the one axis that does not have a vector associated with it yet, and make one that is the unit cross product of the other two */ if( volume->spatial_axes[2] < 0 ) dim = 2; else if( volume->spatial_axes[1] < 0 ) dim = 1; else if( volume->spatial_axes[0] < 0 ) dim = 0; a1 = (dim + 1) % VIO_N_DIMENSIONS; a2 = (dim + 2) % VIO_N_DIMENSIONS; /*--- take cross product */ Transform_elem( transform, 0, dim ) = Transform_elem(transform,1,a1) * Transform_elem(transform,2,a2) - Transform_elem(transform,1,a2) * Transform_elem(transform,2,a1); Transform_elem( transform, 1, dim ) = Transform_elem(transform,2,a1) * Transform_elem(transform,0,a2) - Transform_elem(transform,2,a2) * Transform_elem(transform,0,a1); Transform_elem( transform, 2, dim ) = Transform_elem(transform,0,a1) * Transform_elem(transform,1,a2) - Transform_elem(transform,0,a2) * Transform_elem(transform,1,a1); /*--- normalize vector */ len = Transform_elem(transform,0,dim)*Transform_elem(transform,0,dim) + Transform_elem(transform,1,dim)*Transform_elem(transform,1,dim) + Transform_elem(transform,2,dim)*Transform_elem(transform,2,dim); if( len == 0.0 ) len = 1.0; else len = sqrt( len ); Transform_elem(transform,0,dim) /= len; Transform_elem(transform,1,dim) /= len; Transform_elem(transform,2,dim) /= len; } /*--- find the voxel that maps to the world space origin, when there is no translation, and this is the starts */ compute_transform_inverse( &transform, &inverse ); transform_point( &inverse, world_space_origin[VIO_X], world_space_origin[VIO_Y], world_space_origin[VIO_Z], &starts_3d[VIO_X], &starts_3d[VIO_Y], &starts_3d[VIO_Z] ); /*--- map the VIO_X VIO_Y VIO_Z starts into the arbitrary axis ordering of the volume */ for_less( dim, 0, get_volume_n_dimensions(volume) ) starts[dim] = 0.0; for_less( dim, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[dim]; if( axis >= 0 ) starts[axis] = starts_3d[dim]; } set_volume_starts( volume, starts ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_translation @INPUT : volume @OUTPUT : voxel - returns 0, 0, 0 ... world_space_voxel_maps_to - returns centre of voxel [0][0][0]... @RETURNS : @DESCRIPTION: Reinstated this old function for backward compatibility. Simply returns the voxel 0, 0, 0, and the world coordinate of its centre, to indicate the translational component of the transformation. @METHOD : @GLOBALS : @CALLS : @CREATED : May. 23, 1998 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_translation( VIO_Volume volume, VIO_Real voxel[], VIO_Real world_space_voxel_maps_to[] ) { int dim; for_less( dim, 0, get_volume_n_dimensions(volume) ) voxel[dim] = 0.0; convert_voxel_to_world( volume, voxel, &world_space_voxel_maps_to[VIO_X], &world_space_voxel_maps_to[VIO_Y], &world_space_voxel_maps_to[VIO_Z] ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : reorder_voxel_to_xyz @INPUT : volume voxel @OUTPUT : xyz @RETURNS : @DESCRIPTION: Passes back the voxel coordinates corresponding to the x, y, and z axes, if any. @METHOD : @GLOBALS : @CALLS : @CREATED : May 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void reorder_voxel_to_xyz( VIO_Volume volume, VIO_Real voxel[], VIO_Real xyz[] ) { int c, axis; for_less( c, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[c]; if( axis >= 0 ) xyz[c] = voxel[axis]; else xyz[c] = 0.0; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : reorder_xyz_to_voxel @INPUT : volume xyz @OUTPUT : voxel @RETURNS : @DESCRIPTION: Passes back the voxel coordinates converted from those corresponding to the x, y, and z axis. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void reorder_xyz_to_voxel( VIO_Volume volume, VIO_Real xyz[], VIO_Real voxel[] ) { int c, axis, n_dims; n_dims = get_volume_n_dimensions( volume ); for_less( c, 0, n_dims ) voxel[c] = 0.0; for_less( c, 0, VIO_N_DIMENSIONS ) { axis = volume->spatial_axes[c]; if( axis >= 0 ) voxel[axis] = xyz[c]; } } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_voxel_to_world @INPUT : volume x_voxel y_voxel z_voxel @OUTPUT : x_world y_world z_world @RETURNS : @DESCRIPTION: Converts the given voxel position to a world coordinate. Note that centre of first voxel corresponds to (0.0,0.0,0.0) in voxel coordinates. @CREATED : Mar 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - checks to recompute transform ---------------------------------------------------------------------------- */ VIOAPI void convert_voxel_to_world( VIO_Volume volume, VIO_Real voxel[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ) { VIO_Real xyz[VIO_N_DIMENSIONS]; check_recompute_world_transform( volume ); reorder_voxel_to_xyz( volume, voxel, xyz ); /* apply linear transform */ general_transform_point( &volume->voxel_to_world_transform, xyz[VIO_X], xyz[VIO_Y], xyz[VIO_Z], x_world, y_world, z_world ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_3D_voxel_to_world @INPUT : volume voxel1 voxel2 voxel3 @OUTPUT : x_world y_world z_world @RETURNS : @DESCRIPTION: Convenience function which performs same task as convert_voxel_to_world(), but for 3D volumes only. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void convert_3D_voxel_to_world( VIO_Volume volume, VIO_Real voxel1, VIO_Real voxel2, VIO_Real voxel3, VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ) { VIO_Real voxel[VIO_MAX_DIMENSIONS]; if( get_volume_n_dimensions(volume) != 3 ) { print_error( "convert_3D_voxel_to_world: Volume must be 3D.\n" ); return; } voxel[0] = voxel1; voxel[1] = voxel2; voxel[2] = voxel3; convert_voxel_to_world( volume, voxel, x_world, y_world, z_world ); } /* ----------------------------- MNI Header -----------------------------------@NAME : convert_voxel_normal_vector_to_world @INPUT : volume voxel_vector0 voxel_vector1 voxel_vector2 @OUTPUT : x_world y_world z_world @RETURNS : @DESCRIPTION: Converts a voxel vector to world coordinates. Assumes the vector is a normal vector (ie. a derivative), so transforms by transpose of inverse transform. @CREATED : Mar 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - checks to recompute transform ---------------------------------------------------------------------------- */ VIOAPI void convert_voxel_normal_vector_to_world( VIO_Volume volume, VIO_Real voxel_vector[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ) { VIO_Real xyz[VIO_N_DIMENSIONS]; VIO_Transform *inverse; check_recompute_world_transform( volume ); if( get_transform_type( &volume->voxel_to_world_transform ) != LINEAR ) handle_internal_error( "Cannot get normal vector of nonlinear xforms."); inverse = get_inverse_linear_transform_ptr( &volume->voxel_to_world_transform ); /* transform vector by transpose of inverse transformation */ reorder_voxel_to_xyz( volume, voxel_vector, xyz ); *x_world = Transform_elem(*inverse,0,0) * xyz[VIO_X] + Transform_elem(*inverse,1,0) * xyz[VIO_Y] + Transform_elem(*inverse,2,0) * xyz[VIO_Z]; *y_world = Transform_elem(*inverse,0,1) * xyz[VIO_X] + Transform_elem(*inverse,1,1) * xyz[VIO_Y] + Transform_elem(*inverse,2,1) * xyz[VIO_Z]; *z_world = Transform_elem(*inverse,0,2) * xyz[VIO_X] + Transform_elem(*inverse,1,2) * xyz[VIO_Y] + Transform_elem(*inverse,2,2) * xyz[VIO_Z]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_voxel_vector_to_world @INPUT : volume voxel_vector @OUTPUT : x_world y_world z_world @RETURNS : @DESCRIPTION: Converts a voxel vector to world coordinates. @CREATED : Mar 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void convert_voxel_vector_to_world( VIO_Volume volume, VIO_Real voxel_vector[], VIO_Real *x_world, VIO_Real *y_world, VIO_Real *z_world ) { int i; VIO_Real origin[VIO_MAX_DIMENSIONS], x0, y0, z0, x1, y1, z1; for_less( i, 0, VIO_MAX_DIMENSIONS ) origin[i] = 0.0; convert_voxel_to_world( volume, origin, &x0, &y0, &z0 ); convert_voxel_to_world( volume, voxel_vector, &x1, &y1, &z1 ); *x_world = x1 - x0; *y_world = y1 - y0; *z_world = z1 - z0; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_world_vector_to_voxel @INPUT : volume x_world y_world z_world @OUTPUT : voxel_vector @RETURNS : @DESCRIPTION: Converts a world vector to voxel coordinates. @CREATED : Mar 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void convert_world_vector_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real voxel_vector[] ) { int c; VIO_Real voxel[VIO_MAX_DIMENSIONS], origin[VIO_MAX_DIMENSIONS]; convert_world_to_voxel( volume, 0.0, 0.0, 0.0, origin ); convert_world_to_voxel( volume, x_world, y_world, z_world, voxel ); for_less( c, 0, get_volume_n_dimensions(volume) ) voxel_vector[c] = voxel[c] - origin[c]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_world_to_voxel @INPUT : volume x_world y_world z_world @OUTPUT : x_voxel y_voxel z_voxel @RETURNS : @DESCRIPTION: Converts from world coordinates to voxel coordinates. @CREATED : Mar 1993 David MacDonald @MODIFIED : May 22, 1997 D. MacDonald - checks to recompute transform ---------------------------------------------------------------------------- */ VIOAPI void convert_world_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real voxel[] ) { VIO_Real xyz[VIO_N_DIMENSIONS]; check_recompute_world_transform( volume ); general_inverse_transform_point( &volume->voxel_to_world_transform, x_world, y_world, z_world, &xyz[VIO_X], &xyz[VIO_Y], &xyz[VIO_Z] ); reorder_xyz_to_voxel( volume, xyz, voxel ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : convert_3D_world_to_voxel @INPUT : volume x_world y_world z_world @OUTPUT : voxel1 voxel2 voxel3 @RETURNS : @DESCRIPTION: Convenience function that does same task as convert_world_to_voxel(), but only for 3D volumes. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void convert_3D_world_to_voxel( VIO_Volume volume, VIO_Real x_world, VIO_Real y_world, VIO_Real z_world, VIO_Real *voxel1, VIO_Real *voxel2, VIO_Real *voxel3 ) { VIO_Real voxel[VIO_MAX_DIMENSIONS]; if( get_volume_n_dimensions(volume) != 3 ) { print_error( "convert_3D_world_to_voxel: Volume must be 3D.\n" ); return; } convert_world_to_voxel( volume, x_world, y_world, z_world, voxel ); *voxel1 = voxel[VIO_X]; *voxel2 = voxel[VIO_Y]; *voxel3 = voxel[VIO_Z]; } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_voxel_min @INPUT : volume @OUTPUT : @RETURNS : min valid voxel @DESCRIPTION: Returns the minimum valid voxel value. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_voxel_min( VIO_Volume volume ) { return( volume->voxel_min ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_voxel_max @INPUT : volume @OUTPUT : @RETURNS : max valid voxel @DESCRIPTION: Returns the maximum valid voxel value. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_voxel_max( VIO_Volume volume ) { return( volume->voxel_max ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_voxel_range @INPUT : volume @OUTPUT : voxel_min voxel_max @RETURNS : @DESCRIPTION: Passes back the min and max voxel values stored in the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_voxel_range( VIO_Volume volume, VIO_Real *voxel_min, VIO_Real *voxel_max ) { *voxel_min = get_volume_voxel_min( volume ); *voxel_max = get_volume_voxel_max( volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_voxel_range @INPUT : volume voxel_min voxel_max @OUTPUT : @RETURNS : @DESCRIPTION: Sets the valid range of voxels. If an invalid range is specified (voxel_min >= voxel_max), the full range of the volume's type is used. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_voxel_range( VIO_Volume volume, VIO_Real voxel_min, VIO_Real voxel_max ) { VIO_Real real_min = 0.0; VIO_Real real_max = 0.0; if( voxel_min >= voxel_max ) /*VF: trying to fix the situation when whole volume have the same value all around*/ { switch( get_volume_data_type( volume ) ) { case VIO_UNSIGNED_BYTE: voxel_min = 0.0; voxel_max = (VIO_Real) UCHAR_MAX; break; case VIO_SIGNED_BYTE: voxel_min = (VIO_Real) SCHAR_MIN; voxel_max = (VIO_Real) SCHAR_MAX; break; case VIO_UNSIGNED_SHORT: voxel_min = 0.0; voxel_max = (VIO_Real) USHRT_MAX; break; case VIO_SIGNED_SHORT: voxel_min = (VIO_Real) SHRT_MIN; voxel_max = (VIO_Real) SHRT_MAX; break; case VIO_UNSIGNED_INT: voxel_min = 0.0; voxel_max = (VIO_Real) UINT_MAX; break; case VIO_SIGNED_INT: voxel_min = (VIO_Real) INT_MIN; voxel_max = (VIO_Real) INT_MAX; break; case VIO_FLOAT: voxel_min = (VIO_Real) -FLT_MAX; voxel_max = (VIO_Real) FLT_MAX; break; default: case VIO_DOUBLE: voxel_min = (VIO_Real) -DBL_MAX; voxel_max = (VIO_Real) DBL_MAX; break; } } if( volume->real_range_set ) get_volume_real_range( volume, &real_min, &real_max ); volume->voxel_min = voxel_min; volume->voxel_max = voxel_max; if( volume->real_range_set ) set_volume_real_range( volume, real_min, real_max ); #ifdef HAVE_MINC1 else cache_volume_range_has_changed( volume ); #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_real_range @INPUT : volume @OUTPUT : min_value max_value @RETURNS : @DESCRIPTION: Passes back the minimum and maximum scaled values. These are the minimum and maximum stored voxel values scaled to the real value domain. @METHOD : @GLOBALS : @CALLS : @CREATED : June, 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void get_volume_real_range( VIO_Volume volume, VIO_Real *min_value, VIO_Real *max_value ) { *min_value = get_volume_real_min( volume ); *max_value = get_volume_real_max( volume ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_real_min @INPUT : volume @OUTPUT : @RETURNS : real range minimum @DESCRIPTION: Returns the minimum of the real range of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_real_min( VIO_Volume volume ) { VIO_Real real_min; real_min = get_volume_voxel_min( volume ); if( volume->real_range_set ) real_min = convert_voxel_to_value( volume, real_min ); return( real_min ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : get_volume_real_max @INPUT : volume @OUTPUT : @RETURNS : real range max @DESCRIPTION: Returns the maximum of the real range of the volume. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Real get_volume_real_max( VIO_Volume volume ) { VIO_Real real_max; real_max = get_volume_voxel_max( volume ); if( volume->real_range_set ) real_max = convert_voxel_to_value( volume, real_max ); return( real_max ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : set_volume_real_range @INPUT : volume real_min real_max @OUTPUT : @RETURNS : @DESCRIPTION: Sets the range of real values to which the valid voxel range maps @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI void set_volume_real_range( VIO_Volume volume, VIO_Real real_min, VIO_Real real_max ) { VIO_Real voxel_min, voxel_max; if( get_volume_data_type(volume) == VIO_FLOAT || get_volume_data_type(volume) == VIO_DOUBLE ) { /* as float and double use the voxel range */ volume->real_range_set = FALSE; set_volume_voxel_range( volume, real_min, real_max ); /*VF: fix stupid logic, which fails when volume has the same value*/ if(real_min==real_max) { volume->voxel_min = real_min; volume->voxel_max = real_max; } /* these really shouldn't be needed but let's be "safe" */ volume->real_value_scale = 1.0; volume->real_value_translation = 0.0; } else { get_volume_voxel_range( volume, &voxel_min, &voxel_max ); if( voxel_min < voxel_max ) { volume->real_value_scale = (real_max - real_min) / (voxel_max - voxel_min); volume->real_value_translation = real_min - voxel_min * volume->real_value_scale; /* We never really want a scale value of zero. */ if (real_max == real_min) { volume->real_value_scale = 1.0; } } else { // FIXME: is scale = 0 correct?? volume->real_value_scale = 0.0; volume->real_value_translation = real_min; } volume->real_range_set = TRUE; } #ifdef HAVE_MINC1 if( volume->is_cached_volume ) cache_volume_range_has_changed( volume ); #endif } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_volume_definition_no_alloc @INPUT : volume nc_data_type signed_flag voxel_min voxel_max @OUTPUT : @RETURNS : @DESCRIPTION: Copies the volume to a new volume, optionally changing type (if nc_data_type is not MI_ORIGINAL_TYPE), but not allocating the volume voxel data (alloc_volume_data() must subsequently be called). @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : Nov. 15, 1996 D. MacDonald - handles space type ---------------------------------------------------------------------------- */ VIOAPI VIO_Volume copy_volume_definition_no_alloc( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max) { int c, sizes[VIO_MAX_DIMENSIONS]; VIO_Real separations[VIO_MAX_DIMENSIONS]; VIO_Real starts[VIO_MAX_DIMENSIONS]; VIO_Real dir_cosine[VIO_N_DIMENSIONS]; VIO_Volume copy; if( nc_data_type == MI_ORIGINAL_TYPE ) { nc_data_type = volume->nc_data_type; signed_flag = volume->signed_flag; get_volume_voxel_range( volume, &voxel_min, &voxel_max ); } copy = create_volume( get_volume_n_dimensions(volume), volume->dimension_names, nc_data_type, signed_flag, voxel_min, voxel_max ); for_less( c, 0, VIO_N_DIMENSIONS ) copy->spatial_axes[c] = volume->spatial_axes[c]; set_volume_real_range( copy, get_volume_real_min(volume), get_volume_real_max(volume) ); get_volume_sizes( volume, sizes ); set_volume_sizes( copy, sizes ); get_volume_separations( volume, separations ); set_volume_separations( copy, separations ); get_volume_starts( volume, starts ); set_volume_starts( copy, starts ); for_less( c, 0, get_volume_n_dimensions(volume) ) { get_volume_direction_cosine( volume, c, dir_cosine ); set_volume_direction_unit_cosine( copy, c, dir_cosine ); } set_volume_space_type( copy, volume->coordinate_system_name ); for_less( c, 0, get_volume_n_dimensions(volume) ) { if (is_volume_dimension_irregular(volume, c)) { VIO_Real *irr_starts = malloc(sizeof(VIO_Real) * sizes[c]); VIO_Real *irr_widths = malloc(sizeof(VIO_Real) * sizes[c]); get_volume_irregular_starts( volume, c, sizes[c], irr_starts); set_volume_irregular_starts( volume, c, sizes[c], irr_starts); get_volume_irregular_widths( volume, c, sizes[c], irr_widths); set_volume_irregular_widths( volume, c, sizes[c], irr_widths); free( irr_starts ); free( irr_widths ); } } return( copy ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_volume_definition @INPUT : volume nc_data_type signed_flag voxel_min voxel_max @OUTPUT : @RETURNS : @DESCRIPTION: Copies the volume to a new volume, optionally changing type (if nc_data_type is not MI_ORIGINAL_TYPE), allocating the volume voxel data, but not initializing the data. @METHOD : @GLOBALS : @CALLS : @CREATED : 1993 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Volume copy_volume_definition( VIO_Volume volume, nc_type nc_data_type, VIO_BOOL signed_flag, VIO_Real voxel_min, VIO_Real voxel_max ) { VIO_Volume copy; copy = copy_volume_definition_no_alloc( volume, nc_data_type, signed_flag, voxel_min, voxel_max ); alloc_volume_data( copy ); if( !volume_is_alloced( copy ) ) { delete_volume( copy ); copy = NULL; } return( copy ); } /* ----------------------------- MNI Header ----------------------------------- @NAME : copy_volume @INPUT : volume @OUTPUT : @RETURNS : copy of volume @DESCRIPTION: Creates an exact copy of a volume, including voxel values. @METHOD : @GLOBALS : @CALLS : @CREATED : Jun 21, 1995 David MacDonald @MODIFIED : ---------------------------------------------------------------------------- */ VIOAPI VIO_Volume copy_volume( VIO_Volume volume ) { VIO_Volume copy; void *src = NULL, *dest = NULL; int d, n_voxels, sizes[VIO_MAX_DIMENSIONS]; if( volume->is_cached_volume ) { print_error( "copy_volume(): copying cached volumes not implemented.\n" ); return( NULL ); } copy = copy_volume_definition( volume, MI_ORIGINAL_TYPE, FALSE, 0.0, 0.0 ); if( !copy ) { return( NULL ); } /* --- find out how many voxels are in the volume */ get_volume_sizes( volume, sizes ); n_voxels = 1; for_less( d, 0, get_volume_n_dimensions(volume) ) n_voxels *= sizes[d]; /* --- get a pointer to the beginning of the voxels */ GET_VOXEL_PTR( src, volume, 0, 0, 0, 0, 0 ); GET_VOXEL_PTR( dest, copy, 0, 0, 0, 0, 0 ); /* --- assuming voxels are contiguous, copy them in one chunk */ (void) memcpy( dest, src, (size_t) n_voxels * (size_t) get_type_size( get_volume_data_type(volume)) ); return( copy ); } /* These are not public functions, so they are not VIOAPI yet */ VIO_BOOL is_volume_dimension_irregular(VIO_Volume volume, int idim) { if (idim > volume->array.n_dimensions) { return (0); } return (volume->irregular_starts[idim] != NULL); } long get_volume_irregular_starts(VIO_Volume volume, int idim, long count, VIO_Real *starts) { int i; if (idim >= volume->array.n_dimensions) { return (0); } if (volume->irregular_starts[idim] == NULL) { return (0); } if (count > volume->array.sizes[idim]) { count = volume->array.sizes[idim]; } for (i = 0; i < count; i++) { starts[i] = volume->irregular_starts[idim][i]; } return (count); } long get_volume_irregular_widths(VIO_Volume volume, int idim, long count, VIO_Real *widths) { int i; if (idim >= volume->array.n_dimensions) { return (0); } if (volume->irregular_widths[idim] == NULL) { return (0); } if (count > volume->array.sizes[idim]) { count = volume->array.sizes[idim]; } for (i = 0; i < count; i++) { widths[i] = volume->irregular_widths[idim][i]; } return (count); } long set_volume_irregular_starts(VIO_Volume volume, int idim, long count, VIO_Real *starts) { int i; if (idim >= volume->array.n_dimensions) { return (0); } if (volume->irregular_starts[idim] != NULL) { free(volume->irregular_starts[idim]); } if (starts == NULL) { return (0); } if (count > volume->array.sizes[idim]) { count = volume->array.sizes[idim]; } volume->irregular_starts[idim] = malloc(count * sizeof (VIO_Real)); if (volume->irregular_starts[idim] == NULL) { return (0); } for (i = 0; i < count; i++) { volume->irregular_starts[idim][i] = starts[i]; } return (count); } long set_volume_irregular_widths(VIO_Volume volume, int idim, long count, VIO_Real *widths) { int i; if (idim >= volume->array.n_dimensions) { return (0); } if (volume->irregular_widths[idim] != NULL) { free(volume->irregular_widths[idim]); } if (widths == NULL) { return (0); } if (count > volume->array.sizes[idim]) { count = volume->array.sizes[idim]; } volume->irregular_widths[idim] = malloc(count * sizeof (VIO_Real)); if (volume->irregular_widths[idim] == NULL) { return (0); } for (i = 0; i < count; i++) { volume->irregular_widths[idim][i] = widths[i]; } return (count); } VIOAPI VIO_Real nonspatial_voxel_to_world(VIO_Volume volume, int idim, int voxel) { VIO_Real world; if (is_volume_dimension_irregular(volume, idim)) { if (voxel < 0) { world = 0.0; } else if (voxel >= volume->array.sizes[idim]) { /* If we are asking for a position PAST the end of the axis, * return the very last position on the axis, defined as the * last start position plus the last width. * NOTE! TODO: FIXME! We should take the axis alignment into * account here. */ voxel = volume->array.sizes[idim] - 1; world = (volume->irregular_starts[idim][voxel] + volume->irregular_widths[idim][voxel]); } else { world = volume->irregular_starts[idim][voxel]; } } else { world = volume->starts[idim] + (voxel * volume->separations[idim]); } return (world); } VIOAPI long nonspatial_world_to_voxel(VIO_Volume volume, int idim, VIO_Real world) { long voxel; long i; if (is_volume_dimension_irregular(volume, idim)) { voxel = volume->array.sizes[idim]; for (i = 0; i < volume->array.sizes[idim]; i++) { if (world < (volume->irregular_starts[idim][i] + volume->irregular_widths[idim][i])) { voxel = i; break; } } } else { voxel = VIO_ROUND((world - volume->starts[idim]) / volume->separations[idim]); } return (voxel); } libminc-libminc-2-3-00/volume_io/example/000077500000000000000000000000001257462267400202725ustar00rootroot00000000000000libminc-libminc-2-3-00/volume_io/example/convert_volume_to_byte.c000066400000000000000000000035351257462267400252400ustar00rootroot00000000000000#include int main( int argc, char *argv[] ) { Volume volume; VIO_Status status; int x, y, z, sizes[N_DIMENSIONS]; char *input_filename, *output_filename, *history; double min_value, max_value, value, new_value, new_voxel; VIO_BOOL thresholding; if( argc < 3 ) { print( "Usage: %s input_volume output_volume [min max new_value]\n", argv[0] ); return( 1 ); } input_filename = argv[1]; output_filename = argv[2]; if( argc == 6 ) { (void) sscanf( argv[3], "%lf", &min_value ); (void) sscanf( argv[4], "%lf", &max_value ); (void) sscanf( argv[5], "%lf", &new_value ); thresholding = TRUE; } else thresholding = FALSE; status = input_volume( input_filename, 3, File_order_dimension_names, NC_UNSPECIFIED, FALSE, 0.0, 0.0, TRUE, &volume, (minc_input_options *) NULL ) ; if( status != VIO_OK ) return( 1 ); /* --- convert new_value to voxel */ new_voxel = CONVERT_VALUE_TO_VOXEL( volume, new_value ); get_volume_sizes( volume, sizes ); if( thresholding ) { for_less( x, 0, sizes[X] ) { for_less( y, 0, sizes[Y] ) { for_less( z, 0, sizes[Z] ) { GET_VALUE_3D( value, volume, x, y, z ); if( value < min_value || value > max_value ) { SET_VOXEL_3D( volume, x, y, z, new_voxel ); } } } } } history = "Converted volume to byte"; status = output_volume( output_filename, NC_BYTE, FALSE, 0.0, 255.0, volume, history, (minc_output_options *) NULL ); return( 0 ); }